density
This commit is contained in:
@@ -153,14 +153,14 @@ export function ProjectDetailPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="space-y-4">
|
||||
<div className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel 2xl:p-5">
|
||||
<section className="page-stack">
|
||||
<div className="surface-panel">
|
||||
<div className="flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Project</p>
|
||||
<h3 className="mt-2 text-xl font-bold text-text">{project.projectNumber}</h3>
|
||||
<p className="section-kicker">PROJECT</p>
|
||||
<h3 className="module-title">{project.projectNumber}</h3>
|
||||
<p className="mt-1 text-sm text-text">{project.name}</p>
|
||||
<div className="mt-3 flex flex-wrap gap-2">
|
||||
<div className="mt-2.5 flex flex-wrap gap-2">
|
||||
<ProjectStatusBadge status={project.status} />
|
||||
<ProjectPriorityBadge priority={project.priority} />
|
||||
</div>
|
||||
@@ -183,36 +183,34 @@ export function ProjectDetailPage() {
|
||||
<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">Linked Work Orders</p><div className="mt-2 text-base font-bold text-text">{project.rollups.workOrderCount}</div><div className="mt-1 text-xs text-muted">{project.rollups.activeWorkOrderCount} active</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">Overdue Work Orders</p><div className="mt-2 text-base font-bold text-text">{project.rollups.overdueWorkOrderCount}</div><div className="mt-1 text-xs text-muted">{project.rollups.completedWorkOrderCount} complete</div></article>
|
||||
</section>
|
||||
<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 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Project Cockpit</p>
|
||||
<h4 className="mt-2 text-lg font-bold text-text">Cross-functional execution view</h4>
|
||||
<p className="mt-2 text-sm text-muted">Commercial, supply, execution, purchasing, and delivery signals for this program in one place.</p>
|
||||
<p className="section-kicker">PROJECT COCKPIT</p>
|
||||
</div>
|
||||
<div className="rounded-2xl border border-line/70 bg-page/60 px-3 py-2 text-right">
|
||||
<div className="text-xs font-semibold uppercase tracking-[0.16em] text-muted">Milestone Progress</div>
|
||||
<div className="surface-panel-tight text-right">
|
||||
<div className="metric-kicker">Milestone Progress</div>
|
||||
<div className="mt-1 text-2xl font-bold text-text">{completionPercent}%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5 grid gap-3 xl:grid-cols-4">
|
||||
<div className="mt-3 grid gap-2.5 xl:grid-cols-4">
|
||||
<article className="rounded-[18px] border border-line/70 bg-page/60 p-3"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Commercial</p><div className="mt-2 text-base font-bold text-text">{formatCurrency(project.cockpit.commercial.activeDocumentTotal)}</div><div className="mt-1 text-xs text-muted">{project.cockpit.commercial.activeDocumentNumber ? `${project.cockpit.commercial.activeDocumentNumber} - ${project.cockpit.commercial.activeDocumentStatus}` : "Link a quote or sales order"}</div></article>
|
||||
<article className="rounded-[18px] border border-line/70 bg-page/60 p-3"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Supply</p><div className="mt-2 text-base font-bold text-text">{planning ? planning.summary.uncoveredItemCount : project.cockpit.risk.shortageItemCount} shortage items</div><div className="mt-1 text-xs text-muted">{planning ? `Build ${planning.summary.totalBuildQuantity} - Buy ${planning.summary.totalPurchaseQuantity}` : `Uncovered qty ${project.cockpit.risk.totalUncoveredQuantity}`}</div></article>
|
||||
<article className="rounded-[18px] border border-line/70 bg-page/60 p-3"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Execution</p><div className="mt-2 text-base font-bold text-text">{project.rollups.activeWorkOrderCount} active work orders</div><div className="mt-1 text-xs text-muted">{nextWorkOrder ? `${nextWorkOrder.workOrderNumber} due ${nextWorkOrder.dueDate ? new Date(nextWorkOrder.dueDate).toLocaleDateString() : "unscheduled"}` : "No active work order due date"}</div></article>
|
||||
<article className="rounded-[18px] border border-line/70 bg-page/60 p-3"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Delivery</p><div className="mt-2 text-base font-bold text-text">{project.cockpit.delivery.shipmentStatus ? project.cockpit.delivery.shipmentStatus.replaceAll("_", " ") : "Not linked"}</div><div className="mt-1 text-xs text-muted">{project.cockpit.delivery.shipmentNumber ? `${project.cockpit.delivery.shipmentNumber} - ${project.cockpit.delivery.packageCount} package(s)` : "Link a shipment to track delivery"}</div></article>
|
||||
</div>
|
||||
<div className="mt-5 grid gap-3 xl:grid-cols-3">
|
||||
<div className="mt-3 grid gap-2.5 xl:grid-cols-3">
|
||||
<article className="rounded-[18px] border border-line/70 bg-page/60 p-3"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Purchasing Coverage</p><div className="mt-2 text-base font-bold text-text">{project.cockpit.purchasing.totalReceivedQuantity}/{project.cockpit.purchasing.totalOrderedQuantity} received</div><div className="mt-1 text-xs text-muted">{project.cockpit.purchasing.linkedPurchaseOrderCount} linked PO(s) - {project.cockpit.purchasing.totalOutstandingQuantity} outstanding</div></article>
|
||||
<article className="rounded-[18px] border border-line/70 bg-page/60 p-3"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Readiness Score</p><div className={`mt-2 text-base font-bold ${riskTone}`}>{readinessScore}%</div><div className="mt-1 text-xs text-muted">{project.cockpit.risk.riskLevel} risk - {project.cockpit.risk.shortageItemCount} shortage item(s)</div></article>
|
||||
<article className="rounded-[18px] border border-line/70 bg-page/60 p-3"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Material Spend</p><div className="mt-2 text-base font-bold text-text">${project.cockpit.purchasing.linkedLineValue.toFixed(2)}</div><div className="mt-1 text-xs text-muted">{project.cockpit.purchasing.vendorCount} vendor(s) across {project.cockpit.purchasing.linkedLineCount} linked line(s)</div></article>
|
||||
</div>
|
||||
<div className="mt-5 grid gap-3 xl:grid-cols-4">
|
||||
<div className="mt-3 grid gap-2.5 xl:grid-cols-4">
|
||||
<article className="rounded-[18px] border border-line/70 bg-page/60 p-3"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Booked Revenue</p><div className="mt-2 text-base font-bold text-text">{formatCurrency(project.cockpit.costs.bookedRevenue)}</div><div className="mt-1 text-xs text-muted">Quoted baseline {formatCurrency(project.cockpit.costs.quotedRevenue)}</div></article>
|
||||
<article className="rounded-[18px] border border-line/70 bg-page/60 p-3"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Purchase Commitment</p><div className="mt-2 text-base font-bold text-text">${project.cockpit.costs.linkedPurchaseCommitment.toFixed(2)}</div><div className="mt-1 text-xs text-muted">Linked PO line value already committed</div></article>
|
||||
<article className="rounded-[18px] border border-line/70 bg-page/60 p-3"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Planned Material Cost</p><div className="mt-2 text-base font-bold text-text">${project.cockpit.costs.plannedMaterialCost.toFixed(2)}</div><div className="mt-1 text-xs text-muted">Issued so far ${project.cockpit.costs.issuedMaterialCost.toFixed(2)}</div></article>
|
||||
<article className="rounded-[18px] border border-line/70 bg-page/60 p-3"><p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Build Load</p><div className="mt-2 text-base font-bold text-text">{project.cockpit.costs.completedBuildQuantity}/{project.cockpit.costs.buildQuantity}</div><div className="mt-1 text-xs text-muted">{project.cockpit.costs.plannedOperationHours.toFixed(1)} planned operation hours</div></article>
|
||||
</div>
|
||||
<div className="mt-5 grid gap-3 xl:grid-cols-[minmax(0,1fr)_minmax(0,1fr)]">
|
||||
<div className="mt-3 grid gap-2.5 xl:grid-cols-[minmax(0,1fr)_minmax(0,1fr)]">
|
||||
<article className="rounded-[18px] border border-line/70 bg-page/60 p-3">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Next Checkpoints</p>
|
||||
<div className="mt-4 space-y-3">
|
||||
@@ -227,17 +225,16 @@ export function ProjectDetailPage() {
|
||||
</div>
|
||||
</section>
|
||||
<section className="grid gap-3 xl:grid-cols-[minmax(0,1.15fr)_minmax(320px,0.85fr)]">
|
||||
<article className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel 2xl:p-5">
|
||||
<article className="surface-panel">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Actionable Cockpit</p>
|
||||
<p className="mt-2 text-sm text-muted">Turn current exceptions into purchasing, manufacturing, and planning follow-through.</p>
|
||||
<p className="section-kicker">ACTIONABLE COCKPIT</p>
|
||||
</div>
|
||||
<Link to="/planning/workbench" className="inline-flex items-center justify-center rounded-2xl border border-line/70 px-2 py-2 text-sm font-semibold text-text">
|
||||
Open workbench
|
||||
</Link>
|
||||
</div>
|
||||
<div className="mt-5 grid gap-3 xl:grid-cols-2">
|
||||
<div className="mt-3 grid gap-2.5 xl:grid-cols-2">
|
||||
<div className="rounded-[18px] border border-line/70 bg-page/60 p-3">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Build Follow-Through</p>
|
||||
<div className="mt-2 text-base font-bold text-text">{topBuildRecommendation ? topBuildRecommendation.itemSku : "No build recommendation"}</div>
|
||||
@@ -269,7 +266,7 @@ export function ProjectDetailPage() {
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4 flex flex-wrap gap-3">
|
||||
<div className="mt-3 flex flex-wrap gap-3">
|
||||
<Link to={`/manufacturing/work-orders/new?projectId=${project.id}`} className="inline-flex items-center justify-center rounded-2xl border border-line/70 px-2 py-2 text-sm font-semibold text-text">
|
||||
New project work order
|
||||
</Link>
|
||||
@@ -283,9 +280,9 @@ export function ProjectDetailPage() {
|
||||
</Link>
|
||||
</div>
|
||||
</article>
|
||||
<article className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel 2xl:p-5">
|
||||
<div className="flex items-center justify-between gap-3"><div><p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Linked Purchasing</p><p className="mt-2 text-sm text-muted">Purchase orders and receipts tied back to the project sales order.</p></div>{project.salesOrderId ? <Link to="/purchasing/orders" className="inline-flex items-center justify-center rounded-2xl border border-line/70 px-2 py-2 text-sm font-semibold text-text">Open purchasing</Link> : null}</div>
|
||||
{project.cockpit.purchasing.purchaseOrders.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 linked purchase orders are tied to this project yet.</div> : <div className="mt-6 space-y-3">{project.cockpit.purchasing.purchaseOrders.slice(0, 5).map((purchaseOrder) => (<Link key={purchaseOrder.id} to={`/purchasing/orders/${purchaseOrder.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-start justify-between gap-3"><div><div className="font-semibold text-text">{purchaseOrder.documentNumber}</div><div className="mt-1 text-xs text-muted">{purchaseOrder.vendorName} - {purchaseOrder.status.replaceAll("_", " ")}</div></div><div className="text-right text-xs text-muted"><div>${purchaseOrder.linkedLineValue.toFixed(2)} linked value</div><div>{purchaseOrder.totalReceivedQuantity}/{purchaseOrder.totalOrderedQuantity} received</div></div></div></Link>))}</div>}
|
||||
<article className="surface-panel">
|
||||
<div className="flex items-center justify-between gap-3"><div><p className="section-kicker">LINKED PURCHASING</p></div>{project.salesOrderId ? <Link to="/purchasing/orders" className="inline-flex items-center justify-center rounded-2xl border border-line/70 px-2 py-2 text-sm font-semibold text-text">Open purchasing</Link> : null}</div>
|
||||
{project.cockpit.purchasing.purchaseOrders.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">No linked purchase orders are tied to this project yet.</div> : <div className="mt-4 space-y-2.5">{project.cockpit.purchasing.purchaseOrders.slice(0, 5).map((purchaseOrder) => (<Link key={purchaseOrder.id} to={`/purchasing/orders/${purchaseOrder.id}`} className="block rounded-[16px] border border-line/70 bg-page/60 px-3 py-2.5 transition hover:bg-page/80"><div className="flex flex-wrap items-start justify-between gap-3"><div><div className="font-semibold text-text">{purchaseOrder.documentNumber}</div><div className="mt-1 text-xs text-muted">{purchaseOrder.vendorName} - {purchaseOrder.status.replaceAll("_", " ")}</div></div><div className="text-right text-xs text-muted"><div>${purchaseOrder.linkedLineValue.toFixed(2)} linked value</div><div>{purchaseOrder.totalReceivedQuantity}/{purchaseOrder.totalOrderedQuantity} received</div></div></div></Link>))}</div>}
|
||||
</article>
|
||||
<article className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel 2xl:p-5">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Readiness Drivers</p>
|
||||
|
||||
Reference in New Issue
Block a user