mirror of
https://github.com/temetro/temetro.git
synced 2026-08-05 16:37:42 +00:00
frontend: UI fixes + settings/patients features
- 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>
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user