mirror of
https://github.com/temetro/temetro.git
synced 2026-08-27 19:06:52 +00:00
0f10aafa43
- Appointments: a Calendar button next to Add opens a month-grid dialog (reuses the Calendar primitive + shared ScheduleList); the schedule day is marked and selecting it lists that day's appointments. - Patients sub-nav: new Prescriptions page (mock list + KPIs) with a compact "New prescription" dialog reusing the patient quick-search pattern. - Notes: deleting a note now goes through a reusable ConfirmDialog instead of deleting immediately. - New top-level Messages page: two-pane email-style inbox (list + reading pane with mock reply composer), mirroring the Notes layout. - Sidebar logo bumped from size-9 to size-10. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
100 lines
3.0 KiB
TypeScript
100 lines
3.0 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
|
|
import {
|
|
type Appointment,
|
|
ScheduleList,
|
|
} from "@/components/appointments/appointments-view";
|
|
import { Calendar } from "@/components/ui/calendar";
|
|
import {
|
|
Dialog,
|
|
DialogDescription,
|
|
DialogHeader,
|
|
DialogPanel,
|
|
DialogPopup,
|
|
DialogTitle,
|
|
} from "@/components/ui/dialog";
|
|
|
|
// The mock schedule all belongs to this day (matches "Wednesday, June 5" in the
|
|
// Appointments view). Month is zero-based, so 5 = June.
|
|
const SCHEDULE_DATE = new Date(2026, 5, 5);
|
|
|
|
const sameDay = (a: Date, b: Date) =>
|
|
a.getFullYear() === b.getFullYear() &&
|
|
a.getMonth() === b.getMonth() &&
|
|
a.getDate() === b.getDate();
|
|
|
|
const fullDate = (d: Date) =>
|
|
d.toLocaleDateString("en-US", {
|
|
weekday: "long",
|
|
month: "long",
|
|
day: "numeric",
|
|
});
|
|
|
|
// A month-grid calendar (à la Google Calendar) shown in a dialog. The day that
|
|
// owns the mock schedule is ringed; selecting it lists those appointments, while
|
|
// any other day shows an empty note. Mock-only — there's no per-date backend.
|
|
export function CalendarDialog({
|
|
open,
|
|
onOpenChange,
|
|
schedule,
|
|
}: {
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
schedule: Appointment[];
|
|
}) {
|
|
const [selected, setSelected] = useState<Date>(SCHEDULE_DATE);
|
|
|
|
const dayItems = sameDay(selected, SCHEDULE_DATE) ? schedule : [];
|
|
|
|
return (
|
|
<Dialog onOpenChange={onOpenChange} open={open}>
|
|
<DialogPopup className="sm:max-w-2xl">
|
|
<DialogHeader>
|
|
<DialogTitle>Calendar</DialogTitle>
|
|
<DialogDescription>
|
|
Browse the schedule by date. Sample data.
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<DialogPanel className="flex flex-col gap-5 sm:flex-row sm:gap-6">
|
|
<div className="flex justify-center sm:block">
|
|
<Calendar
|
|
className="rounded-2xl border bg-card/30 p-3"
|
|
defaultMonth={SCHEDULE_DATE}
|
|
mode="single"
|
|
modifiers={{ scheduled: SCHEDULE_DATE }}
|
|
modifiersClassNames={{
|
|
scheduled: "[&_button]:ring-2 [&_button]:ring-primary",
|
|
}}
|
|
onSelect={(d) => d && setSelected(d)}
|
|
selected={selected}
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex min-w-0 flex-1 flex-col gap-3">
|
|
<div>
|
|
<h3 className="font-medium text-foreground text-sm">
|
|
{fullDate(selected)}
|
|
</h3>
|
|
<p className="text-muted-foreground text-xs">
|
|
{dayItems.length === 1
|
|
? "1 appointment"
|
|
: `${dayItems.length} appointments`}
|
|
</p>
|
|
</div>
|
|
{dayItems.length > 0 ? (
|
|
<ScheduleList items={dayItems} />
|
|
) : (
|
|
<div className="flex flex-1 items-center justify-center rounded-2xl border border-dashed bg-card/20 px-4 py-10 text-center text-muted-foreground text-sm">
|
|
No appointments on this day.
|
|
</div>
|
|
)}
|
|
</div>
|
|
</DialogPanel>
|
|
</DialogPopup>
|
|
</Dialog>
|
|
);
|
|
}
|