import { useEffect, useState } from "react"; import { toast } from "react-toastify"; import { requests } from "../api/client"; import type { ChangeRequest } from "../api/types"; import { Modal } from "./Modal"; import { Button } from "./ui"; type Decision = "APPROVE" | "REJECT" | "REQUEST_CHANGES"; const MAX = 500; const meta: Record = { APPROVE: { title: "Approve request", verb: "Approve", variant: "success", blurb: "The agent will be allowed to proceed. A platform-signed receipt will be issued.", commentRequired: false, }, REJECT: { title: "Reject request", verb: "Reject", variant: "danger", blurb: "This is a hard blocker. The agent must submit a new request to try again.", commentRequired: false, }, REQUEST_CHANGES: { title: "Request changes", verb: "Request changes", variant: "primary", blurb: "The agent will see your notes and can update the request for re-review.", commentRequired: true, }, }; export function DecisionModal({ request, decision, onClose, onDone, }: { request: ChangeRequest; decision: Decision | null; onClose: () => void; onDone: (updated: ChangeRequest) => void; }) { const [comment, setComment] = useState(""); const [loading, setLoading] = useState(false); useEffect(() => { setComment(""); }, [decision]); if (!decision) return null; const m = meta[decision]; const tooLong = comment.length > MAX; const missingRequired = m.commentRequired && comment.trim().length === 0; const submit = async () => { if (tooLong || missingRequired) return; setLoading(true); try { const updated = await requests.decide(request.request_id, decision, comment.trim() || undefined); toast.success(`${m.verb}d`); onDone(updated); onClose(); } catch (err) { toast.error(err instanceof Error ? err.message : "Failed"); } finally { setLoading(false); } }; return ( } >

{request.title}

{request.changes.length} change{request.changes.length === 1 ? "" : "s"} ·{" "} {request.agent?.name}

{m.blurb}

Comment {m.commentRequired ? (required) : "(optional)"} MAX * 0.8 ? "text-pending" : "text-faint"}`}> {comment.length}/{MAX}