Files
thrillwiki_laravel/memory-bank/prompts/ReviewsListingPagePrompt.md

25 KiB

Reviews Listing Page Implementation Prompt

Django Parity Reference

Django Implementation: reviews/views.py - ReviewListView (similar patterns to other listing views) Django Template: reviews/templates/reviews/review_list.html Django Features: Social interaction display, sentiment analysis, review verification, context-aware filtering, real-time engagement metrics

Core Implementation Requirements

Laravel/Livewire Architecture

Generate the reviews listing system using ThrillWiki's custom generators:

# Generate main reviews listing with social interaction support
php artisan make:thrillwiki-livewire ReviewsListing --paginated --cached --with-tests

# Generate social interaction components
php artisan make:thrillwiki-livewire ReviewSocialInteractions --reusable --with-tests

# Generate sentiment analysis display
php artisan make:thrillwiki-livewire ReviewSentimentAnalysis --reusable --cached

# Generate review verification system
php artisan make:thrillwiki-livewire ReviewVerificationBadges --reusable --with-tests

# Generate context-aware filters
php artisan make:thrillwiki-livewire ReviewsContextFilters --reusable --cached

# Generate real-time engagement metrics
php artisan make:thrillwiki-livewire ReviewEngagementMetrics --reusable --with-tests

# Generate review quality indicators
php artisan make:thrillwiki-livewire ReviewQualityIndicators --reusable --cached

# Generate user credibility system
php artisan make:thrillwiki-livewire UserCredibilityBadges --reusable --with-tests

Django Parity Features

1. Social Review Search Functionality

Django Implementation: Multi-faceted search across:

  • Review content (content__icontains)
  • Reviewer username (user__username__icontains)
  • Reviewable entity (reviewable__name__icontains)
  • Review tags (tags__name__icontains)
  • Experience context (experience_context__icontains)
  • Visit verification status (verified_visit)

Laravel Implementation:

public function socialReviewSearch($query, $context = 'all')
{
    return Review::query()
        ->when($query, function ($q) use ($query) {
            $terms = explode(' ', $query);
            foreach ($terms as $term) {
                $q->where(function ($subQuery) use ($term) {
                    $subQuery->where('content', 'ilike', "%{$term}%")
                            ->orWhere('title', 'ilike', "%{$term}%")
                            ->orWhere('experience_context', 'ilike', "%{$term}%")
                            ->orWhereHas('user', function($userQuery) use ($term) {
                                $userQuery->where('username', 'ilike', "%{$term}%")
                                         ->orWhere('display_name', 'ilike', "%{$term}%");
                            })
                            ->orWhereHas('reviewable', function($entityQuery) use ($term) {
                                $entityQuery->where('name', 'ilike', "%{$term}%");
                            })
                            ->orWhereHas('tags', function($tagQuery) use ($term) {
                                $tagQuery->where('name', 'ilike', "%{$term}%");
                            });
                });
            }
        })
        ->when($context !== 'all', function ($q) use ($context) {
            $q->where('reviewable_type', $this->getModelClass($context));
        })
        ->with([
            'user' => fn($q) => $q->with(['profile', 'credibilityBadges']),
            'reviewable',
            'likes' => fn($q) => $q->with('user:id,username'),
            'comments' => fn($q) => $q->with('user:id,username')->limit(3),
            'tags',
            'verificationBadges'
        ])
        ->withCount(['likes', 'dislikes', 'comments', 'shares'])
        ->addSelect([
            'engagement_score' => DB::raw('(likes_count * 2 + comments_count * 3 + shares_count * 4)')
        ]);
}

2. Advanced Social Filtering

Django Filters:

  • Review rating (1-5 stars)
  • Verification status (verified, unverified, disputed)
  • Sentiment analysis (positive, neutral, negative)
  • Social engagement level (high, medium, low)
  • Review recency (last_day, last_week, last_month, last_year)
  • User credibility level (expert, trusted, verified, new)
  • Review context (solo_visit, group_visit, family_visit, enthusiast_visit)
  • Review completeness (photos, detailed, brief)

