feat(orders): make Send PO modal wider and two-column, scrollable body
This commit is contained in:
parent
f44f090351
commit
0eef0abd92
1 changed files with 118 additions and 110 deletions
|
|
@ -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)}
|
||||
|
|
|
|||
Loading…
Reference in a new issue