feat(plant_kanban): polish KPI strip + chips + toolbar buttons
User feedback after first deploy: 7 KPI tiles wrapped to second line
(grid was repeat(5, 1fr) but I had added 2 new ones), and the
controls felt cramped.
Layout fix:
- .kpi-strip grid: repeat(5, 1fr) → repeat(8, 1fr) so the row stays
one line and there's room for the new Awaiting QC tile.
Missing KPI added:
- Awaiting QC — fp.job.card_state='awaiting_qc' count. Operators
couldn't see when QC was blocking job close from the KPI strip
(only visible inside the column). Server-side count + filter
clause + matching filter chip.
Visual polish (all light + dark via existing token system):
- KPI tiles: padding 6→10px, value font 20→26px, label font 9→10px,
subtle 135deg linear-gradient bg per kind (urgent/warn/good/qc),
hover lifts the tile with translateY + shadow.
- Filter chips: padding 4/12→7/16px, font 11→13px, gradient bg,
active state has gradient blue + shadow.
- Search input: padding 5/10→9/14px, font 12→14px, focus ring.
- Toolbar buttons (Station/All Plant/Manager/Scan QR/Hand Off):
padding 5/10→8/14px, font 12→14px, gradients, hover lift.
Dark mode handled automatically — all gradients reference
$plant-* tokens which already have @if $o-webclient-color-scheme ==
dark global overrides in _plant_tokens.scss.
Version bump fusion_plating_shopfloor 19.0.34.0.0 → 19.0.34.1.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
|
||||
{
|
||||
'name': 'Fusion Plating — Shop Floor',
|
||||
'version': '19.0.34.0.0',
|
||||
'version': '19.0.34.1.0',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Shop-floor tablet stations, QR scanning, bake window enforcer.',
|
||||
'description': """
|
||||
|
||||
@@ -95,6 +95,8 @@ class PlantKanbanController(http.Controller):
|
||||
)))
|
||||
if filters.get('mine'):
|
||||
domain.append(('card_state', 'in', ('ready_mine', 'running_mine')))
|
||||
if filters.get('awaiting_qc'):
|
||||
domain.append(('card_state', '=', 'awaiting_qc'))
|
||||
# Spec 2026-05-25 — post-shop state filter chips
|
||||
if filters.get('awaiting_cert'):
|
||||
domain.append(('state', '=', 'awaiting_cert'))
|
||||
@@ -145,6 +147,9 @@ class PlantKanbanController(http.Controller):
|
||||
'on_hold': sum(
|
||||
1 for j in jobs if j.card_state == 'on_hold'
|
||||
),
|
||||
'awaiting_qc': sum(
|
||||
1 for j in jobs if j.card_state == 'awaiting_qc'
|
||||
),
|
||||
# Spec 2026-05-25 — post-shop state KPIs
|
||||
'awaiting_cert': sum(
|
||||
1 for j in jobs if j.state == 'awaiting_cert'
|
||||
|
||||
@@ -1,20 +1,27 @@
|
||||
// _filter_chip.scss — depends on _plant_tokens.scss
|
||||
// 2026-05-25: bigger touch target + gradient bg.
|
||||
|
||||
.o_fp_filter_chip {
|
||||
padding: 4px 12px;
|
||||
font-size: 11px;
|
||||
background: $plant-card-bg;
|
||||
padding: 7px 16px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
background: linear-gradient(135deg, $plant-card-bg 0%, $plant-bg 100%);
|
||||
border: 1px solid $plant-card-border;
|
||||
border-radius: 14px;
|
||||
border-radius: 18px;
|
||||
color: $plant-muted;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
transition: transform 0.1s ease, box-shadow 0.1s ease;
|
||||
|
||||
&.active {
|
||||
background: #1d4ed8;
|
||||
background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);
|
||||
border-color: #1d4ed8;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 2px 4px rgba(29, 78, 216, 0.25);
|
||||
}
|
||||
&:hover:not(.active) {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.06);
|
||||
}
|
||||
&:hover:not(.active) { background: $plant-bg; }
|
||||
}
|
||||
|
||||
@@ -1,34 +1,66 @@
|
||||
// _kpi_tile.scss — depends on _plant_tokens.scss
|
||||
//
|
||||
// 2026-05-25: redesigned for the 8-tile row. Narrower individual width
|
||||
// (grid handles that), more vertical presence via padding + larger
|
||||
// typography, subtle 135deg gradients per kind that work in both light
|
||||
// and dark modes (gradient stops use $plant-* tokens — dark variants
|
||||
// flip automatically via the @if $o-webclient-color-scheme branch).
|
||||
|
||||
.o_fp_kpi_tile {
|
||||
padding: 6px 10px;
|
||||
background: $plant-card-bg;
|
||||
border-radius: 6px;
|
||||
padding: 10px 12px;
|
||||
background: linear-gradient(135deg, $plant-card-bg 0%, $plant-bg 100%);
|
||||
border-radius: 8px;
|
||||
border: 1px solid $plant-card-border;
|
||||
display: flex; flex-direction: column; gap: 1px;
|
||||
display: flex; flex-direction: column; gap: 3px;
|
||||
cursor: pointer;
|
||||
transition: background 0.1s;
|
||||
transition: transform 0.1s ease, box-shadow 0.1s ease, border-color 0.1s ease;
|
||||
text-align: left;
|
||||
color: $plant-text;
|
||||
font-family: inherit;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.04);
|
||||
min-width: 0; // grid-track minmax handles sizing; let label truncate
|
||||
|
||||
&:hover { background: $plant-bg; }
|
||||
&:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.08);
|
||||
}
|
||||
&.active {
|
||||
border-color: $plant-mine-border;
|
||||
background: $plant-mine-bg;
|
||||
background: linear-gradient(135deg, $plant-mine-bg 0%, $plant-card-bg 100%);
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.10);
|
||||
}
|
||||
|
||||
// Kind-specific subtle gradients. Each pulls from existing tokens
|
||||
// so dark mode auto-flips via the @if $o-webclient-color-scheme
|
||||
// == dark global override in _plant_tokens.scss.
|
||||
&.urgent {
|
||||
background: linear-gradient(135deg, $plant-card-bg 0%, $plant-hold-bg 100%);
|
||||
.kpi-val { color: $plant-hold-border; }
|
||||
}
|
||||
&.warn {
|
||||
background: linear-gradient(135deg, $plant-card-bg 0%, $plant-bake-bg 100%);
|
||||
.kpi-val { color: $plant-idle-border; }
|
||||
}
|
||||
&.good {
|
||||
background: linear-gradient(135deg, $plant-card-bg 0%, $plant-done-bg 100%);
|
||||
.kpi-val { color: $plant-done-border; }
|
||||
}
|
||||
&.qc {
|
||||
background: linear-gradient(135deg, $plant-card-bg 0%, $plant-qc-bg 100%);
|
||||
.kpi-val { color: $plant-qc-border; }
|
||||
}
|
||||
&.urgent .kpi-val { color: $plant-hold-border; }
|
||||
&.warn .kpi-val { color: $plant-idle-border; }
|
||||
&.good .kpi-val { color: $plant-done-border; }
|
||||
|
||||
.kpi-val {
|
||||
font-size: 20px; font-weight: 700;
|
||||
font-size: 26px; font-weight: 700;
|
||||
color: $plant-text; line-height: 1;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.kpi-lbl {
|
||||
font-size: 9px; font-weight: 600;
|
||||
font-size: 10px; font-weight: 600;
|
||||
color: $plant-muted;
|
||||
text-transform: uppercase; letter-spacing: 0.04em;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,67 +34,87 @@
|
||||
.floor-title { font-size: 16px; font-weight: 700; }
|
||||
.floor-controls { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
|
||||
|
||||
// 2026-05-25 — toolbar buttons bigger + gradients for visual weight
|
||||
.station-picker {
|
||||
padding: 5px 10px;
|
||||
background: $plant-mine-bg;
|
||||
padding: 8px 14px;
|
||||
background: linear-gradient(135deg, $plant-mine-bg 0%, $plant-card-bg 100%);
|
||||
border: 1px solid $plant-mine-border;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
border-radius: 7px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #856404;
|
||||
color: $plant-text;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
|
||||
}
|
||||
.mode-toggle {
|
||||
display: inline-flex;
|
||||
border: 1px solid $plant-card-border;
|
||||
border-radius: 6px;
|
||||
border-radius: 7px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
|
||||
.mode-btn {
|
||||
padding: 5px 12px;
|
||||
font-size: 12px;
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
background: $plant-card-bg;
|
||||
background: linear-gradient(135deg, $plant-card-bg 0%, $plant-bg 100%);
|
||||
color: $plant-muted;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
border-right: 1px solid $plant-card-border;
|
||||
font-family: inherit;
|
||||
&:last-child { border-right: 0; }
|
||||
&.active { background: #1d4ed8; color: #fff; }
|
||||
&.active {
|
||||
background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);
|
||||
color: #fff;
|
||||
}
|
||||
&:hover:not(.active) { background: $plant-bg; }
|
||||
}
|
||||
}
|
||||
.toolbar-btn {
|
||||
padding: 5px 10px;
|
||||
font-size: 12px;
|
||||
background: $plant-card-bg;
|
||||
padding: 8px 14px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
background: linear-gradient(135deg, $plant-card-bg 0%, $plant-bg 100%);
|
||||
border: 1px solid $plant-card-border;
|
||||
border-radius: 6px;
|
||||
border-radius: 7px;
|
||||
cursor: pointer;
|
||||
color: $plant-text;
|
||||
font-family: inherit;
|
||||
&:hover { background: $plant-bg; }
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
|
||||
transition: transform 0.1s ease, box-shadow 0.1s ease;
|
||||
&:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.08);
|
||||
}
|
||||
&.handoff {
|
||||
background: #ffc107;
|
||||
background: linear-gradient(135deg, #ffd966 0%, #ffc107 100%);
|
||||
border-color: #d39e00;
|
||||
color: #856404;
|
||||
color: #5e4400;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
.kpi-strip { display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; }
|
||||
// 8 tiles — Work Orders, At My Station, Bakes Due, On Hold,
|
||||
// Awaiting QC, Awaiting CoC, Ready to Ship, Overdue.
|
||||
.kpi-strip { display: grid; grid-template-columns: repeat(8, 1fr); gap: 8px; }
|
||||
|
||||
.search-row { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
|
||||
.search-row { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
|
||||
.search-input {
|
||||
flex: 1; min-width: 200px;
|
||||
padding: 5px 10px;
|
||||
flex: 1; min-width: 220px;
|
||||
padding: 9px 14px;
|
||||
border: 1px solid $plant-card-border;
|
||||
border-radius: 6px;
|
||||
border-radius: 8px;
|
||||
background: $plant-card-bg;
|
||||
color: $plant-text;
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
font-family: inherit;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.04) inset;
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: #1d4ed8;
|
||||
box-shadow: 0 0 0 3px rgba(29, 78, 216, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
// Board: fixed-width columns with horizontal scroll on smaller
|
||||
|
||||
@@ -47,6 +47,11 @@
|
||||
kind="'urgent'"
|
||||
active="!!state.filters.on_hold"
|
||||
onClick="() => this.toggleFilter('on_hold')"/>
|
||||
<FpKpiTile value="state.data.kpis.awaiting_qc"
|
||||
label="'Awaiting QC'"
|
||||
kind="'qc'"
|
||||
active="!!state.filters.awaiting_qc"
|
||||
onClick="() => this.toggleFilter('awaiting_qc')"/>
|
||||
<!-- Spec 2026-05-25 — post-shop state tiles -->
|
||||
<FpKpiTile value="state.data.kpis.awaiting_cert"
|
||||
label="'Awaiting CoC'"
|
||||
@@ -89,6 +94,9 @@
|
||||
<FpFilterChip label="'FAIR'"
|
||||
active="!!state.filters.fair"
|
||||
onToggle="() => this.toggleFilter('fair')"/>
|
||||
<FpFilterChip label="'Awaiting QC'"
|
||||
active="!!state.filters.awaiting_qc"
|
||||
onToggle="() => this.toggleFilter('awaiting_qc')"/>
|
||||
<!-- Spec 2026-05-25 — post-shop state chips -->
|
||||
<FpFilterChip label="'Awaiting CoC'"
|
||||
active="!!state.filters.awaiting_cert"
|
||||
|
||||
Reference in New Issue
Block a user