mirror of
https://github.com/temetro/temetro.git
synced 2026-08-06 00:47:41 +00:00
2c239fbd27
- Record history: replace fragile COSS Timeline separator math with a continuous-rail list so every audited change renders in full (RTL-safe). - Prescriptions: new reusable COSS DatePicker (Popover + Calendar) replaces raw <input type="date"> for Start/End date. - Update banner: rebuilt with COSS Alert variant="warning"; pinned physical bottom-right so it stays bottom-right under Arabic RTL. - AI setup notice: thin attached warning banner with clearer copy; now shown above the input in active chats too, not just the empty state. - Settings profile: wire the previously dead Specialty (Select) and Professional links (editable rows) into the persisted preferences. - Patients: add a status filter and broaden search to conditions/allergies. - Sidebar user menu: quick language switch submenu (RTL already wired). - ai-elements: fix a subset of Base UI type drift (22 -> 12; remainder is cmdk->Autocomplete migration drift kept behind ignoreBuildErrors). - i18n: new keys added across all five locales (en/de/fr/ar/so). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
104 lines
2.9 KiB
TypeScript
104 lines
2.9 KiB
TypeScript
"use client";
|
|
|
|
import { CalendarIcon } from "lucide-react";
|
|
import type { Matcher } from "react-day-picker";
|
|
import { useState } from "react";
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
import { Calendar } from "@/components/ui/calendar";
|
|
import {
|
|
Popover,
|
|
PopoverPopup,
|
|
PopoverTrigger,
|
|
} from "@/components/ui/popover";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
// Parse a `YYYY-MM-DD` string as a LOCAL date. `new Date("YYYY-MM-DD")` parses
|
|
// as UTC and can shift the day across timezones, so build it from parts.
|
|
function parseISODate(value?: string): Date | undefined {
|
|
if (!value) return undefined;
|
|
const [y, m, d] = value.split("-").map(Number);
|
|
if (!y || !m || !d) return undefined;
|
|
return new Date(y, m - 1, d);
|
|
}
|
|
|
|
function toISODate(date: Date): string {
|
|
const y = date.getFullYear();
|
|
const m = String(date.getMonth() + 1).padStart(2, "0");
|
|
const d = String(date.getDate()).padStart(2, "0");
|
|
return `${y}-${m}-${d}`;
|
|
}
|
|
|
|
export interface DatePickerProps {
|
|
/** Selected date as `YYYY-MM-DD` (matches native date-input semantics). */
|
|
value?: string;
|
|
onChange: (value: string) => void;
|
|
/** Earliest selectable date, `YYYY-MM-DD`. */
|
|
min?: string;
|
|
/** Latest selectable date, `YYYY-MM-DD`. */
|
|
max?: string;
|
|
placeholder?: string;
|
|
id?: string;
|
|
className?: string;
|
|
disabled?: boolean;
|
|
}
|
|
|
|
// A COSS date picker: an outline Button trigger opening a Popover with the COSS
|
|
// Calendar. Drop-in replacement for `<input type="date">`, keeping the same
|
|
// string value shape so callers don't change their state handling.
|
|
export function DatePicker({
|
|
value,
|
|
onChange,
|
|
min,
|
|
max,
|
|
placeholder,
|
|
id,
|
|
className,
|
|
disabled,
|
|
}: DatePickerProps): React.ReactElement {
|
|
const [open, setOpen] = useState(false);
|
|
const selected = parseISODate(value);
|
|
const minDate = parseISODate(min);
|
|
const maxDate = parseISODate(max);
|
|
|
|
const disabledMatchers: Matcher[] = [
|
|
...(minDate ? [{ before: minDate }] : []),
|
|
...(maxDate ? [{ after: maxDate }] : []),
|
|
];
|
|
|
|
return (
|
|
<Popover onOpenChange={setOpen} open={open}>
|
|
<PopoverTrigger
|
|
render={
|
|
<Button
|
|
className={cn(
|
|
"w-full justify-between font-normal",
|
|
!selected && "text-muted-foreground",
|
|
className,
|
|
)}
|
|
disabled={disabled}
|
|
id={id}
|
|
variant="outline"
|
|
/>
|
|
}
|
|
>
|
|
{selected ? selected.toLocaleDateString() : (placeholder ?? "")}
|
|
<CalendarIcon className="opacity-80" />
|
|
</PopoverTrigger>
|
|
<PopoverPopup align="start" className="w-auto">
|
|
<Calendar
|
|
autoFocus
|
|
defaultMonth={selected ?? minDate}
|
|
disabled={disabledMatchers.length ? disabledMatchers : undefined}
|
|
mode="single"
|
|
onSelect={(date?: Date) => {
|
|
onChange(date ? toISODate(date) : "");
|
|
if (date) setOpen(false);
|
|
}}
|
|
selected={selected}
|
|
/>
|
|
</PopoverPopup>
|
|
</Popover>
|
|
);
|
|
}
|