Skip to content

feat(web): convert Reports, Feed, Water, Stock, History, Expenses and Inventory to MUI, add FilterBar - #899

Open
mforce wants to merge 14 commits into
mainfrom
feat/831-mui-ledgers
Open

mforce wants to merge 14 commits into
mainfrom
feat/831-mui-ledgers

Conversation

@mforce

@mforce mforce commented Sep 18, 2026

Copy link
Copy Markdown
Owner

Summary

Part of #831. Converts seven of the eight ledger/support screens (Sales ships separately, see below) from hand-rolled CSS to MUI, plus the new shared FilterBar component (pair 7) every filter-row screen here uses.

Not in this PR: Sales (own PR, ~1,860 lines + 4,110 test lines, per the slice brief). .order-panel, tr.discounted, .toolbar, table.data, .page-head, .muted stay declared in styles.css — Sales and/or Audit (#833) are still live consumers.

Test rewrite table

File Before → after Why
ReportsPage.test.tsx toHaveClass("num")toHaveStyle({textAlign:"right"}) MUI owns the class name now; textAlign is the real observable effect
StockPage.test.tsx table.data/div.toolbar.MuiTableContainer-root/.MuiPaper-outlined retired classes replaced with the new markup's structural hooks
HistoryPage.test.tsx .entry-readout/.entry-row → role-scoped (role="status"/"alert", role="group" named) matches the identical pattern DailyEntryPage.test.tsx already established for the same EntryRow component
ExpensesPage.test.tsx div.toolbar.MuiPaper-outlined same retired-class fix as Stock's

Mutation/defect checks

  • Caught and fixed before commit: PICKER_SX (the 15rem filter-row width cap) had leaked onto Expenses' edit-dialog FlockPicker, which the retired CSS explicitly gave full width inside a dialog — separate fix commit, verified against the original .dialog .form-grid .named-picker override.
  • Caught and fixed before commit: an unnecessary .actionsBox swap on Inventory's item-panel close button, reverted (.actions carries a real phone-stacking rule).

CSS deletion counts

None in this PR. .order-panel (Sales/Inventory/Expenses were its remaining three consumers; Inventory and Expenses converted here, Sales converts in the follow-up PR, which deletes the rule) and tr.discounted/tr.discounted .badge-warn (Sales-only) are deleted in the follow-up PR. .toolbar, table.data, tr.inactive td, .page-head, .muted/.error/.success/.warn stay declared — Audit (#833) and/or #832's CRUD lists are still live consumers, confirmed by grep before touching each class.

Test plan

  • npm run typecheck clean
  • npm test (vitest) — full suite 3121/3122 (1 pre-existing flake, confirmed unrelated and green standalone)
  • dotnet test tests/Cluckwork.Application.Tests --filter "FullyQualifiedName~ImagePin|FullyQualifiedName~RealTree" — 14/14 (before the docs amendment commit on the follow-up PR)
  • Guards green: styles.declared-tokens.test.ts, styles.harness-selectors.test.ts, farmTheme.policy.test.ts, styles.elevation.test.ts
  • Isolated-stack full quick Playwright suite (in progress — will report separately)
  • Before/after 1:1 captures at 1280×800 and 390×844, light/dark (in progress — will attach separately)

Paper(variant="outlined") + Stack(direction="row", flexWrap, useFlexGap)
holding a caller-supplied filter row, plus a FilterDateField helper that
carries #653's 12rem bounded date width from md up and widens to one
control per line below it. Ledger screens adopt it starting with the
next commit; Audit (#833) can pick it up from this branch or from main
once this lands.
Pair 7 (FilterBar), pair 9 (Table/TableContainer), pair 10 (title row)
and pair 11 (Feed's inline capture form). Feed's flock pickers keep
the retired `.form-grid .named-picker` sizing (15rem/8rem/100%) as an
inline sx constant so the closed/open states stop shifting siblings.
Same shape as Feed: pair 7 (FilterBar), pair 9 (Table), pair 10 (title
row), pair 11 (capture form, including the meter-readings checkbox to
FormControlLabel+Checkbox). All 30 existing tests pass unchanged.
Pairs 7/9/10/11 across the grade table, the lots filter+table, the
movement ledger table, and the write-off dialog. Adds the #150
scroll-shadow gradient to the theme's MuiTableContainer phone
override (a verbatim copy of table.data's, which #897 never carried
over) so a wide MUI table keeps the same "more to scroll" edge cue.

Two StockPage.test.tsx assertions checked class-name implementation
detail (table.data / div.toolbar); rewrote both against the new
markup's real structural hooks.
History's adjust dialog mirrors Daily entry's two-step grading layout
exactly (its own comments say so) and needs the identical MUI grid to
convert away from .entry-form/.entry-rows without drifting from it.
Pure extraction, no behavior change; DailyEntryPage's 82 tests stay
green.
Pairs 7/9/10/11 across the filter row, the entries table (Voided rows
keep their muted tone via sx inheritance) and the adjust dialog, which
now renders through the shared EntryRow grid DailyEntryPage's capture
form already uses (keeping History's own visible .step-n pill, per
#830's comment). 2 of 75 tests selected by retired
.entry-readout/.entry-row classes rewritten to the same role-scoped
pattern DailyEntryPage.test.tsx already established.
Pairs 7/9/10/11/15: FilterBar now holds the whole filter row (dates,
category select, manage-categories/clear-filters links) where
.filters used to wrap a bounded .toolbar beside a bare category
field; the categories panel converts to the same Box-between-two-
Dividers ruled region FlocksPage established for pair 15; both forms
and the ledger table convert to MUI. 1 of 71 tests rewritten from the
retired div.toolbar class to .MuiPaper-outlined, same fix as Stock's.
Pairs 9/10/11/15: title row gets its New item action beside it via
Stack justify-content space-between, both catalog dialogs and the two
item-panel dialogs (purchase, adjust) convert to Stack+TextField, the
item panel becomes the same Box-between-two-Dividers ruled region
Expenses/Flocks already use, and both tables convert. Inventory has
no list-level filter row, so it does not call FilterBar. All 73
existing tests pass unchanged.
…831)

PICKER_SX (flex 0 1 15rem) belongs on a picker inside a horizontal
filter/capture row, replicating the retired .form-grid .named-picker
rule. Inside a vertical dialog Stack every child already stretches
full width by default, which is what the retired
.dialog .form-grid .named-picker override gave it — the edit dialog's
FlockPicker had picked up the row cap by copy-paste and would have
rendered too narrow. No behavior asserted by a test (jsdom computes no
layout); caught by re-reading the CSS this markup replaces.
.actions carries a real phone-stacking rule (styles.css, D3.4's
default), not a bare layout hook — dropping it for a plain Box in the
prior commit was an unnecessary deviation with no CSS deletion behind
it. Reverts to the retained class.
@coderabbitai

coderabbitai Bot commented Sep 18, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 83bb97c9-516b-4728-977f-4d2872275564


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…tory (#831)

The full quick Playwright suite caught a real regression: "no walked
screen overflows the viewport horizontally" located /stock and
/history by `content: "table.data"`, and both now render MUI's
<Table> with no `data` class, same shape #832 already hit for
/customers and /flocks (whose fix — switch to `role=table` — this
mirrors). Also updates mutation-check.sh's EXPECT_MSG_FOR and
mutants.ts's phone-table-overflow-unclipped mutant, which asserted
/history still overflows under that mutant's table.data-scoped CSS
override — it no longer does, narrowing the mutant to /sales alone
(the follow-up PR converts Sales too and needs to retire or retarget
this mutant per #824's rule).
#831)

TextField select whose value can be "" with a placeholder option needs
slotProps.inputLabel.shrink=true or the label rests on top of the
placeholder text (the owner caught this on #897's Grade select; #833
hit it again on Audit's filters). Expenses' category filter and its
record-expense form both have the same shape. Adds the unit assertion
#897 established (label carries MuiInputLabel-shrink) for both.
CodeRabbit on #901 (FilterBar cherry-picked into Audit): sx={{ ...,
...sx }} only spreads a plain object's own enumerable properties, so a
caller passing a theme-callback function or an sx array had it
silently dropped instead of merged. MUI accepts an sx array and
applies each entry in order; using that form keeps the bounded-width
default AND the caller's own sx, of any shape. Added a test that
passes a function sx and asserts it applied (confirmed red against
the prior spread-based merge, green after this fix).
@mforce

mforce commented Sep 18, 2026

Copy link
Copy Markdown
Owner Author

Before/after captures — 1280×800, light + dark (1/2)

Isolated-stack captures at head f2722e9, before rebuilt from origin/main. All 1:1 (deviceScaleFactor 1), readme-farm's demo profile has no operational data, so these are captured against default-farm (the simulation fixture) instead — DemoDataSeeder seeds no feed/water/expense/inventory data, only flocks/entries for the Dashboard.

History shows the adjust dialog open; Expenses shows the categories panel open; Inventory shows an item panel open — per the screenshot rule.

Reports

Before:
After:
Before (dark):
After (dark):

Feed

Before:
After:
Before (dark):
After (dark):

Water

Before:
After:
Before (dark):
After (dark):

Stock

Before:
After:
Before (dark):
After (dark):

History (adjust dialog open)

Before:
After:
Before (dark):
After (dark):

Expenses (categories panel open)

Before:
After:
Before (dark):
After (dark):

Inventory (item panel open)

Before:
After:
Before (dark):
After (dark):

390×844 frames in the next comment.

@mforce

mforce commented Sep 18, 2026

Copy link
Copy Markdown
Owner Author

Before/after captures — 390×844, light + dark (2/2)

Same stacks/commit as the 1280×800 comment above (f2722e9 after, origin/main before). Same interactive states: History's adjust dialog open, Expenses' categories panel open, Inventory's item panel open.

Reports

Before:
After:
Before (dark):
After (dark):

Feed

Before:
After:
Before (dark):
After (dark):

Water

Before:
After:
Before (dark):
After (dark):

Stock

Before:
After:
Before (dark):
After (dark):

History (adjust dialog open)

Before:
After:
Before (dark):
After (dark):

Expenses (categories panel open)

Before:
After:
Before (dark):
After (dark):

Inventory (item panel open)

Before:
After:
Before (dark):
After (dark):

@mforce

mforce commented Sep 18, 2026

Copy link
Copy Markdown
Owner Author

Runtime verification (isolated stack, not cluckwork-sim)

Test plan checklist, closing out the two pending items:

  • Isolated-stack full quick Playwright suite — 54 passed, 1 skipped (deliberately off by default: session-refresh.spec.ts's slow real-token-lifetime test), 0 failed, both projects (chromium + chromium-phone, all 55 specs), against a stack built at this PR's head.
  • Before/after 1:1 captures at 1280×800 and 390×844, light/dark — attached above (two comments, split by viewport to stay under the attachment-count limit).

One real regression found and fixed during this verification, not by me — tools/simulation/ui/specs/phone.spec.ts's "no walked screen overflows the viewport horizontally" test still located /stock and /history via content: "table.data". This PR's MUI <Table> conversion drops that class (same shape as #832's /customers//flocks conversion), so the first suite run came back 53/55. Root-caused, reported, and fixed live by another agent working the same issue — commit 83eb5c4 switches both routes to content: "role=table" (the exact fix #832 already established). Re-ran the full suite after the fix: 54/55 (see above).

One false alarm, stated plainly rather than left standing: my first capture round showed the Expenses page's two Category <select> fields with their floating label overlapping the value/placeholder text. I initially reported this as a real defect, since the fix code (slotProps.inputLabel.shrink=true) was already present with a comment citing the #897/#833 precedent. It turned out to be AGENTS.md's own warning applying to my own isolated stack: my Docker image was built before a slightly earlier version of that fix, and the running container served stale bytes. Rebuilt the image from current source and recaptured — clean, no overlap, in both viewports and both themes. Correcting the record here since the first-round screenshots (now superseded) would otherwise have implied a bug that isn't there.

DIRECTION.md compliance, checked against docs/designs/864-visual-language/DIRECTION.md: ruled rows and the --rule-strong total rule are present on every table; numerals are right-aligned and tabular throughout (Reports' money/count columns, Stock's Available/Restricted, History's totals); FilterBar's outlined border is visible in every frame that carries one; the History adjust dialog, Expenses categories panel, and Inventory item panel are all plain ruled regions with no card-fill background, matching the "no card fills on the drill-down regions" rule. Radii read as the documented 4/8/12px tiers (controls/cards/dialogs) by eye across both themes.

Isolated stack (cw899, its own compose project + ports, distinct from and never touching the shared cluckwork-sim stack), torn down after use.

@mforce

mforce commented Sep 18, 2026

Copy link
Copy Markdown
Owner Author

SHA note: the captures/suite above were verified at f2722e9. The branch has since moved to 2da532a (one more fix: FilterDateField's sx merge, { ...sx } → an array, so a function/array-form sx isn't silently dropped — flagged by CodeRabbit on #901 which cherry-picked this component). Checked independently rather than taking that on faith: none of the seven screens captured here (grep -n "FilterDateField" web/src/routes/*.tsx) pass an sx prop to FilterDateField at all, so this fix has zero visual effect on any of the attached frames — no recapture needed. Suite result and captures above remain valid for 2da532a.

… list (#831)

Coordinator review of the rendered frames: Category and Flock cells
wrapped onto multiple lines at 1280 (the #897 regression this repo has
hit before). Pins whiteSpace:nowrap on date/category/amount/flock/
record-history cells and their headers; description and note stay
free text and keep wrapping. Also converts the categories-management
panel from a bulleted <ul> to a ruled MUI List (dividers, no bullets),
matching direction A's ruled-row language rather than a bulleted list
inside a ruled region.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant