improvement: v1 standalone treatment/case creation made possible.
This commit is contained in:
@@ -1,26 +1,23 @@
|
||||
'use client';
|
||||
|
||||
import { useLocale, useTranslations } from 'next-intl';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { CalendarDays } from 'lucide-react';
|
||||
import { Card } from '@/components/ui/shared/Card';
|
||||
import { ScheduleDayPicker } from '@/components/ui/shared/ScheduleDayPicker';
|
||||
import { startOfLocalDay } from '@/components/appointments/appointmentTime';
|
||||
import {
|
||||
treatmentTypeBannerStyle,
|
||||
treatmentTypeLabelFromCatalog,
|
||||
} from '@/components/shared/treatmentTypeDisplay';
|
||||
import { DayStripCard } from '@/components/ui/treatment/DayStripCard';
|
||||
import type { DayStripItem } from '@/components/treatment/dayStrip';
|
||||
import type { TreatmentCatalogEntry } from '@/types/treatment-catalog';
|
||||
import type { TreatmentAppointment } from '@/types/treatment';
|
||||
import { APP_DATE, formatAppTimeRange } from '@/lib/i18n/format';
|
||||
|
||||
interface AppointmentsStripProps {
|
||||
stripHidden: boolean;
|
||||
onToggleStripHidden: () => void;
|
||||
selectedDay: Date;
|
||||
onSelectDay: (day: Date) => void;
|
||||
appointments: TreatmentAppointment[];
|
||||
selectedAppointmentId: string | null;
|
||||
onSelectAppointment: (id: string) => void;
|
||||
items: DayStripItem[];
|
||||
selectedItemId: string | null;
|
||||
onSelectItem: (item: DayStripItem) => void;
|
||||
onDeleteUnscheduled?: (item: DayStripItem) => void;
|
||||
treatmentCatalog: TreatmentCatalogEntry[];
|
||||
loading?: boolean;
|
||||
}
|
||||
@@ -30,13 +27,13 @@ export function AppointmentsStrip({
|
||||
onToggleStripHidden,
|
||||
selectedDay,
|
||||
onSelectDay,
|
||||
appointments,
|
||||
selectedAppointmentId,
|
||||
onSelectAppointment,
|
||||
items,
|
||||
selectedItemId,
|
||||
onSelectItem,
|
||||
onDeleteUnscheduled,
|
||||
treatmentCatalog,
|
||||
loading = false,
|
||||
}: AppointmentsStripProps) {
|
||||
const locale = useLocale();
|
||||
const t = useTranslations('treatment');
|
||||
|
||||
if (stripHidden) {
|
||||
@@ -54,6 +51,9 @@ export function AppointmentsStrip({
|
||||
);
|
||||
}
|
||||
|
||||
const timed = items.filter((item) => item.kind === 'appointment');
|
||||
const unscheduled = items.filter((item) => item.kind === 'unscheduled');
|
||||
|
||||
return (
|
||||
<Card className="space-y-4">
|
||||
<div className="flex items-center justify-between gap-3 flex-wrap">
|
||||
@@ -80,37 +80,44 @@ export function AppointmentsStrip({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{appointments.length === 0 && (
|
||||
<p className="text-sm text-text-muted">{t('emptyDay')}</p>
|
||||
)}
|
||||
{appointments.map((a) => {
|
||||
const sel = a.id === selectedAppointmentId;
|
||||
const timeLabel = formatAppTimeRange(a.startAt, a.endAt, locale);
|
||||
const purposeLabel = treatmentTypeLabelFromCatalog(a.purpose, treatmentCatalog);
|
||||
const purposeIndex = treatmentCatalog.findIndex((e) => e.code === a.purpose);
|
||||
return (
|
||||
<Card
|
||||
as="button"
|
||||
key={a.id}
|
||||
type="button"
|
||||
onClick={() => onSelectAppointment(a.id)}
|
||||
padding="none"
|
||||
style={treatmentTypeBannerStyle(a.purpose, purposeIndex < 0 ? 0 : purposeIndex)}
|
||||
className={`
|
||||
text-start rounded-[var(--radius-sm)] px-3 py-2 w-full sm:w-auto sm:min-w-[200px] sm:max-w-[280px] transition-shadow min-h-[52px]
|
||||
focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/45
|
||||
${sel ? 'ring-2 ring-primary ring-offset-2 ring-offset-background-secondary shadow-[inset_0_1px_0_rgba(255,255,255,0.06)]' : 'hover:brightness-110'}
|
||||
`}
|
||||
>
|
||||
<p className="text-[11px] font-medium tabular-nums opacity-95">{timeLabel}</p>
|
||||
<p className="text-sm font-medium leading-tight truncate mt-0.5">
|
||||
{a.patientFirstName} {a.patientLastName}
|
||||
</p>
|
||||
<p className="text-[11px] opacity-90 mt-0.5 truncate">{purposeLabel}</p>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
<div className="space-y-3">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{timed.length === 0 && unscheduled.length === 0 && (
|
||||
<p className="text-sm text-text-muted">{t('emptyDay')}</p>
|
||||
)}
|
||||
{timed.map((item) => (
|
||||
<DayStripCard
|
||||
key={`${item.kind}-${item.id}`}
|
||||
item={item}
|
||||
selected={item.id === selectedItemId}
|
||||
treatmentCatalog={treatmentCatalog}
|
||||
onSelect={() => onSelectItem(item)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{unscheduled.length > 0 ? (
|
||||
<div className="space-y-2">
|
||||
<p className="text-[10px] uppercase tracking-wide text-text-muted">{t('unscheduledHeading')}</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{unscheduled.map((item) => (
|
||||
<DayStripCard
|
||||
key={`${item.kind}-${item.id}`}
|
||||
item={item}
|
||||
selected={item.id === selectedItemId}
|
||||
treatmentCatalog={treatmentCatalog}
|
||||
onSelect={() => onSelectItem(item)}
|
||||
canDelete={item.canDelete}
|
||||
onDelete={
|
||||
item.canDelete && onDeleteUnscheduled
|
||||
? () => onDeleteUnscheduled(item)
|
||||
: undefined
|
||||
}
|
||||
deleteAriaLabel={t('deleteEmptyTreatment')}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user