mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-05-18 20:08:19 +00:00
feat: Contacts page + P360 for all tabs + dynamic column toggle + slot flicker fix
Contacts page:
- New /contacts route — shows leads with source=PHONE/WALK_IN/REFERRAL
- Leads page now excludes those sources (campaign-sourced only)
- Sidebar: Contacts nav item added for all roles; Leads added for cc-agent
- Same LeadTable + pagination + CSV export pattern as All Leads
P360 context panel for all worklist tabs (#6-10):
- WorklistPanel: onSelectLead → onSelectItem (generic WorklistSelection)
- call-desk: handleSelectItem builds ContextPanelSubject for any row type
- ContextPanelSubject type replaces (lead as any).patientId casts
- Highlight tracks row.id (mc-*/fu-*/lead-*) not lead.id
Dynamic column toggle (blank-screen fix):
- missed-calls + call-recordings refactored to React Aria dynamic
collections API (Table.Header columns={} + Table.Row columns={})
- Fixes "Cell count must match column count" crash on column hide
- Row-header column metadata in columnDefs instead of hardcoded JSX
Slot flickering fix (#2):
- Removed clinic + timeSlot from slot-fetch useEffect deps (circular
loop: effect sets clinic → clinic in deps → re-fires)
- Memoized timeSlotSelectItems
Other:
- GlobalSearch hidden (stale appointment state on navigation)
- Branch column: shows campaign name from relation, falls back to DID
- formatSource maps PHONE → "Phone"
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -146,7 +146,12 @@ export const AppointmentForm = ({
|
||||
setTimeSlotItems(autoItems);
|
||||
}
|
||||
}).catch(() => setTimeSlotItems([]));
|
||||
}, [doctor, date, clinic, timeSlot]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps — clinic and timeSlot
|
||||
// deliberately excluded. Including clinic causes a loop: the effect calls
|
||||
// setClinic() for auto-selection → clinic changes → effect re-fires → loop.
|
||||
// timeSlot is only needed for the synthetic "current" option injection which
|
||||
// is a read, not a trigger. Re-fetch should only happen on doctor/date change.
|
||||
}, [doctor, date]);
|
||||
|
||||
// Availability state
|
||||
const [bookedSlots, setBookedSlots] = useState<string[]>([]);
|
||||
@@ -256,11 +261,11 @@ export const AppointmentForm = ({
|
||||
return items;
|
||||
}, [filteredDoctors, doctors, doctor]);
|
||||
|
||||
const timeSlotSelectItems = timeSlotItems.map(slot => ({
|
||||
const timeSlotSelectItems = useMemo(() => timeSlotItems.map(slot => ({
|
||||
...slot,
|
||||
isDisabled: bookedSlots.includes(slot.id),
|
||||
label: bookedSlots.includes(slot.id) ? `${slot.label} (Booked)` : slot.label,
|
||||
}));
|
||||
})), [timeSlotItems, bookedSlots]);
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!date || !timeSlot || !doctor || !department) {
|
||||
|
||||
Reference in New Issue
Block a user