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

@@ -1,7 +1,7 @@
// Transform platform GraphQL responses → frontend entity types
// Platform remaps field names during sync — this layer normalizes them
import type { Lead, Campaign, Ad, FollowUp, LeadActivity, Call, Patient } from '@/types/entities';
import type { Lead, Campaign, Ad, FollowUp, LeadActivity, Call, Patient, Appointment } from '@/types/entities';
type PlatformNode = Record<string, any>;
@@ -153,6 +153,25 @@ export function transformCalls(data: any): Call[] {
}));
}
export function transformAppointments(data: any): Appointment[] {
return extractEdges(data, 'appointments').map((n) => ({
id: n.id,
createdAt: n.createdAt,
scheduledAt: n.scheduledAt,
durationMinutes: n.durationMin ?? 30,
appointmentType: n.appointmentType,
appointmentStatus: n.status,
doctorName: n.doctorName,
doctorId: n.doctor?.id ?? null,
department: n.department,
reasonForVisit: n.reasonForVisit,
patientId: n.patient?.id ?? null,
patientName: n.patient?.fullName ? `${n.patient.fullName.firstName} ${n.patient.fullName.lastName}`.trim() : null,
patientPhone: n.patient?.phones?.primaryPhoneNumber ?? null,
clinicName: n.doctor?.clinic?.clinicName ?? null,
}));
}
export function transformPatients(data: any): Patient[] {
return extractEdges(data, 'patients').map((n) => ({
id: n.id,