feat: Contacts page + P360 for all tabs + dynamic column toggle + slot flicker fix

Contacts page:
 - New /contacts route — shows leads with source=PHONE/WALK_IN/REFERRAL
 - Leads page now excludes those sources (campaign-sourced only)
 - Sidebar: Contacts nav item added for all roles; Leads added for cc-agent
 - Same LeadTable + pagination + CSV export pattern as All Leads

P360 context panel for all worklist tabs (#6-10):
 - WorklistPanel: onSelectLead → onSelectItem (generic WorklistSelection)
 - call-desk: handleSelectItem builds ContextPanelSubject for any row type
 - ContextPanelSubject type replaces (lead as any).patientId casts
 - Highlight tracks row.id (mc-*/fu-*/lead-*) not lead.id

Dynamic column toggle (blank-screen fix):
 - missed-calls + call-recordings refactored to React Aria dynamic
   collections API (Table.Header columns={} + Table.Row columns={})
 - Fixes "Cell count must match column count" crash on column hide
 - Row-header column metadata in columnDefs instead of hardcoded JSX

Slot flickering fix (#2):
 - Removed clinic + timeSlot from slot-fetch useEffect deps (circular
   loop: effect sets clinic → clinic in deps → re-fires)
 - Memoized timeSlotSelectItems

Other:
 - GlobalSearch hidden (stale appointment state on navigation)
 - Branch column: shows campaign name from relation, falls back to DID
 - formatSource maps PHONE → "Phone"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-16 16:54:30 +05:30
parent c22d82f8c5
commit ca482e731e
12 changed files with 524 additions and 237 deletions

View File

@@ -15,7 +15,7 @@ import { useAuth } from '@/providers/auth-provider';
import { useData } from '@/providers/data-provider';
import { useMaintShortcuts } from '@/hooks/use-maint-shortcuts';
import { useNetworkStatus } from '@/hooks/use-network-status';
import { GlobalSearch } from '@/components/shared/global-search';
// import { GlobalSearch } from '@/components/shared/global-search';
import { apiClient } from '@/lib/api-client';
import { cx } from '@/utils/cx';
@@ -121,7 +121,11 @@ export const AppShell = ({ children }: AppShellProps) => {
{/* Persistent top bar — visible on all pages */}
{(hasAgentConfig || isAdmin) && (
<div className="flex shrink-0 items-center gap-2 border-b border-secondary px-4 py-2">
<GlobalSearch />
{/* GlobalSearch hidden — navigation on result click
routes to Patient 360 with stale appointment state
from the call desk. Revisit when the Patient 360
route properly resets context on mount. (#4) */}
{/* <GlobalSearch /> */}
<div className="ml-auto flex items-center gap-2">
{isAdmin && <NotificationBell />}
{hasAgentConfig && (