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

563 lines
15 KiB
Markdown

# 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`
```typescript
// Change from: setInterval(loadWithCleanup, 10000)
// To: setInterval(loadWithCleanup, POLLING_INTERVALS.CRITICAL)
```
2. `src/components/features/admin/guest-requests.tsx`
```typescript
// Change from: setInterval(fetchWithCleanup, 30000)
// To: setInterval(fetchWithCleanup, POLLING_INTERVALS.CRITICAL)
// Also add real-time subscription
```
3. `src/components/features/admin/activity-feed.tsx`
```typescript
// Add real-time + use POLLING_INTERVALS.BACKGROUND
```
### 2.2 Add Real-Time to Guest Requests
**File:** `src/components/features/admin/guest-requests.tsx`
**Implementation:**
```typescript
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:**
```typescript
// 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 });
}
}
```
```typescript
// 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
```typescript
// 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
```typescript
// 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
```typescript
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
```typescript
// 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
- [x] RSVP → Master Guest List sync
- [x] Seating Chart real-time
- [x] Reports Summary real-time
- [x] Guest update/delete events
- [x] 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.