mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-05-18 20:08:19 +00:00
Layout (P1-adjacent):
- context-panel switches to an edit-only layout when editingAppointment
is set. Previously AppointmentForm rendered inline BELOW the AI panel,
crushing the AI area into a ~2-line strip that made the returning-
patient summary + quick actions unusable. Edit view gets full height
with a "Back to context" button.
P2s:
- Remove Attempted sub-tab from Missed Calls worklist (Pending only).
- Add CALL_DROPPED disposition option + propagate through every
per-disposition Record<CallDisposition,...> map (incoming-call-card,
call-log, call-history, agent-detail, patient-360).
- Block SLA-gaming on unanswered calls: Book Appt / Enquiry / Transfer
buttons on active-call-card are disabled until the call reaches the
answered state (wasAnsweredRef). The disposition filter was already
in place; this closes the upstream entry.
- Data labels on performance charts: my-performance bar chart shows
value on top of each bar; donut shows {d}% slice labels; team-
performance day trend line shows per-point values.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
86 lines
4.0 KiB
TypeScript
86 lines
4.0 KiB
TypeScript
import type { FC } from 'react';
|
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
import { faArrowRight } from '@fortawesome/pro-duotone-svg-icons';
|
|
import { Badge } from '@/components/base/badges/badges';
|
|
import { Button } from '@/components/base/buttons/button';
|
|
|
|
const ArrowRight: FC<{ className?: string }> = ({ className }) => <FontAwesomeIcon icon={faArrowRight} className={className} />;
|
|
import { formatShortDate } from '@/lib/format';
|
|
import type { Call, CallDisposition } from '@/types/entities';
|
|
|
|
interface CallLogProps {
|
|
calls: Call[];
|
|
}
|
|
|
|
const dispositionConfig: Record<CallDisposition, { label: string; color: 'success' | 'brand' | 'blue-light' | 'warning' | 'gray' | 'error' }> = {
|
|
APPOINTMENT_BOOKED: { label: 'Booked', color: 'success' },
|
|
APPOINTMENT_RESCHEDULED: { label: 'Rescheduled', color: 'warning' },
|
|
APPOINTMENT_CANCELLED: { label: 'Cancelled', color: 'error' },
|
|
FOLLOW_UP_SCHEDULED: { label: 'Follow-up', color: 'brand' },
|
|
INFO_PROVIDED: { label: 'Info', color: 'blue-light' },
|
|
NO_ANSWER: { label: 'No Answer', color: 'warning' },
|
|
WRONG_NUMBER: { label: 'Wrong #', color: 'gray' },
|
|
NOT_INTERESTED: { label: 'Not Interested', color: 'error' },
|
|
CALLBACK_REQUESTED: { label: 'Callback', color: 'brand' },
|
|
CALL_DROPPED: { label: 'Call Dropped', color: 'gray' },
|
|
};
|
|
|
|
const formatDuration = (seconds: number | null): string => {
|
|
if (seconds === null || seconds === 0) return '0 min';
|
|
const minutes = Math.round(seconds / 60);
|
|
return `${minutes} min`;
|
|
};
|
|
|
|
export const CallLog = ({ calls }: CallLogProps) => {
|
|
return (
|
|
<div className="rounded-2xl border border-secondary bg-primary">
|
|
<div className="flex items-center justify-between border-b border-secondary px-5 py-4">
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-sm font-bold text-primary">Today's Calls</span>
|
|
<Badge size="sm" color="gray">{calls.length}</Badge>
|
|
</div>
|
|
</div>
|
|
|
|
{calls.length > 0 ? (
|
|
<div className="divide-y divide-secondary">
|
|
{calls.map((call) => {
|
|
const config = call.disposition !== null
|
|
? dispositionConfig[call.disposition]
|
|
: null;
|
|
|
|
return (
|
|
<div
|
|
key={call.id}
|
|
className="flex items-center gap-3 px-5 py-3"
|
|
>
|
|
<span className="w-20 shrink-0 text-xs text-quaternary">
|
|
{call.startedAt !== null ? formatShortDate(call.startedAt) : '—'}
|
|
</span>
|
|
<span className="min-w-0 flex-1 truncate text-sm font-medium text-primary">
|
|
{call.leadName ?? call.callerNumber?.[0]?.number ?? 'Unknown'}
|
|
</span>
|
|
{config !== null && (
|
|
<Badge size="sm" color={config.color}>{config.label}</Badge>
|
|
)}
|
|
<span className="w-12 shrink-0 text-right text-xs text-quaternary">
|
|
{formatDuration(call.durationSeconds)}
|
|
</span>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
) : (
|
|
<div className="flex items-center justify-center py-8">
|
|
<p className="text-sm text-quaternary">No calls handled today</p>
|
|
</div>
|
|
)}
|
|
|
|
<div className="border-t border-secondary px-5 py-3">
|
|
<Button href="/call-history" color="link-color" size="sm" iconTrailing={ArrowRight}>
|
|
View Full History
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|