Files
moyosapp_beta.0.0.3.3_beta1/REALTIME_IMPLEMENTATION_GUIDE.md
moyoza 0e17c61740 feat: Implement real-time event handling for guest and table management
- Added real-time event emissions for guest creation, update, and deletion across various API routes.
- Enhanced guest request submission to include real-time notifications.
- Introduced real-time updates for table creation to keep the admin dashboard synchronized.
- Improved error handling for real-time event emissions to ensure reliability during guest and table operations.
2026-01-27 00:46:10 +02:00

15 KiB

Real-Time Data Synchronization - Complete Implementation Guide

📋 Executive Summary

Status: Critical fixes completed. Remaining tasks documented for phased implementation.

Key Achievements:

  • RSVP → Master Guest List sync fixed
  • Seating Chart real-time updates added
  • Reports Summary real-time updates added
  • Guest update/delete events implemented
  • Polling intervals standardized

Remaining Work:

  • ⚠️ Table management routes need POST/PUT/DELETE handlers
  • ⚠️ Some components still use hardcoded polling intervals
  • ⚠️ Guest requests need real-time events

🔍 Current Architecture

Real-Time Infrastructure

  • Technology: Server-Sent Events (SSE) via /api/realtime
  • Transport: EventSource API (browser-native, Next.js compatible)
  • Backend: Redis Pub/Sub for multi-instance support
  • Fallback: In-memory broadcast if Redis unavailable
  • Auto-reconnect: Built into EventSource

Event Flow

API Route (data change)
  ↓
RealtimeManager.sendEvent()
  ↓
POST /api/realtime
  ↓
Redis Pub/Sub (if available)
  ↓
SSE Clients (/api/realtime GET)
  ↓
Client Components (subscribe)
  ↓
UI Updates

Phase 1: Critical Fixes (COMPLETED)

1.1 RSVP Status Sync

Problem: RSVP confirmations didn't update Master Guest List

Solution:

  • Added rsvpStatus field updates in RSVP routes
  • Added refreshGuestList() call in onRSVPUpdate handler
  • Added real-time events with rsvpStatus in payload

Files Modified:

  • src/app/api/rsvp/submit/route.ts
  • src/app/api/rsvp/update/route.ts
  • src/app/api/admin/rsvp/on-behalf/route.ts
  • src/components/features/admin/admin-dashboard.tsx

1.2 Seating Chart Real-Time

Problem: Seating assignments required manual refresh

Solution:

  • Added subscriptions to seating, guest, and table events
  • Auto-refresh on table assignment changes

Files Modified:

  • src/components/features/admin/seating-chart-enhanced.tsx

1.3 Reports Summary Real-Time

Problem: Reports showed stale data

Solution:

  • Added subscriptions to rsvp, guest, seating, and table events
  • Auto-refresh when any relevant data changes

Files Modified:

  • src/components/features/admin/reports-exports.tsx

1.4 Guest Events

Problem: Guest updates/deletions didn't emit events

Solution:

  • Added guest.create, guest.update, guest.delete events
  • Added bulk operation events

Files Modified:

  • src/app/api/admin/guests/route.ts (POST, PUT, DELETE)
  • src/app/api/admin/guests/bulk-update/route.ts
  • src/app/api/admin/guests/bulk-delete/route.ts

⚠️ Phase 2: Standardization (TODO)

2.1 Polling Intervals

Created: src/lib/polling-intervals.ts

Files to Update:

  1. src/components/features/dashboard/guest-directory.tsx

    // Change from: setInterval(loadWithCleanup, 10000)
    // To: setInterval(loadWithCleanup, POLLING_INTERVALS.CRITICAL)
    
  2. src/components/features/admin/guest-requests.tsx

    // Change from: setInterval(fetchWithCleanup, 30000)
    // To: setInterval(fetchWithCleanup, POLLING_INTERVALS.CRITICAL)
    // Also add real-time subscription
    
  3. src/components/features/admin/activity-feed.tsx

    // Add real-time + use POLLING_INTERVALS.BACKGROUND
    

2.2 Add Real-Time to Guest Requests

File: src/components/features/admin/guest-requests.tsx

Implementation:

useEffect(() => {
  const { realtimeManager } = require('@/lib/realtime');
  realtimeManager.connect();
  
  const unsubscribe = realtimeManager.subscribe('guest-request', (event) => {
    if (event.action === 'create' || event.action === 'update') {
      fetchWithCleanup();
    }
  });
  
  return () => unsubscribe();
}, []);

Also need: Add events to src/app/api/admin/guest-requests/route.ts (if POST/PUT handlers exist)


⚠️ Phase 3: Missing API Routes (TODO)

3.1 Table Management Routes

Issue: Component calls POST/PATCH/DELETE on /api/admin/tables, but routes don't exist

Files to Create/Update:

  • src/app/api/admin/tables/route.ts - Add POST handler
  • src/app/api/admin/tables/[id]/route.ts - Add PATCH and DELETE handlers

Implementation Example:

// src/app/api/admin/tables/route.ts

// POST - Create table
export async function POST(request: NextRequest) {
  try {
    const session = await verifyAdminSession(request);
    if (!session || !session.authenticated) {
      return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
    }

    const body = await request.json();
    const { name, capacity } = body;

    const newTable = await prisma.table.create({
      data: { name, capacity: capacity || 8 },
    });

    // Emit real-time event
    try {
      const { RealtimeManager } = await import('@/lib/realtime');
      await RealtimeManager.sendEvent({
        type: 'table',
        action: 'create',
        data: {
          tableId: newTable.id,
          name: newTable.name,
          capacity: newTable.capacity,
        },
        timestamp: new Date().toISOString(),
      });
    } catch (error) {
      console.error('Failed to emit table create event:', error);
    }

    return NextResponse.json({ success: true, table: newTable }, { status: 201 });
  } catch (error) {
    return NextResponse.json({ error: "Failed to create table" }, { status: 500 });
  }
}
// src/app/api/admin/tables/[id]/route.ts

// PATCH - Update table
export async function PATCH(
  request: NextRequest,
  { params }: { params: Promise<{ id: string }> }
) {
  try {
    const session = await verifyAdminSession(request);
    if (!session || !session.authenticated) {
      return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
    }

    const { id } = await params;
    const body = await request.json();
    const { name, capacity } = body;

    const updatedTable = await prisma.table.update({
      where: { id },
      data: { name, capacity },
    });

    // Emit real-time event
    try {
      const { RealtimeManager } = await import('@/lib/realtime');
      await RealtimeManager.sendEvent({
        type: 'table',
        action: 'update',
        data: {
          tableId: updatedTable.id,
          name: updatedTable.name,
          capacity: updatedTable.capacity,
        },
        timestamp: new Date().toISOString(),
      });
    } catch (error) {
      console.error('Failed to emit table update event:', error);
    }

    return NextResponse.json({ success: true, table: updatedTable });
  } catch (error) {
    return NextResponse.json({ error: "Failed to update table" }, { status: 500 });
  }
}

// DELETE - Delete table
export async function DELETE(
  request: NextRequest,
  { params }: { params: Promise<{ id: string }> }
) {
  try {
    const session = await verifyAdminSession(request);
    if (!session || !session.authenticated) {
      return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
    }

    const { id } = await params;

    const deletedTable = await prisma.table.delete({
      where: { id },
    });

    // Emit real-time event
    try {
      const { RealtimeManager } = await import('@/lib/realtime');
      await RealtimeManager.sendEvent({
        type: 'table',
        action: 'delete',
        data: {
          tableId: deletedTable.id,
          name: deletedTable.name,
        },
        timestamp: new Date().toISOString(),
      });
    } catch (error) {
      console.error('Failed to emit table delete event:', error);
    }

    return NextResponse.json({ success: true, message: "Table deleted" });
  } catch (error) {
    return NextResponse.json({ error: "Failed to delete table" }, { status: 500 });
  }
}

📊 Component Real-Time Status

Admin Dashboard Components

Component Real-Time Polling Status
Master Guest List RSVP, Guest FIXED
RSVP Section RSVP Working
Stats RSVP 60s Working
Seating Chart Seating, Guest, Table FIXED
Reports Summary RSVP, Guest, Seating, Table FIXED
Music Requests Music Working
Gallery Gallery Working
Guestbook Guestbook Working
Tributes Tribute Working
Guest Requests 30s ⚠️ TODO
Activity Feed 30s ⚠️ TODO
Who's Who RSVP 30s ⚠️ Could remove polling

Guest Dashboard Components

Component Real-Time Polling Status
Guest Profile RSVP, Seating 30s Working
Table Reveal Seating Working
Music Music 30s Working
Gallery ⚠️ Should add real-time
Guestbook Guestbook 30s Working
Guest Directory RSVP, Guest 10s ⚠️ Should use 30s
Who's Who RSVP Working

🎯 Implementation Priority

COMPLETED (Phase 1)

  1. RSVP → Master Guest List sync
  2. Seating Chart real-time
  3. Reports Summary real-time
  4. Guest update/delete events
  5. Polling intervals constants

🔄 IN PROGRESS (Phase 2)

  1. ⚠️ Update polling intervals to use constants
  2. ⚠️ Add real-time to Guest Requests
  3. ⚠️ Add real-time to Activity Feed

📋 PLANNED (Phase 3)

  1. ⚠️ Add table management routes (POST/PATCH/DELETE)
  2. ⚠️ Add table events
  3. ⚠️ Add guest-request events

🚀 FUTURE (Phase 4)

  1. ⚠️ Create unified useRealtimeSync hook
  2. ⚠️ Refactor components to use unified hook
  3. ⚠️ Add optimistic updates
  4. ⚠️ Add connection status indicators

📝 Code Examples

Example 1: Adding Real-Time to a Component

// Before
useEffect(() => {
  fetchData();
  const interval = setInterval(fetchData, 30000);
  return () => clearInterval(interval);
}, []);

// After
import { POLLING_INTERVALS } from '@/lib/polling-intervals';

useEffect(() => {
  fetchData();
  
  // Real-time subscription
  const { realtimeManager } = require('@/lib/realtime');
  realtimeManager.connect();
  
  const unsubscribe = realtimeManager.subscribe('your-event-type', (event) => {
    if (event.action === 'create' || event.action === 'update') {
      fetchData();
    }
  });
  
  // Polling fallback
  const interval = setInterval(fetchData, POLLING_INTERVALS.CRITICAL);
  
  return () => {
    unsubscribe();
    clearInterval(interval);
  };
}, []);

Example 2: Emitting Events from API Routes

// After data modification
try {
  const { RealtimeManager } = await import('@/lib/realtime');
  await RealtimeManager.sendEvent({
    type: 'your-event-type',
    action: 'create' | 'update' | 'delete',
    data: {
      // Include relevant data for clients
      id: updatedItem.id,
      // ... other fields
    },
    timestamp: new Date().toISOString(),
  });
} catch (error) {
  // Don't fail the API call if real-time fails
  console.error('Failed to emit real-time event:', error);
}

Example 3: Debounced Real-Time Updates

useEffect(() => {
  const { realtimeManager } = require('@/lib/realtime');
  realtimeManager.connect();
  
  let debounceTimeout: NodeJS.Timeout | null = null;
  
  const unsubscribe = realtimeManager.subscribe('event-type', (event) => {
    // Clear existing timeout
    if (debounceTimeout) clearTimeout(debounceTimeout);
    
    // Debounce rapid updates
    debounceTimeout = setTimeout(() => {
      fetchData();
    }, 1000); // 1 second debounce
  });
  
  return () => {
    if (debounceTimeout) clearTimeout(debounceTimeout);
    unsubscribe();
  };
}, []);

🧪 Testing Guide

Manual Testing

  1. RSVP Sync Test:

    • Open admin dashboard in two browser windows
    • In window 1: Go to RSVP section
    • In window 2: Go to Master Guest List
    • Have a guest confirm RSVP
    • Verify: Master Guest List updates within 1-2 seconds
  2. Seating Chart Test:

    • Open seating chart
    • Assign guest to table (in another window or via API)
    • Verify: Seating chart updates automatically
  3. Reports Test:

    • Open Reports & Exports
    • Have guest confirm/decline RSVP
    • Verify: Headcount numbers update automatically

Automated Testing

// Example test for real-time sync
describe('RSVP → Master Guest List Sync', () => {
  it('should update guest list when RSVP is confirmed', async () => {
    // 1. Mock real-time event
    // 2. Trigger RSVP confirmation
    // 3. Verify guest list refresh was called
    // 4. Verify guest status updated
  });
});

🔧 Troubleshooting

Issue: Real-Time Not Working

Check:

  1. SSE connection established? (Check Network tab for /api/realtime)
  2. Redis available? (Check server logs)
  3. Events being emitted? (Check API route logs)
  4. Components subscribed? (Check component useEffect)

Issue: Too Many Refreshes

Solution: Increase debounce time or add more specific event filtering

Issue: Stale Data

Solution:

  1. Check cache invalidation tags
  2. Verify real-time events include all necessary data
  3. Ensure polling fallback is working

📈 Performance Considerations

Current Optimizations

  • Debouncing (500ms-2000ms)
  • Event filtering (only subscribe to needed events)
  • Parallel event emission (bulk operations)
  • Non-blocking (events don't fail API calls)

Recommendations

  • Monitor SSE connection count
  • Track event emission rate
  • Consider batching events for bulk operations
  • Add connection pooling if needed

🎓 Best Practices

  1. Always use debouncing for rapid updates
  2. Keep polling as fallback - real-time can fail
  3. Don't block API responses - emit events asynchronously
  4. Include relevant data in events - avoid unnecessary refetches
  5. Clean up subscriptions - prevent memory leaks
  6. Handle errors gracefully - real-time failures shouldn't break features

📚 Reference

  • Real-Time Manager: src/lib/realtime.ts
  • SSE Endpoint: src/app/api/realtime/route.ts
  • Admin Realtime Hook: src/hooks/use-admin-realtime.ts
  • Polling Constants: src/lib/polling-intervals.ts
  • Full Audit: REALTIME_AUDIT.md

Completion Checklist

Critical Fixes

  • RSVP → Master Guest List sync
  • Seating Chart real-time
  • Reports Summary real-time
  • Guest update/delete events
  • Polling intervals constants

Standardization

  • Update all polling intervals
  • Add real-time to Guest Requests
  • Add real-time to Activity Feed

Missing Routes

  • Add table POST/PATCH/DELETE routes
  • Add table events
  • Add guest-request events

Enhancement

  • Create unified hook
  • Refactor components
  • Add connection status UI
  • Performance testing

Last Updated: Based on comprehensive audit and critical fixes implementation.