From 307cc3cc642e9cdb27f450c557afe7e912978b11 Mon Sep 17 00:00:00 2001 From: Khalid Abdi Date: Tue, 16 Jun 2026 20:45:30 +0300 Subject: [PATCH] frontend: clinical UI fixes + chat/composer polish - chat composer: attach works with text (sr-only file input); lighter bordered input box - messages: defer "+" menu actions past close so file/appointment pickers fire - tasks: top-level New task button - chat: condensed appointment card with "View in calendar" deep-link; appointments page auto-opens the month calendar from ?calendar=&date= - lab: work-queue rows expand to show task detail + complete - inventory: clickable item detail dialog - pharmacy: Dispense action records a dispense + "Recently dispensed" feed; expiring badge uses endDate and only flags <=2 days left - prescriptions: keyboard nav in patient search, inventory-backed medication combobox (free-text fallback), optional start/end dates; thread dates through - sidebar: whole nav scrolls as one - i18n keys for all new strings Co-Authored-By: Claude Opus 4.8 --- .../appointments/appointments-view.tsx | 10 +- .../appointments/calendar-dialog.tsx | 24 +- frontend/components/chat/chat-input.tsx | 10 +- frontend/components/chat/record-list-card.tsx | 109 +++++++- frontend/components/lab/lab-view.tsx | 118 ++++++--- .../components/messages/messages-view.tsx | 18 +- .../pharmacy/inventory-detail-dialog.tsx | 131 +++++++++ .../components/pharmacy/inventory-view.tsx | 37 ++- .../components/pharmacy/pharmacy-view.tsx | 123 ++++++++- .../prescriptions/add-prescription-dialog.tsx | 250 +++++++++++++++++- .../prescription-detail-sheet.tsx | 15 ++ .../prescriptions/prescriptions-view.tsx | 2 + .../sidebar-02/nav-chat-history.tsx | 2 +- frontend/components/tasks/tasks-view.tsx | 16 +- frontend/components/ui/sidebar.tsx | 5 +- frontend/lib/dispenses.ts | 46 ++++ frontend/lib/i18n/locales/en/translation.json | 47 +++- frontend/lib/prescriptions.ts | 4 + 18 files changed, 866 insertions(+), 101 deletions(-) create mode 100644 frontend/components/pharmacy/inventory-detail-dialog.tsx create mode 100644 frontend/lib/dispenses.ts diff --git a/frontend/components/appointments/appointments-view.tsx b/frontend/components/appointments/appointments-view.tsx index e3d4b1c..8cd61fb 100644 --- a/frontend/components/appointments/appointments-view.tsx +++ b/frontend/components/appointments/appointments-view.tsx @@ -9,6 +9,7 @@ import { Stethoscope, Users, } from "lucide-react"; +import { useSearchParams } from "next/navigation"; import { type ReactNode, useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; @@ -192,8 +193,14 @@ function Section({ export function AppointmentsView() { const { t } = useTranslation(); + // Deep-link from the AI chat appointment card: `?calendar=1&date=YYYY-MM-DD` + // opens the month calendar on that date. + const searchParams = useSearchParams(); + const dateParam = searchParams.get("date"); const [addOpen, setAddOpen] = useState(false); - const [calendarOpen, setCalendarOpen] = useState(false); + const [calendarOpen, setCalendarOpen] = useState( + () => searchParams.get("calendar") != null, + ); const [appointments, setAppointments] = useState([]); const [query, setQuery] = useState(""); const [selectedAppt, setSelectedAppt] = useState(null); @@ -381,6 +388,7 @@ export function AppointmentsView() { diff --git a/frontend/components/appointments/calendar-dialog.tsx b/frontend/components/appointments/calendar-dialog.tsx index 865f008..e64f8ec 100644 --- a/frontend/components/appointments/calendar-dialog.tsx +++ b/frontend/components/appointments/calendar-dialog.tsx @@ -1,7 +1,7 @@ "use client"; import { ChevronLeft, ChevronRight } from "lucide-react"; -import { useMemo, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { @@ -53,18 +53,32 @@ export function CalendarDialog({ open, onOpenChange, appointments, + initialDate, }: { open: boolean; onOpenChange: (open: boolean) => void; appointments: Appointment[]; + // Optional deep-link target (YYYY-MM-DD): when set, the calendar opens on this + // date's month with the day selected (e.g. from the AI chat appointment card). + initialDate?: string | null; }) { const { t } = useTranslation(); - // First-of-month for the displayed month; defaults to TODAY's month. + const startKey = + initialDate && /^\d{4}-\d{2}-\d{2}$/.test(initialDate) ? initialDate : TODAY; + // First-of-month for the displayed month; defaults to the deep-link/TODAY month. const [viewMonth, setViewMonth] = useState(() => { - const t = parseKey(TODAY); - return new Date(t.getFullYear(), t.getMonth(), 1); + const d = parseKey(startKey); + return new Date(d.getFullYear(), d.getMonth(), 1); }); - const [selectedKey, setSelectedKey] = useState(TODAY); + const [selectedKey, setSelectedKey] = useState(startKey); + + // When opened via a deep-link date, jump the view to that month/day. + useEffect(() => { + if (!open || !initialDate || !/^\d{4}-\d{2}-\d{2}$/.test(initialDate)) return; + const d = parseKey(initialDate); + setViewMonth(new Date(d.getFullYear(), d.getMonth(), 1)); + setSelectedKey(initialDate); + }, [open, initialDate]); const byDate = useMemo(() => { const map = new Map(); diff --git a/frontend/components/chat/chat-input.tsx b/frontend/components/chat/chat-input.tsx index e225777..834c374 100644 --- a/frontend/components/chat/chat-input.tsx +++ b/frontend/components/chat/chat-input.tsx @@ -106,10 +106,10 @@ export function ChatInput({ event.preventDefault(); submit(); }} - className="w-full shrink-0 overflow-hidden rounded-[28px] border border-border bg-input shadow-sm" + className="w-full shrink-0 overflow-hidden rounded-[28px] border border-input bg-background shadow-sm transition-shadow focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/24 dark:bg-input/30" > {/* Textarea + toolbar, filling the rounded card. */} -
+