"use client"; import { useState, useTransition } from "react"; import { toast } from "sonner"; import { SendIcon, XIcon } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { approveOutboundAction, cancelOutboundAction, sendOutboundAction, } from "@/server/actions"; /** * The outbox, and the click no tool can make. * * This is the whole reason "approve each one" means anything: `approveOutbound` * is reachable from a server action and from nowhere else, so a connected * assistant can write a message and cannot put it in front of anybody. The * button below is that gate, and it draws nothing on a day with no drafts — * which is most days. * * Approving and sending are one click on purpose. Two clicks for "yes I meant * it" is the shape people learn to double-tap through, and the confirmation * that matters is reading the message, which is printed right here. */ export type OutboxRow = { id: string; subject: string; body: string; toEmail: string; contactName: string; fromEmail: string; application: string; status: string; error: string; }; export function Outbox({ rows, blockedBecause }: { rows: OutboxRow[]; blockedBecause: string }) { const [pending, startTransition] = useTransition(); const [open, setOpen] = useState(null); if (rows.length === 0) return null; const act = (id: string, what: "send" | "cancel") => startTransition(async () => { try { if (what === "cancel") { await cancelOutboundAction(id); toast.success("Called off"); return; } // Approve and send in one move: the person has read it, which is the // confirmation that matters. await approveOutboundAction(id).catch(() => undefined); const result = await sendOutboundAction(id); if (result.sent) toast.success("Sent from your mailbox"); else toast.error(result.reason); } catch (error) { toast.error(error instanceof Error ? error.message : "Could not do that."); } }); return ( Waiting to go {rows.length === 1 ? "1 message" : `${rows.length} messages`}

Written for you, and not sent. Nothing leaves until you press Send here — an assistant cannot make this click, which is what makes it worth having.

{blockedBecause &&

{blockedBecause}

}
{rows.map((row) => (
{open === row.id && (
                {row.body}
              
)} {row.error &&

{row.error}

}
{open !== row.id && ( )}
))}
); }