Laravel Filters Implementation:

public function applySocialFilters($query, $filters)
{
    return $query
        ->when($filters['rating_range'] ?? null, function ($q, $range) {
            [$min, $max] = explode('-', $range);
            $q->whereBetween('rating', [$min, $max]);
        })
        ->when($filters['verification_status'] ?? null, function ($q, $status) {
            switch ($status) {
                case 'verified':
                    $q->where('verified_visit', true);
                    break;
                case 'unverified':
                    $q->where('verified_visit', false);
                    break;
                case 'disputed':
                    $q->where('verification_disputed', true);
                    break;
            }
        })
        ->when($filters['sentiment'] ?? null, function ($q, $sentiment) {
            $sentimentRanges = [
                'positive' => [0.6, 1.0],
                'neutral' => [0.4, 0.6],
                'negative' => [0.0, 0.4]
            ];
            if (isset($sentimentRanges[$sentiment])) {
                $q->whereBetween('sentiment_score', $sentimentRanges[$sentiment]);
            }
        })
        ->when($filters['engagement_level'] ?? null, function ($q, $level) {
            $engagementThresholds = [
                'high' => 20,
                'medium' => 5,
                'low' => 0
            ];
            if (isset($engagementThresholds[$level])) {
                $q->havingRaw('(likes_count + comments_count + shares_count) >= ?', 
                             [$engagementThresholds[$level]]);
            }
        })
        ->when($filters['recency'] ?? null, function ($q, $recency) {
            $timeRanges = [
                'last_day' => now()->subDay(),
                'last_week' => now()->subWeek(),
                'last_month' => now()->subMonth(),
                'last_year' => now()->subYear()
            ];
            if (isset($timeRanges[$recency])) {
                $q->where('created_at', '>=', $timeRanges[$recency]);
            }
        })
        ->when($filters['user_credibility'] ?? null, function ($q, $credibility) {
            $q->whereHas('user', function ($userQuery) use ($credibility) {
                switch ($credibility) {
                    case 'expert':
                        $userQuery->whereHas('credibilityBadges', fn($badge) => 
                                   $badge->where('type', 'expert'));
                        break;
                    case 'trusted':
                        $userQuery->where('trust_score', '>=', 80);
                        break;
                    case 'verified':
                        $userQuery->whereNotNull('email_verified_at');
                        break;
                    case 'new':
                        $userQuery->where('created_at', '>=', now()->subMonths(3));
                        break;
                }
            });
        })
        ->when($filters['review_context'] ?? null, function ($q, $context) {
            $q->where('visit_context', $context);
        })
        ->when($filters['completeness'] ?? null, function ($q, $completeness) {
            switch ($completeness) {
                case 'photos':
                    $q->whereHas('photos');
                    break;
                case 'detailed':
                    $q->whereRaw('LENGTH(content) > 500');
                    break;
                case 'brief':
                    $q->whereRaw('LENGTH(content) <= 200');
                    break;
            }
        });
}

3. Real-Time Social Engagement Display

Social Metrics:

  • Like/dislike counts with user attribution
  • Comment threads with nested replies
  • Share counts across platforms
  • User credibility and verification badges
  • Sentiment analysis visualization
  • Engagement trend tracking

Screen-Agnostic Design Implementation

Mobile Layout (320px - 767px)

  • Social Review Cards: Compact cards with engagement metrics
  • Touch Interactions: Swipe-to-like, pull-to-refresh, tap interactions
  • Social Actions: Prominent like/comment/share buttons
  • User Attribution: Clear reviewer identification with badges

Mobile Component Structure:

<div class="reviews-mobile-layout">
    <!-- Social Search Bar -->
    <div class="sticky top-0 bg-white dark:bg-gray-900 z-20 p-4">
        <livewire:reviews-social-search />
        <div class="flex items-center mt-2 space-x-2">
            <button wire:click="filterByContext('park')" 
                    class="flex items-center space-x-1 px-3 py-1 {{ $activeContext === 'park' ? 'bg-blue-500 text-white' : 'bg-blue-100 dark:bg-blue-900' }} rounded-full">
                <span class="text-sm">Parks</span>
            </button>
            <button wire:click="filterByContext('ride')" 
                    class="flex items-center space-x-1 px-3 py-1 {{ $activeContext === 'ride' ? 'bg-green-500 text-white' : 'bg-green-100 dark:bg-green-900' }} rounded-full">
                <span class="text-sm">Rides</span>
            </button>
            <button wire:click="toggleVerifiedOnly" 
                    class="flex items-center space-x-1 px-2 py-1 {{ $verifiedOnly ? 'bg-orange-500 text-white' : 'bg-orange-100 dark:bg-orange-900' }} rounded-full">
                <svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
                    <path fill-rule="evenodd" d="M6.267 3.455a3.066 3.066 0 001.745-.723 3.066 3.066 0 013.976 0 3.066 3.066 0 001.745.723 3.066 3.066 0 012.812 2.812c.051.643.304 1.254.723 1.745a3.066 3.066 0 010 3.976 3.066 3.066 0 00-.723 1.745 3.066 3.066 0 01-2.812 2.812 3.066 3.066 0 00-1.745.723 3.066 3.066 0 01-3.976 0 3.066 3.066 0 00-1.745-.723 3.066 3.066 0 01-2.812-2.812 3.066 3.066 0 00-.723-1.745 3.066 3.066 0 010-3.976 3.066 3.066 0 00.723-1.745 3.066 3.066 0 012.812-2.812zm7.44 5.252a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
                </svg>
                <span class="text-xs">Verified</span>
            </button>
        </div>
    </div>
    
    <!-- Community Engagement Banner -->
    <div class="bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500 text-white p-4 m-4 rounded-lg">
        <livewire:reviews-community-stats :compact="true" />
    </div>
    
    <!-- Quick Filters -->
    <div class="horizontal-scroll p-4 pb-2">
        <livewire:reviews-quick-filters />
    </div>
    
    <!-- Review Cards -->
    <div class="space-y-4 p-4">
        @foreach($reviews as $review)
            <livewire:review-mobile-card :review="$review" :show-social="true" :key="$review->id" />
        @endforeach
    </div>
    
    <!-- Mobile Pagination -->
    <div class="sticky bottom-0 bg-white dark:bg-gray-900 p-4">
        {{ $reviews->links('pagination.mobile') }}
    </div>
</div>

Tablet Layout (768px - 1023px)

  • Social Stream Layout: Two-column review stream with engagement sidebar
  • Interactive Comments: Expandable comment threads
  • Multi-Touch Gestures: Pinch-to-zoom on photos, swipe between reviews
  • Social Activity Feed: Real-time updates on review interactions

Tablet Component Structure:

<div class="reviews-tablet-layout flex h-screen">
    <!-- Social Filter Sidebar -->
    <div class="w-80 bg-gray-50 dark:bg-gray-800 overflow-y-auto">
        <div class="p-6">
            <livewire:reviews-social-search :advanced="true" />
            <div class="mt-6">
                <livewire:reviews-context-filters :expanded="true" />
            </div>
            <div class="mt-6">
                <livewire:reviews-social-filters :show-engagement="true" />
            </div>
            <div class="mt-6">
                <livewire:reviews-community-stats :detailed="true" />
            </div>
        </div>
    </div>
    
    <!-- Main Content Area -->
    <div class="flex-1 flex flex-col">
        <!-- Social Header -->
        <div class="bg-white dark:bg-gray-900 p-4 border-b border-gray-200 dark:border-gray-700">
            <div class="flex items-center justify-between">
                <div class="flex items-center space-x-4">
                    <h2 class="text-xl font-semibold">{{ $reviews->total() }} Community Reviews</h2>
                    <livewire:reviews-engagement-overview />
                </div>
                <div class="flex items-center space-x-2">
                    <livewire:reviews-sort-selector />
                    <livewire:reviews-view-toggle />
                </div>
            </div>
        </div>
        
        <!-- Content Stream -->
        <div class="flex-1 overflow-y-auto p-6">
            @if($view === 'stream')
                <div class="space-y-6">
                    @foreach($reviews as $review)
                        <livewire:review-tablet-card :review="$review" :interactive="true" :key="$review->id" />
                    @endforeach
                </div>
            @elseif($view === 'sentiment')
                <livewire:reviews-sentiment-analysis :reviews="$reviews" />
            @else
                <livewire:reviews-engagement-dashboard :reviews="$reviews" />
            @endif
            
            <div class="mt-6">
                {{ $reviews->links() }}
            </div>
        </div>
    </div>
</div>

Desktop Layout (1024px - 1919px)

  • Three-Pane Social Layout: Filters + reviews + activity feed
  • Advanced Social Features: Real-time notifications, user following
  • Rich Interaction: Hover states, contextual menus, drag-and-drop
  • Community Moderation: Flagging, reporting, and moderation tools

Desktop Component Structure:

<div class="reviews-desktop-layout flex h-screen">
    <!-- Advanced Social Filters -->
    <div class="w-80 bg-gray-50 dark:bg-gray-800 overflow-y-auto">
        <div class="p-6">
            <livewire:reviews-social-search :advanced="true" :autocomplete="true" />
            <div class="mt-6">
                <livewire:reviews-context-filters :advanced="true" :show-statistics="true" />
            </div>
            <div class="mt-6">
                <livewire:reviews-social-filters :advanced="true" :show-engagement="true" />
            </div>
        </div>
    </div>
    
    <!-- Main Content -->
    <div class="flex-1 flex flex-col">
        <!-- Social Dashboard Header -->
        <div class="bg-white dark:bg-gray-900 p-6 border-b border-gray-200 dark:border-gray-700">
            <div class="flex items-center justify-between mb-4">
                <div class="flex items-center space-x-6">
                    <h1 class="text-2xl font-bold">{{ $reviews->total() }} Community Reviews</h1>
                    <livewire:reviews-social-summary />
                </div>
                <div class="flex items-center space-x-4">
                    <livewire:reviews-sort-selector :advanced="true" />
                    <livewire:reviews-view-selector />
                    <livewire:reviews-moderation-tools />
                </div>
            </div>
            <livewire:reviews-advanced-search />
        </div>
        
        <!-- Content Area -->
        <div class="flex-1 overflow-y-auto">
            @if($view === 'feed')
                <div class="p-6 space-y-6">
                    @foreach($reviews as $review)
                        <livewire:review-desktop-card :review="$review" :comprehensive="true" :key="$review->id" />
                    @endforeach
                    <div class="mt-8">
                        {{ $reviews->links('pagination.desktop') }}
                    </div>
                </div>
            @elseif($view === 'sentiment')
                <div class="p-6">
                    <livewire:reviews-sentiment-dashboard :reviews="$reviews" :interactive="true" />
                </div>
            @elseif($view === 'moderation')
                <div class="p-6">
                    <livewire:reviews-moderation-dashboard :reviews="$reviews" />
                </div>
            @else
                <div class="p-6">
                    <livewire:reviews-social-analytics :reviews="$reviews" />
                </div>
            @endif
        </div>
    </div>
    
    <!-- Social Activity Panel -->
    <div class="w-80 bg-gray-50 dark:bg-gray-800 overflow-y-auto">
        <div class="p-6">
            <livewire:reviews-social-activity />
            <div class="mt-6">
                <livewire:reviews-trending-topics />
            </div>
            <div class="mt-6">
                <livewire:reviews-featured-reviewers />
            </div>
        </div>
    </div>
</div>

Large Screen Layout (1920px+)

  • Dashboard-Style Social Interface: Comprehensive community analytics
  • Multi-Panel Views: Simultaneous review streams and analytics
  • Advanced Visualizations: Sentiment analysis charts and engagement networks
  • Community Management: Advanced moderation and user management tools

