Skip to content

fix(web): fill the expanded Lay rate chart and allow ranges up to a year - #1162

Merged
mforce merged 3 commits into
mainfrom
fix/1161-lay-rate-fill-and-year
Oct 10, 2026
Merged

mforce merged 3 commits into
mainfrom
fix/1161-lay-rate-fill-and-year

Conversation

@mforce

@mforce mforce commented Oct 10, 2026 •

Copy link
Copy Markdown
Owner

Screenshots: before and after at 1280 and 390, plus 7, 2, 90 and 365 days

Closes #1161

What changes

The expanded Lay rate chart now stretches a range that fits. If the range's 22px slots fit the daily window, the slots widen to fill the plot from the y-axis to the right edge, and the date labels widen with them. A range that does not fit keeps the 22px slots, the horizontal scroll, the overview map sync and the edge fades.

The maintainer's 2026-10-10 decision also raises the custom range ceiling in the expanded chart from 90 to 365 days. The card stays at 14.

How "fits" is decided

dayWindow already answered fits from layout: days times the 22px slot plus the measured gap, compared with the region's clientWidth, never scrollWidth (#941 rule 3). It now also returns stretch, which is fits on a measured region. An unmeasured region fits but does not stretch. Without this, the chart's first commit would lay out a 90-day range stretched to 100% width, scroll to the newest day while nothing overflowed, and then re-render on 22px slots scrolled back to the oldest day. The ResizeObserver already re-runs dayWindow on resize, so the stretch follows the window.

stretch puts is-fit on .lay-expand-scroll. The slots and date-rule cells then use flex: 1 1 0 with min-width: 0, so a cell holding a date is the same width as its bar's slot. A layout effect keyed on stretch re-places the readout, because crossing the threshold moves the slots without resizing anything the readout observes. The justify-content: flex-end that parked a short range on the right is gone, because a range now either fills the window or overflows it.

Maximum bar width: 40px

The slot stretches, and the bar inside it stops at 40px, a little under two 22px slots. A 2-day range draws two 40px bars, each centred in its wide tinted slot, and still fills the plot. At 1280, 30 days gives about 32px slots, so the bars fill their slots.

Nothing else from #941 changes: the day states and hatching, the reserved readout row and #962's clamping, focus on a selected bar, Tab landing on a day in view, edge fades only at a real edge, and farm-local dates. They all read slot positions from the DOM, not from a fixed width.

365 days: the data path

  • The expanded chart makes one call, GET /reports/production?from&to[&flockId]. The server aggregates it per day, with no paging.
  • The server allows up to ReportEndpoints.MaxRangeDays = 366 (src/Cluckwork.Api/Modules/Insights/Reports/ReportEndpoints.cs:15), so 365 passes.
  • The web+api: replace the Dashboard's 10,000-row drain ceiling with a proper morning-brief design #942 drain ceiling (MAX_DRAIN_PAGES in Dashboard.tsx) applies only to the Morning panel's listDailyEntries/listFlocks drain. The expanded chart never uses it, so no ceiling changed.
  • 365 days is 365 slot buttons and about 9,490px of strip at 1280. Past 90 days the overview map drops its 1px gaps (is-dense), so all 365 marks keep a width in a phone's 332px map.

Strings

rangeErrorTooLong already takes the ceiling as a parameter. The Help text and glossary entry for the expanded chart now say 365 in en, es and tl. The sentence saying bars never stretch is replaced by one about fill versus scroll. specs/product/GLOSSARY.md is updated to match.

Tests

  • dayWindow.test.ts: stretch is true for 30 days and for 41 days (just fits), false for 42 and 90, and false when the region is unmeasured.
  • ExpandedLayRate.test.tsx: a range that fits gets is-fit only once measured. A range that does not fit keeps fixed slots and lights the right cue.
  • ExpandedLayRate.styles.test.ts: fixed 0 0 var(--slot) slots while scrolling, 1 1 0 and 100% in fit mode, and the 40px bar cap.
  • expanded-chart-fit.spec.ts (Playwright, three tests, about 8s): date cells line up with their bars at 30 and 41 days, the readout follows its day when the window narrows past the threshold and widens back, and at 390 a 365-day map has no zero-width mark and jumps where it is pressed. Each test turned red when its fix was removed.
  • layRateRange.test.ts and Dashboard.test.tsx: the expanded chart accepts 365 days and rejects 366, and the card still accepts 14 and rejects 15.

Browser check (after stack, 1280x800 unless noted)

Range is-fit Slot Bar Region / strip width Opens at
2 days yes 536px 40px 1076 / 1076 start
7 days yes 150px 40px 1076 / 1076 start
30 days yes 32px 32px 1076 / 1076 start
30 days, 390 no 22px 18px 288 / 718 newest end (scrollLeft 430)
90 days no 22px 18px 1076 / 2336 newest end (scrollLeft 1260)
365 days no 22px n/a 1076 / 9486 newest end (scrollLeft 8410)

The 365-day range made one report request and loaded without an error.

A range whose 22px slots fit the window now stretches to fill the plot from
the axis to the right edge, bars capped at 40px; a longer range keeps the
fixed slots, scroll, overview map and edge cues. The expanded chart's custom
range ceiling rises from 90 to 365 days; the card stays at 14.

Closes #1161
@mforce

mforce commented Oct 10, 2026 •

Copy link
Copy Markdown
Owner Author

Screenshots

After frames captured at 59b6abc. Before frames come from origin/main (bd4e5ec). Both are from isolated stacks rebuilt at those commits, on readme-farm, at 1:1.

30 days (the preset)

Before After
1280 light Before, 30 days, 1280 light After, 30 days, 1280 light
1280 dark Before, 30 days, 1280 dark After, 30 days, 1280 dark
390 light Before, 30 days, 390 light After, 30 days, 390 light

At 1280 the 30 slots measure 32px and fill the plot from the axis. At 390, 30 days does not fit, so the phone keeps the 22px slots and opens scrolled to the newest day, the same as before.

Maximum bar width (after only)

7 days, 1280 light 2 days, 1280 light
After, 7 days, 1280 light After, 2 days, 1280 light

The slots measure 150px (7 days) and 536px (2 days), and the bars stop at 40px. Since round 1, each date label is centred under its own bar.

Scrolling and the overview map (after only)

90 days, 1280 light 365 days, 1280 light 365 days, 1280 dark 365 days, 390 light
After, 90 days, 1280 light After, 365 days, 1280 light After, 365 days, 1280 dark After, 365 days, 390 light

These ranges keep 22px slots. The 90-day strip is 2,336px wide and the 365-day strip is 9,486px, and each opens scrolled to its end with the newest day at the right edge. At 390 the 365-day map drops its 1px gaps, so each of the 365 marks keeps a width inside the 332px map. The demo farm's history starts partway through the year, so the 365-day map is empty on the left. The only console error in any run was the sign-in page's usual 401 session probe.

…ones, re-place the readout across the fit threshold
@mforce

mforce commented Oct 10, 2026

Copy link
Copy Markdown
Owner Author

Codex Astra round 1 at af8bd8a

Fixed in 59b6abc. The browser spec that pins all three findings is in ae761c2 (tools/simulation/ui/specs/expanded-chart-fit.spec.ts).

1. Stretched date cells drifted from their bars (P2). The stretched date cells now have min-width: 0. Their overflow stays visible and the date row still clips. The spec checks the largest distance between a date cell's centre and its bar's centre at 30 days and at 41 days (the range that just fits at 1280). Both are under 0.5px. Restoring min-width: auto turns this test red at 28.8px.

2. The 365-day overview disappeared on phones (P2). Past 90 days the map gets is-dense, which sets the gap to 0. Up to 90 days the map looks as it did under #941's limit. At 390 with 365 days, the spec checks that all 365 marks have a positive width and none overhangs the map. It then presses the middle of the map and checks that the window scrolls to the middle of the range and the box is centred on the press, inside the map. Restoring the 1px gap turns this test red with a mark width of 0.

3. The readout went stale across the fit threshold (P2). A layout effect keyed on view.stretch re-runs placeReadout after the fit class changes. The spec selects day 16 of the 30 days at 1280, narrows the window to 760 so the chart scrolls, and widens it back to 1280. The readout stays within 1px of its day at every step. A stack built without the effect fails the narrowing step, with the readout 37.8px off.

Each mutation turned only its own test red. Ran on isolated stacks, now torn down. I recaptured the screenshot comment at 59b6abc and added a 365-day frame at 390.

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.

Expanded lay rate chart leaves the left 40% of the plot empty

1 participant