This commit is contained in:
2026-03-18 23:06:44 -05:00
parent 17b73a4597
commit 52bc98c16e
3 changed files with 93 additions and 92 deletions

View File

@@ -87,7 +87,7 @@ export function InventorySkuMasterPage() {
return (
<div key={node.id} className="space-y-2">
<div className="rounded-[18px] border border-line/70 bg-page/60 px-3 py-3" style={{ marginLeft: `${depth * 16}px` }}>
<div className="rounded-[18px] border border-line/70 bg-page/60 px-2 py-2" style={{ marginLeft: `${depth * 16}px` }}>
<div className="flex flex-wrap items-center justify-between gap-2">
<div className="min-w-0">
<div className="flex flex-wrap items-center gap-2">
@@ -107,8 +107,10 @@ export function InventorySkuMasterPage() {
</span>
)}
<div className="min-w-0">
<div className="text-sm font-semibold text-text">{node.code} <span className="text-muted">- {node.label}</span></div>
<div className="mt-1 text-xs text-muted">Level {node.level} {node.childCount} child branch(es)</div>
<div className="text-sm font-semibold text-text">
{node.code} <span className="text-muted">- {node.label}</span>
</div>
<div className="mt-1 text-xs text-muted">Level {node.level} - {node.childCount} child branch(es)</div>
</div>
</div>
</div>
@@ -193,13 +195,12 @@ export function InventorySkuMasterPage() {
}
return (
<section className="space-y-6">
<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">Inventory Master Data</p>
<h3 className="mt-2 text-xl font-bold text-text">SKU Master Builder</h3>
<p className="mt-2 max-w-3xl text-sm text-muted">Define family roots, branch-specific child codes, and the family-scoped short-code suffix that finishes each generated SKU.</p>
<p className="section-kicker">INVENTORY MASTER DATA</p>
<h3 className="module-title">SKU MASTER BUILDER</h3>
</div>
<Link to="/inventory/items" className="inline-flex items-center justify-center rounded-2xl border border-line/70 px-2 py-2 text-sm font-semibold text-text">
Back to items
@@ -207,13 +208,13 @@ export function InventorySkuMasterPage() {
</div>
</div>
<div className="grid gap-6 xl:grid-cols-[0.9fr_1.5fr]">
<div className="space-y-6">
<section className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel 2xl:p-5">
<div className="text-sm font-semibold text-text">Families</div>
<div className="mt-4 space-y-2">
<div className="grid gap-3 xl:grid-cols-[0.9fr_1.5fr]">
<div className="space-y-3">
<section className="surface-panel">
<p className="section-kicker">FAMILIES</p>
<div className="mt-3 space-y-2">
{catalog.families.length === 0 ? (
<div className="rounded-[18px] border border-dashed border-line/70 bg-page/60 px-4 py-6 text-sm text-muted">No SKU families defined yet.</div>
<div className="rounded-[18px] border border-dashed border-line/70 bg-page/60 px-3 py-5 text-sm text-muted">No SKU families defined yet.</div>
) : (
catalog.families.map((family) => (
<button
@@ -224,13 +225,18 @@ export function InventorySkuMasterPage() {
setExpandedNodeIds([]);
setNodeForm((current) => ({ ...current, familyId: family.id, parentNodeId: null }));
}}
className={`block w-full rounded-[18px] border px-3 py-3 text-left transition ${
className={`block w-full rounded-[18px] border px-2 py-2 text-left transition ${
selectedFamilyId === family.id ? "border-brand bg-brand/8" : "border-line/70 bg-page/60 hover:bg-page/80"
}`}
>
<div className="text-sm font-semibold text-text">{family.code} <span className="text-muted">({family.sequenceCode})</span></div>
<div className="text-sm font-semibold text-text">
{family.code} <span className="text-muted">({family.sequenceCode})</span>
</div>
<div className="mt-1 text-xs text-muted">{family.name}</div>
<div className="mt-2 text-xs text-muted">{family.childNodeCount} branch nodes next {family.sequenceCode}{String(family.nextSequenceNumber).padStart(4, "0")}</div>
<div className="mt-2 text-xs text-muted">
{family.childNodeCount} branch nodes - next {family.sequenceCode}
{String(family.nextSequenceNumber).padStart(4, "0")}
</div>
</button>
))
)}
@@ -238,9 +244,9 @@ export function InventorySkuMasterPage() {
</section>
{canManage ? (
<section className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel 2xl:p-5">
<div className="text-sm font-semibold text-text">Add family</div>
<form className="mt-4 space-y-3" onSubmit={handleCreateFamily}>
<section className="surface-panel">
<p className="section-kicker">ADD FAMILY</p>
<form className="mt-3 space-y-3" onSubmit={handleCreateFamily}>
<div className="grid gap-3 sm:grid-cols-2">
<label className="block">
<span className="mb-2 block text-xs font-semibold uppercase tracking-[0.16em] text-muted">Family code</span>
@@ -265,26 +271,26 @@ export function InventorySkuMasterPage() {
) : null}
</div>
<div className="space-y-6">
<section className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel 2xl:p-5">
<div className="space-y-3">
<section className="surface-panel">
<div className="flex flex-col gap-2 lg:flex-row lg:items-center lg:justify-between">
<div>
<div className="text-sm font-semibold text-text">Branch tree</div>
<p className="section-kicker">BRANCH TREE</p>
<div className="mt-1 text-xs text-muted">{status}</div>
</div>
{selectedFamilyId ? (
<div className="text-xs text-muted">Up to 6 total SKU levels including family root.</div>
<div className="text-xs text-muted">Up to 6 total SKU levels.</div>
) : null}
</div>
<div className="mt-4 space-y-3">
{selectedFamilyId ? renderNodes(null) : <div className="rounded-[18px] border border-dashed border-line/70 bg-page/60 px-4 py-6 text-sm text-muted">Select a family to inspect or extend its branch tree.</div>}
<div className="mt-3 space-y-2">
{selectedFamilyId ? renderNodes(null) : <div className="rounded-[18px] border border-dashed border-line/70 bg-page/60 px-3 py-5 text-sm text-muted">Select a family to inspect or extend its branch tree.</div>}
</div>
</section>
{canManage ? (
<section className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel 2xl:p-5">
<div className="text-sm font-semibold text-text">Add branch node</div>
<form className="mt-4 space-y-3" onSubmit={handleCreateNode}>
<section className="surface-panel">
<p className="section-kicker">ADD BRANCH NODE</p>
<form className="mt-3 space-y-3" onSubmit={handleCreateNode}>
<div className="grid gap-3 sm:grid-cols-2">
<label className="block">
<span className="mb-2 block text-xs font-semibold uppercase tracking-[0.16em] text-muted">Family</span>

View File

@@ -385,24 +385,23 @@ export function WorkbenchPage() {
}
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 xl:flex-row xl:items-start xl:justify-between">
<div>
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Planning</p>
<h3 className="mt-2 text-2xl font-bold text-text">Planning Workbench</h3>
<p className="mt-2 max-w-4xl text-sm text-muted">A reactive planning surface for projects, work orders, operations, shortages, and schedule risk. Use it as the daily planner cockpit, not just a chart.</p>
<p className="section-kicker">PLANNING</p>
<h3 className="module-title">PLANNING WORKBENCH</h3>
</div>
<div className="rounded-[18px] border border-line/70 bg-page/60 px-3 py-3 text-sm">
<div className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Workbench Status</div>
<div className="mt-2 font-semibold text-text">{status}</div>
<div className="rounded-[18px] border border-line/70 bg-page/60 px-2 py-2 text-sm">
<div className="section-kicker">WORKBENCH STATUS</div>
<div className="mt-1 font-semibold text-text">{status}</div>
</div>
</div>
<div className="mt-5 grid gap-3 xl:grid-cols-4">
<div className="mt-3 grid gap-2 xl:grid-cols-4">
{modeOptions.map((option) => (
<button key={option.value} type="button" onClick={() => setWorkbenchMode(option.value)} className={`rounded-[18px] border px-3 py-3 text-left transition ${workbenchMode === option.value ? "border-brand bg-brand/10" : "border-line/70 bg-page/60 hover:border-brand/40"}`}>
<button key={option.value} type="button" onClick={() => setWorkbenchMode(option.value)} className={`rounded-[18px] border px-2 py-2 text-left transition ${workbenchMode === option.value ? "border-brand bg-brand/10" : "border-line/70 bg-page/60 hover:border-brand/40"}`}>
<div className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">{option.label}</div>
<div className="mt-2 text-sm font-semibold text-text">{option.detail}</div>
<div className="mt-1 text-sm font-semibold text-text">{option.detail}</div>
</button>
))}
</div>
@@ -438,20 +437,19 @@ export function WorkbenchPage() {
<div className="grid gap-3 xl:grid-cols-[320px_minmax(0,1fr)_360px]">
<aside className="space-y-3">
<section className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel">
<section 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">Exception Rail</p>
<p className="mt-2 text-sm text-muted">Late, at-risk, and unscheduled items that require planner attention.</p>
<p className="section-kicker">EXCEPTION RAIL</p>
</div>
<span className="rounded-full border border-line/70 px-2 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-muted">{exceptions.length}</span>
</div>
{exceptions.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 planning exceptions are active.</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 planning exceptions are active.</div>
) : (
<div className="mt-5 space-y-3">
<div className="mt-3 space-y-2">
{exceptions.map((exception: PlanningExceptionDto) => (
<button key={exception.id} type="button" onClick={() => setSelectedFocusId(exception.id.startsWith("project-") ? exception.id : exception.id.replace("work-order-unscheduled-", "work-order-"))} className="block w-full rounded-[18px] border border-line/70 bg-page/60 p-3 text-left transition hover:bg-page/80">
<button key={exception.id} type="button" onClick={() => setSelectedFocusId(exception.id.startsWith("project-") ? exception.id : exception.id.replace("work-order-unscheduled-", "work-order-"))} className="block w-full rounded-[18px] border border-line/70 bg-page/60 px-2 py-2 text-left transition hover:bg-page/80">
<div className="flex items-start justify-between gap-3">
<div>
<div className="text-xs font-semibold uppercase tracking-[0.16em] text-muted">{exception.kind === "PROJECT" ? "Project" : "Work Order"}</div>
@@ -466,14 +464,14 @@ export function WorkbenchPage() {
</div>
)}
</section>
<section className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Planner Actions</p>
<div className="mt-4 space-y-2 rounded-[18px] border border-line/70 bg-page/60 p-3 text-sm">
<section className="surface-panel">
<p className="section-kicker">PLANNER ACTIONS</p>
<div className="mt-3 space-y-2 rounded-[18px] border border-line/70 bg-page/60 px-2 py-2 text-sm">
<div className="flex items-center justify-between gap-3"><span className="text-muted">Uncovered quantity</span><span className="font-semibold text-text">{planningRollup?.summary.totalUncoveredQuantity ?? 0}</span></div>
<div className="flex items-center justify-between gap-3"><span className="text-muted">Projects with linked demand</span><span className="font-semibold text-text">{planningRollup?.summary.projectCount ?? 0}</span></div>
<div className="flex items-center justify-between gap-3"><span className="text-muted">Overloaded stations</span><span className="font-semibold text-text">{summary?.overloadedStations ?? 0}</span></div>
</div>
<div className="mt-4 flex flex-wrap gap-2">
<div className="mt-3 flex flex-wrap gap-2">
<Link to="/projects" className="rounded-2xl border border-line/70 px-2 py-2 text-sm font-semibold text-text">Open projects</Link>
<Link to="/manufacturing/work-orders" className="rounded-2xl border border-line/70 px-2 py-2 text-sm font-semibold text-text">Open work orders</Link>
<Link to="/purchasing/orders" className="rounded-2xl border border-line/70 px-2 py-2 text-sm font-semibold text-text">Open purchasing</Link>
@@ -481,7 +479,7 @@ export function WorkbenchPage() {
</section>
</aside>
<div className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel">
<div className="surface-panel">
{workbenchMode === "overview" ? (
<OverviewBoard
focusRecords={filteredFocusRecords}
@@ -501,16 +499,16 @@ export function WorkbenchPage() {
</div>
<aside className="space-y-3">
<section className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Focus Drawer</p>
<section className="surface-panel">
<p className="section-kicker">FOCUS DRAWER</p>
{selectedFocus ? (
<div className="mt-4 space-y-3">
<div className="rounded-[18px] border border-line/70 bg-page/60 p-3">
<div className="mt-3 space-y-2">
<div className="rounded-[18px] border border-line/70 bg-page/60 px-2 py-2">
<div className="text-xs font-semibold uppercase tracking-[0.16em] text-muted">{selectedFocus.kind}</div>
<div className="mt-2 text-base font-bold text-text">{selectedFocus.title}</div>
<div className="mt-2 text-xs text-muted">{selectedFocus.ownerLabel ?? "No context label"}</div>
</div>
<div className="rounded-[18px] border border-line/70 bg-page/60 p-3 text-sm">
<div className="rounded-[18px] border border-line/70 bg-page/60 px-2 py-2 text-sm">
<div className="flex items-center justify-between gap-3"><span className="text-muted">Status</span><span className="font-semibold text-text">{selectedFocus.status.replaceAll("_", " ")}</span></div>
<div className="mt-2 flex items-center justify-between gap-3"><span className="text-muted">Readiness</span><span className="font-semibold text-text">{selectedFocus.readinessState.replaceAll("_", " ")} ({selectedFocus.readinessScore}%)</span></div>
<div className="mt-2 flex items-center justify-between gap-3"><span className="text-muted">Window</span><span className="font-semibold text-text">{formatDate(selectedFocus.start)} - {formatDate(selectedFocus.end)}</span></div>
@@ -520,8 +518,8 @@ export function WorkbenchPage() {
{selectedFocus.blockedReason ? <div className="mt-3 text-xs text-muted">{selectedFocus.blockedReason}</div> : null}
</div>
{selectedFocus.kind === "OPERATION" ? (
<div className="rounded-[18px] border border-line/70 bg-page/60 p-3 text-sm">
<div className="text-xs font-semibold uppercase tracking-[0.16em] text-muted">Workbench Rebalance</div>
<div className="rounded-[18px] border border-line/70 bg-page/60 px-2 py-2 text-sm">
<div className="section-kicker">WORKBENCH REBALANCE</div>
<div className="mt-3">
<label className="mb-2 block text-xs font-semibold uppercase tracking-[0.16em] text-muted">Target Station</label>
<select
@@ -548,7 +546,7 @@ export function WorkbenchPage() {
/>
</div>
{selectedRescheduleStation ? (
<div className="mt-3 rounded-[16px] border border-line/70 bg-surface/80 p-3 text-xs text-muted">
<div className="mt-3 rounded-[16px] border border-line/70 bg-surface/80 px-2 py-2 text-xs text-muted">
<div className="flex items-center justify-between gap-3">
<span>Capacity</span>
<span className="font-semibold text-text">
@@ -623,13 +621,13 @@ export function WorkbenchPage() {
</div>
</div>
) : (
<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">Select a project or work order to inspect it.</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">Select a project or work order to inspect it.</div>
)}
</section>
<section className="rounded-[20px] border border-line/70 bg-surface/90 p-4 shadow-panel">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">{workbenchMode === "heatmap" ? "Selected Day" : "Upcoming Agenda"}</p>
<section className="surface-panel">
<p className="section-kicker">{workbenchMode === "heatmap" ? "SELECTED DAY" : "UPCOMING AGENDA"}</p>
{workbenchMode === "heatmap"
? (selectedHeatmapCell ? <SelectedDayPanel cell={selectedHeatmapCell} onSelect={setSelectedFocusId} /> : <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">Select a day in the heatmap to inspect its load.</div>)
? (selectedHeatmapCell ? <SelectedDayPanel cell={selectedHeatmapCell} onSelect={setSelectedFocusId} /> : <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">Select a day in the heatmap to inspect its load.</div>)
: <AgendaBoard records={agendaItems.slice(0, 8)} onSelect={setSelectedFocusId} compact />}
</section>
</aside>
@@ -640,9 +638,9 @@ export function WorkbenchPage() {
function MetricCard({ label, value }: { label: string; value: string | number }) {
return (
<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">{label}</p>
<div className="mt-2 text-xl font-extrabold text-text">{value}</div>
<div className="mt-1 text-xl font-extrabold text-text">{value}</div>
</article>
);
}
@@ -690,20 +688,19 @@ function OverviewBoard({
}
return (
<div className="space-y-4">
<div className="space-y-3">
<div className="flex flex-wrap items-center justify-between gap-3">
<div>
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Overview</p>
<p className="mt-2 text-sm text-muted">Scan project rollups, active work, station load, and release blockers without leaving the planner.</p>
<p className="section-kicker">OVERVIEW</p>
</div>
</div>
{groupMode === "projects" ? (
<div className="grid gap-3 xl:grid-cols-[minmax(0,1.15fr)_minmax(0,0.85fr)]">
<section className="rounded-[18px] border border-line/70 bg-page/60 p-3">
<section className="rounded-[18px] border border-line/70 bg-page/60 px-2 py-2">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Program Queue</p>
<div className="mt-3 space-y-3">
{projects.map((record) => (
<button key={record.id} type="button" onClick={() => onSelect(record.id)} className="block w-full rounded-[16px] border border-line/70 bg-surface/80 p-3 text-left transition hover:bg-surface">
<button key={record.id} type="button" onClick={() => onSelect(record.id)} className="block w-full rounded-[16px] border border-line/70 bg-surface/80 px-2 py-2 text-left transition hover:bg-surface">
<div className="flex flex-wrap items-center justify-between gap-3">
<div>
<div className="font-semibold text-text">{record.title}</div>
@@ -718,11 +715,11 @@ function OverviewBoard({
))}
</div>
</section>
<section className="rounded-[18px] border border-line/70 bg-page/60 p-3">
<section className="rounded-[18px] border border-line/70 bg-page/60 px-2 py-2">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Operation Load</p>
<div className="mt-3 space-y-2">
{operations.slice(0, 10).map((record) => (
<button key={record.id} type="button" onClick={() => onSelect(record.id)} className="flex w-full items-center justify-between gap-3 rounded-[16px] border border-line/70 bg-surface/80 px-3 py-2 text-left transition hover:bg-surface">
<button key={record.id} type="button" onClick={() => onSelect(record.id)} className="flex w-full items-center justify-between gap-3 rounded-[16px] border border-line/70 bg-surface/80 px-2 py-2 text-left transition hover:bg-surface">
<div>
<div className="font-semibold text-text">{record.title}</div>
<div className="mt-1 text-xs text-muted">{record.ownerLabel ?? "No parent work order"}</div>
@@ -738,11 +735,10 @@ function OverviewBoard({
</div>
) : null}
{groupMode === "stations" ? (
<section className="rounded-[18px] border border-line/70 bg-page/60 p-3">
<section className="rounded-[18px] border border-line/70 bg-page/60 px-2 py-2">
<div className="flex flex-wrap items-center justify-between gap-3">
<div>
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Work Center Load</p>
<p className="mt-2 text-sm text-muted">Drag operations between stations to rebalance capacity. If a heatmap day is selected, drops target that date on the new station.</p>
</div>
{draggingOperation ? (
<div className="rounded-2xl border border-brand/40 bg-brand/10 px-3 py-2 text-xs font-semibold text-text">
@@ -770,7 +766,7 @@ function OverviewBoard({
event.preventDefault();
void onDropStation(station.stationId);
}}
className={`rounded-[16px] border bg-surface/80 p-3 transition ${
className={`rounded-[16px] border bg-surface/80 px-2 py-2 transition ${
dropStationId === station.stationId
? "border-brand bg-brand/10"
: station.overloaded
@@ -798,7 +794,7 @@ function OverviewBoard({
<div>Actual {station.totalActualMinutes} min</div>
</div>
{draggingOperation ? (
<div className="mt-3 rounded-[14px] border border-line/70 bg-page/60 p-2 text-xs text-muted">
<div className="mt-2 rounded-[14px] border border-line/70 bg-page/60 px-2 py-2 text-xs text-muted">
<div className="flex items-center justify-between gap-3">
<span>Projected util after drop</span>
<span className="font-semibold text-text">
@@ -833,7 +829,7 @@ function OverviewBoard({
onDragOperation(null);
onDropStationChange(null);
}}
className="cursor-grab rounded-[14px] border border-line/70 bg-page/60 p-2 active:cursor-grabbing"
className="cursor-grab rounded-[14px] border border-line/70 bg-page/60 px-2 py-2 active:cursor-grabbing"
>
<button type="button" onClick={() => onSelect(record.id)} className="block w-full text-left">
<div className="flex items-center justify-between gap-3">
@@ -859,11 +855,11 @@ function OverviewBoard({
</section>
) : null}
{groupMode === "exceptions" ? (
<section className="rounded-[18px] border border-line/70 bg-page/60 p-3">
<section className="rounded-[18px] border border-line/70 bg-page/60 px-2 py-2">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Dispatch Exceptions</p>
<div className="mt-3 space-y-2">
{exceptionRows.map((record) => (
<button key={record.id} type="button" onClick={() => onSelect(record.id)} className="flex w-full items-center justify-between gap-3 rounded-[16px] border border-line/70 bg-surface/80 px-3 py-2 text-left transition hover:bg-surface">
<button key={record.id} type="button" onClick={() => onSelect(record.id)} className="flex w-full items-center justify-between gap-3 rounded-[16px] border border-line/70 bg-surface/80 px-2 py-2 text-left transition hover:bg-surface">
<div>
<div className="font-semibold text-text">{record.title}</div>
<div className="mt-1 text-xs text-muted">{record.readinessState} - shortage {record.totalShortageQuantity}</div>
@@ -877,11 +873,11 @@ function OverviewBoard({
</div>
</section>
) : null}
<section className="rounded-[18px] border border-line/70 bg-page/60 p-3">
<section className="rounded-[18px] border border-line/70 bg-page/60 px-2 py-2">
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted">Active Work Orders</p>
<div className="mt-3 grid gap-3 xl:grid-cols-2">
{workOrders.map((record) => (
<button key={record.id} type="button" onClick={() => onSelect(record.id)} className="rounded-[16px] border border-line/70 bg-surface/80 p-3 text-left transition hover:bg-surface">
<button key={record.id} type="button" onClick={() => onSelect(record.id)} className="rounded-[16px] border border-line/70 bg-surface/80 px-2 py-2 text-left transition hover:bg-surface">
<div className="font-semibold text-text">{record.title}</div>
<div className="mt-2 flex items-center justify-between gap-3 text-xs text-muted">
<span>{record.readinessState}</span>
@@ -902,12 +898,11 @@ function HeatmapBoard({ heatmap, selectedDate, onSelectDate }: { heatmap: Heatma
}
return (
<div className="space-y-4">
<div className="space-y-3">
<div>
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Load Heatmap</p>
<p className="mt-2 text-sm text-muted">Dense daily load scan for operations and work orders, with late and blocked pressure highlighted.</p>
<p className="section-kicker">LOAD HEATMAP</p>
</div>
<div className="overflow-x-auto rounded-[18px] border border-line/70 bg-page/60 p-4">
<div className="overflow-x-auto rounded-[18px] border border-line/70 bg-page/60 px-3 py-3">
<div className="flex gap-2">
<div className="flex flex-col gap-2 pt-7">
{["M", "T", "W", "T", "F", "S", "S"].map((label) => <div key={label} className="h-9 text-xs font-semibold text-muted">{label}</div>)}
@@ -936,16 +931,15 @@ function HeatmapBoard({ heatmap, selectedDate, onSelectDate }: { heatmap: Heatma
function AgendaBoard({ records, onSelect, compact = false }: { records: FocusRecord[]; onSelect: (id: string) => void; compact?: boolean }) {
return (
<div className={compact ? "mt-4 space-y-3" : "space-y-4"}>
<div className={compact ? "mt-3 space-y-2" : "space-y-3"}>
{!compact ? (
<div>
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Agenda</p>
<p className="mt-2 text-sm text-muted">Upcoming projects, work orders, and milestones ordered by due date.</p>
<p className="section-kicker">AGENDA</p>
</div>
) : null}
<div className="space-y-2">
{records.map((record) => (
<button key={record.id} type="button" onClick={() => onSelect(record.id)} className="flex w-full items-center justify-between gap-3 rounded-[16px] border border-line/70 bg-page/60 px-3 py-3 text-left transition hover:bg-page/80">
<button key={record.id} type="button" onClick={() => onSelect(record.id)} className="flex w-full items-center justify-between gap-3 rounded-[16px] border border-line/70 bg-page/60 px-2 py-2 text-left transition hover:bg-page/80">
<div>
<div className="font-semibold text-text">{record.title}</div>
<div className="mt-1 text-xs text-muted">{record.kind} - {record.ownerLabel ?? "No context"}</div>
@@ -963,8 +957,8 @@ function AgendaBoard({ records, onSelect, compact = false }: { records: FocusRec
function SelectedDayPanel({ cell, onSelect }: { cell: HeatmapCell; onSelect: (id: string) => void }) {
return (
<div className="mt-4 space-y-3">
<div className="rounded-[18px] border border-line/70 bg-page/60 p-3">
<div className="mt-3 space-y-2">
<div className="rounded-[18px] border border-line/70 bg-page/60 px-2 py-2">
<div className="text-sm font-semibold text-text">{formatDate(cell.dateKey, { weekday: "short", month: "short", day: "numeric" })}</div>
<div className="mt-2 flex items-center justify-between gap-3 text-xs text-muted">
<span>{cell.count} scheduled</span>
@@ -973,7 +967,7 @@ function SelectedDayPanel({ cell, onSelect }: { cell: HeatmapCell; onSelect: (id
</div>
<div className="space-y-2">
{cell.tasks.slice(0, 8).map((task) => (
<button key={task.id} type="button" onClick={() => onSelect(task.id)} className="block w-full rounded-[16px] border border-line/70 bg-page/60 p-3 text-left transition hover:bg-page/80">
<button key={task.id} type="button" onClick={() => onSelect(task.id)} className="block w-full rounded-[16px] border border-line/70 bg-page/60 px-2 py-2 text-left transition hover:bg-page/80">
<div className="font-semibold text-text">{task.title}</div>
<div className="mt-1 text-xs text-muted">{task.status.replaceAll("_", " ")} - {task.ownerLabel ?? "No context"}</div>
</button>