Files
dyolink/frontend/src/components/ui/treatment/NewTreatmentPatientPicker.tsx

99 lines
3.5 KiB
TypeScript

'use client';
import { useTranslations } from 'next-intl';
import { Button } from '@/components/ui/shared/Button';
import { PatientSearchCombobox } from '@/components/ui/patient/PatientSearchCombobox';
import { usePatientSearchQuery } from '@/lib/hooks/usePatientSearchQuery';
import { formatMobileForDisplay } from '@/lib/phone';
import type { Patient } from '@/types/patient';
const pickerChoiceClass =
'w-full rounded-[var(--radius-md)] border border-primary/40 bg-primary-soft px-3 py-2 text-start transition-colors hover:border-primary disabled:opacity-50 disabled:cursor-not-allowed focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/45';
interface NewTreatmentPatientPickerProps {
creating?: boolean;
currentPatient?: {
id: string;
displayName: string;
mobile?: string | null;
email?: string | null;
} | null;
onSelectWalkIn: () => void | Promise<void>;
onSelectPatient: (patient: Patient) => void | Promise<void>;
onSelectCurrentPatient?: () => void | Promise<void>;
onCancel: () => void;
}
function contactLine(
patient: { mobile?: string | null; email?: string | null },
fallback: string,
): string {
const mobile = patient.mobile?.trim()
? formatMobileForDisplay(patient.mobile.trim())
: '';
if (mobile) return mobile;
const email = patient.email?.trim();
if (email) return email;
return fallback;
}
export function NewTreatmentPatientPicker({
creating = false,
currentPatient = null,
onSelectWalkIn,
onSelectPatient,
onSelectCurrentPatient,
onCancel,
}: NewTreatmentPatientPickerProps) {
const t = useTranslations('treatment');
const tCommon = useTranslations('common');
const tPatients = useTranslations('patients');
const { search, setSearch, patients, loading } = usePatientSearchQuery(!creating);
return (
<div className="space-y-3 rounded-[var(--radius-md)] border border-border/70 bg-background-secondary/40 p-3">
<p className="text-sm font-medium text-text-primary">{t('newTreatmentPatientPrompt')}</p>
<button
type="button"
disabled={creating}
onClick={() => void onSelectWalkIn()}
className={pickerChoiceClass}
>
<p className="text-sm font-medium text-text-primary">{t('walkIn')}</p>
<p className="text-xs text-text-muted mt-0.5">{t('walkInPickerHint')}</p>
</button>
{currentPatient && onSelectCurrentPatient ? (
<button
type="button"
disabled={creating}
onClick={() => void onSelectCurrentPatient()}
aria-label={t('newTreatmentUseCurrent', { name: currentPatient.displayName })}
className={pickerChoiceClass}
>
<p className="text-sm font-medium text-text-primary break-words">{currentPatient.displayName}</p>
<p className="text-xs text-text-muted mt-0.5 break-words">
{contactLine(currentPatient, t('newTreatmentUseCurrentHint'))}
</p>
</button>
) : null}
<PatientSearchCombobox
search={search}
onSearchChange={setSearch}
patients={patients}
loading={loading || creating}
onSelectPatient={(patient) => {
if (creating) return;
void onSelectPatient(patient);
}}
placeholder={tPatients('searchPlaceholder')}
emptyResultsMessage={tPatients('noResults')}
/>
<div className="flex justify-end">
<Button type="button" variant="ghost" size="sm" disabled={creating} onClick={onCancel}>
{tCommon('cancel')}
</Button>
</div>
</div>
);
}