// Derive a flat meeting/booking list from the shared lead recordview. // One lead row carries at most one meeting (meeting_datetime + mode + address), // owned by owner_agent. No separate agent/meeting table exists — the agent // roster is whatever owner_agent values the leads carry. import { useMemo } from 'react'; import { useLeads } from './leads'; import type { LeadRecord } from './types'; export interface Meeting { leadId: string; leadName: string; /** Normalized agent display name, or 'Unassigned'. */ agent: string; agentId: number | null; /** ISO datetime of the slot. */ datetime: string; /** Raw mode key (video | phone | branch | home_visit | …). */ mode: string; /** Zoom link / branch name / address — may be empty. */ address?: string; /** current_state_name — where the lead sits in the lifecycle. */ stage: string; interest?: string; } const UNASSIGNED = 'Unassigned'; // owner_agent arrives as {id,name} (clean lookup rows), a bare string (legacy / // test rows), or null. Flatten to a stable {name,id}. function normAgent(raw: unknown): { name: string; id: number | null } { if (raw && typeof raw === 'object') { const o = raw as { id?: number; name?: string }; const name = (o.name ?? '').trim(); return { name: name || UNASSIGNED, id: typeof o.id === 'number' ? o.id : null }; } if (typeof raw === 'string') { const name = raw.trim(); return { name: name || UNASSIGNED, id: null }; } return { name: UNASSIGNED, id: null }; } function recordToMeeting(r: LeadRecord): Meeting | null { if (!r.meeting_datetime) return null; const dt = new Date(r.meeting_datetime); if (Number.isNaN(dt.getTime())) return null; const agent = normAgent(r.owner_agent); const address = typeof r.meeting_address === 'string' ? r.meeting_address.trim() : ''; return { leadId: String(r.instance_id), leadName: r.lead_name ?? `Lead ${r.instance_id}`, agent: agent.name, agentId: agent.id, datetime: r.meeting_datetime, mode: (r.meeting_mode ?? '').toLowerCase(), address: address || undefined, stage: r.current_state_name ?? '—', interest: r.product_interest ?? undefined, }; } export function useMeetings(): { meetings: Meeting[]; agents: string[]; loading: boolean; error: string | null } { const { records, loading, error } = useLeads(); return useMemo(() => { const meetings = records .map(recordToMeeting) .filter((m): m is Meeting => m !== null) .sort((a, b) => new Date(a.datetime).getTime() - new Date(b.datetime).getTime()); // Roster ordered by meeting load, Unassigned always last. const counts = new Map(); for (const m of meetings) counts.set(m.agent, (counts.get(m.agent) ?? 0) + 1); const agents = [...counts.keys()].sort((a, b) => { if (a === UNASSIGNED) return 1; if (b === UNASSIGNED) return -1; return (counts.get(b) ?? 0) - (counts.get(a) ?? 0); }); return { meetings, agents, loading, error }; }, [records, loading, error]); }