Repository navigation
SPA: table layout — provenance column to one line, date-range filters into a bounded toolbar #653
Description
Activity
- addedenhancementNew feature or requestNew feature or requestarea:frontendReact/Vite web clientReact/Vite web client
on Sep 2, 2026 Groundwork landed early, in #661.
While shipping #651 + #652 it turned out
.toolbarhas zero consumers in the SPA —grep -rn "toolbar" web/src --include='*.tsx'returns nothing, and the built JS carries no such class. #651 restyled it anyway (inset fill,--r-panelradius, no shadow), so when this slice moves the date-range filters into a bounded toolbar, the styling is already there and already reasoned about.Two decisions you inherit rather than re-derive:
- Inset, not carded. Elevation encodes what floats; a filter bar does not.
- It keeps a hairline border despite being inset.
--canvasand--surface-2sit between 1.05:1 and 1.21:1 in every palette and both modes — measured with the repo's owncontrast()helper inweb/src/test/cssTokens.ts— so a borderless inset fill is invisible on all of them, not just the light ones.
--r-panel: 10pxwas added in the same PR and.toolbaris currently its only consumer.The rule carries a comment saying it is not dead code. Once this slice adds the consumers, that comment is stale and should go.
Also worth knowing before you start:
.stat/.stat-labelno longer exist (#654 replaced them with.capture-tile*) and.eyebrowwas deleted as dead in #661. The design notes for all of it are indocs/designs/651-652-spa-elevation-and-caps.md.Shipped in #668, merged as
80b53f4b.Delivered:
ProvenanceCellrenders one muted line (relative time + actor local-part) with the full UTC stamp in thetitle, and the column isnowrapwith a max width so it can never again be the widest thing in the table. Reference/order-number cells stop wrapping. Date-range filters sit in the bounded.toolbaron Reports, History, Feed, Water and Expenses. A newweb/src/lib/relativeTime.tscomputes day boundaries on the farm clock.A conflict this issue did not anticipate, and how it was resolved.
ProvenanceCellrendered UTC deliberately — its own comment, from #494's review: "these are audit instants, and the trail is displayed in UTC everywhere else it appears." This issue asked for the farm clock. Both are right about different things, so: the relative phrase uses the farm clock ("2 days ago" is a statement about the reader's day) and the exact instant in thetitlestays UTC. #494's decision survives untouched for the precise stamp. That reasoning is in a comment on the helper so a future reader does not "fix" one half.Intl.RelativeTimeFormatwas considered and rejected, with reasons recorded in the code — the repo's rule is framework facility first, hand-rolled only with justification. Three: its output never reaches i18next so it cannot enter the es/tl native-review pipeline; it has no timezone awareness, so the farm-local day maths is needed regardless; andnew Intl.RelativeTimeFormat("tl").resolvedOptions().localereturnsfilon this toolchain, an identity mismatch with a stack that keys strictly ontl. i18next's own_one/base plural resolution is used instead.Two errors in this issue's own body, both filed rather than silently worked around:
- Audit is listed as a date-range-filter screen. It has none —
AuditPage.tsxfilters on entity type and action only, no date input anywhere. The acceptance criterion is satisfied there vacuously, which is not the same as satisfied → SPA: Audit has no date-range filter, and an audit trail is the screen that most needs one #666. - Expenses is not a from/to pair — it is a single
type="month"picker, so it got the toolbar treatment without being forced into a shape it does not have → SPA: Expenses filters by a single month, where every sibling screen filters by a date range #667.
Playwright callers swept per #394: no spec reads provenance text.
- Audit is listed as a date-range-filter screen. It has none —
Follow-through: this criterion was unmet on a third screen
Recording against the closed issue because its acceptance criterion — "date-range filters sit in a toolbar at a bounded width on every screen that has one" — turns out to have been unmet on Stock, which this slice did not reach. Found by a design pre-review on #666/#667, not by a user report.
StockPage.tsx:533wraps the egg-lot production-date filter in<div className="filters">. The width cap atstyles.css:1083-1085is scoped to.toolbar input[type="date"], so it never applies and those two inputs render at the row's full width — the exact defect this issue was filed to fix.Everything else on that screen was already right: the pair works, hits a server-side window (#465), and already has the two-variant empty state with a clear-filters action. The fix is one word,
filters→toolbar, followingReportsPage.tsx:85's form (the whole bar is the toolbar, since that section has no non-date filter to keep outside it).Being fixed in the #666/#667 PR rather than filed separately (owner's call, 2026-09-03) — a one-line change did not warrant its own slice. No new issue filed; this comment is the record.
Worth noting how it was missed and how it was found: #666 was itself filed because this issue's criterion was satisfied vacuously on Audit (no date filter to move). The same check was never run against screens that do have a filter but were not in this issue's file list.
grep -rn 'type="date"' web/src --include='*.tsx'returns ten hits across seven files; four are entry-form fields, and Stock was the one filter-shaped hit nobody had classified.- added a commit that references this issue
on Sep 12, 2026
Part of the SPA look-and-feel pass (design review 2026-09-02). Sibling slices: numeric typesetting, elevation hierarchy, all-caps labels, dashboard rework, empty states, visual identity.
Problem
Two layout faults visible in the committed screenshots:
docs/images/sales.pngHISTORY:Created by sim-sales-1@sim.local on 2026-08-16 20:09:49/Confirmed 2026-08-16 20:09:49— three lines per row, the widest column in the table, and it squeezes REFERENCE and DATE into wrapping.ProvenanceCell(web/src/components/ProvenanceCell.tsx) renders the full audit stamp inline on every list screen that has it.docs/images/reports.png:From/To<input type="date">stretch to ~950px because the form-grid input rule fills the column. Two 10-character dates in a 950px field.Proposal
ProvenanceCell→ one muted line: relative time + actor (2 days ago · sim-sales-1), the full timestamp in atitleand available in the row's detail / audit link (Audit: entity-scoped "View history" — who created/changed a specific record, and when #493 already links the trail). Column getswhite-space: nowrapand a max width; never wider than the reference column.From/Tointo a.toolbarrow,max-width: 12remeach, side by side; the same treatment for every other date-range filter (History, Audit, Feed, Water, Expenses).table.data— give the reference/id columnwhite-space: nowrapsoSO-66C100ADstops splitting.Acceptance
useFarmToday/web/src/lib/dates.ts), not the device clock — Farm settings — profile (name/logo) + locale/timezone/currency editing + §4.6 currency-change guard (API + SPA) #123.ProvenanceCell's new output (frontend tests ship in the same PR); i18n strings for the relative-time phrasing in en / es / tl.tools/simulation/ui/specs that read the provenance text are updated — read the callers (Daily entry: require grading to reconcile sellable eggs before submit #394).Siblings: #650 #651 #652 #654 #655 #656 #657