Skip to content

Three reviewer findings: an axis that shrank, a horizon that was short, and focus left on a hidden node - #184

Merged
satvikOS merged 1 commit into
mainfrom
fix/reviewer-findings-charts-and-nav
Aug 23, 2026
Merged

Three reviewer findings: an axis that shrank, a horizon that was short, and focus left on a hidden node#184
satvikOS merged 1 commit into
mainfrom
fix/reviewer-findings-charts-and-nav

Conversation

@satvikOS

@satvikOS satvikOS commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

Three CodeRabbit findings from #155 and #161, recorded as follow-ups and not yet done. A fourth from the same batch — the handoff spec's unguarded cleanup — became #183 after it turned main red twice on commits that changed no code. That is the argument for clearing the rest of the list now rather than later.

1. yMax replaced the scale instead of raising its floor

BarChart.tsx:77 and LineAreaChart.tsx:68 both read niceAxis(yMaxProp ?? rawMax, …) — while the comment immediately above each says yMax "raises the floor of the axis; it never becomes the scale on its own."

?? makes both sentences false. A caller passing a yMax below the data maximum replaces the scale with it, and every mark above it renders past plotH / above padTop — outside the plot area.

Now Math.max(yMaxProp ?? 0, rawMax), which is what the comments already described.

Latent, not livegit grep yMax finds no caller passing one today. Fixed because the prop is public surface and the next caller would have found it the hard way.

2. The forward bucketer's horizon was short by however much of today had passed

bucketByWeekForward documents "Bucket 0 is the next seven days" and computed start = startOfDay(now), so end = start + weeks*WEEK sat a fraction of a day before the horizon it promised. With the suite's own noon clock that is twelve hours — anything scheduled in that gap failed t < end and vanished from the series.

This one is live: dashboard/page.tsx:189 feeds it the real event trend. And it fails quietly, which is what makes it worth fixing — a bucketer returns a number either way, and an under-count is indistinguishable from a quiet calendar.

All four pre-existing cases still hold, because none of them sat near a boundary — which is exactly why none of them caught it. Two boundary tests added: an event at now+6.75d must be in bucket 0 (the seventh day the doc promises, which the midnight anchor pushed into bucket 1), and an event one hour before the end of the stated horizon must be counted at all.

Control: restoring startOfDay(now) fails exactly those two, and leaves the eleven pre-existing tests passing.

3. Escape left focus on the element it had just hidden

SideNav.tsx moves focus into the drawer when it opens, and Escape called closeDrawer(), which hides that element — leaving focus on a hidden node, so the next Tab resumed from the top of the document rather than from the control the person was using.

Focus now returns to the button that owns the drawer via [aria-controls="app-sidenav"] — the relationship WAI-ARIA already defines, verified present at ShellHeader.tsx:52 — rather than by an id, so nothing silently stops matching if the markup moves.

A local artifact worth recording

