You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
SPA: table layout — provenance column to one line, date-range filters into a bounded toolbar #653
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:
Provenance column carries sentences.docs/images/sales.png HISTORY: 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.
Report date range spans the page.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 a title and 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 gets white-space: nowrap and a max width; never wider than the reference column.
Reports: From / To into a .toolbar row, max-width: 12rem each, side by side; the same treatment for every other date-range filter (History, Audit, Feed, Water, Expenses).
While there: column widths on table.data — give the reference/id column white-space: nowrap so SO-66C100AD stops splitting.
Acceptance
Provenance renders one line on every list screen; the full stamp is still reachable (title / detail).
While shipping #651 + #652 it turned out .toolbar has 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-panel radius, 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.--canvas and --surface-2 sit between 1.05:1 and 1.21:1 in every palette and both modes — measured with the repo's own contrast() helper in web/src/test/cssTokens.ts — so a borderless inset fill is invisible on all of them, not just the light ones.
--r-panel: 10px was added in the same PR and .toolbar is 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-label no longer exist (#654 replaced them with .capture-tile*) and .eyebrow was deleted as dead in #661. The design notes for all of it are in docs/designs/651-652-spa-elevation-and-caps.md.
Delivered:ProvenanceCell renders one muted line (relative time + actor local-part) with the full UTC stamp in the title, and the column is nowrap with 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 .toolbar on Reports, History, Feed, Water and Expenses. A new web/src/lib/relativeTime.ts computes day boundaries on the farm clock.
A conflict this issue did not anticipate, and how it was resolved.ProvenanceCell rendered 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 the title stays 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.RelativeTimeFormat was 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; and new Intl.RelativeTimeFormat("tl").resolvedOptions().locale returns fil on this toolchain, an identity mismatch with a stack that keys strictly on tl. 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:
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:533 wraps the egg-lot production-date filter in <div className="filters">. The width cap at styles.css:1083-1085 is 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, following ReportsPage.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.
Activity
mforce commentedon 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:
--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.docs(web): record that .toolbar is styled ahead of #653, not dead (#651)
feat(web): elevation hierarchy and sentence-case labels (#651, #652) (#…
feat(web): one-line provenance, bounded date filters, and empty state…
mforce commentedon Sep 2, 2026
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:
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.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.
mforce commentedon Sep 3, 2026
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.docs: name the scoped filtered-empty key and state the #653 relations…
Merge pull request #678 from mforce/feat/666-667-spa-date-range-filters
chore(main): release 0.1.0 (#542)
feat(web): add the shared FilterBar component (#831)
feat(web): add the shared FilterBar component (#831)