Performance Optimization Strategy

Social Engagement Caching

public function mount()
{
    $this->socialStats = Cache::remember(
        'reviews.social.stats',
        now()->addMinutes(15),
        fn() => $this->calculateSocialStatistics()
    );
    
    $this->trendingTopics = Cache::remember(
        'reviews.trending.topics',
        now()->addHours(1),
        fn() => $this->loadTrendingTopics()
    );
}

public function getReviewsProperty()
{
    $cacheKey = "reviews.listing." . md5(serialize([
        'search' => $this->search,
        'filters' => $this->filters,
        'context_filter' => $this->contextFilter,
        'sort' => $this->sort,
        'page' => $this->page,
        'user_id' => auth()->id() // For personalized content
    ]));
    
    return Cache::remember($cacheKey, now()->addMinutes(10), function() {
        return $this->socialReviewSearch($this->search, $this->contextFilter)
            ->applySocialFilters($this->filters)
            ->orderBy($this->sort['column'], $this->sort['direction'])
            ->paginate(12);
    });
}

Real-Time Social Features

// Optimized query for social engagement data
public function optimizedSocialQuery()
{
    return Review::select([
        'reviews.*',
        DB::raw('COALESCE(likes_count.count, 0) as likes_count'),
        DB::raw('COALESCE(comments_count.count, 0) as comments_count'),
        DB::raw('COALESCE(shares_count.count, 0) as shares_count'),
        DB::raw('(COALESCE(likes_count.count, 0) * 2 + 
                  COALESCE(comments_count.count, 0) * 3 + 
                  COALESCE(shares_count.count, 0) * 4) as engagement_score'),
        DB::raw('CASE 
            WHEN sentiment_score >= 0.6 THEN "positive"
            WHEN sentiment_score >= 0.4 THEN "neutral"
            ELSE "negative"
        END as sentiment_category')
    ])
    ->leftJoin(DB::raw('(SELECT review_id, COUNT(*) as count FROM review_likes GROUP BY review_id) as likes_count'), 
              'reviews.id', '=', 'likes_count.review_id')
    ->leftJoin(DB::raw('(SELECT review_id, COUNT(*) as count FROM review_comments GROUP BY review_id) as comments_count'), 
              'reviews.id', '=', 'comments_count.review_id')
    ->leftJoin(DB::raw('(SELECT review_id, COUNT(*) as count FROM review_shares GROUP BY review_id) as shares_count'), 
              'reviews.id', '=', 'shares_count.review_id')
    ->with([
        'user:id,username,display_name,avatar_url',
        'user.credibilityBadges:id,user_id,type,title',
        'reviewable:id,name,type',
        'verificationBadges:id,review_id,type,verified_at',
        'recentLikes' => fn($q) => $q->with('user:id,username')->limit(5),
        'topComments' => fn($q) => $q->with('user:id,username')->orderBy('likes_count', 'desc')->limit(3)
    ]);
}

Component Reuse Strategy

Shared Components

  • ReviewSocialInteractions: Like/comment/share functionality across all review contexts
  • ReviewVerificationBadges: Trust and verification indicators for authentic reviews
  • ReviewEngagementMetrics: Real-time engagement tracking and display
  • UserCredibilityBadges: User reputation and expertise indicators

Context Variations

  • ParkReviewsListing: Park-specific reviews with location context
  • RideReviewsListing: Ride-specific reviews with experience context
  • UserReviewsListing: User profile reviews with credibility focus
  • FeaturedReviewsListing: High-engagement reviews with community highlights

Testing Requirements

Feature Tests

/** @test */
public function can_filter_reviews_by_social_engagement()
{
    $highEngagement = Review::factory()->create(['content' => 'Amazing experience!']);
    $highEngagement->likes()->createMany(15, ['user_id' => User::factory()]);
    $highEngagement->comments()->createMany(8, ['user_id' => User::factory()]);
    
    $lowEngagement = Review::factory()->create(['content' => 'Okay ride']);
    $lowEngagement->likes()->create(['user_id' => User::factory()]);
    
    Livewire::test(ReviewsListing::class)
        ->set('filters.engagement_level', 'high')
        ->assertSee($highEngagement->content)
        ->assertDontSee($lowEngagement->content);
}

/** @test */
public function displays_user_credibility_correctly()
{
    $expertUser = User::factory()->create(['username' => 'expert_reviewer']);
    $expertUser->credibilityBadges()->create(['type' => 'expert', 'title' => 'Theme Park Expert']);
    
    $expertReview = Review::factory()->create([
        'user_id' => $expertUser->id,
        'content' => 'Professional analysis'
    ]);
    
    Livewire::test(ReviewsListing::class)
        ->assertSee('Theme Park Expert')
        ->assertSee($expertReview->content);
}

/** @test */
public function maintains_django_parity_performance_with_social_data()
{
    Review::factory()->count(30)->create();
    
    $start = microtime(true);
    Livewire::test(ReviewsListing::class);
    $end = microtime(true);
    
    $this->assertLessThan(0.5, $end - $start); // < 500ms with social data
}

Social Interaction Tests

/** @test */
public function calculates_engagement_scores_accurately()
{
    $review = Review::factory()->create();
    $review->likes()->createMany(10, ['user_id' => User::factory()]);
    $review->comments()->createMany(5, ['user_id' => User::factory()]);
    $review->shares()->createMany(2, ['user_id' => User::factory()]);
    
    $component = Livewire::test(ReviewsListing::class);
    $reviewData = $component->get('reviews')->first();
    
    // Engagement score = (likes * 2) + (comments * 3) + (shares * 4)
    $expectedScore = (10 * 2) + (5 * 3) + (2 * 4); // 43
    $this->assertEquals($expectedScore, $reviewData->engagement_score);
}

/** @test */
public function handles_real_time_social_updates()
{
    $review = Review::factory()->create();
    
    $component = Livewire::test(ReviewsListing::class);
    
    // Simulate real-time like
    $review->likes()->create(['user_id' => User::factory()->create()]);
    
    $component->call('refreshEngagement', $review->id)
              ->assertSee('1 like');
}

Performance Targets

Universal Performance Standards with Social Features

  • Initial Load: < 500ms (including engagement metrics)
  • Social Interaction Response: < 200ms for like/comment actions
  • Real-time Updates: < 100ms for engagement refresh
  • Sentiment Analysis: < 150ms for sentiment visualization
  • Community Statistics: < 100ms (cached)

Social Content Caching Strategy

  • Engagement Metrics: 10 minutes (frequently changing)
  • Trending Topics: 1 hour (community trends)
  • User Credibility: 6 hours (reputation changes slowly)
  • Social Statistics: 15 minutes (community activity)

Success Criteria Checklist

Django Parity Verification

  • Social review search matches Django behavior exactly
  • Engagement metrics calculated identically to Django
  • Verification systems work like Django implementation
  • Sentiment analysis provides same results as Django
  • Community features match Django social functionality

Screen-Agnostic Compliance

  • Mobile layout optimized for social interaction
  • Tablet layout provides effective community browsing
  • Desktop layout maximizes social engagement features
  • Large screen layout provides comprehensive community management
  • All layouts handle real-time social updates gracefully

Performance Benchmarks

  • Initial load under 500ms including social data
  • Social interactions under 200ms response time
  • Real-time updates under 100ms
  • Community statistics under 100ms (cached)
  • Social caching reduces server load by 70%

Social Feature Completeness

  • Engagement metrics display accurately across all contexts
  • User credibility systems provide meaningful trust indicators
  • Verification badges work for authentic experience validation
  • Community moderation tools function effectively
  • Real-time social updates work seamlessly across devices

This prompt ensures complete Django parity while providing comprehensive social review capabilities that foster authentic community engagement while maintaining ThrillWiki's screen-agnostic design principles.