feat(orders): make Send PO modal wider and two-column, scrollable body

This commit is contained in:
SJCoetz 2026-07-09 23:48:26 +02:00
parent f44f090351
commit 0eef0abd92

View file

@ -1098,8 +1098,8 @@ export default function OrderDetailPage({ params }: { params: Promise<{ id: stri
}
}}
>
<DialogContent>
<DialogHeader>
<DialogContent className="sm:max-w-3xl max-h-[90vh] grid-rows-[auto_1fr_auto] gap-0 p-0 overflow-hidden">
<DialogHeader className="px-6 pt-6 pb-4 border-b">
<DialogTitle>
Send {sendPo?.po_number ?? "PO"} to{" "}
{sendPo?.expand?.supplier?.suppliername ?? "supplier"}
@ -1110,124 +1110,132 @@ export default function OrderDetailPage({ params }: { params: Promise<{ id: stri
Supplier Email section once the IMAP poller is wired up.
</DialogDescription>
</DialogHeader>
<div className="space-y-3">
<div className="space-y-1.5">
<Label htmlFor="send-to">To</Label>
<Input
id="send-to"
value={sendTo}
onChange={(e) => setSendTo(e.target.value)}
placeholder="supplier@example.com"
autoFocus
/>
</div>
<div className="grid grid-cols-2 gap-3">
{/* Body — scrolls independently. On wide screens the delivery
config lives on the left and the message + attachments
live on the right; on small screens everything stacks. */}
<div className="grid gap-6 px-6 py-4 overflow-y-auto lg:grid-cols-[minmax(0,1fr)_minmax(0,1.1fr)]">
<div className="space-y-3">
<div className="space-y-1.5">
<Label htmlFor="send-cc">CC</Label>
<Label htmlFor="send-to">To</Label>
<Input
id="send-cc"
value={sendCc}
onChange={(e) => setSendCc(e.target.value)}
placeholder="optional"
id="send-to"
value={sendTo}
onChange={(e) => setSendTo(e.target.value)}
placeholder="supplier@example.com"
autoFocus
/>
</div>
<div className="space-y-1.5">
<Label htmlFor="send-bcc">BCC</Label>
<Input
id="send-bcc"
value={sendBcc}
onChange={(e) => setSendBcc(e.target.value)}
placeholder="optional"
/>
</div>
</div>
<div className="space-y-1.5">
<Label htmlFor="send-subject">Subject</Label>
<Input
id="send-subject"
value={sendSubject}
onChange={(e) => setSendSubject(e.target.value)}
placeholder={`Purchase Order ${sendPo?.po_number ?? ""}`}
/>
</div>
<div className="space-y-1.5">
<Label htmlFor="send-message">Message</Label>
<Textarea
id="send-message"
value={sendMessage}
onChange={(e) => setSendMessage(e.target.value)}
placeholder="Hi Supplier, please see attached PO. Confirm receipt and ETA. Thanks — WFDash Orders"
rows={5}
maxLength={2000}
disabled={sendMessageLoading}
/>
<p className="text-xs text-muted-foreground">
{sendMessageLoading
? "Loading line count for preview…"
: `${sendMessage.length}/2000`}
</p>
</div>
<div className="space-y-2 rounded-md border p-3">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium">Additional attachments</p>
<p className="text-xs text-muted-foreground">
Files sent alongside the PO PDF (supplier quotes, drawings, etc.).
</p>
</div>
<label className="inline-flex cursor-pointer items-center gap-1.5 rounded-md border bg-background px-2 py-1 text-xs hover:bg-accent">
<Paperclip className="h-3 w-3" />
{attachmentUploading ? "Uploading…" : "Attach files"}
<input
type="file"
multiple
className="hidden"
onChange={(e) => uploadAttachments(e.target.files)}
disabled={attachmentUploading}
<div className="grid grid-cols-2 gap-3">
<div className="space-y-1.5">
<Label htmlFor="send-cc">CC</Label>
<Input
id="send-cc"
value={sendCc}
onChange={(e) => setSendCc(e.target.value)}
placeholder="optional"
/>
</label>
</div>
<div className="space-y-1.5">
<Label htmlFor="send-bcc">BCC</Label>
<Input
id="send-bcc"
value={sendBcc}
onChange={(e) => setSendBcc(e.target.value)}
placeholder="optional"
/>
</div>
</div>
{attachments.length === 0 ? (
<p className="text-xs text-muted-foreground">No additional files attached.</p>
) : (
<ul className="space-y-1">
{attachments.map((token) => (
<li
key={token}
className="flex items-center justify-between gap-2 rounded border bg-muted/30 px-2 py-1 text-xs"
>
<a
href={sendPo ? `/api/v1/files/purchase_orders/${sendPo.id}/${encodeURIComponent(token)}` : "#"}
target="_blank"
rel="noopener noreferrer"
className="truncate text-foreground hover:underline"
>
{token}
</a>
<Button
type="button"
variant="ghost"
size="icon"
className="h-5 w-5 text-muted-foreground hover:text-destructive"
onClick={() => removeAttachment(token)}
disabled={attachmentUploading}
aria-label={`Remove ${token}`}
>
<Trash2 className="h-3 w-3" />
</Button>
</li>
))}
</ul>
<div className="space-y-1.5">
<Label htmlFor="send-subject">Subject</Label>
<Input
id="send-subject"
value={sendSubject}
onChange={(e) => setSendSubject(e.target.value)}
placeholder={`Purchase Order ${sendPo?.po_number ?? ""}`}
/>
</div>
{sendPo?.status === "mail_failed" && sendPo?.email_error && (
<div className="rounded-md border border-destructive/30 bg-destructive/5 px-3 py-2 text-xs text-destructive">
<span className="font-medium">Previous failure:</span>{" "}
{sendPo.email_error}
</div>
)}
</div>
{sendPo?.status === "mail_failed" && sendPo?.email_error && (
<div className="rounded-md border border-destructive/30 bg-destructive/5 px-3 py-2 text-xs text-destructive">
<span className="font-medium">Previous failure:</span>{" "}
{sendPo.email_error}
<div className="space-y-3">
<div className="space-y-1.5">
<Label htmlFor="send-message">Message</Label>
<Textarea
id="send-message"
value={sendMessage}
onChange={(e) => setSendMessage(e.target.value)}
placeholder="Hi Supplier, please see attached PO. Confirm receipt and ETA. Thanks — WFDash Orders"
rows={8}
maxLength={2000}
disabled={sendMessageLoading}
className="min-h-32"
/>
<p className="text-xs text-muted-foreground">
{sendMessageLoading
? "Loading line count for preview…"
: `${sendMessage.length}/2000`}
</p>
</div>
)}
<div className="space-y-2 rounded-md border p-3">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium">Additional attachments</p>
<p className="text-xs text-muted-foreground">
Files sent alongside the PO PDF (supplier quotes, drawings, etc.).
</p>
</div>
<label className="inline-flex cursor-pointer items-center gap-1.5 rounded-md border bg-background px-2 py-1 text-xs hover:bg-accent">
<Paperclip className="h-3 w-3" />
{attachmentUploading ? "Uploading…" : "Attach files"}
<input
type="file"
multiple
className="hidden"
onChange={(e) => uploadAttachments(e.target.files)}
disabled={attachmentUploading}
/>
</label>
</div>
{attachments.length === 0 ? (
<p className="text-xs text-muted-foreground">No additional files attached.</p>
) : (
<ul className="space-y-1">
{attachments.map((token) => (
<li
key={token}
className="flex items-center justify-between gap-2 rounded border bg-muted/30 px-2 py-1 text-xs"
>
<a
href={sendPo ? `/api/v1/files/purchase_orders/${sendPo.id}/${encodeURIComponent(token)}` : "#"}
target="_blank"
rel="noopener noreferrer"
className="truncate text-foreground hover:underline"
>
{token}
</a>
<Button
type="button"
variant="ghost"
size="icon"
className="h-5 w-5 text-muted-foreground hover:text-destructive"
onClick={() => removeAttachment(token)}
disabled={attachmentUploading}
aria-label={`Remove ${token}`}
>
<Trash2 className="h-3 w-3" />
</Button>
</li>
))}
</ul>
)}
</div>
</div>
</div>
<DialogFooter>
<DialogFooter className="px-6 py-4 border-t">
<Button
variant="outline"
onClick={() => setSendPo(null)}