feat: appointments v2 + patients redesign + call history agent filter + datepicker placement

Appointments v2:
 - Lean 6-column table (eye icon, patient 2-line, date+time 2-line,
   doctor+dept 2-line, status badge, reminder button)
 - Detail side panel on eye click (read-only: all fields + patient phone
   via PhoneActionCell)
 - Reschedule flow: pencil in panel → modal confirm → dedicated
   ReschedulePanel with department/doctor/date/slot/complaint fields
 - Cancel flow: modal confirm before cancelling
 - WhatsApp reminder button for upcoming booked appointments
 - DatePicker popoverPlacement prop for narrow panels (opens upward)

Patients page redesign:
 - Phone column uses PhoneActionCell (clickable to dial)
 - Email split into own column
 - Actions column replaced by hamburger menu (SMS + WhatsApp)
 - View (eye) button removed — row click opens profile panel

Call History agent filter:
 - Missed calls excluded from agent's personal history
 - Chain name parsing for agent matching
 - "Missed" filter option hidden for agents
 - Subtitle: "134 completed" (no "0 missed")

DatePicker:
 - New popoverPlacement prop forwarded to AriaPopover
 - Default "bottom start", use "top start" in constrained panels

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-16 20:51:57 +05:30
parent df08bcfc19
commit dd8e05b343
5 changed files with 800 additions and 58 deletions

View File

@@ -26,13 +26,19 @@ import type { Call, CallDirection, CallDisposition } from '@/types/entities';
type FilterKey = 'all' | 'inbound' | 'outbound' | 'missed';
const filterItems = [
const allFilterItems = [
{ id: 'all' as const, label: 'All Calls' },
{ id: 'inbound' as const, label: 'Inbound' },
{ id: 'outbound' as const, label: 'Outbound' },
{ id: 'missed' as const, label: 'Missed' },
];
const agentFilterItems = [
{ id: 'all' as const, label: 'All Calls' },
{ id: 'inbound' as const, label: 'Inbound' },
{ id: 'outbound' as const, label: 'Outbound' },
];
const dispositionConfig: Record<CallDisposition, { label: string; color: 'success' | 'brand' | 'blue-light' | 'warning' | 'gray' | 'error' }> = {
APPOINTMENT_BOOKED: { label: 'Appt Booked', color: 'success' },
APPOINTMENT_RESCHEDULED: { label: 'Appt Rescheduled', color: 'warning' },
@@ -188,7 +194,7 @@ export const CallHistoryPage = () => {
<TableCard.Header
title={isAdmin ? 'Call History' : 'My Call History'}
badge={String(filteredCalls.length)}
description={`${completedCount} completed \u00B7 ${missedCount} missed`}
description={isAdmin ? `${completedCount} completed \u00B7 ${missedCount} missed` : `${completedCount} completed`}
contentTrailing={
<div className="flex items-center gap-2">
<div className="w-44">
@@ -197,7 +203,7 @@ export const CallHistoryPage = () => {
placeholder="All Calls"
selectedKey={filter}
onSelectionChange={(key) => setFilter(key as FilterKey)}
items={filterItems}
items={isAdmin ? allFilterItems : agentFilterItems}
aria-label="Filter calls"
>
{(item) => (