Skip to content

Add period navigation arrows to Reports view#1707

Closed
joaocbatista wants to merge 0 commit intowe-promise:mainfrom
joaocbatista:main
Closed

Add period navigation arrows to Reports view#1707
joaocbatista wants to merge 0 commit intowe-promise:mainfrom
joaocbatista:main

Conversation

@joaocbatista
Copy link
Copy Markdown

@joaocbatista joaocbatista commented May 8, 2026

Closes #1681
Adds previous/next navigation arrows to the period display in the Reports view, allowing users to move between periods without manually changing dates.
Changes:

Monthly: navigate one month at a time
Quarterly: navigate one quarter at a time
YTD: navigate one year at a time
Last 6 months: navigate 6 months at a time, correctly extending to current month end when the window reaches the present
Custom: period display hidden (date picker is sufficient)
Next arrow is disabled when already at the current period

Summary by CodeRabbit

  • New Features

    • Period navigation added to reports with previous/next chevrons for monthly, quarterly, YTD, and last‑6‑months; next chevron is disabled on the latest period. Custom period hides the period display and shows only the custom date picker.
  • Localization

    • Added "Previous period" and "Next period" translation strings.
  • Tests

    • Added/updated tests covering period navigation and date‑range swap behavior.

Review Change Stack

@brin-security-scanner brin-security-scanner Bot added contributor:verified Contributor passed trust analysis. pr:verified PR passed security analysis. labels May 8, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 617a46471e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread app/views/reports/index.html.erb Outdated
Comment on lines +144 to +146
<%= link_to reports_path(period_type: @period_type, start_date: next_start, end_date: next_end),
aria: { label: "Next period" },
class: "#{ at_latest ? 'text-disabled pointer-events-none' : 'text-secondary hover:text-primary transition-colors' }" do %>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Remove the active href from the disabled next arrow

When the report is already at the latest period, this still renders an anchor with an href to the next future period and only adds pointer-events-none. That blocks mouse clicks, but the link remains focusable and can still be activated from the keyboard or by assistive tech, so users on the current month/quarter/YTD can navigate into a future period despite the arrow appearing disabled. Render a non-link/omit the href or otherwise remove it from the tab order when at_latest is true.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This sounds worth addressing, @joaocbatista ... also tests?

Copy link
Copy Markdown
Author

@joaocbatista joaocbatista May 8, 2026

Choose a reason for hiding this comment

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

sorry, still understanding the structure of the code and tests.

I've now added and fixed some tests based on coderabbitai feedback, made sure tests are passing as expected.

@jjmata
Copy link
Copy Markdown
Collaborator

jjmata commented May 8, 2026

Can you include screenshots in light and dark mode, please? 🙏

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 8, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The reports index now computes and exposes period navigation (@nav) for supported period types; the view conditionally renders previous/next chevrons (skipping :custom) and disables next at the latest boundary. i18n keys and controller tests were added/updated to cover navigation behavior.

Changes

Period Navigation UI

Layer / File(s) Summary
Controller: expose nav and view flags
app/controllers/reports_controller.rb
Sets @has_accounts and populates @nav by calling build_period_navigation in report setup.
Period navigation computation
app/controllers/reports_controller.rb
Adds build_period_navigation private method to compute prev/next start/end and at_latest for monthly, quarterly, ytd, last_6_months; returns nil for unsupported types.
View: conditional period navigation UI
app/views/reports/index.html.erb
Replaces static showing-period block with unless @period_type == :custom wrapper, renders Previous chevron as link using @nav[:prev_*], and renders Next chevron as link or disabled span based on @nav[:at_latest].
Localization keys
config/locales/views/reports/en.yml
Adds en.reports.index.previous_period and en.reports.index.next_period.
Controller tests
test/controllers/reports_controller_test.rb
Refines swapped-date assertion and adds tests for monthly/quarterly/last_6_months/custom navigation behaviors, including checks for prev/next links and disabled next arrow at latest boundary.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • we-promise/sure#276: Both PRs modify the reporting feature's controller and index view period-handling (reports_controller.rb and app/views/reports/index.html.erb).
  • we-promise/sure#622: Also modifies reports controller/view and may touch overlapping report setup or UI logic.

Suggested reviewers

  • jjmata
  • sokie

Poem

🐇 I hopped through months and quarters bright,
Chevrons gleamed and dates took flight.
Prev to the left and next held tight,
I nudged the links with gentle might.
A rabbit's hop made navigation right.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding navigation arrows for switching report periods.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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 and usage tips.