tsc initially reported four errors in src/lib/service-notice/* after #180 merged. Not a defect on main: the new ServiceNotice model needs prisma generate, which CI runs and a stale local node_modules had not. Clean after regenerating — worth knowing before someone reports main as broken.

Verified: tsc clean, 4067 passed / 1 pre-existing skip, re-verified after merging #179's DonutChart changes (charts: 90 passed).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Prevented bar and line charts from clipping values when configured axis limits are lower than the data.
    • Corrected weekly time-series forecasting to include the complete forward horizon.
    • Improved mobile navigation accessibility by restoring focus to the drawer toggle after closing with Escape.
  • Tests

    • Added coverage for events at the beginning and end of weekly forecast periods.

…t, and focus left on a hidden node

Three CodeRabbit findings from #155 and #161 that were recorded as follow-ups
and not yet done. A fourth from the same batch — the handoff spec's unguarded
cleanup — was done as #183 after it turned main red twice on commits that
changed no code, which is the argument for clearing the rest of the list now
rather than later.

── 1. `yMax` replaced the scale instead of raising its floor ───────────────

BarChart.tsx:77 and LineAreaChart.tsx:68 both read:

    niceAxis(yMaxProp ?? rawMax, …)

while the comment immediately above each says `yMax` "raises the floor of the
axis; it never becomes the scale on its own" and "does not become the scale
unnoticed". `??` makes both sentences false: a caller passing a yMax BELOW the
data maximum replaces the scale with it, and every mark above it renders past
`plotH` / above `padTop` — outside the plot area.

Now `Math.max(yMaxProp ?? 0, rawMax)`, which is what the comments already
described. LATENT, NOT LIVE, and said so plainly: `git grep yMax` finds no
caller passing one today. Fixed because the prop is public surface and the next
caller would have found it the hard way.

── 2. The forward bucketer's horizon was short by however much of today had
      already passed ───────────────────────────────────────────────────────

`bucketByWeekForward` documents "Bucket 0 is the next seven days" and computed
`start = startOfDay(now)`, so `end = start + weeks*WEEK` sat a fraction of a day
BEFORE the horizon it promised. With the suite's own noon clock that is TWELVE
HOURS: anything scheduled in that gap failed `t < end` and was dropped from the
series entirely.

This one is live — `dashboard/page.tsx:189` feeds it the real event trend. And
it fails quietly, which is what makes it worth fixing: a bucketer returns a
number either way, and an under-count is indistinguishable from a quiet
calendar.

`start = now.getTime()`. All four pre-existing cases still hold (days(1) and
days(2) -> bucket 0, days(9) -> 1, days(20) -> 2, laterToday -> 0), because
none of them sat near a boundary — which is exactly why none of them caught it.

Two tests added at the boundaries that were unguarded: an event at now+6.75d
must be in bucket 0 (the seventh day the doc promises, which the midnight
anchor pushed into bucket 1), and an event one hour before the end of the
stated horizon must be counted at all.

CONTROL: restoring `startOfDay(now)` fails EXACTLY those two and leaves the
eleven pre-existing tests passing.

── 3. Escape left focus on the element it had just hidden ──────────────────

`SideNav.tsx` moves focus INTO the drawer when it opens (`panel.current?.focus()`)
and Escape called `closeDrawer()`, which hides that element — leaving focus on a
hidden node, so the next Tab resumed from the top of the document rather than
from the control the person was using. Focus now returns to the button that owns
the drawer, found by `[aria-controls="app-sidenav"]` — the relationship WAI-ARIA
already defines, verified present at ShellHeader.tsx:52 — rather than by an id,
so nothing silently stops matching if the markup moves.

── A local artifact worth recording ────────────────────────────────────────

`tsc` initially reported four errors in `src/lib/service-notice/*` after #180
merged. Not a defect on main: the new `ServiceNotice` model needs
`prisma generate`, which CI runs and a stale local node_modules had not. Clean
after regenerating. Worth knowing before someone reports main as broken.

Verified: tsc clean, 4067 passed / 1 pre-existing skip, and re-verified after
merging #179's DonutChart changes (charts: 90 passed).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

satvikOS has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@coderabbitai

coderabbitai Bot commented Aug 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6ac33d1b-ea40-4179-986a-dd94b8efb751

📥 Commits

Reviewing files that changed from the base of the PR and between 9313935 and 5236d72.

📒 Files selected for processing (5)
  • apps/web/src/components/charts/BarChart.tsx
  • apps/web/src/components/charts/LineAreaChart.tsx
  • apps/web/src/components/charts/timeseries.test.ts
  • apps/web/src/components/charts/timeseries.ts
  • apps/web/src/components/shell/SideNav.tsx

Included review availability: Your plan provides up to 10 included reviews per hour; 1 remains after this review.


📝 Walkthrough

Walkthrough

The changes prevent chart clipping, correct forward weekly forecast boundaries, and restore focus to the mobile navigation trigger after Escape closes the drawer.

Changes

Chart axis scaling

Layer / File(s) Summary
Chart axis maximum handling
apps/web/src/components/charts/BarChart.tsx, apps/web/src/components/charts/LineAreaChart.tsx
Both charts now use the greater of the configured yMax and the computed data maximum. Existing axis tick generation remains unchanged.

Forward weekly bucketing

Layer / File(s) Summary
Forward window boundaries
apps/web/src/components/charts/timeseries.ts, apps/web/src/components/charts/timeseries.test.ts
bucketByWeekForward starts at the exact now timestamp. Tests cover events at the first bucket boundary and the final horizon boundary.

Mobile navigation focus

Layer / File(s) Summary
Drawer Escape focus restoration
apps/web/src/components/shell/SideNav.tsx
Escape closes the mobile drawer and returns focus to its controlling element.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 5236d

The changes correct chart scaling, include the promised forward time horizon, and return focus to the drawer control after Escape; no actionable merge-blocking risk remains beyond normal checks and review.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the three main fixes: chart axis scaling, forecast horizon coverage, and focus restoration.
Docstring Coverage ✅ Passed Docstring check was indeterminate for this PR — some files could not be analyzed in time. Not blocking.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/reviewer-findings-charts-and-nav

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

@satvikOS
satvikOS merged commit 1ac4b56 into main Aug 23, 2026
5 checks passed
satvikOS pushed a commit that referenced this pull request Aug 23, 2026
Three conflicts, each resolved as a union rather than by taking a side.

layout.tsx — #161 landed ShellNavProvider around the header and the side nav;
this branch inserts SkipLink at the same point. Both kept. SkipLink is FIRST
(its whole feature is being the first Tab stop) and sits outside the provider:
it is a bare server-rendered <a> with no drawer state, so nesting it would pull
it across the client boundary for nothing.

dashboard/page.tsx — two hunks, both unions.
  · the timeseries import now carries bucketByDay (this branch) AND
    forwardDelta (#184).
  · #184 changed the event spark's delta from trendDelta to forwardDelta:
    eventSpark is bucketed FORWARD, so bucket 0 is the next seven days and
    reading the LAST two buckets compared the far end of the horizon with
    itself. That is kept, alongside this branch's move of the activity
    bucketing to the server. bucketByWeekForward's `now` anchor is untouched.

settings/page.tsx — #156 converted every <form action={...}> here to
<ReportingForm>, and #167 replaced the "club member" fallback with "—". The
conflict was only the email form's opening tag. Resolved to #156's
<ReportingForm> with this branch's gap-3, so the three-state radio group ships
inside the wrapper that lets a refusal reach the person instead of throwing.

One seam the merge creates rather than inherits: readEmailMode refused an
unoffered mode by throwing a bare Error. Under #156 that is not a refusal — it
is caught as an unexpected fault, replaced with "Something went wrong on our
side", and logged as `[admin] action failed`. So the sentence written for the
person reached nobody, and a rejected radio value was reported as a server
fault. It throws a Refusal now; the existing assertion (toThrow(/Choose one
of/)) still holds, because Refusal extends Error.

Verified nothing was dropped: the merged tree touches exactly the 19 files this
PR declares, and every line it removes relative to main is one of the five
defects being fixed — the two-state checkbox write, the old receipt label, the
client-side timestamp payload. No ReportingForm reverted to <form>, and "—" is
still the no-institution-role fallback.

Also checked, and NOT redundant: the monogram fix. #180 reworked
InstitutionMark and the brand slots, but initials() lives in ui/Avatar.tsx,
main never touched it, and InstitutionMark carries no initials logic at all.
Twelve call sites still read it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@satvikOS
satvikOS deleted the fix/reviewer-findings-charts-and-nav branch August 25, 2026 18:50
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.

2 participants