feat: worklist sorting, contextual disposition, context panel redesign, notifications

- Worklist default sort descending (newest first), sortable column headers (PRIORITY, PATIENT, SLA) via React Aria
- Contextual disposition: auto-selects based on in-call actions (appointment → APPOINTMENT_BOOKED, enquiry → INFO_PROVIDED, transfer → FOLLOW_UP_SCHEDULED)
- Context panel redesign: collapsible AI Insight, Upcoming (appointments + follow-ups + linked patient), Recent (calls + activities) sections; auto-collapse on AI chat start
- Appointments added to DataProvider with APPOINTMENTS_QUERY, Appointment type, transform
- Notification bell for admin/supervisor: performance alerts (idle time, NPS, conversion thresholds) with toast on load + bell dropdown with dismiss; demo alerts as fallback
- Slideout z-index fix: added z-50 to slideout ModalOverlay matching modal component

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-30 14:45:52 +05:30
parent 0477064b3e
commit c3c3f4b3d7
18 changed files with 882 additions and 389 deletions

View File

@@ -8,6 +8,7 @@ import {
FOLLOW_UPS_QUERY,
LEAD_ACTIVITIES_QUERY,
CALLS_QUERY,
APPOINTMENTS_QUERY,
PATIENTS_QUERY,
} from '@/lib/queries';
import {
@@ -17,10 +18,11 @@ import {
transformFollowUps,
transformLeadActivities,
transformCalls,
transformAppointments,
transformPatients,
} from '@/lib/transforms';
import type { Lead, Campaign, Ad, LeadActivity, FollowUp, WhatsAppTemplate, Agent, Call, LeadIngestionSource, Patient } from '@/types/entities';
import type { Lead, Campaign, Ad, LeadActivity, FollowUp, WhatsAppTemplate, Agent, Call, LeadIngestionSource, Patient, Appointment } from '@/types/entities';
type DataContextType = {
leads: Lead[];
@@ -31,6 +33,7 @@ type DataContextType = {
templates: WhatsAppTemplate[];
agents: Agent[];
calls: Call[];
appointments: Appointment[];
patients: Patient[];
ingestionSources: LeadIngestionSource[];
loading: boolean;
@@ -63,6 +66,7 @@ export const DataProvider = ({ children }: DataProviderProps) => {
const [followUps, setFollowUps] = useState<FollowUp[]>([]);
const [leadActivities, setLeadActivities] = useState<LeadActivity[]>([]);
const [calls, setCalls] = useState<Call[]>([]);
const [appointments, setAppointments] = useState<Appointment[]>([]);
const [patients, setPatients] = useState<Patient[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
@@ -84,13 +88,14 @@ export const DataProvider = ({ children }: DataProviderProps) => {
try {
const gql = <T,>(query: string) => apiClient.graphql<T>(query, undefined, { silent: true }).catch(() => null);
const [leadsData, campaignsData, adsData, followUpsData, activitiesData, callsData, patientsData] = await Promise.all([
const [leadsData, campaignsData, adsData, followUpsData, activitiesData, callsData, appointmentsData, patientsData] = await Promise.all([
gql<any>(LEADS_QUERY),
gql<any>(CAMPAIGNS_QUERY),
gql<any>(ADS_QUERY),
gql<any>(FOLLOW_UPS_QUERY),
gql<any>(LEAD_ACTIVITIES_QUERY),
gql<any>(CALLS_QUERY),
gql<any>(APPOINTMENTS_QUERY),
gql<any>(PATIENTS_QUERY),
]);
@@ -100,6 +105,7 @@ export const DataProvider = ({ children }: DataProviderProps) => {
if (followUpsData) setFollowUps(transformFollowUps(followUpsData));
if (activitiesData) setLeadActivities(transformLeadActivities(activitiesData));
if (callsData) setCalls(transformCalls(callsData));
if (appointmentsData) setAppointments(transformAppointments(appointmentsData));
if (patientsData) setPatients(transformPatients(patientsData));
} catch (err: any) {
setError(err.message ?? 'Failed to load data');
@@ -122,7 +128,7 @@ export const DataProvider = ({ children }: DataProviderProps) => {
return (
<DataContext.Provider value={{
leads, campaigns, ads, followUps, leadActivities, templates, agents, calls, patients, ingestionSources,
leads, campaigns, ads, followUps, leadActivities, templates, agents, calls, appointments, patients, ingestionSources,
loading, error,
updateLead, addCall, refresh: fetchData,
}}>