@joaocbatista
Copy link
Copy Markdown
Author

image image

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
app/views/reports/index.html.erb (1)

93-157: 🏗️ Heavy lift

Extract period-navigation date math out of the ERB.

The 30+ line case @period_type`` block computes prev/next ranges and at_latest inline in the template. This is exactly the kind of "heavy logic" the project guidelines push out of views, and it would also let you unit-test the boundary cases (last_6_months transition, quarter end, YTD year rollover) without going through the view.

A few options, in increasing scope:

  • A ReportsHelper#period_nav(period_type, start_date, end_date) returning a small struct/hash with prev_start, prev_end, next_start, next_end, at_latest.
  • A PORO like Reports::PeriodNavigator.new(period_type, start_date, end_date) exposing #prev, #next, #at_latest?.
  • A DS::PeriodNavigator ViewComponent that owns the markup as well, since the chevron+label+chevron pattern is reusable, has conditional disabled state, and a clear API — a good fit per the "Use ViewComponents when … reused / has variants / accessibility" guideline.

As per coding guidelines: "Avoid heavy logic in ERB views; prefer helpers and components" and "Keep domain logic out of views".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/views/reports/index.html.erb` around lines 93 - 157, The view contains
heavy date-calculation logic (the case on `@period_type` computing
prev_start/prev_end/next_start/next_end/at_latest) that should be extracted;
implement a helper or PORO (suggested: ReportsHelper#period_nav(period_type,
start_date, end_date) returning a hash/struct with keys prev_start, prev_end,
next_start, next_end, at_latest OR a class
Reports::PeriodNavigator.new(period_type, start_date, end_date) exposing methods
prev, next, at_latest?) that encapsulates the month/quarter/ytd/last_6_months
logic, update the ERB to call that API (passing `@period_type`, `@start_date`,
`@end_date`) and use the returned values for link_to and the disabled state so the
view only renders markup and the navigator (prev/next/at_latest) is fully
unit-testable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/views/reports/index.html.erb`:
- Line 131: Replace the hard-coded aria label strings used for the pagination
controls ("Previous period" and "Next period") with translations using the t()
helper (e.g., change aria: { label: "Previous period" } and aria: { label: "Next
period" } to use t("reports.index.previous_period") and
t("reports.index.next_period") respectively) and add corresponding
previous_period and next_period keys under reports.index in
config/locales/en.yml.
- Around line 117-126: The :last_6_months branch computes
prev_start/prev_end/next_start/next_end incorrectly when next_end is clamped to
the current month: if you clamp next_end to Date.current.end_of_month you must
re-anchor next_start to be exactly 6 months before that clamped next_end (so the
next window remains 6 months) and keep at_latest computed from the clamped end;
move the entire date-range calculation (the block that defines prev_start,
prev_end, next_start, next_end, at_latest for all cases) into a helper (e.g.,
ReportsHelper#period_bounds or similar) and call it from the view, and replace
hard-coded aria-label strings "Previous period"/"Next period" with translation
lookups using t(...) (e.g., t('reports.previous_period'),
t('reports.next_period')) so labels are localized.

---

Nitpick comments:
In `@app/views/reports/index.html.erb`:
- Around line 93-157: The view contains heavy date-calculation logic (the case
on `@period_type` computing prev_start/prev_end/next_start/next_end/at_latest)
that should be extracted; implement a helper or PORO (suggested:
ReportsHelper#period_nav(period_type, start_date, end_date) returning a
hash/struct with keys prev_start, prev_end, next_start, next_end, at_latest OR a
class Reports::PeriodNavigator.new(period_type, start_date, end_date) exposing
methods prev, next, at_latest?) that encapsulates the
month/quarter/ytd/last_6_months logic, update the ERB to call that API (passing
`@period_type`, `@start_date`, `@end_date`) and use the returned values for link_to
and the disabled state so the view only renders markup and the navigator
(prev/next/at_latest) is fully unit-testable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 01780560-94c4-43dc-9fa6-682f2e764c35

📥 Commits

Reviewing files that changed from the base of the PR and between 81cdccb and c3c1a4c.

📒 Files selected for processing (1)
  • app/views/reports/index.html.erb

Comment thread app/views/reports/index.html.erb Outdated
Comment thread app/views/reports/index.html.erb Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/controllers/reports_controller_test.rb`:
- Around line 110-112: The current assertions use start_date.strftime("%b") and
end_date.strftime("%b") which are too permissive; update the assertions in
reports_controller_test.rb (the lines using assert_includes `@response.body` with
start_date and end_date) to check for a more specific representation of the
swapped dates—e.g., use full formatted dates including year (start_date.strftime
with "%b %d, %Y" or ISO "%Y-%m-%d") or assert the exact date-range string
rendered by the view, or target the specific HTML element/text node that
contains the range (assert_includes `@response.body`, exact_range_string or
assert_match /#{Regexp.escape(formatted_date)}/) so the test verifies the swap
precisely rather than any three-letter month substring.
- Around line 272-284: In the test method "last 6 months next window extends to
current month end when crossing boundary" remove the debug puts line and replace
the placeholder assert with a concrete assertion: compute the expected_end as
Date.current.end_of_month and assert that at least one href in the captured
hrefs (from `@response.body.scan`(...)) includes an end_date query parameter equal
to that expected_end (or otherwise verify the generated next-window link extends
to the current month end); keep the existing get call and hrefs extraction but
assert presence of the expected end_date in those hrefs instead of using assert
true.
- Around line 286-293: The test "quarterly period navigation shows previous and
next quarter links" only asserts the previous-quarter link; add an assertion for
the next-quarter link by computing next_start = (Date.current.end_of_quarter +
1.day).beginning_of_quarter and next_end = next_start.end_of_quarter and then
call assert_select "a[href=?]", reports_path(period_type: :quarterly,
start_date: next_start, end_date: next_end) alongside the existing previous-link
assertion so both navigation directions are verified in the test.
- Around line 254-259: The test "monthly period navigation disables next arrow
on current month" uses a too-broad selector; tighten it to target the actual
next-period control (e.g. the next-arrow button/link) instead of any span.
Update the assertion in that test to scope into the period navigation component
(using its container selector or id) and assert on the specific control's
attributes (for example the element with aria-label like "Next period" or the
next-arrow button/link) having aria-disabled="true" so the test only passes when
the next-arrow control is disabled.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f9daa6f4-332e-4fb5-bcaf-732f35b584d4

📥 Commits

Reviewing files that changed from the base of the PR and between c3c1a4c and 5923e20.

📒 Files selected for processing (3)
  • app/views/reports/index.html.erb
  • config/locales/views/reports/en.yml
  • test/controllers/reports_controller_test.rb
✅ Files skipped from review due to trivial changes (1)
  • config/locales/views/reports/en.yml
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/views/reports/index.html.erb

Comment thread test/controllers/reports_controller_test.rb Outdated
Comment thread test/controllers/reports_controller_test.rb
Comment thread test/controllers/reports_controller_test.rb
Comment thread test/controllers/reports_controller_test.rb
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/controllers/reports_controller.rb`:
- Around line 1099-1109: The forward-navigation snap for the :last_6_months
branch uses the wrong threshold: replace the current condition that compares
candidate_start + 6.months against Date.current.beginning_of_month with a check
against the current month's end using the candidate window's end; compute
candidate_end = candidate_start + 6.months - 1.day (same formula used for
prev_end/next_end) and trigger the snap only when candidate_end >
Date.current.end_of_month so the transition does not skip the immediate next
6-month block.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 707c9983-f90a-4f89-a728-0c98d059a98b

📥 Commits

Reviewing files that changed from the base of the PR and between 5923e20 and e095f74.

📒 Files selected for processing (2)
  • app/controllers/reports_controller.rb
  • app/views/reports/index.html.erb
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/views/reports/index.html.erb

Comment on lines +1099 to +1109
when :last_6_months
prev_start = @start_date.beginning_of_month - 6.months
prev_end = prev_start + 6.months - 1.day
candidate_start = @start_date.beginning_of_month + 6.months
if candidate_start + 6.months >= Date.current.beginning_of_month
next_end = Date.current.end_of_month
next_start = (next_end + 1.day - 6.months).beginning_of_month
else
next_start = candidate_start
next_end = next_start + 6.months - 1.day
end
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

last_6_months forward navigation skips one period due to an overly broad snap condition.

The condition on line 1103 is candidate_start + 6.months >= Date.current.beginning_of_month, which fires one full step too early. Concrete example (today = May 8 2026):

  1. Default view: Nov 2025 – May 2026 (at_latest = true, next disabled ✓).
  2. Click prevMay 2025 – Oct 2025.
  3. Click next from May–Oct 2025:
    • candidate_start = Nov 1 2025
    • Nov 1 + 6m = May 1 2026 >= May 1 2026snap fires → result: Dec 2025 – May 2026
    • Nov 2025 is skipped entirely.

The snap should only activate when the candidate window's end date would exceed the current month's end. Correcting the condition:

-        candidate_start = `@start_date.beginning_of_month` + 6.months
-        if candidate_start + 6.months >= Date.current.beginning_of_month
+        candidate_start = `@start_date.beginning_of_month` + 6.months
+        candidate_end   = candidate_start + 6.months - 1.day
+        if candidate_end > Date.current.end_of_month
           next_end   = Date.current.end_of_month
           next_start = (next_end + 1.day - 6.months).beginning_of_month
         else
           next_start = candidate_start
-          next_end   = next_start + 6.months - 1.day
+          next_end   = candidate_end
         end

With the fix:

  • From May–Oct 2025: candidate_end = Apr 30 2026 > May 31 2026 → false → Nov 2025 – Apr 2026
  • From Nov 2025–Apr 2026: candidate_end = Oct 31 2026 > May 31 2026 → true → snap to Dec 2025 – May 2026
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/controllers/reports_controller.rb` around lines 1099 - 1109, The
forward-navigation snap for the :last_6_months branch uses the wrong threshold:
replace the current condition that compares candidate_start + 6.months against
Date.current.beginning_of_month with a check against the current month's end
using the candidate window's end; compute candidate_end = candidate_start +
6.months - 1.day (same formula used for prev_end/next_end) and trigger the snap
only when candidate_end > Date.current.end_of_month so the transition does not
skip the immediate next 6-month block.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/controllers/reports_controller_test.rb`:
- Around line 317-318: The test for custom-period display currently asserts that
the previous period control is hidden but misses checking the next period
control. Add an assertion similar to the existing one but for "a[aria-label=?]"
with I18n.t("reports.index.next_period") and count: 0 to the same test method to
verify the next period control is also hidden.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a86964d2-6e02-4481-9be6-775421a18267

📥 Commits

Reviewing files that changed from the base of the PR and between e095f74 and ff35238.

📒 Files selected for processing (2)
  • app/views/reports/index.html.erb
  • test/controllers/reports_controller_test.rb
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/views/reports/index.html.erb

Comment thread test/controllers/reports_controller_test.rb
Copy link
Copy Markdown
Collaborator

@jjmata jjmata left a comment

Choose a reason for hiding this comment

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

Could we try for consitency with budget period navigation? Is there design system components for there?

Image

Copy link
Copy Markdown
Collaborator

jjmata commented May 9, 2026

Good feature overall — clean i18n usage and sensible period math. A few things worth addressing before merge:

1. Nil-crash risk in the view

build_period_navigation returns nil for any period_type that doesn't match one of the four when branches. The view guards with unless @period_type == :custom, which currently works because all non-custom types are handled — but if a new period type is added later (or an unexpected param value slips through), @nav[:prev_start] will raise NoMethodError on nil.

Safer guard:

<% if @nav %>
  <div class="flex items-center gap-3 text-sm text-subdued">
    ...
  </div>
<% end %>

This is also more expressive: "show navigation when it's available," rather than "show navigation unless custom."

2. YTD case has no test

The test suite covers monthly, quarterly, last-6-months, and custom, but not :ytd. Given the next_end logic for YTD (next_year == Date.current.year ? Date.current : Date.new(next_year, 12, 31)) it'd be worth one test asserting the prev link and one checking that next is disabled when already in the current year.

3. Inconsistent indentation in the quarterly test

test "quarterly period navigation shows previous and next quarter links" do
    get reports_path(period_type: :quarterly)   # 6-space indent instead of 4

The body uses 6 spaces instead of the 4-space style used everywhere else in the file.


Generated by Claude Code


Generated by Claude Code

@joaocbatista
Copy link
Copy Markdown
Author

adjusted the style of the arrows to match the one found in the budget screen. missed that we already had this pattern in the app. it's looking better now.

image

@jjmata jjmata requested a review from gariasf May 9, 2026 11:23
@jjmata
Copy link
Copy Markdown
Collaborator

jjmata commented May 9, 2026

Thoughts, @gariasf? This should probably get DS treatment as well, right?

@gariasf
Copy link
Copy Markdown
Collaborator

gariasf commented May 9, 2026

Hey @joaocbatista, thanks for taking this on! Coming at this fresh — turns out the budget header already does this exact job, so most of what's below is just "mirror that". (as @jjmata already pointed out). I'd suggest not get inspiration but rather just go with that, to be consistent. I understand that the expected behaviour is the same, right?

A few things to lift over:

  • A "Today" reset link on the right side. After a few back-clicks the only way home in Reports is to re-tap the period-type tab - the budget pattern parks a DS::Link variant: "outline" button at ml-auto that makes this trivial.
  • A jump-to-period menu instead of a static label. Budget wraps the period name in a DS::Menu so users can pick directly. Reports currently renders a <span>, which means chevrons are the only navigation surface.
  • Tighter, primary-weight period name. "Showing data from May 1, 2026 to May 31, 2026" is much wordier than anywhere else in the app, and text-subdued on the wrapper drops it visually below the tabs. The budget treatment is text-primary font-medium on a short label like "May 2026".
  • gap-1 outside the chevron cluster instead of gap-3. The internal cluster gap (gap-2) already matches budget.

Two smaller things while we're here:

  • The disabled chevron is a <span> with aria-disabled="true" - screen readers don't pick up disabled state on a non-interactive element. Render it as <button disabled> so the aria-disabled actually applies (codex already nudged at the keyboard-focus version of this).
  • @jjmata's earlier suggestion - swap unless @period_type == :custom for if @nav. Tracks the controller's actual contract and won't quietly break if a new period type lands later.

Not blocking, but worth flagging: now both budget and reports hand-roll the disabled icon-link. A DS::Link disabled: arg would let neither have to.

I also ran this through Claude Design and here is an interesting suggestion that requires a bit more work, if we're feeling that it goes in the right direction:

CleanShot 2026-05-09 at 16 24 22@2x

If we end up introducing a new component, we might as well add it as a Design System component or just a separate template that could be reused.

Another option, is to re-use the UI we have on Budgets, but skip the period selector (the arrow and popup). This would better align with the domain behaviour on reports.

CleanShot 2026-05-09 at 16 26 45@2x

@joaocbatista
Copy link
Copy Markdown
Author

Hey @joaocbatista, thanks for taking this on! Coming at this fresh — turns out the budget header already does this exact job, so most of what's below is just "mirror that". (as @jjmata already pointed out). I'd suggest not get inspiration but rather just go with that, to be consistent. I understand that the expected behaviour is the same, right?

A few things to lift over:

  • A "Today" reset link on the right side. After a few back-clicks the only way home in Reports is to re-tap the period-type tab - the budget pattern parks a DS::Link variant: "outline" button at ml-auto that makes this trivial.
  • A jump-to-period menu instead of a static label. Budget wraps the period name in a DS::Menu so users can pick directly. Reports currently renders a <span>, which means chevrons are the only navigation surface.
  • Tighter, primary-weight period name. "Showing data from May 1, 2026 to May 31, 2026" is much wordier than anywhere else in the app, and text-subdued on the wrapper drops it visually below the tabs. The budget treatment is text-primary font-medium on a short label like "May 2026".
  • gap-1 outside the chevron cluster instead of gap-3. The internal cluster gap (gap-2) already matches budget.

Two smaller things while we're here:

  • The disabled chevron is a <span> with aria-disabled="true" - screen readers don't pick up disabled state on a non-interactive element. Render it as <button disabled> so the aria-disabled actually applies (codex already nudged at the keyboard-focus version of this).
  • @jjmata's earlier suggestion - swap unless @period_type == :custom for if @nav. Tracks the controller's actual contract and won't quietly break if a new period type lands later.

Not blocking, but worth flagging: now both budget and reports hand-roll the disabled icon-link. A DS::Link disabled: arg would let neither have to.

I also ran this through Claude Design and here is an interesting suggestion that requires a bit more work, if we're feeling that it goes in the right direction:

CleanShot 2026-05-09 at 16 24 22@2x If we end up introducing a new component, we might as well add it as a Design System component or just a separate template that could be reused.

Another option, is to re-use the UI we have on Budgets, but skip the period selector (the arrow and popup). This would better align with the domain behaviour on reports.

CleanShot 2026-05-09 at 16 26 45@2x

implemented your main suggestions, let me know your thoughts.

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor:verified Contributor passed trust analysis. pr:verified PR passed security analysis.

Development

Successfully merging this pull request may close these issues.

3 participants