Skip to content

SPA: table layout — provenance column to one line, date-range filters into a bounded toolbar #653

Description

@mforce

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:

  1. 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.
  2. 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

  1. 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.
  2. 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).
  3. While there: column widths on table.data — give the reference/id column white-space: nowrap so SO-66C100AD stops splitting.

Acceptance

Siblings: #650 #651 #652 #654 #655 #656 #657

Activity

  1. mforce commented on Sep 2, 2026

    @mforce
    OwnerAuthor

    Groundwork landed early, in #661.

    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.

  2. mforce commented on Sep 2, 2026

    @mforce
    OwnerAuthor

    Shipped in #668, merged as 80b53f4b.

    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:

    Playwright callers swept per #394: no spec reads provenance text.

  3. mforce commented on Sep 3, 2026

    @mforce
    OwnerAuthor

    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.

  4. added 2 commits that reference this issue on Sep 3, 2026
  5. added a commit that references this issue on Sep 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions