201 lines
5.1 KiB
TypeScript
201 lines
5.1 KiB
TypeScript
import {
|
|
addCalendarDays,
|
|
isSameLocalCalendarDay,
|
|
startOfLocalDay,
|
|
} from '@/lib/appointmentTime';
|
|
import type {
|
|
FdiToothId,
|
|
LinkedOrganizationOption,
|
|
PastTreatment,
|
|
SaveTreatmentPayload,
|
|
SendTreatmentRecordPayload,
|
|
TreatmentAppointment,
|
|
} from '@/types/treatment';
|
|
|
|
const MOCK_DELAY_MS = 280;
|
|
|
|
function sleep(ms = MOCK_DELAY_MS) {
|
|
return new Promise<void>((resolve) => setTimeout(resolve, ms));
|
|
}
|
|
|
|
function pad2(n: number) {
|
|
return String(n).padStart(2, '0');
|
|
}
|
|
|
|
/** Builds deterministic ids so React keys stay stable across hot reloads */
|
|
function mockApptId(dayKey: string, slotIndex: number | string) {
|
|
return `mock-appt-${dayKey}-${slotIndex}`;
|
|
}
|
|
|
|
function buildSlotsForDay(day: Date, userId: string): TreatmentAppointment[] {
|
|
const dayStart = startOfLocalDay(day);
|
|
const y = dayStart.getFullYear();
|
|
const m = dayStart.getMonth();
|
|
const d = dayStart.getDate();
|
|
const dayKey = `${y}-${pad2(m + 1)}-${pad2(d)}`;
|
|
|
|
const base: Omit<TreatmentAppointment, 'id' | 'startAt' | 'endAt'>[] = [
|
|
{
|
|
patientId: 'pat-alice',
|
|
patientFirstName: 'Alice',
|
|
patientLastName: 'Moradi',
|
|
providerUserId: userId,
|
|
purpose: 'consultation',
|
|
},
|
|
{
|
|
patientId: 'pat-babak',
|
|
patientFirstName: 'Babak',
|
|
patientLastName: 'Karimi',
|
|
providerUserId: userId,
|
|
purpose: 'filling',
|
|
},
|
|
{
|
|
patientId: 'pat-sara',
|
|
patientFirstName: 'Sara',
|
|
patientLastName: 'Hosseini',
|
|
providerUserId: userId,
|
|
purpose: 'endo',
|
|
},
|
|
];
|
|
|
|
const slots: TreatmentAppointment[] = [];
|
|
const times = [
|
|
[9, 0, 9, 45],
|
|
[11, 15, 12, 0],
|
|
[14, 30, 15, 30],
|
|
];
|
|
|
|
times.forEach(([h1, m1, h2, m2], i) => {
|
|
const baseSlot = base[i % base.length];
|
|
const startAt = new Date(y, m, d, h1, m1, 0, 0).toISOString();
|
|
const endAt = new Date(y, m, d, h2, m2, 0, 0).toISOString();
|
|
slots.push({
|
|
id: mockApptId(dayKey, i),
|
|
...baseSlot,
|
|
startAt,
|
|
endAt,
|
|
});
|
|
});
|
|
|
|
const today = new Date();
|
|
if (isSameLocalCalendarDay(day, today)) {
|
|
const now = today.getTime();
|
|
const start = new Date(now - 12 * 60 * 1000);
|
|
const end = new Date(now + 48 * 60 * 1000);
|
|
slots.unshift({
|
|
id: mockApptId(dayKey, 'now'),
|
|
patientId: 'pat-hesam',
|
|
patientFirstName: 'Hesam',
|
|
patientLastName: 'Aghaie',
|
|
providerUserId: userId,
|
|
startAt: start.toISOString(),
|
|
endAt: end.toISOString(),
|
|
purpose: 'visit',
|
|
});
|
|
}
|
|
|
|
return slots.sort((a, b) => new Date(a.startAt).getTime() - new Date(b.startAt).getTime());
|
|
}
|
|
|
|
const MOCK_HISTORY: Record<string, PastTreatment[]> = {
|
|
'pat-hesam': [
|
|
{
|
|
id: 'pt-h-1',
|
|
patientId: 'pat-hesam',
|
|
title: 'Root canal 45',
|
|
treatmentAt: new Date(2026, 8, 12).toISOString(),
|
|
status: 'completed',
|
|
records: [
|
|
{
|
|
id: 'ptr-1',
|
|
treatmentType: 'endo',
|
|
teeth: ['45'] as FdiToothId[],
|
|
notes: 'Instrumented and temporized.',
|
|
},
|
|
],
|
|
documents: [
|
|
{
|
|
id: 'doc-1',
|
|
fileName: 'periapical-45.png',
|
|
mimeType: 'image/png',
|
|
sizeBytes: 842_120,
|
|
},
|
|
{
|
|
id: 'doc-2',
|
|
fileName: 'consent-signed.pdf',
|
|
mimeType: 'application/pdf',
|
|
sizeBytes: 312_000,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'pt-h-2',
|
|
patientId: 'pat-hesam',
|
|
title: 'Filling 14, 15',
|
|
treatmentAt: new Date(2025, 9, 10).toISOString(),
|
|
status: 'completed',
|
|
records: [
|
|
{
|
|
id: 'ptr-2',
|
|
treatmentType: 'filling',
|
|
teeth: ['14', '15'] as FdiToothId[],
|
|
notes: 'Composite restoration.',
|
|
},
|
|
],
|
|
documents: [
|
|
{
|
|
id: 'doc-3',
|
|
fileName: 'notes.txt',
|
|
mimeType: 'text/plain',
|
|
sizeBytes: 420,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
'pat-alice': [
|
|
{
|
|
id: 'pt-a-1',
|
|
patientId: 'pat-alice',
|
|
title: 'Hygiene visit',
|
|
treatmentAt: addCalendarDays(new Date(), -21).toISOString(),
|
|
status: 'completed',
|
|
records: [{ id: 'ptr-a', treatmentType: 'hygiene', teeth: [], notes: 'Scale & polish.' }],
|
|
documents: [],
|
|
},
|
|
],
|
|
};
|
|
|
|
const MOCK_ORGS: LinkedOrganizationOption[] = [
|
|
{ id: 'org-lab-north', name: 'North Dental Lab', active: true },
|
|
{ id: 'org-lab-smile', name: 'Smile Ceramics', active: true },
|
|
{ id: 'org-lab-old', name: 'Legacy Lab (inactive)', active: false },
|
|
];
|
|
|
|
export async function fetchMyAppointmentsForDay(
|
|
userId: string,
|
|
day: Date,
|
|
): Promise<TreatmentAppointment[]> {
|
|
await sleep();
|
|
return buildSlotsForDay(day, userId);
|
|
}
|
|
|
|
export async function fetchPastTreatments(patientId: string): Promise<PastTreatment[]> {
|
|
await sleep();
|
|
return MOCK_HISTORY[patientId] ?? [];
|
|
}
|
|
|
|
export async function fetchLinkedOrganizations(): Promise<LinkedOrganizationOption[]> {
|
|
await sleep(180);
|
|
return MOCK_ORGS;
|
|
}
|
|
|
|
export async function saveTreatmentDraft(_payload: SaveTreatmentPayload): Promise<{ ok: true }> {
|
|
await sleep();
|
|
return { ok: true };
|
|
}
|
|
|
|
export async function sendTreatmentRecord(_payload: SendTreatmentRecordPayload): Promise<{ ok: true }> {
|
|
await sleep();
|
|
return { ok: true };
|
|
}
|