import { useState } from "react"; import { useParams } from "react-router-dom"; import { AlertCircle, ArrowLeft, Loader2, MessagesSquare } from "lucide-react"; import { Alert, AlertDescription } from "../../../components/ui/alert.tsx"; import { Badge } from "../../../components/ui/badge.tsx"; import { EmptyState } from "../../../components/ui/empty-state.tsx"; import { usePatientSupportTicketThread, TICKET_STATUS_LABELS, TICKET_STATUS_VARIANT, type PatientSupportTicketEvent, } from "../../../hooks/usePatientSupportTickets"; import { formatDateTime } from "../../../utils/dateUtils"; /** * One problem a patient reported, the answer, and the reply that follows it. * * Two sides only. A patient's ticket belongs to Akashic from the moment it is raised and never moves * to a contractor, so there is no escalation, no hand-back and no third speaker to name. */ /** * Who spoke, in the reader's own terms - never an account id, and never guessed when it is blank. * The reader's own replies say "You" rather than their name: the name is stored on the ticket for * whoever answers, and reading it back tells them nothing while putting it on a shared screen. */ const speakerLabel = (event: PatientSupportTicketEvent): string => { if (event.side === "patient") return "You"; return event.authorName ? `${event.authorName} · Akashic support` : "Akashic support"; }; export const MySupportTicketDetail = () => { const { ticketId } = useParams<{ ticketId: string }>(); const { ticket, events, loading, error, notFound, reply, replying, replyError } = usePatientSupportTicketThread(ticketId); const [draft, setDraft] = useState(""); const submitReply = async (formEvent: React.FormEvent) => { formEvent.preventDefault(); if (!draft.trim() || replying) return; try { await reply(draft.trim()); setDraft(""); } catch { // `replyError` carries what to say. The draft stays: losing what somebody just wrote is // worse than making them press send again. } }; return (
{error && ( {error} )} {loading && (

Loading…

)} {/* A stale bookmark, or an id that was never theirs - an ordinary state, never a failure. */} {!loading && notFound && ( This could not be found. The link may be out of date. )} {!loading && ticket && ( <>

{ticket.subject}

{TICKET_STATUS_LABELS[ticket.status]}

{ticket.reference && `${ticket.reference} — `} Reported {formatDateTime(ticket.createdAt)}

{ticket.body}

{/* Oldest first, so the thread reads top to bottom the way it happened. */}
{events.length === 0 ? ( ) : ( events.map((item, index) => (

{speakerLabel(item)} · {formatDateTime(item.at)}

{item.kind === "reply" ? (

{item.body}

) : (

Marked {item.status ? TICKET_STATUS_LABELS[item.status] : "updated"}.

)}
)) )}
{ticket.status !== "resolved" && (