fix: P2 defect batch + context-panel edit takeover

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>
This commit is contained in:
2026-04-15 11:48:39 +05:30
parent 5632f15031
commit d3cbf4d2bb
12 changed files with 66 additions and 52 deletions

View File

@@ -73,6 +73,7 @@ const dispositionConfig: Record<CallDisposition, { label: string; color: 'succes
WRONG_NUMBER: { label: 'Wrong Number', color: 'gray' },
NOT_INTERESTED: { label: 'Not Interested', color: 'error' },
CALLBACK_REQUESTED: { label: 'Callback', color: 'brand' },
CALL_DROPPED: { label: 'Call Dropped', color: 'gray' },
};
const DirectionIcon = ({ direction, status }: { direction: CallDirection | null; status: Call['callStatus'] }) => {

View File

@@ -43,6 +43,7 @@ const dispositionConfig: Record<CallDisposition, { label: string; color: 'succes
WRONG_NUMBER: { label: 'Wrong Number', 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 => {

View File

@@ -217,6 +217,7 @@ export const MyPerformancePage = () => {
],
barWidth: '50%',
itemStyle: { borderRadius: [4, 4, 0, 0] },
label: { show: true, position: 'top', fontSize: 11, color: '#344054', fontWeight: 600 },
}],
}}
style={{ height: 240 }}
@@ -244,8 +245,9 @@ export const MyPerformancePage = () => {
type: 'pie',
radius: ['45%', '70%'],
center: ['35%', '50%'],
avoidLabelOverlap: false,
label: { show: false },
avoidLabelOverlap: true,
label: { show: true, formatter: '{d}%', fontSize: 11, color: '#344054', fontWeight: 600 },
labelLine: { show: true, length: 6, length2: 6 },
data: Object.entries(data.dispositions).map(([name, value], i) => ({
name,
value,

View File

@@ -59,6 +59,7 @@ const DISPOSITION_COLORS: Record<CallDisposition, 'success' | 'brand' | 'blue' |
NO_ANSWER: 'warning',
NOT_INTERESTED: 'error',
CALLBACK_REQUESTED: 'gray',
CALL_DROPPED: 'gray',
};
const TABS = [

View File

@@ -234,8 +234,8 @@ export const TeamPerformancePage = () => {
xAxis: { type: 'category', data: days },
yAxis: { type: 'value' },
series: [
{ name: 'Inbound', type: 'line', data: days.map(d => dayMap[d].inbound), smooth: true, color: '#2060A0' },
{ name: 'Outbound', type: 'line', data: days.map(d => dayMap[d].outbound), smooth: true, color: '#E88C30' },
{ name: 'Inbound', type: 'line', data: days.map(d => dayMap[d].inbound), smooth: true, color: '#2060A0', label: { show: true, fontSize: 10, color: '#344054', fontWeight: 600, position: 'top' } },
{ name: 'Outbound', type: 'line', data: days.map(d => dayMap[d].outbound), smooth: true, color: '#E88C30', label: { show: true, fontSize: 10, color: '#344054', fontWeight: 600, position: 'top' } },
],
};
}, [allCalls]);