"use client"; import { AlertTriangle, CalendarPlus, Check, ClipboardList, Pill, X } from "lucide-react"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; import type { ActionPreviewData } from "@/lib/ai-chat"; import { type AppointmentInput, createAppointment } from "@/lib/appointments"; import { type PrescriptionInput, createPrescription } from "@/lib/prescriptions"; import { type TaskInput, createTask } from "@/lib/tasks"; import { notify } from "@/lib/toast"; type Status = "pending" | "committing" | "done" | "rejected"; const ICONS = { appointment: CalendarPlus, task: ClipboardList, prescription: Pill, } as const; // Summarise the proposed record into a couple of readable lines per kind. function summarize(data: ActionPreviewData): string[] { const r = data.record as Record; if (data.kind === "appointment") { return [ String(r.name ?? ""), [r.date, r.time].filter(Boolean).join(" · "), [r.type, r.provider].filter(Boolean).join(" · "), ].filter(Boolean); } if (data.kind === "task") { return [ String(r.title ?? ""), [r.assignee, r.due, r.priority].filter(Boolean).join(" · "), ].filter(Boolean); } // prescription return [ [r.medication, r.dose].filter(Boolean).join(" "), [r.frequency, r.duration].filter(Boolean).join(" · "), String(r.name ?? ""), ].filter(Boolean); } async function commit(data: ActionPreviewData): Promise { if (data.kind === "appointment") { await createAppointment(data.record as AppointmentInput); } else if (data.kind === "task") { await createTask(data.record as TaskInput); } else { await createPrescription(data.record as PrescriptionInput); } } // The human approval gate for an agent-proposed add. The agent drafts the record // (dry run, nothing written); the clinician reviews it here and must approve // before it is committed via the matching RBAC-gated create endpoint. export function ActionPreviewCard({ data }: { data: ActionPreviewData }) { const { t } = useTranslation(); const [status, setStatus] = useState("pending"); const Icon = ICONS[data.kind]; const hasIssues = (data.issues?.length ?? 0) > 0; const lines = summarize(data); const approve = async () => { setStatus("committing"); try { await commit(data); setStatus("done"); notify.success( t("chat.actionCard.addedTitle"), t(`chat.actionCard.kind.${data.kind}`), ); } catch { setStatus("pending"); notify.error( t("chat.actionCard.failedTitle"), t("chat.actionCard.failedBody"), ); } }; return (
{t(`chat.actionCard.title.${data.kind}`)}
{lines.map((line, i) => (

{line}

))}
{hasIssues ? (
    {data.issues!.slice(0, 5).map((issue) => (
  • {issue}
  • ))}
) : null} {status === "done" ? (

{t("chat.actionCard.added")}

) : status === "rejected" ? (

{t("chat.actionCard.discarded")}

) : (
)}
); }