feat(po-detail): show all attachments grouped by type
Split the flat document list into two labeled sections: Submission (po-document/* keys) and Delivery Receipt (receipt/* keys). Delivery notes from the Receipt record are also surfaced under that section. Added `receipt` to the PoWithRelations type so the field is available inside the component. Fixes #10
This commit is contained in:
parent
080dafb473
commit
e2f1fa6d50
1 changed files with 38 additions and 9 deletions
|
|
@ -65,6 +65,7 @@ type PoWithRelations = {
|
|||
sortOrder: number;
|
||||
}[];
|
||||
documents: { id: string; fileName: string; fileSize: number; storageKey: string; uploadedAt: Date }[];
|
||||
receipt: { id: string; storageKey: string; fileName: string; notes: string | null; confirmedAt: Date } | null;
|
||||
actions: { id: string; actionType: string; note: string | null; metadata: import("@prisma/client").Prisma.JsonValue; createdAt: Date; actor: { name: string } }[];
|
||||
};
|
||||
|
||||
|
|
@ -149,9 +150,10 @@ export async function PoDetail({ po, currentUserId, currentRole, readOnly = fals
|
|||
? "Submitter updated these line items after edits were requested. Previous values shown with strikethrough."
|
||||
: "Line items were amended by manager. Current values shown; original values shown with strikethrough.";
|
||||
|
||||
const downloadUrls = await Promise.all(
|
||||
const downloadUrlList = await Promise.all(
|
||||
po.documents.map((doc) => generateDownloadUrl(doc.storageKey))
|
||||
);
|
||||
const urlByDocId = new Map(po.documents.map((doc, i) => [doc.id, downloadUrlList[i]]));
|
||||
|
||||
const canConfirmReceipt =
|
||||
(po.status === "PAID_DELIVERED" || po.status === "PARTIALLY_CLOSED" || po.status === "PARTIALLY_PAID") &&
|
||||
|
|
@ -399,15 +401,19 @@ export async function PoDetail({ po, currentUserId, currentRole, readOnly = fals
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* Documents */}
|
||||
{po.documents.length > 0 && (
|
||||
<div className="rounded-lg border border-neutral-200 bg-white p-6">
|
||||
<h3 className="text-sm font-semibold text-neutral-900 mb-4">Attachments</h3>
|
||||
{/* Documents — grouped by type */}
|
||||
{(() => {
|
||||
const submissionDocs = po.documents.filter((d) => !d.storageKey.startsWith("receipt/"));
|
||||
const receiptDocs = po.documents.filter((d) => d.storageKey.startsWith("receipt/"));
|
||||
const hasDeliverySection = receiptDocs.length > 0 || !!po.receipt?.notes;
|
||||
if (submissionDocs.length === 0 && !hasDeliverySection) return null;
|
||||
|
||||
const renderDocList = (docs: typeof po.documents) => (
|
||||
<ul className="space-y-2">
|
||||
{po.documents.map((doc, i) => (
|
||||
{docs.map((doc) => (
|
||||
<li key={doc.id} className="flex items-center gap-3 text-sm">
|
||||
<a
|
||||
href={downloadUrls[i]}
|
||||
href={urlByDocId.get(doc.id)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="font-medium text-primary-600 hover:underline"
|
||||
|
|
@ -420,8 +426,31 @@ export async function PoDetail({ po, currentUserId, currentRole, readOnly = fals
|
|||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-neutral-200 bg-white p-6">
|
||||
<h3 className="text-sm font-semibold text-neutral-900 mb-4">Attachments</h3>
|
||||
<div className="space-y-5">
|
||||
{submissionDocs.length > 0 && (
|
||||
<div>
|
||||
<p className="text-xs font-medium text-neutral-500 uppercase tracking-wide mb-2">Submission</p>
|
||||
{renderDocList(submissionDocs)}
|
||||
</div>
|
||||
)}
|
||||
{hasDeliverySection && (
|
||||
<div>
|
||||
<p className="text-xs font-medium text-neutral-500 uppercase tracking-wide mb-2">Delivery Receipt</p>
|
||||
{po.receipt?.notes && (
|
||||
<p className="text-sm text-neutral-600 italic mb-2">“{po.receipt.notes}”</p>
|
||||
)}
|
||||
{receiptDocs.length > 0 && renderDocList(receiptDocs)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
|
||||
{/* Confirm receipt CTA */}
|
||||
{canConfirmReceipt && (
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue