This commit is contained in:
2026-03-18 22:51:17 -05:00
parent dc07bfc8e0
commit 17b73a4597
8 changed files with 128 additions and 122 deletions

View File

@@ -29,6 +29,8 @@ This file is the running release and change log for CODEXIUM. Keep it updated wh
- Continued density standardization across CRM detail internals and inventory item editing so secondary cards, timeline/history panels, thumbnail panels, BOM/routing editors, and empty states use the tighter shared surface treatment with less filler copy - Continued density standardization across CRM detail internals and inventory item editing so secondary cards, timeline/history panels, thumbnail panels, BOM/routing editors, and empty states use the tighter shared surface treatment with less filler copy
- Continued density standardization across inventory detail transaction/transfer/reservation surfaces, and fixed item-editor navigation controls so SKU master and cancel actions navigate reliably from the create-item form - Continued density standardization across inventory detail transaction/transfer/reservation surfaces, and fixed item-editor navigation controls so SKU master and cancel actions navigate reliably from the create-item form
- Continued density standardization across sales, purchasing, shipping, and manufacturing editor internals, and standardized form-header cancel actions onto button-driven navigation to avoid in-form route-transition edge cases - Continued density standardization across sales, purchasing, shipping, and manufacturing editor internals, and standardized form-header cancel actions onto button-driven navigation to avoid in-form route-transition edge cases
- Continued density standardization across sales, purchasing, shipping, and manufacturing detail internals, including denser KPI strips, tighter side panels, shorter empty states, and less redundant context copy on high-traffic record views
- Continued density standardization across shared attachment and revision-comparison surfaces, and changed inventory item-editor exit actions to hard navigation so SKU master and cancel transitions no longer depend on client-side router state
- Project-side milestone and work-order rollups surfaced on project list and detail pages - Project-side milestone and work-order rollups surfaced on project list and detail pages
- Inventory SKU master builder with family-level sequence codes, branch-aware taxonomy management, and generated SKU previews on the item form - Inventory SKU master builder with family-level sequence codes, branch-aware taxonomy management, and generated SKU previews on the item form
- Thumbnail image attachment staging on inventory item create/edit pages, with upload-on-save and replacement/removal support - Thumbnail image attachment staging on inventory item create/edit pages, with upload-on-save and replacement/removal support

View File

@@ -100,7 +100,7 @@ function buildFieldChanges(left: ComparisonField[], right: ComparisonField[]): A
function ComparisonCard({ label, document }: { label: string; document: ComparisonDocument }) { function ComparisonCard({ label, document }: { label: string; document: ComparisonDocument }) {
return ( return (
<article className="rounded-[18px] border border-line/70 bg-page/60 p-4"> <article className="rounded-[18px] border border-line/70 bg-page/60 px-3 py-3">
<div className="flex flex-wrap items-start justify-between gap-3"> <div className="flex flex-wrap items-start justify-between gap-3">
<div> <div>
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">{label}</p> <p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">{label}</p>
@@ -111,7 +111,7 @@ function ComparisonCard({ label, document }: { label: string; document: Comparis
{document.status} {document.status}
</span> </span>
</div> </div>
<dl className="mt-4 grid gap-3 sm:grid-cols-2"> <dl className="mt-3 grid gap-2 sm:grid-cols-2">
{document.metaFields.map((field) => ( {document.metaFields.map((field) => (
<div key={`${label}-${field.label}`}> <div key={`${label}-${field.label}`}>
<dt className="text-xs font-semibold uppercase tracking-[0.16em] text-muted">{field.label}</dt> <dt className="text-xs font-semibold uppercase tracking-[0.16em] text-muted">{field.label}</dt>
@@ -119,15 +119,15 @@ function ComparisonCard({ label, document }: { label: string; document: Comparis
</div> </div>
))} ))}
</dl> </dl>
<div className="mt-4 grid gap-3 sm:grid-cols-2"> <div className="mt-3 grid gap-2 sm:grid-cols-2">
{document.totalFields.map((field) => ( {document.totalFields.map((field) => (
<div key={`${label}-total-${field.label}`} className="rounded-[16px] border border-line/70 bg-surface/80 px-3 py-3"> <div key={`${label}-total-${field.label}`} className="rounded-[16px] border border-line/70 bg-surface/80 px-2 py-2">
<div className="text-xs font-semibold uppercase tracking-[0.16em] text-muted">{field.label}</div> <div className="text-xs font-semibold uppercase tracking-[0.16em] text-muted">{field.label}</div>
<div className="mt-1 text-sm font-semibold text-text">{field.value}</div> <div className="mt-1 text-sm font-semibold text-text">{field.value}</div>
</div> </div>
))} ))}
</div> </div>
<div className="mt-4"> <div className="mt-3">
<div className="text-xs font-semibold uppercase tracking-[0.16em] text-muted">Notes</div> <div className="text-xs font-semibold uppercase tracking-[0.16em] text-muted">Notes</div>
<p className="mt-2 whitespace-pre-line text-sm leading-6 text-text">{document.notes || "No notes recorded."}</p> <p className="mt-2 whitespace-pre-line text-sm leading-6 text-text">{document.notes || "No notes recorded."}</p>
</div> </div>
@@ -164,11 +164,11 @@ export function DocumentRevisionComparison({
const totalChanges = buildFieldChanges(leftDocument.totalFields, rightDocument.totalFields); const totalChanges = buildFieldChanges(leftDocument.totalFields, rightDocument.totalFields);
return ( return (
<section className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel 2xl:p-5"> <section className="surface-panel">
<div className="flex flex-col gap-3 xl:flex-row xl:items-start xl:justify-between"> <div className="flex flex-col gap-3 xl:flex-row xl:items-start xl:justify-between">
<div> <div>
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">{title}</p> <p className="section-kicker">{title}</p>
<p className="mt-2 text-sm text-muted">{description}</p> <p className="mt-1 text-sm text-muted">{description}</p>
</div> </div>
<div className="grid gap-3 sm:grid-cols-2"> <div className="grid gap-3 sm:grid-cols-2">
<label className="block min-w-[220px]"> <label className="block min-w-[220px]">
@@ -202,19 +202,19 @@ export function DocumentRevisionComparison({
</label> </label>
</div> </div>
</div> </div>
<div className="mt-5 grid gap-3 xl:grid-cols-2"> <div className="mt-3 grid gap-3 xl:grid-cols-2">
<ComparisonCard label="Baseline" document={leftDocument} /> <ComparisonCard label="Baseline" document={leftDocument} />
<ComparisonCard label="Compare To" document={rightDocument} /> <ComparisonCard label="Compare To" document={rightDocument} />
</div> </div>
<div className="mt-5 grid gap-3 xl:grid-cols-2"> <div className="mt-3 grid gap-3 xl:grid-cols-2">
<article className="rounded-[18px] border border-line/70 bg-page/60 p-4"> <article className="rounded-[18px] border border-line/70 bg-page/60 px-3 py-3">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Field Changes</p> <p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Field Changes</p>
{metaChanges.length === 0 && totalChanges.length === 0 ? ( {metaChanges.length === 0 && totalChanges.length === 0 ? (
<div className="mt-4 text-sm text-muted">No header or total changes between the selected revisions.</div> <div className="mt-3 text-sm text-muted">No header or total changes.</div>
) : ( ) : (
<div className="mt-4 space-y-3"> <div className="mt-3 space-y-2">
{[...metaChanges, ...totalChanges].map((change) => ( {[...metaChanges, ...totalChanges].map((change) => (
<div key={change.label} className="rounded-[16px] border border-line/70 bg-surface/80 px-3 py-3"> <div key={change.label} className="rounded-[16px] border border-line/70 bg-surface/80 px-2 py-2">
<div className="text-xs font-semibold uppercase tracking-[0.16em] text-muted">{change.label}</div> <div className="text-xs font-semibold uppercase tracking-[0.16em] text-muted">{change.label}</div>
<div className="mt-2 text-sm text-text"> <div className="mt-2 text-sm text-text">
{change.leftValue} {"->"} {change.rightValue} {change.leftValue} {"->"} {change.rightValue}
@@ -224,28 +224,28 @@ export function DocumentRevisionComparison({
</div> </div>
)} )}
</article> </article>
<article className="rounded-[18px] border border-line/70 bg-page/60 p-4"> <article className="rounded-[18px] border border-line/70 bg-page/60 px-3 py-3">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Line Changes</p> <p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Line Changes</p>
<div className="mt-4 grid gap-3 sm:grid-cols-3"> <div className="mt-3 grid gap-2 sm:grid-cols-3">
<div className="rounded-[16px] border border-line/70 bg-surface/80 px-3 py-3"> <div className="rounded-[16px] border border-line/70 bg-surface/80 px-2 py-2">
<div className="text-xs font-semibold uppercase tracking-[0.16em] text-muted">Added</div> <div className="text-xs font-semibold uppercase tracking-[0.16em] text-muted">Added</div>
<div className="mt-1 text-base font-bold text-text">{diffRows.filter((row) => row.status === "ADDED").length}</div> <div className="mt-1 text-base font-bold text-text">{diffRows.filter((row) => row.status === "ADDED").length}</div>
</div> </div>
<div className="rounded-[16px] border border-line/70 bg-surface/80 px-3 py-3"> <div className="rounded-[16px] border border-line/70 bg-surface/80 px-2 py-2">
<div className="text-xs font-semibold uppercase tracking-[0.16em] text-muted">Removed</div> <div className="text-xs font-semibold uppercase tracking-[0.16em] text-muted">Removed</div>
<div className="mt-1 text-base font-bold text-text">{diffRows.filter((row) => row.status === "REMOVED").length}</div> <div className="mt-1 text-base font-bold text-text">{diffRows.filter((row) => row.status === "REMOVED").length}</div>
</div> </div>
<div className="rounded-[16px] border border-line/70 bg-surface/80 px-3 py-3"> <div className="rounded-[16px] border border-line/70 bg-surface/80 px-2 py-2">
<div className="text-xs font-semibold uppercase tracking-[0.16em] text-muted">Changed</div> <div className="text-xs font-semibold uppercase tracking-[0.16em] text-muted">Changed</div>
<div className="mt-1 text-base font-bold text-text">{diffRows.filter((row) => row.status === "CHANGED").length}</div> <div className="mt-1 text-base font-bold text-text">{diffRows.filter((row) => row.status === "CHANGED").length}</div>
</div> </div>
</div> </div>
{diffRows.length === 0 ? ( {diffRows.length === 0 ? (
<div className="mt-4 text-sm text-muted">No line-level changes between the selected revisions.</div> <div className="mt-3 text-sm text-muted">No line-level changes.</div>
) : ( ) : (
<div className="mt-4 space-y-3"> <div className="mt-3 space-y-2">
{diffRows.map((row) => ( {diffRows.map((row) => (
<div key={row.key} className="rounded-[16px] border border-line/70 bg-surface/80 px-3 py-3"> <div key={row.key} className="rounded-[16px] border border-line/70 bg-surface/80 px-2 py-2">
<div className="flex flex-wrap items-center justify-between gap-3"> <div className="flex flex-wrap items-center justify-between gap-3">
<div className="text-sm font-semibold text-text">{row.right?.title ?? row.left?.title}</div> <div className="text-sm font-semibold text-text">{row.right?.title ?? row.left?.title}</div>
<span className="text-xs font-semibold uppercase tracking-[0.16em] text-muted">{row.status}</span> <span className="text-xs font-semibold uppercase tracking-[0.16em] text-muted">{row.status}</span>

View File

@@ -133,12 +133,12 @@ export function FileAttachmentsPanel({
} }
return ( return (
<article className="min-w-0 rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel 2xl:p-5"> <article className="surface-panel min-w-0">
<div className="flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between"> <div className="flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between">
<div> <div>
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">{eyebrow}</p> <p className="section-kicker">{eyebrow}</p>
<h4 className="mt-2 text-lg font-bold text-text">{title}</h4> <h4 className="text-lg font-bold text-text">{title}</h4>
<p className="mt-2 text-sm text-muted">{description}</p> <p className="mt-1 text-sm text-muted">{description}</p>
</div> </div>
{canWriteFiles ? ( {canWriteFiles ? (
<label className="inline-flex cursor-pointer items-center justify-center rounded-2xl bg-brand px-2 py-2 text-sm font-semibold text-white"> <label className="inline-flex cursor-pointer items-center justify-center rounded-2xl bg-brand px-2 py-2 text-sm font-semibold text-white">
@@ -147,17 +147,17 @@ export function FileAttachmentsPanel({
</label> </label>
) : null} ) : null}
</div> </div>
<div className="mt-5 rounded-2xl border border-line/70 bg-page/70 px-2 py-2 text-sm text-muted">{status}</div> <div className="mt-3 rounded-2xl border border-line/70 bg-page/70 px-2 py-2 text-sm text-muted">{status}</div>
{!canReadFiles ? ( {!canReadFiles ? (
<div className="mt-5 rounded-[18px] border border-dashed border-line/70 bg-page/60 px-4 py-8 text-center text-sm text-muted"> <div className="mt-3 rounded-[18px] border border-dashed border-line/70 bg-page/60 px-3 py-5 text-center text-sm text-muted">
You do not have permission to view file attachments. You do not have permission to view file attachments.
</div> </div>
) : attachments.length === 0 ? ( ) : attachments.length === 0 ? (
<div className="mt-5 rounded-[18px] border border-dashed border-line/70 bg-page/60 px-4 py-8 text-center text-sm text-muted"> <div className="mt-3 rounded-[18px] border border-dashed border-line/70 bg-page/60 px-3 py-5 text-center text-sm text-muted">
{emptyMessage} {emptyMessage}
</div> </div>
) : ( ) : (
<div className="mt-5 space-y-3"> <div className="mt-3 space-y-2">
{attachments.map((attachment) => ( {attachments.map((attachment) => (
<div <div
key={attachment.id} key={attachment.id}
@@ -166,7 +166,7 @@ export function FileAttachmentsPanel({
<div className="min-w-0"> <div className="min-w-0">
<p className="truncate text-sm font-semibold text-text">{attachment.originalName}</p> <p className="truncate text-sm font-semibold text-text">{attachment.originalName}</p>
<p className="mt-1 text-xs text-muted"> <p className="mt-1 text-xs text-muted">
{attachment.mimeType} · {formatFileSize(attachment.sizeBytes)} · {new Date(attachment.createdAt).toLocaleString()} {attachment.mimeType} - {formatFileSize(attachment.sizeBytes)} - {new Date(attachment.createdAt).toLocaleString()}
</p> </p>
</div> </div>
<div className="flex shrink-0 gap-3"> <div className="flex shrink-0 gap-3">

View File

@@ -444,12 +444,16 @@ export function InventoryFormPage({ mode }: InventoryFormPageProps) {
} }
} }
function forceNavigate(path: string) {
window.location.assign(path);
}
function openSkuMaster() { function openSkuMaster() {
navigate("/inventory/sku-master"); forceNavigate("/inventory/sku-master");
} }
function closeEditor() { function closeEditor() {
navigate(mode === "create" ? "/inventory/items" : `/inventory/items/${itemId}`); forceNavigate(mode === "create" ? "/inventory/items" : `/inventory/items/${itemId}`);
} }
return ( return (

View File

@@ -431,8 +431,8 @@ export function WorkOrderDetailPage() {
</div> </div>
</section> </section>
) : null} ) : null}
<section className="grid gap-3 xl:grid-cols-6"> <section className="grid gap-2 xl:grid-cols-6">
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Planned</p><div className="mt-2 text-base font-bold text-text">{workOrder.quantity}</div></article> <article className="surface-panel-tight"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Planned</p><div className="mt-2 text-base font-bold text-text">{workOrder.quantity}</div></article>
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Completed</p><div className="mt-2 text-base font-bold text-text">{workOrder.completedQuantity}</div></article> <article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Completed</p><div className="mt-2 text-base font-bold text-text">{workOrder.completedQuantity}</div></article>
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Remaining</p><div className="mt-2 text-base font-bold text-text">{workOrder.dueQuantity}</div></article> <article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Remaining</p><div className="mt-2 text-base font-bold text-text">{workOrder.dueQuantity}</div></article>
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Project</p><div className="mt-2 text-base font-bold text-text">{workOrder.projectNumber || "Unlinked"}</div></article> <article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Project</p><div className="mt-2 text-base font-bold text-text">{workOrder.projectNumber || "Unlinked"}</div></article>

View File

@@ -341,19 +341,19 @@ export function PurchaseDetailPage() {
</div> </div>
</section> </section>
) : null} ) : null}
<section className="grid gap-3 xl:grid-cols-4"> <section className="grid gap-2 xl:grid-cols-4">
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Issue Date</p><div className="mt-2 text-base font-bold text-text">{new Date(activeDocument.issueDate).toLocaleDateString()}</div></article> <article className="surface-panel-tight"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Issue Date</p><div className="mt-2 text-base font-bold text-text">{new Date(activeDocument.issueDate).toLocaleDateString()}</div></article>
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Lines</p><div className="mt-2 text-base font-bold text-text">{activeDocument.lineCount}</div></article> <article className="surface-panel-tight"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Lines</p><div className="mt-2 text-base font-bold text-text">{activeDocument.lineCount}</div></article>
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Receipts</p><div className="mt-2 text-base font-bold text-text">{activeDocument.receipts.length}</div></article> <article className="surface-panel-tight"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Receipts</p><div className="mt-2 text-base font-bold text-text">{activeDocument.receipts.length}</div></article>
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Qty Remaining</p><div className="mt-2 text-base font-bold text-text">{activeDocument.lines.reduce((sum, line) => sum + line.remainingQuantity, 0)}</div></article> <article className="surface-panel-tight"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Qty Remaining</p><div className="mt-2 text-base font-bold text-text">{activeDocument.lines.reduce((sum, line) => sum + line.remainingQuantity, 0)}</div></article>
</section> </section>
<section className="grid gap-3 xl:grid-cols-4"> <section className="grid gap-2 xl:grid-cols-4">
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Subtotal</p><div className="mt-2 text-base font-bold text-text">${activeDocument.subtotal.toFixed(2)}</div></article> <article className="surface-panel-tight"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Subtotal</p><div className="mt-2 text-base font-bold text-text">${activeDocument.subtotal.toFixed(2)}</div></article>
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Total</p><div className="mt-2 text-base font-bold text-text">${activeDocument.total.toFixed(2)}</div></article> <article className="surface-panel-tight"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Total</p><div className="mt-2 text-base font-bold text-text">${activeDocument.total.toFixed(2)}</div></article>
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Tax</p><div className="mt-2 text-base font-bold text-text">${activeDocument.taxAmount.toFixed(2)}</div><div className="mt-1 text-xs text-muted">{activeDocument.taxPercent.toFixed(2)}%</div></article> <article className="surface-panel-tight"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Tax</p><div className="mt-2 text-base font-bold text-text">${activeDocument.taxAmount.toFixed(2)}</div><div className="mt-1 text-xs text-muted">{activeDocument.taxPercent.toFixed(2)}%</div></article>
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Freight</p><div className="mt-2 text-base font-bold text-text">${activeDocument.freightAmount.toFixed(2)}</div></article> <article className="surface-panel-tight"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Freight</p><div className="mt-2 text-base font-bold text-text">${activeDocument.freightAmount.toFixed(2)}</div></article>
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Payment Terms</p><div className="mt-2 text-base font-bold text-text">{activeDocument.paymentTerms || "N/A"}</div></article> <article className="surface-panel-tight"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Payment Terms</p><div className="mt-2 text-base font-bold text-text">{activeDocument.paymentTerms || "N/A"}</div></article>
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Currency</p><div className="mt-2 text-base font-bold text-text">{activeDocument.currencyCode || "USD"}</div></article> <article className="surface-panel-tight"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Currency</p><div className="mt-2 text-base font-bold text-text">{activeDocument.currencyCode || "USD"}</div></article>
</section> </section>
<section className="surface-panel"> <section className="surface-panel">
<div className="flex items-center justify-between gap-3"> <div className="flex items-center justify-between gap-3">
@@ -362,11 +362,11 @@ export function PurchaseDetailPage() {
</div> </div>
</div> </div>
{activeDocument.revisions.length === 0 ? ( {activeDocument.revisions.length === 0 ? (
<div className="mt-6 rounded-[18px] border border-dashed border-line/70 bg-page/60 px-4 py-8 text-center text-sm text-muted"> <div className="mt-3 rounded-[18px] border border-dashed border-line/70 bg-page/60 px-3 py-5 text-center text-sm text-muted">
No revisions have been recorded yet. No revisions recorded yet.
</div> </div>
) : ( ) : (
<div className="mt-6 space-y-3"> <div className="mt-3 space-y-2">
{activeDocument.revisions.map((revision) => ( {activeDocument.revisions.map((revision) => (
<article key={revision.id} className="rounded-[18px] border border-line/70 bg-page/60 p-3"> <article key={revision.id} className="rounded-[18px] border border-line/70 bg-page/60 p-3">
<div className="flex flex-wrap items-start justify-between gap-3"> <div className="flex flex-wrap items-start justify-between gap-3">
@@ -417,37 +417,37 @@ export function PurchaseDetailPage() {
/> />
) : null} ) : null}
<div className="grid gap-3 xl:grid-cols-[minmax(0,1.05fr)_minmax(320px,0.95fr)]"> <div className="grid gap-3 xl:grid-cols-[minmax(0,1.05fr)_minmax(320px,0.95fr)]">
<article className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel 2xl:p-5"> <article className="surface-panel">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Vendor</p> <p className="section-kicker">VENDOR</p>
<dl className="mt-5 grid gap-3"> <dl className="mt-5 grid gap-3">
<div><dt className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Account</dt><dd className="mt-1 text-sm text-text"><Link to={`/crm/vendors/${activeDocument.vendorId}`} className="hover:text-brand">{activeDocument.vendorName}</Link></dd></div> <div><dt className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Account</dt><dd className="mt-1 text-sm text-text"><Link to={`/crm/vendors/${activeDocument.vendorId}`} className="hover:text-brand">{activeDocument.vendorName}</Link></dd></div>
<div><dt className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Email</dt><dd className="mt-1 text-sm text-text">{activeDocument.vendorEmail}</dd></div> <div><dt className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Email</dt><dd className="mt-1 text-sm text-text">{activeDocument.vendorEmail}</dd></div>
</dl> </dl>
</article> </article>
<article className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel 2xl:p-5"> <article className="surface-panel">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Project Link</p> <p className="section-kicker">PROJECT LINK</p>
{activeDocument.projectId ? ( {activeDocument.projectId ? (
<div className="mt-3 space-y-2"> <div className="mt-3 space-y-2">
<Link to={`/projects/${activeDocument.projectId}`} className="inline-flex items-center rounded-2xl border border-line/70 px-3 py-2 text-sm font-semibold text-text hover:bg-page/70"> <Link to={`/projects/${activeDocument.projectId}`} className="inline-flex items-center rounded-2xl border border-line/70 px-3 py-2 text-sm font-semibold text-text hover:bg-page/70">
{activeDocument.projectNumber} / {activeDocument.projectName} {activeDocument.projectNumber} / {activeDocument.projectName}
</Link> </Link>
<p className="text-sm text-muted">This purchase order is linked to the project context used by project cockpit and downstream rollups.</p> <p className="text-sm text-muted">Project cockpit and rollups use this linkage.</p>
</div> </div>
) : ( ) : (
<p className="mt-3 text-sm text-muted">No linked project is currently attached to this purchase order.</p> <p className="mt-3 text-sm text-muted">No linked project.</p>
)} )}
<p className="mt-5 text-xs font-semibold uppercase tracking-[0.24em] text-muted">Notes</p> <p className="mt-4 text-xs font-semibold uppercase tracking-[0.24em] text-muted">Notes</p>
<p className="mt-3 whitespace-pre-line text-sm leading-6 text-text">{activeDocument.notes || "No notes recorded for this document."}</p> <p className="mt-3 whitespace-pre-line text-sm leading-6 text-text">{activeDocument.notes || "No notes recorded for this document."}</p>
</article> </article>
</div> </div>
<section className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel 2xl:p-5"> <section className="surface-panel">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Demand Context</p> <p className="section-kicker">DEMAND CONTEXT</p>
{demandContextItems.length === 0 ? ( {demandContextItems.length === 0 ? (
<div className="mt-6 rounded-[18px] border border-dashed border-line/70 bg-page/60 px-4 py-8 text-center text-sm text-muted"> <div className="mt-3 rounded-[18px] border border-dashed border-line/70 bg-page/60 px-3 py-5 text-center text-sm text-muted">
No active shared shortage or buy-signal records currently point at items on this purchase order. No active shortage or buy-signal context for these items.
</div> </div>
) : ( ) : (
<div className="mt-5 space-y-3"> <div className="mt-3 space-y-2">
{demandContextItems.map((item) => ( {demandContextItems.map((item) => (
<div key={item.itemId} className="rounded-[18px] border border-line/70 bg-page/60 p-3"> <div key={item.itemId} className="rounded-[18px] border border-line/70 bg-page/60 p-3">
<div className="flex flex-wrap items-center justify-between gap-3"> <div className="flex flex-wrap items-center justify-between gap-3">
@@ -464,12 +464,12 @@ export function PurchaseDetailPage() {
</div> </div>
)} )}
</section> </section>
<section className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel 2xl:p-5"> <section className="surface-panel">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Line Items</p> <p className="section-kicker">LINE ITEMS</p>
{activeDocument.lines.length === 0 ? ( {activeDocument.lines.length === 0 ? (
<div className="mt-6 rounded-[18px] border border-dashed border-line/70 bg-page/60 px-4 py-8 text-center text-sm text-muted">No line items have been added yet.</div> <div className="mt-3 rounded-[18px] border border-dashed border-line/70 bg-page/60 px-3 py-5 text-center text-sm text-muted">No line items added yet.</div>
) : ( ) : (
<div className="mt-6 overflow-hidden rounded-2xl border border-line/70"> <div className="mt-3 overflow-hidden rounded-2xl border border-line/70">
<table className="min-w-full divide-y divide-line/70 text-sm"> <table className="min-w-full divide-y divide-line/70 text-sm">
<thead className="bg-page/80 text-left text-muted"> <thead className="bg-page/80 text-left text-muted">
<tr><th className="px-2 py-2">Item</th><th className="px-2 py-2">Description</th><th className="px-2 py-2">Demand Source</th><th className="px-2 py-2">Ordered</th><th className="px-2 py-2">Received</th><th className="px-2 py-2">Remaining</th><th className="px-2 py-2">UOM</th><th className="px-2 py-2">Unit Cost</th><th className="px-2 py-2">Total</th></tr> <tr><th className="px-2 py-2">Item</th><th className="px-2 py-2">Description</th><th className="px-2 py-2">Demand Source</th><th className="px-2 py-2">Ordered</th><th className="px-2 py-2">Received</th><th className="px-2 py-2">Remaining</th><th className="px-2 py-2">UOM</th><th className="px-2 py-2">Unit Cost</th><th className="px-2 py-2">Total</th></tr>
@@ -504,7 +504,7 @@ export function PurchaseDetailPage() {
All ordered quantities have been received for this purchase order. All ordered quantities have been received for this purchase order.
</div> </div>
) : ( ) : (
<form className="mt-5 space-y-4" onSubmit={handleReceiptSubmit}> <form className="mt-3 space-y-3" onSubmit={handleReceiptSubmit}>
<div className="grid gap-3 xl:grid-cols-2"> <div className="grid gap-3 xl:grid-cols-2">
<label className="block"> <label className="block">
<span className="mb-2 block text-sm font-semibold text-text">Receipt date</span> <span className="mb-2 block text-sm font-semibold text-text">Receipt date</span>
@@ -576,7 +576,7 @@ export function PurchaseDetailPage() {
</div> </div>
))} ))}
</div> </div>
<div className="flex flex-col gap-3 rounded-2xl border border-line/70 bg-page/70 px-2 py-2 sm:flex-row sm:items-center sm:justify-between"> <div className="flex flex-col gap-2 rounded-2xl border border-line/70 bg-page/70 px-2 py-2 sm:flex-row sm:items-center sm:justify-between">
<span className="min-w-0 text-sm text-muted">{receiptStatus}</span> <span className="min-w-0 text-sm text-muted">{receiptStatus}</span>
<button <button
type="submit" type="submit"
@@ -593,11 +593,11 @@ export function PurchaseDetailPage() {
<article className="surface-panel"> <article className="surface-panel">
<p className="section-kicker">RECEIPT HISTORY</p> <p className="section-kicker">RECEIPT HISTORY</p>
{activeDocument.receipts.length === 0 ? ( {activeDocument.receipts.length === 0 ? (
<div className="mt-6 rounded-[18px] border border-dashed border-line/70 bg-page/60 px-4 py-8 text-center text-sm text-muted"> <div className="mt-3 rounded-[18px] border border-dashed border-line/70 bg-page/60 px-3 py-5 text-center text-sm text-muted">
No purchase receipts have been recorded for this order yet. No purchase receipts recorded yet.
</div> </div>
) : ( ) : (
<div className="mt-6 space-y-3"> <div className="mt-3 space-y-2">
{activeDocument.receipts.map((receipt) => ( {activeDocument.receipts.map((receipt) => (
<article key={receipt.id} className="rounded-[18px] border border-line/70 bg-page/60 p-3"> <article key={receipt.id} className="rounded-[18px] border border-line/70 bg-page/60 p-3">
<div className="flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between"> <div className="flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between">

View File

@@ -334,7 +334,7 @@ export function SalesDetailPage({ entity }: { entity: SalesDocumentEntity }) {
} }
return ( return (
<section className="space-y-4"> <section className="page-stack">
<div className="surface-panel"> <div className="surface-panel">
<div className="flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between"> <div className="flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between">
<div> <div>
@@ -417,41 +417,41 @@ export function SalesDetailPage({ entity }: { entity: SalesDocumentEntity }) {
</div> </div>
</section> </section>
) : null} ) : null}
<section className="grid gap-3 xl:grid-cols-4"> <section className="grid gap-2 xl:grid-cols-4">
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"> <article className="surface-panel-tight">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Issue Date</p> <p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Issue Date</p>
<div className="mt-2 text-base font-bold text-text">{new Date(activeDocument.issueDate).toLocaleDateString()}</div> <div className="mt-2 text-base font-bold text-text">{new Date(activeDocument.issueDate).toLocaleDateString()}</div>
</article> </article>
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"> <article className="surface-panel-tight">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Expires</p> <p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Expires</p>
<div className="mt-2 text-base font-bold text-text">{activeDocument.expiresAt ? new Date(activeDocument.expiresAt).toLocaleDateString() : "N/A"}</div> <div className="mt-2 text-base font-bold text-text">{activeDocument.expiresAt ? new Date(activeDocument.expiresAt).toLocaleDateString() : "N/A"}</div>
</article> </article>
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"> <article className="surface-panel-tight">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Lines</p> <p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Lines</p>
<div className="mt-2 text-base font-bold text-text">{activeDocument.lineCount}</div> <div className="mt-2 text-base font-bold text-text">{activeDocument.lineCount}</div>
</article> </article>
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"> <article className="surface-panel-tight">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Approval</p> <p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Approval</p>
<div className="mt-2 text-base font-bold text-text">{activeDocument.approvedAt ? new Date(activeDocument.approvedAt).toLocaleDateString() : "Pending"}</div> <div className="mt-2 text-base font-bold text-text">{activeDocument.approvedAt ? new Date(activeDocument.approvedAt).toLocaleDateString() : "Pending"}</div>
<div className="mt-1 text-xs text-muted">{activeDocument.approvedByName ?? "No approver recorded"}</div> <div className="mt-1 text-xs text-muted">{activeDocument.approvedByName ?? "No approver recorded"}</div>
</article> </article>
</section> </section>
<section className="grid gap-3 xl:grid-cols-4"> <section className="grid gap-2 xl:grid-cols-4">
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"> <article className="surface-panel-tight">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Discount</p> <p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Discount</p>
<div className="mt-2 text-base font-bold text-text">-${activeDocument.discountAmount.toFixed(2)}</div> <div className="mt-2 text-base font-bold text-text">-${activeDocument.discountAmount.toFixed(2)}</div>
<div className="mt-1 text-xs text-muted">{activeDocument.discountPercent.toFixed(2)}%</div> <div className="mt-1 text-xs text-muted">{activeDocument.discountPercent.toFixed(2)}%</div>
</article> </article>
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"> <article className="surface-panel-tight">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Tax</p> <p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Tax</p>
<div className="mt-2 text-base font-bold text-text">${activeDocument.taxAmount.toFixed(2)}</div> <div className="mt-2 text-base font-bold text-text">${activeDocument.taxAmount.toFixed(2)}</div>
<div className="mt-1 text-xs text-muted">{activeDocument.taxPercent.toFixed(2)}%</div> <div className="mt-1 text-xs text-muted">{activeDocument.taxPercent.toFixed(2)}%</div>
</article> </article>
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"> <article className="surface-panel-tight">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Freight</p> <p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Freight</p>
<div className="mt-2 text-base font-bold text-text">${activeDocument.freightAmount.toFixed(2)}</div> <div className="mt-2 text-base font-bold text-text">${activeDocument.freightAmount.toFixed(2)}</div>
</article> </article>
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"> <article className="surface-panel-tight">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Total</p> <p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Total</p>
<div className="mt-2 text-base font-bold text-text">${activeDocument.total.toFixed(2)}</div> <div className="mt-2 text-base font-bold text-text">${activeDocument.total.toFixed(2)}</div>
</article> </article>
@@ -463,11 +463,11 @@ export function SalesDetailPage({ entity }: { entity: SalesDocumentEntity }) {
</div> </div>
</div> </div>
{activeDocument.revisions.length === 0 ? ( {activeDocument.revisions.length === 0 ? (
<div className="mt-6 rounded-[18px] border border-dashed border-line/70 bg-page/60 px-4 py-8 text-center text-sm text-muted"> <div className="mt-3 rounded-[18px] border border-dashed border-line/70 bg-page/60 px-3 py-5 text-center text-sm text-muted">
No revisions have been recorded yet. No revisions recorded yet.
</div> </div>
) : ( ) : (
<div className="mt-6 space-y-3"> <div className="mt-3 space-y-2">
{activeDocument.revisions.map((revision) => ( {activeDocument.revisions.map((revision) => (
<article key={revision.id} className="rounded-[18px] border border-line/70 bg-page/60 p-3"> <article key={revision.id} className="rounded-[18px] border border-line/70 bg-page/60 p-3">
<div className="flex flex-wrap items-start justify-between gap-3"> <div className="flex flex-wrap items-start justify-between gap-3">
@@ -517,8 +517,8 @@ export function SalesDetailPage({ entity }: { entity: SalesDocumentEntity }) {
/> />
) : null} ) : null}
<div className="grid gap-3 xl:grid-cols-[minmax(0,1.05fr)_minmax(320px,0.95fr)]"> <div className="grid gap-3 xl:grid-cols-[minmax(0,1.05fr)_minmax(320px,0.95fr)]">
<article className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel 2xl:p-5"> <article className="surface-panel">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Customer</p> <p className="section-kicker">CUSTOMER</p>
<dl className="mt-5 grid gap-3"> <dl className="mt-5 grid gap-3">
<div> <div>
<dt className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Account</dt> <dt className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Account</dt>
@@ -530,30 +530,30 @@ export function SalesDetailPage({ entity }: { entity: SalesDocumentEntity }) {
</div> </div>
</dl> </dl>
</article> </article>
<article className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel 2xl:p-5"> <article className="surface-panel">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Project Link</p> <p className="section-kicker">PROJECT LINK</p>
{activeDocument.linkedProjectId ? ( {activeDocument.linkedProjectId ? (
<div className="mt-3 space-y-2"> <div className="mt-3 space-y-2">
<Link to={`/projects/${activeDocument.linkedProjectId}`} className="inline-flex items-center rounded-2xl border border-line/70 px-3 py-2 text-sm font-semibold text-text hover:bg-page/70"> <Link to={`/projects/${activeDocument.linkedProjectId}`} className="inline-flex items-center rounded-2xl border border-line/70 px-3 py-2 text-sm font-semibold text-text hover:bg-page/70">
{activeDocument.linkedProjectNumber} / {activeDocument.linkedProjectName} {activeDocument.linkedProjectNumber} / {activeDocument.linkedProjectName}
</Link> </Link>
<p className="text-sm text-muted">This {entity === "quote" ? "quote" : "sales order"} is already linked to a project, and downstream WO/PO launches will carry that project context.</p> <p className="text-sm text-muted">Downstream WO and PO launches carry this project context.</p>
</div> </div>
) : ( ) : (
<p className="mt-3 text-sm text-muted">No linked project is currently attached to this {entity === "quote" ? "quote" : "sales order"}.</p> <p className="mt-3 text-sm text-muted">No linked project.</p>
)} )}
<p className="mt-5 text-xs font-semibold uppercase tracking-[0.24em] text-muted">Notes</p> <p className="mt-4 text-xs font-semibold uppercase tracking-[0.24em] text-muted">Notes</p>
<p className="mt-3 whitespace-pre-line text-sm leading-6 text-text">{activeDocument.notes || "No notes recorded for this document."}</p> <p className="mt-3 whitespace-pre-line text-sm leading-6 text-text">{activeDocument.notes || "No notes recorded for this document."}</p>
</article> </article>
</div> </div>
<section className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel 2xl:p-5"> <section className="surface-panel">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Line Items</p> <p className="section-kicker">LINE ITEMS</p>
{activeDocument.lines.length === 0 ? ( {activeDocument.lines.length === 0 ? (
<div className="mt-6 rounded-[18px] border border-dashed border-line/70 bg-page/60 px-4 py-8 text-center text-sm text-muted"> <div className="mt-3 rounded-[18px] border border-dashed border-line/70 bg-page/60 px-3 py-5 text-center text-sm text-muted">
No line items have been added yet. No line items added yet.
</div> </div>
) : ( ) : (
<div className="mt-6 overflow-hidden rounded-2xl border border-line/70"> <div className="mt-3 overflow-hidden rounded-2xl border border-line/70">
<table className="min-w-full divide-y divide-line/70 text-sm"> <table className="min-w-full divide-y divide-line/70 text-sm">
<thead className="bg-page/80 text-left text-muted"> <thead className="bg-page/80 text-left text-muted">
<tr> <tr>
@@ -595,23 +595,23 @@ export function SalesDetailPage({ entity }: { entity: SalesDocumentEntity }) {
<div>Status {planning.status}</div> <div>Status {planning.status}</div>
</div> </div>
</div> </div>
<div className="mt-5 grid gap-3 xl:grid-cols-4"> <div className="mt-4 grid gap-2 xl:grid-cols-4">
<article className="rounded-[18px] border border-line/70 bg-page/70 px-3 py-3"> <article className="surface-panel-tight bg-page/70 shadow-none">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Build Recommendations</p> <p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Build Recommendations</p>
<div className="mt-2 text-base font-bold text-text">{planning.summary.totalBuildQuantity}</div> <div className="mt-2 text-base font-bold text-text">{planning.summary.totalBuildQuantity}</div>
<div className="mt-1 text-xs text-muted">{planning.summary.buildRecommendationCount} items</div> <div className="mt-1 text-xs text-muted">{planning.summary.buildRecommendationCount} items</div>
</article> </article>
<article className="rounded-[18px] border border-line/70 bg-page/70 px-3 py-3"> <article className="surface-panel-tight bg-page/70 shadow-none">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Purchase Recommendations</p> <p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Purchase Recommendations</p>
<div className="mt-2 text-base font-bold text-text">{planning.summary.totalPurchaseQuantity}</div> <div className="mt-2 text-base font-bold text-text">{planning.summary.totalPurchaseQuantity}</div>
<div className="mt-1 text-xs text-muted">{planning.summary.purchaseRecommendationCount} items</div> <div className="mt-1 text-xs text-muted">{planning.summary.purchaseRecommendationCount} items</div>
</article> </article>
<article className="rounded-[18px] border border-line/70 bg-page/70 px-3 py-3"> <article className="surface-panel-tight bg-page/70 shadow-none">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Uncovered</p> <p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Uncovered</p>
<div className="mt-2 text-base font-bold text-text">{planning.summary.totalUncoveredQuantity}</div> <div className="mt-2 text-base font-bold text-text">{planning.summary.totalUncoveredQuantity}</div>
<div className="mt-1 text-xs text-muted">{planning.summary.uncoveredItemCount} items</div> <div className="mt-1 text-xs text-muted">{planning.summary.uncoveredItemCount} items</div>
</article> </article>
<article className="rounded-[18px] border border-line/70 bg-page/70 px-3 py-3"> <article className="surface-panel-tight bg-page/70 shadow-none">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Planned Items</p> <p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Planned Items</p>
<div className="mt-2 text-base font-bold text-text">{planning.summary.itemCount}</div> <div className="mt-2 text-base font-bold text-text">{planning.summary.itemCount}</div>
<div className="mt-1 text-xs text-muted">{planning.summary.lineCount} sales lines</div> <div className="mt-1 text-xs text-muted">{planning.summary.lineCount} sales lines</div>
@@ -682,7 +682,7 @@ export function SalesDetailPage({ entity }: { entity: SalesDocumentEntity }) {
</Link> </Link>
</div> </div>
) : null} ) : null}
<div className="mt-5 space-y-3"> <div className="mt-4 space-y-2">
{planning.lines.map((line) => ( {planning.lines.map((line) => (
<div key={line.lineId} className="rounded-[18px] border border-line/70 bg-page/60 p-3"> <div key={line.lineId} className="rounded-[18px] border border-line/70 bg-page/60 p-3">
<div className="mb-3"> <div className="mb-3">
@@ -712,11 +712,11 @@ export function SalesDetailPage({ entity }: { entity: SalesDocumentEntity }) {
) : null} ) : null}
</div> </div>
{shipments.length === 0 ? ( {shipments.length === 0 ? (
<div className="mt-6 rounded-[18px] border border-dashed border-line/70 bg-page/60 px-4 py-8 text-center text-sm text-muted"> <div className="mt-3 rounded-[18px] border border-dashed border-line/70 bg-page/60 px-3 py-5 text-center text-sm text-muted">
No shipments have been created for this sales order yet. No shipments created yet.
</div> </div>
) : ( ) : (
<div className="mt-6 space-y-3"> <div className="mt-3 space-y-2">
{shipments.map((shipment) => ( {shipments.map((shipment) => (
<Link key={shipment.id} to={`/shipping/shipments/${shipment.id}`} className="block rounded-[18px] border border-line/70 bg-page/60 p-3 transition hover:bg-page/80"> <Link key={shipment.id} to={`/shipping/shipments/${shipment.id}`} className="block rounded-[18px] border border-line/70 bg-page/60 p-3 transition hover:bg-page/80">
<div className="flex flex-wrap items-center justify-between gap-3"> <div className="flex flex-wrap items-center justify-between gap-3">

View File

@@ -265,20 +265,20 @@ export function ShipmentDetailPage() {
</section> </section>
) : null} ) : null}
<section className="grid gap-3 xl:grid-cols-4"> <section className="grid gap-2 xl:grid-cols-4">
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"> <article className="surface-panel-tight">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Carrier</p> <p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Carrier</p>
<div className="mt-2 text-base font-bold text-text">{shipment.carrier || "Not set"}</div> <div className="mt-2 text-base font-bold text-text">{shipment.carrier || "Not set"}</div>
</article> </article>
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"> <article className="surface-panel-tight">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Ordered Units</p> <p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Ordered Units</p>
<div className="mt-2 text-base font-bold text-text">{totalOrderedQuantity}</div> <div className="mt-2 text-base font-bold text-text">{totalOrderedQuantity}</div>
</article> </article>
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"> <article className="surface-panel-tight">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Picked Units</p> <p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Picked Units</p>
<div className="mt-2 text-base font-bold text-text">{totalPickedQuantity}</div> <div className="mt-2 text-base font-bold text-text">{totalPickedQuantity}</div>
</article> </article>
<article className="rounded-[18px] border border-line/70 bg-surface/90 px-3 py-3 shadow-panel"> <article className="surface-panel-tight">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Packages</p> <p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Packages</p>
<div className="mt-2 text-base font-bold text-text">{shipment.packageCount}</div> <div className="mt-2 text-base font-bold text-text">{shipment.packageCount}</div>
</article> </article>
@@ -324,8 +324,8 @@ export function ShipmentDetailPage() {
</div> </div>
</article> </article>
<article className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel 2xl:p-5"> <article className="surface-panel">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Timing</p> <p className="section-kicker">TIMING</p>
<dl className="mt-5 grid gap-3"> <dl className="mt-5 grid gap-3">
<div> <div>
<dt className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Ship Date</dt> <dt className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Ship Date</dt>
@@ -353,8 +353,8 @@ export function ShipmentDetailPage() {
<div> <div>
<p className="section-kicker">PICK AND ISSUE FROM STOCK</p> <p className="section-kicker">PICK AND ISSUE FROM STOCK</p>
</div> </div>
<div className="rounded-[16px] border border-line/70 bg-page/60 px-3 py-2 text-xs text-muted"> <div className="rounded-[16px] border border-line/70 bg-page/60 px-2 py-2 text-xs text-muted">
Select the sales-order line, source location, and quantity you are physically picking. Select line, location, and quantity.
</div> </div>
</div> </div>
<div className="mt-5 grid gap-3 md:grid-cols-2 xl:grid-cols-5"> <div className="mt-5 grid gap-3 md:grid-cols-2 xl:grid-cols-5">
@@ -470,11 +470,11 @@ export function ShipmentDetailPage() {
</div> </div>
</div> </div>
{shipment.picks.length === 0 ? ( {shipment.picks.length === 0 ? (
<div className="mt-6 rounded-[18px] border border-dashed border-line/70 bg-page/60 px-4 py-8 text-center text-sm text-muted"> <div className="mt-3 rounded-[18px] border border-dashed border-line/70 bg-page/60 px-3 py-5 text-center text-sm text-muted">
No shipment picks have been posted yet. No shipment picks posted yet.
</div> </div>
) : ( ) : (
<div className="mt-5 space-y-3"> <div className="mt-3 space-y-2">
{shipment.picks.map((pick) => ( {shipment.picks.map((pick) => (
<div key={pick.id} className="rounded-[18px] border border-line/70 bg-page/60 p-3"> <div key={pick.id} className="rounded-[18px] border border-line/70 bg-page/60 p-3">
<div className="flex flex-wrap items-start justify-between gap-3"> <div className="flex flex-wrap items-start justify-between gap-3">
@@ -496,8 +496,8 @@ export function ShipmentDetailPage() {
)} )}
</article> </article>
<article className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel 2xl:p-5"> <article className="surface-panel">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Shipment Notes</p> <p className="section-kicker">SHIPMENT NOTES</p>
<p className="mt-3 whitespace-pre-line text-sm leading-6 text-text">{shipment.notes || "No notes recorded for this shipment."}</p> <p className="mt-3 whitespace-pre-line text-sm leading-6 text-text">{shipment.notes || "No notes recorded for this shipment."}</p>
</article> </article>
</div> </div>
@@ -512,9 +512,9 @@ export function ShipmentDetailPage() {
) : null} ) : null}
</div> </div>
{relatedShipments.length === 0 ? ( {relatedShipments.length === 0 ? (
<div className="mt-6 rounded-[18px] border border-dashed border-line/70 bg-page/60 px-4 py-8 text-center text-sm text-muted">No additional shipments exist for this sales order.</div> <div className="mt-3 rounded-[18px] border border-dashed border-line/70 bg-page/60 px-3 py-5 text-center text-sm text-muted">No additional shipments.</div>
) : ( ) : (
<div className="mt-6 space-y-3"> <div className="mt-3 space-y-2">
{relatedShipments.map((related) => ( {relatedShipments.map((related) => (
<Link key={related.id} to={`/shipping/shipments/${related.id}`} className="block rounded-[18px] border border-line/70 bg-page/60 p-3 transition hover:bg-page/80"> <Link key={related.id} to={`/shipping/shipments/${related.id}`} className="block rounded-[18px] border border-line/70 bg-page/60 p-3 transition hover:bg-page/80">
<div className="flex flex-wrap items-center justify-between gap-3"> <div className="flex flex-wrap items-center justify-between gap-3">