mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-05-18 20:08:19 +00:00
fix: P1 defect batch — hide Decline button, remove No Campaign pill, remove Remind column
- active-call-card: Decline button hidden (reject returns call to Ozonetel queue, product says not needed for now) - all-leads: removed "No Campaign" pill and __none__ filter logic - appointments-v2: removed REMIND column header + cell + unused handleSendReminder, isUpcoming, buildReminderMessage, formatDateTime Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -41,7 +41,7 @@ const formatDuration = (seconds: number): string => {
|
||||
|
||||
export const ActiveCallCard = ({ lead, callerPhone, missedCallId, onCallComplete }: ActiveCallCardProps) => {
|
||||
const { user } = useAuth();
|
||||
const { callState, callDuration, callUcid, isMuted, isOnHold, answer, reject, hangup, toggleMute, toggleHold } = useSip();
|
||||
const { callState, callDuration, callUcid, isMuted, isOnHold, answer, hangup, toggleMute, toggleHold } = useSip();
|
||||
const setCallState = useSetAtom(sipCallStateAtom);
|
||||
const setCallerNumber = useSetAtom(sipCallerNumberAtom);
|
||||
const setCallUcid = useSetAtom(sipCallUcidAtom);
|
||||
@@ -248,7 +248,7 @@ export const ActiveCallCard = ({ lead, callerPhone, missedCallId, onCallComplete
|
||||
</div>
|
||||
<div className="mt-3 flex gap-2">
|
||||
<Button size="sm" color="primary" onClick={answer}>Answer</Button>
|
||||
<Button size="sm" color="tertiary-destructive" onClick={reject}>Decline</Button>
|
||||
{/* Decline hidden per product — reject returns call to Ozonetel queue */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -146,9 +146,7 @@ export const AllLeadsPage = () => {
|
||||
result = result.filter((l) => l.assignedAgent === user.name);
|
||||
}
|
||||
if (campaignFilter) {
|
||||
result = campaignFilter === '__none__'
|
||||
? result.filter((l) => !l.campaignId)
|
||||
: result.filter((l) => l.campaignId === campaignFilter);
|
||||
result = result.filter((l) => l.campaignId === campaignFilter);
|
||||
}
|
||||
return result;
|
||||
}, [sortedLeads, myLeadsOnly, user.name, campaignFilter]);
|
||||
@@ -320,17 +318,6 @@ export const AllLeadsPage = () => {
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
<button
|
||||
onClick={() => { setCampaignFilter(campaignFilter === '__none__' ? null : '__none__'); setCurrentPage(1); }}
|
||||
className={cx(
|
||||
'shrink-0 rounded-full px-3 py-1 text-xs font-medium transition duration-100 ease-linear',
|
||||
campaignFilter === '__none__'
|
||||
? 'bg-brand-solid text-white'
|
||||
: 'bg-secondary text-tertiary hover:text-secondary hover:bg-secondary_hover',
|
||||
)}
|
||||
>
|
||||
No Campaign ({filteredLeads.filter(l => !l.campaignId).length})
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import {
|
||||
faMagnifyingGlass, faPenToSquare, faEye, faBell, faXmark,
|
||||
faMagnifyingGlass, faPenToSquare, faEye, faXmark,
|
||||
faCalendarCheck, faUserDoctor, faBuilding, faStethoscope, faNotesMedical,
|
||||
} from '@fortawesome/pro-duotone-svg-icons';
|
||||
import { faIcon } from '@/lib/icon-wrapper';
|
||||
@@ -77,9 +77,6 @@ const QUERY = `{ appointments(first: 200, orderBy: [{ scheduledAt: DescNullsLast
|
||||
doctor { id fullName { firstName lastName } }
|
||||
} } } }`;
|
||||
|
||||
const formatDateTime = (iso: string): string =>
|
||||
`${formatDateOnly(iso)}, ${formatTimeOnly(iso)}`;
|
||||
|
||||
const getPatientName = (appt: AppointmentRecord): string => {
|
||||
if (!appt.patient?.fullName) return 'Unknown';
|
||||
return `${appt.patient.fullName.firstName} ${appt.patient.fullName.lastName}`.trim() || 'Unknown';
|
||||
@@ -88,25 +85,11 @@ const getPatientName = (appt: AppointmentRecord): string => {
|
||||
const getPhone = (appt: AppointmentRecord): string =>
|
||||
appt.patient?.phones?.primaryPhoneNumber ?? '';
|
||||
|
||||
const isUpcoming = (appt: AppointmentRecord): boolean => {
|
||||
if (appt.status !== 'SCHEDULED' && appt.status !== 'CONFIRMED') return false;
|
||||
if (!appt.scheduledAt) return false;
|
||||
return new Date(appt.scheduledAt).getTime() >= Date.now();
|
||||
};
|
||||
|
||||
// Can edit/reschedule: anything that isn't completed or cancelled
|
||||
const canEdit = (appt: AppointmentRecord): boolean => {
|
||||
return appt.status !== 'COMPLETED' && appt.status !== 'CANCELLED' && appt.status !== 'NO_SHOW';
|
||||
};
|
||||
|
||||
const buildReminderMessage = (appt: AppointmentRecord): string => {
|
||||
const name = getPatientName(appt);
|
||||
const doctor = appt.doctorName ?? 'your doctor';
|
||||
const date = appt.scheduledAt ? formatDateTime(appt.scheduledAt) : 'your scheduled time';
|
||||
const branch = appt.clinic?.clinicName ?? 'our clinic';
|
||||
return `Hi ${name}, this is a reminder for your appointment with ${doctor} on ${date} at ${branch}. Please confirm or call us to reschedule.`;
|
||||
};
|
||||
|
||||
// ── Detail Panel ─────────────────────────────────────────────────
|
||||
const DetailRow = ({ icon, label, value }: { icon: any; label: string; value: string }) => (
|
||||
<div className="flex items-start gap-3 py-2.5">
|
||||
@@ -533,13 +516,6 @@ export const AppointmentsPageV2 = () => {
|
||||
setRescheduleOpen(false);
|
||||
};
|
||||
|
||||
const handleSendReminder = (appt: AppointmentRecord) => {
|
||||
const phone = getPhone(appt);
|
||||
if (!phone) return;
|
||||
const msg = encodeURIComponent(buildReminderMessage(appt));
|
||||
window.open(`https://wa.me/91${phone}?text=${msg}`, '_blank');
|
||||
notify.success('Reminder', `WhatsApp opened for ${getPatientName(appt)}`);
|
||||
};
|
||||
|
||||
const handleRescheduleSaved = () => {
|
||||
setRescheduleOpen(false);
|
||||
@@ -607,7 +583,6 @@ export const AppointmentsPageV2 = () => {
|
||||
<Table.Head label="DATE & TIME" className="w-28" />
|
||||
<Table.Head label="DOCTOR" className="min-w-[160px]" />
|
||||
<Table.Head label="STATUS" className="w-24" />
|
||||
<Table.Head label="REMIND" className="w-20" />
|
||||
</Table.Header>
|
||||
<Table.Body items={pagedRows}>
|
||||
{(appt) => {
|
||||
@@ -615,7 +590,6 @@ export const AppointmentsPageV2 = () => {
|
||||
const phone = getPhone(appt);
|
||||
const statusLabel = STATUS_LABELS[appt.status ?? ''] ?? appt.status ?? '—';
|
||||
const statusColor = STATUS_COLORS[appt.status ?? ''] ?? 'gray';
|
||||
const upcoming = isUpcoming(appt);
|
||||
const isSelected = selectedAppt?.id === appt.id;
|
||||
|
||||
return (
|
||||
@@ -667,21 +641,6 @@ export const AppointmentsPageV2 = () => {
|
||||
</Badge>
|
||||
</Table.Cell>
|
||||
|
||||
{/* Reminder */}
|
||||
<Table.Cell>
|
||||
{upcoming ? (
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); handleSendReminder(appt); }}
|
||||
className="inline-flex items-center gap-1 rounded-lg px-2 py-1 text-xs font-medium text-brand-secondary hover:bg-brand-primary transition duration-100 ease-linear"
|
||||
title="Send WhatsApp reminder"
|
||||
>
|
||||
<FontAwesomeIcon icon={faBell} className="size-3" />
|
||||
Send
|
||||
</button>
|
||||
) : (
|
||||
<span className="text-xs text-quaternary">—</span>
|
||||
)}
|
||||
</Table.Cell>
|
||||
|
||||
</Table.Row>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user