/** * Attachment grouping. * * All PO attachments are stored as `PODocument` rows. The lifecycle stage an * attachment belongs to is encoded in the leading segment of its `storageKey` * (see `buildStorageKey` in `lib/storage.ts`), e.g. `po-document//...` * or `receipt//...`. This module derives a user-facing grouping from * that prefix so the PO details screen can show every attachment grouped by * type (submission, payment, delivery). */ export type AttachmentGroupKey = "submission" | "payment" | "delivery" | "other"; export interface AttachmentGroupMeta { key: AttachmentGroupKey; label: string; description: string; } /** Display order for attachment groups (lifecycle order). */ export const ATTACHMENT_GROUP_ORDER: AttachmentGroupKey[] = [ "submission", "payment", "delivery", "other", ]; export const ATTACHMENT_GROUP_META: Record = { submission: { key: "submission", label: "Submission documents", description: "Uploaded with the purchase order (e.g. invoice, quotation).", }, payment: { key: "payment", label: "Payment documents", description: "Uploaded at payment (e.g. payment proof).", }, delivery: { key: "delivery", label: "Delivery receipts", description: "Uploaded at delivery confirmation (e.g. delivery receipt).", }, other: { key: "other", label: "Other attachments", description: "", }, }; /** * Derive the lifecycle group of an attachment from its storage key prefix. * Unknown prefixes fall back to "other" so nothing is ever hidden. */ export function categorizeAttachment(storageKey: string): AttachmentGroupKey { const prefix = storageKey.split("/")[0]; switch (prefix) { case "po-document": return "submission"; case "payment-document": case "payment": return "payment"; case "receipt": return "delivery"; default: return "other"; } } export interface AttachmentGroup { meta: AttachmentGroupMeta; items: T[]; } /** * Group attachments by lifecycle stage, returning only non-empty groups in * canonical lifecycle order. Item order within each group is preserved. */ export function groupAttachments( documents: T[] ): AttachmentGroup[] { const buckets = new Map(); for (const doc of documents) { const key = categorizeAttachment(doc.storageKey); const bucket = buckets.get(key); if (bucket) bucket.push(doc); else buckets.set(key, [doc]); } return ATTACHMENT_GROUP_ORDER.flatMap((key) => { const items = buckets.get(key); return items && items.length > 0 ? [{ meta: ATTACHMENT_GROUP_META[key], items }] : []; }); }