"use client"; import { CircleCheck, Clock, Pill, Plus, Search } from "lucide-react"; import { type ReactNode, useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { AiBadge } from "@/components/ai-badge"; import { AddPrescriptionDialog, type NewPrescription, } from "@/components/prescriptions/add-prescription-dialog"; import { PrescriptionDetailSheet } from "@/components/prescriptions/prescription-detail-sheet"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; import { ConfirmDialog } from "@/components/ui/confirm-dialog"; import { Input } from "@/components/ui/input"; import { type Prescription, type RxStatus, createPrescription, deletePrescription, formatPrescribedAt, listPrescriptions, } from "@/lib/prescriptions"; import { notify } from "@/lib/toast"; export type { Prescription, RxStatus } from "@/lib/prescriptions"; const statusVariant: Record< RxStatus, "default" | "secondary" | "destructive" | "outline" > = { active: "default", completed: "outline", expired: "destructive", }; function Kpi({ label, value, icon: Icon, }: { label: string; value: string; icon: typeof Pill; }) { return (
{label} {value}
); } function RxRow({ rx, onOpen }: { rx: Prescription; onOpen: () => void }) { const { t } = useTranslation(); return (
{ if (event.key === "Enter" || event.key === " ") { event.preventDefault(); onOpen(); } }} role="button" tabIndex={0} > {rx.initials}
{rx.medication} {rx.dose && ( · {rx.dose} )} {rx.name} · #{rx.fileNumber} · {rx.frequency}
{rx.prescriber} {formatPrescribedAt(rx.prescribedAt)}
{t(`prescriptions.status.${rx.status}`)}
); } function Section({ title, description, children, }: { title: string; description?: string; children: ReactNode; }) { return (

{title}

{description && (

{description}

)}
{children}
); } export function PrescriptionsView() { const { t } = useTranslation(); const [addOpen, setAddOpen] = useState(false); const [list, setList] = useState([]); const [selected, setSelected] = useState(null); const [sheetOpen, setSheetOpen] = useState(false); const [confirmOpen, setConfirmOpen] = useState(false); const [query, setQuery] = useState(""); useEffect(() => { let active = true; listPrescriptions() .then((data) => { if (active) setList(data); }) .catch(() => { /* api-client redirects on 401; otherwise leave the list empty */ }); return () => { active = false; }; }, []); const openRx = (rx: Prescription) => { setSelected(rx); setSheetOpen(true); }; const removeRx = async () => { if (!selected) return; const id = selected.id; try { await deletePrescription(id); setList((prev) => prev.filter((r) => r.id !== id)); setSheetOpen(false); notify.success(t("prescriptions.delete.doneTitle"), selected.medication); } catch { notify.error( t("prescriptions.delete.failedTitle"), t("prescriptions.delete.failedBody"), ); } finally { setConfirmOpen(false); } }; // Persist a new prescription, then add the saved record to the top of the list. const addPrescription = async (rx: NewPrescription) => { try { const created = await createPrescription({ fileNumber: rx.fileNumber, name: rx.name, initials: rx.initials, medication: rx.medication, dose: rx.dose, frequency: rx.frequency, duration: rx.duration || null, startDate: rx.startDate || null, endDate: rx.endDate || null, notes: rx.notes || null, }); setList((prev) => [created, ...prev]); } catch { notify.error( t("prescriptions.addFailedTitle"), t("prescriptions.addFailedBody"), ); } }; // Case-insensitive substring match, same pattern as the Patients page. const search = query.trim().toLowerCase(); const filtered = useMemo(() => { if (!search) return list; return list.filter( (rx) => rx.name.toLowerCase().includes(search) || rx.fileNumber.includes(search) || rx.medication.toLowerCase().includes(search) || rx.prescriber.toLowerCase().includes(search) || rx.status.toLowerCase().includes(search), ); }, [list, search]); const kpis = useMemo( () => [ { label: t("prescriptions.kpi.active"), value: String(list.filter((r) => r.status === "active").length), icon: Pill, }, { label: t("prescriptions.kpi.completed"), value: String(list.filter((r) => r.status === "completed").length), icon: CircleCheck, }, { label: t("prescriptions.kpi.expired"), value: String(list.filter((r) => r.status === "expired").length), icon: Clock, }, ], [list, t], ); return (

{t("prescriptions.title")}

{t("prescriptions.subtitle")}

setQuery(event.target.value)} placeholder={t("prescriptions.searchPlaceholder")} value={query} />
{kpis.map((k) => ( ))}
{filtered.map((rx) => ( openRx(rx)} rx={rx} /> ))} {filtered.length === 0 && (

{search ? t("prescriptions.noMatches") : t("prescriptions.emptyList")}

)}
setConfirmOpen(true)} onOpenChange={setSheetOpen} open={sheetOpen} rx={selected} />
); }