- Build complete Next.js CRM for commercial real estate - Add authentication with JWT sessions and role-based access - Add GoHighLevel API integration for contacts, conversations, opportunities - Add AI-powered Control Center with tool calling - Add Setup page with onboarding checklist (/setup) - Add sidebar navigation with Setup menu item - Fix type errors in onboarding API, GHL services, and control center tools - Add Prisma schema with SQLite for local development - Add UI components with clay morphism design system Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
65 lines
1.7 KiB
TypeScript
65 lines
1.7 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useCallback, useRef } from 'react';
|
|
import { RealtimeEvent, RealtimeMessage } from '@/lib/realtime/events';
|
|
|
|
type EventHandler<T = any> = (data: T) => void;
|
|
|
|
export function useRealtime() {
|
|
const eventSourceRef = useRef<EventSource | null>(null);
|
|
const handlersRef = useRef<Map<RealtimeEvent, Set<EventHandler>>>(new Map());
|
|
|
|
useEffect(() => {
|
|
// Connect to SSE endpoint
|
|
const eventSource = new EventSource('/api/v1/realtime/events');
|
|
eventSourceRef.current = eventSource;
|
|
|
|
eventSource.onmessage = (event) => {
|
|
try {
|
|
const message: RealtimeMessage = JSON.parse(event.data);
|
|
|
|
// Skip connection message
|
|
if ((message as any).type === 'connected') {
|
|
console.log('[Realtime] Connected');
|
|
return;
|
|
}
|
|
|
|
// Dispatch to handlers
|
|
const handlers = handlersRef.current.get(message.event);
|
|
if (handlers) {
|
|
handlers.forEach(handler => handler(message.data));
|
|
}
|
|
} catch (error) {
|
|
console.error('[Realtime] Parse error:', error);
|
|
}
|
|
};
|
|
|
|
eventSource.onerror = (error) => {
|
|
console.error('[Realtime] Connection error:', error);
|
|
// EventSource will auto-reconnect
|
|
};
|
|
|
|
return () => {
|
|
eventSource.close();
|
|
eventSourceRef.current = null;
|
|
};
|
|
}, []);
|
|
|
|
const subscribe = useCallback(<T = any>(
|
|
event: RealtimeEvent,
|
|
handler: EventHandler<T>
|
|
): (() => void) => {
|
|
if (!handlersRef.current.has(event)) {
|
|
handlersRef.current.set(event, new Set());
|
|
}
|
|
handlersRef.current.get(event)!.add(handler);
|
|
|
|
// Return unsubscribe function
|
|
return () => {
|
|
handlersRef.current.get(event)?.delete(handler);
|
|
};
|
|
}, []);
|
|
|
|
return { subscribe };
|
|
}
|