// components/ResolutionActions.tsx // Phase 2B — action bar for the resolution detail page. // // Status-aware actions: // - draft → Send for signature (disabled — Phase 6) // - draft → Cancel (re-enables) // - pending_signatures → Cancel // - pending_signatures → Resend (uses existing /api/resolutions/[id]/resend) // - signed → (no destructive actions; status is final) // - cancelled → (locked — status is final) // // Cancel + Resend are wired to the existing endpoints (Phase 2A). // "Send for signature" is disabled until Phase 6 lands. "use client"; import { useState } from "react"; import { useRouter } from "next/navigation"; import type { NewResolutionStatus } from "@/lib/types"; interface Props { resolutionId: string; status: NewResolutionStatus; } export default function ResolutionActions({ resolutionId, status }: Props) { const router = useRouter(); const [acting, setActing] = useState(null); const [feedback, setFeedback] = useState<{ kind: "ok" | "err"; msg: string } | null>(null); const canSendForSignature = status === "draft"; const canCancel = status === "draft" || status === "pending_signatures"; const canResend = status === "pending_signatures"; const handleCancel = async () => { if (!confirm("Cancel this resolution? Pending signers will be marked expired.")) return; setActing("cancel"); setFeedback(null); try { const res = await fetch(`/api/resolutions/${resolutionId}/cancel`, { method: "POST" }); if (!res.ok) { const j = await res.json().catch(() => ({})); throw new Error(j.error || `HTTP ${res.status}`); } setFeedback({ kind: "ok", msg: "Resolution cancelled." }); router.refresh(); } catch (err) { setFeedback({ kind: "err", msg: err instanceof Error ? err.message : "Cancel failed" }); } finally { setActing(null); } }; const handleResend = async () => { setActing("resend"); setFeedback(null); try { const res = await fetch(`/api/resolutions/${resolutionId}/resend`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({}), }); if (!res.ok) { const j = await res.json().catch(() => ({})); throw new Error(j.error || `HTTP ${res.status}`); } const j = await res.json(); setFeedback({ kind: "ok", msg: `Re-sent to ${j.resent} pending signer(s).` }); router.refresh(); } catch (err) { setFeedback({ kind: "err", msg: err instanceof Error ? err.message : "Resend failed" }); } finally { setActing(null); } }; const handleSendForSignature = async () => { setActing("send"); setFeedback(null); try { const res = await fetch(`/api/resolutions/${resolutionId}/attendees`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ send_for_signature: true }), }); const j = await res.json().catch(() => ({})); if (!res.ok) { throw new Error(j.error || `HTTP ${res.status}`); } const sentCount = j.emails_sent?.filter((e: { ok: boolean }) => e.ok).length ?? 0; const failCount = j.emails_sent?.filter((e: { ok: boolean }) => !e.ok).length ?? 0; const msg = `Sent for signature — ${j.signers_created} signers created` + (sentCount > 0 ? `, ${sentCount} emails sent` : "") + (failCount > 0 ? `, ${failCount} email(s) failed (see server logs)` : ""); setFeedback({ kind: failCount > 0 ? "err" : "ok", msg }); router.refresh(); } catch (err) { setFeedback({ kind: "err", msg: err instanceof Error ? err.message : "Send failed", }); } finally { setActing(null); } }; // No actions for terminal states if (status === "signed" || status === "cancelled") { return (
Status is final — no actions available.
); } return (
{canSendForSignature && ( )} {canResend && ( )} {canCancel && ( )}
{feedback && (

{feedback.msg}

)}
); }