Files
scalesApp/.claude/plans/main-weighing-row-five-groups.md
T

164 lines
7.5 KiB
Markdown

# Main page: 5-group weighing row + form restyle
## Context
The weighing detail panel doesn't match the layout the user wants (reference
screenshot: a single row of five groups — CURRENT READING | TARE | GROSS | NET |
actions). Three things differ today:
1. **Current Reading is a separate full-width block** (`.reading-display`) sitting
*above* the card row, not part of it.
2. **There is a redundant "Scale" card** duplicating the same live weight, which
exists only to host the Measure / Manual entry buttons. The user's call:
these two are the same thing — **merge into one card**.
3. **Tare and Gross are all-or-nothing.** Today a lone first reading renders as a
single neutral "First weighing" card, and Tare/Gross only appear once both
exist. The user wants the five groups always visible, with the first reading
shown plainly in its slot.
Result: a stable five-column row that never changes its column count.
### The one behaviour being deliberately traded away
[Main.jsx:604-605](frontend/src/components/Main.jsx#L604-L605) documents why the
merged card exists: the backend parks a lone reading in `tare`, but a **lighter
second reading moves it to `gross`**. So a single reading labelled "TARE" can
later jump to the GROSS box. The user chose "show it plainly" — accepting that
jump. `firstWeighing` (Main.jsx:290-309) becomes dead and is removed.
## Approach
### 1. Merge Current Reading + Scale into one card
Delete the standalone `.reading-display` block (Main.jsx:581-600) and the Scale
card (647-677). One card takes their place as the row's first column, keeping the
**richer** of the two behaviours — the reading-display value logic, which the
Scale card lacked (disconnected state, `readingError` line):
```jsx
<div className="weight-card weight-card--reading">
<div className="weight-card-header">Current Reading</div>
<div className={`weight-card-value weight-card-value--reading ${…same ternary as today's reading-value…}`}>
{currentWeight !== null && !isNaN(currentWeight)
? `${currentWeight} kg`
: (isConnected ? 'Waiting...' : 'Disconnected')}
</div>
{isConnected && readingError && latestReading && (
<div className="reading-error" title={latestReading.data}>{readingError}</div>
)}
{canMeasure && !isFinalized && (<>
<button className="weight-set-btn" onClick={handleMeasure}
disabled={currentWeight === null || isNaN(currentWeight) || !licenseAllowed}
title={licenseAllowed ? undefined : licenseDetail}>Measure</button>
{!licenseAllowed && <div className="weight-license-block">{licenseDetail}</div>}
{currentUser?.can_manually_measure && (
<button className="weight-manual-btn" onClick={…}>Manual entry</button>
)}
</>)}
</div>
```
Keep the `.reading-value--balanced/--unbalanced` class names on the value so the
existing colour rules (Main.css:547-553) and cockpit glow keep applying
unchanged. The `--reading` modifier carries the larger font that
`.reading-value` had.
### 2. Tare / Gross / Net always render
Replace the ternary at Main.jsx:606-644 with three unconditional cards. Each
shows `—` and no meta when its value is null:
- Tare — `selectedVehicle.tare`, `tare_date`, `tare_user_name`, `tare_manual`
- Gross — same shape with `gross_*`
- Net — `netWeight` (null until both exist)
Add modifier classes `weight-card--tare`, `--gross` (Net already has `--net`).
**This is a prerequisite, not polish:** Main.css:576-578 states the cockpit rules
use `:nth-child` precisely because these cards have no distinguishing class, and
the column count is about to change. Those `:nth-child` selectors must be
rewritten to the modifiers in the same pass or cockpit chrome lands on the wrong
cards.
Net keeps Print Notice + the Design Notice split button inside it (unchanged
markup, including the dropdown menu and backdrop). Gate them on `netWeight !==
null` so an empty Net card shows no dead buttons.
### 3. Row becomes a grid
`.weighing-section` moves from `flex` + `flex-wrap` to a 5-column grid, following
the ratio proven in the concept file
([ScalesAppCyberpunk.css:94](frontend/src/components/concepts/ScalesAppCyberpunk.css#L94)):
```css
.weighing-section {
display: grid;
grid-template-columns: 1.3fr 1fr 1fr 1fr;
gap: 16px;
margin-bottom: 24px;
align-items: stretch;
}
```
Four columns, because Print/Design stay inside Net rather than becoming a
separate action column — the user kept them there. Reading gets the wider track.
Remove `.reading-display`'s `margin-bottom: 20px` block along with the element.
Lift `.vehicle-detail { max-width: 700px }` (Main.css:115) — four readouts do not
fit in 700px. The cockpit override at 851-853 that already does this then becomes
redundant and should go.
Responsive, replacing the cockpit-only `@media (max-width:1150px)` rule:
```css
@media (max-width: 1150px) { .weighing-section { grid-template-columns: 1fr 1fr; } }
@media (max-width: 620px) { .weighing-section { grid-template-columns: 1fr; } }
```
These apply to **both** themes now, so drop the `[data-theme="cockpit"]` prefix
from the wrap rules at 858-872.
### 4. Form restyle (styling only, no new capability)
Confirmed scope: the create form only. There is no edit form and none is being
added. In Main.css:
- `.extra-field input/select` — set explicit `background: var(--surface)` and
`color: var(--text)`. They currently inherit UA defaults and survive cockpit
only by accident of `color-scheme: dark`.
- `.extra-fields` — add `@media (max-width: 768px) { grid-template-columns: 1fr }`;
it is a hard `1fr 1fr` at every width today.
- `.vehicle-form-actions` — add `justify-content: flex-end` to match the
`.manual-actions` precedent (Main.css:539+).
## Critical files
- [frontend/src/components/Main.jsx](frontend/src/components/Main.jsx) — lines
581-741 replaced; `firstWeighing` memo (290-309) deleted
- [frontend/src/components/Main.css](frontend/src/components/Main.css) —
`.reading-display` block removed, `.weighing-section` → grid, `max-width` cap
lifted, cockpit `:nth-child` → modifier classes, form rules
- [frontend/src/components/concepts/ScalesAppCyberpunk.css](frontend/src/components/concepts/ScalesAppCyberpunk.css)
— reference only, not modified. Its hardcoded hex and `var(--cyan)`/`var(--green)`
must **not** be copied; translate to the semantic tokens in
[frontend/src/styles/tokens.css](frontend/src/styles/tokens.css) (`--accent`,
`--success`). tokens.css is semantic-by-rule (header comment, lines 5-9).
No backend, serializer or API change. No new dependency.
## Verification
1. `cd frontend && npx vitest run` — 201 tests still pass
2. Browser check at http://localhost:5172, **both themes** via the header toggle,
walking the full weighing lifecycle on one vehicle:
- no vehicle selected → placeholder unchanged
- fresh vehicle, no readings → 4 cards, all `—`, no dead buttons
- after first Measure → value appears in TARE, Gross/Net still `—`
- after second Measure → Tare+Gross+Net populated, Measure button gone,
Print/Design appear in Net
3. Confirm the merged card still shows: `Disconnected` when the bridge is down,
`Waiting...` when connected with no reading, and the balanced/unbalanced
colour on a live value
4. Widths 1400 / 1100 / 600px — grid collapses 4 → 2 → 1 with no overflow
5. Cockpit chrome (clipped corner, lit top edge, Net glow) lands on the right
cards after the `:nth-child` → modifier rewrite
6. Create form: 2-col above 768px, 1-col below; Cancel/Create right-aligned;
inputs readable in cockpit