cleanup
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user