Skip to content

feat: add Expense/Income tabs to invoices page#92

Merged
emilwojtaszek merged 4 commits into
mainfrom
feat/invoice-type-tabs
Mar 14, 2026
Merged

feat: add Expense/Income tabs to invoices page#92
emilwojtaszek merged 4 commits into
mainfrom
feat/invoice-type-tabs

Conversation

@emilwojtaszek

@emilwojtaszek emilwojtaszek commented Mar 14, 2026

Copy link
Copy Markdown
Member

Summary

  • Replace type dropdown filter with Expense/Income tabs on the invoices list page, defaulting to Expense
  • Reviewers (without view_all_invoice_types permission) see a static Expense label instead of clickable tabs
  • Remove redundant Type column from the table
  • Use <.badge variant="info"> for tag list (blue with outline, consistent with other badges)
  • Add border to filter chips for visual consistency with badges
  • Preserve active tab when filtering or clearing filters

Test plan

  • All 26 existing index tests pass (updated for new default)
  • New tests for tab rendering and tab click navigation
  • Manual: verify Expense tab is active by default on page load
  • Manual: verify switching tabs filters invoices correctly
  • Manual: verify reviewer role sees static Expense label, no clickable tabs
  • Manual: verify clearing filters stays on current tab
  • Manual: verify tag badges and filter chips have consistent outline styling

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Tabbed navigation to switch between Expense and Income invoices (Expense default); restricted users see a static Expense view
  • UI/UX Improvements

    • Filter chips now include a border
    • Tags render as badge-style elements
    • Payment status label shortened to "pending"
    • Type column removed from invoice table; Type select removed from filters
    • Invoice type preserved when applying or clearing other filters via tabs

emilwojtaszek and others added 2 commits March 14, 2026 22:48
Replace the type dropdown filter with dedicated tabs for switching
between expense and income invoices. Defaults to the Expense tab.
Reviewers (without view_all_invoice_types permission) see a static
Expense label instead of clickable tabs.

- Remove Type column from table (redundant with tabs)
- Use badge component for tag list (blue with outline)
- Add border to filter chips for visual consistency
- Preserve active tab when filtering or clearing filters

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Mar 14, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c80bb357-e298-44b5-ac2f-7c756c2a2ba4

📥 Commits

Reviewing files that changed from the base of the PR and between 6742178 and bc22f4d.

📒 Files selected for processing (1)
  • lib/ksef_hub_web/live/invoice_live/index.ex

📝 Walkthrough

Walkthrough

Adds Expense/Income tabbed filtering to the invoices LiveView (Expense default), sanitizes/preserves type across params/events based on role, removes Type select/column from UI, updates components styling and invoice badge text, and updates tests to reflect new tabbed behavior.

Changes

Cohort / File(s) Summary
Core styling
lib/ksef_hub_web/components/core_components.ex
Added border class to the filter_chip span (CSS class change only).
Invoice UI components
lib/ksef_hub_web/components/invoice_components.ex
Replaced manual tag markup with .badge rendering; changed payment badge text for :pending from "payment pending" to "pending"; minor formatting tweaks.
Invoice LiveView & filtering
lib/ksef_hub_web/live/invoice_live/index.ex
Introduces Expense/Income tabs and tab helpers (tab_class/1, tab_url/3, sanitize_type/2); defaults type to :expense when absent or unauthorized; preserves/sanitizes type across params, handle_event flows, and URL patches; removed Type select from filters and Type column from table; removed Type chip from active filters.
Tests
test/ksef_hub_web/live/invoice_live/index_test.exs
Updated tests to default to type=expense, added tab interaction tests, adjusted fixtures and assertions to include/preserve type=expense across filters and navigation.
Manifest
mix.exs
Single-line change recorded.

Sequence Diagram

sequenceDiagram
    participant User
    participant LiveView as InvoiceLiveView
    participant Sanitizer as TypeSanitizer
    participant Store as FilterStore
    participant Renderer as RenderEngine

    User->>LiveView: GET /invoices (no type param)
    LiveView->>Sanitizer: sanitize_type(params.type, user.role)
    Sanitizer-->>LiveView: :expense (default or restricted)
    LiveView->>Store: build_filters(params + type)
    Store-->>LiveView: filters
    LiveView->>Renderer: render(filters, tabs)
    Renderer-->>User: show Expense tab active + invoices

    User->>LiveView: Click "Income" tab
    LiveView->>Sanitizer: sanitize_type(:income, user.role)
    Sanitizer-->>LiveView: :income or :expense (if unauthorized)
    LiveView->>Store: build_filters(type=:income, other_params)
    LiveView->>Renderer: render(updated filters)
    Renderer-->>User: show Income tab active (if allowed)

    User->>LiveView: Apply other filter (category/tag)
    LiveView->>Store: merge_filters(keep current_type)
    Store-->>LiveView: merged filters
    LiveView->>Renderer: render(filtered invoices with preserved type)
    Renderer-->>User: updated list
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped through tabs, expense set in sight,
Income waits politely for a curious bite,
Filters keep their rhythm, type safely in tow,
Badges trimmed and tidy, ready for the show —
A rabbit's little hop makes invoices glow! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main change: adding Expense/Income tabs to the invoices page, which is the primary feature described throughout the PR objectives and file changes.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/invoice-type-tabs
📝 Coding Plan
  • Generate coding plan for human review comments

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
test/ksef_hub_web/live/invoice_live/index_test.exs (1)

118-138: Please add a clear_filters regression test.

The source now has dedicated type-preservation logic for "clear_filters", but this file only exercises "filter", "remove_filter", and direct tab clicks. One interaction test from the Income tab—and ideally the reviewer-on-type=income edge—would close the main gap.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/ksef_hub_web/live/invoice_live/index_test.exs` around lines 118 - 138,
Add a new test (e.g., test "clear_filters preserves type param", %{conn: conn,
company: company}) that opens the LiveView with a URL containing type=income
plus other filters, uses live(...) to mount, verifies a chip is rendered,
triggers the clear_filters event by calling element(view,
"button[phx-click=clear_filters]") |> render_click(), and then asserts the
patched URL still contains type=income (using assert_patched). Ensure the test
mirrors the existing pattern in invoice_live/index_test.exs and uses the same
helpers/variables (live, render, element, render_click, assert_patched) so the
dedicated clear_filters type-preservation logic is exercised.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/ksef_hub_web/live/invoice_live/index.ex`:
- Around line 32-35: The incoming params' :type must be normalized before saving
into assigns so unauthorized values like "income" cannot persist; in
handle_params/3, after calling parse_filters() but before Map.put_new(:type,
:expense) or assigning to `@filters`, coerce/validate the :type field (e.g., via a
small sanitizer function or pattern match) to only allow :expense (or known
values) and replace any other value with :expense, then assign the sanitized map
to the socket (`@filters`) and ensure remove_filter/3 and pagination use that
sanitized assign; update references to filters, parse_filters, handle_params,
and remove_filter/3 accordingly so the raw params never become the socket
`@filters`.

---

Nitpick comments:
In `@test/ksef_hub_web/live/invoice_live/index_test.exs`:
- Around line 118-138: Add a new test (e.g., test "clear_filters preserves type
param", %{conn: conn, company: company}) that opens the LiveView with a URL
containing type=income plus other filters, uses live(...) to mount, verifies a
chip is rendered, triggers the clear_filters event by calling element(view,
"button[phx-click=clear_filters]") |> render_click(), and then asserts the
patched URL still contains type=income (using assert_patched). Ensure the test
mirrors the existing pattern in invoice_live/index_test.exs and uses the same
helpers/variables (live, render, element, render_click, assert_patched) so the
dedicated clear_filters type-preservation logic is exercised.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dc9541f8-aeee-4dec-8358-fff0d262196f

📥 Commits

Reviewing files that changed from the base of the PR and between 83512c1 and 9d72112.

📒 Files selected for processing (4)
  • lib/ksef_hub_web/components/core_components.ex
  • lib/ksef_hub_web/components/invoice_components.ex
  • lib/ksef_hub_web/live/invoice_live/index.ex
  • test/ksef_hub_web/live/invoice_live/index_test.exs

Comment thread lib/ksef_hub_web/live/invoice_live/index.ex
…roles

Raw params could persist an unauthorized :type (e.g. :income for reviewers)
in @filters, leaking into URLs built by remove_filter, clear_filters, and
pagination. Now sanitize_type/2 forces :expense before storing in assigns.
Also adds a test verifying clear_filters preserves the type param.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
lib/ksef_hub_web/live/invoice_live/index.ex (1)

314-333: Optional accessibility polish for tab UI.

Consider adding active-state semantics (e.g., aria-current) to the selected tab link to improve screen-reader feedback.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/ksef_hub_web/live/invoice_live/index.ex` around lines 314 - 333, Add
active-state accessibility by setting aria-current="page" on the selected tab
elements: when rendering the .link for Expense and Income (the calls to
tab_url/3 and tab_class/1) include aria-current="page" conditionally when
`@filters`[:type] matches the tab symbol, and for the fallback <span> (used when
!@can_view_all_types) add aria-current="page" since it represents the active
tab; use the existing tab_class(`@filters`[:type] == ...) logic to determine the
condition and ensure both link and non-link states expose the same active
semantics.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@lib/ksef_hub_web/live/invoice_live/index.ex`:
- Around line 314-333: Add active-state accessibility by setting
aria-current="page" on the selected tab elements: when rendering the .link for
Expense and Income (the calls to tab_url/3 and tab_class/1) include
aria-current="page" conditionally when `@filters`[:type] matches the tab symbol,
and for the fallback <span> (used when !@can_view_all_types) add
aria-current="page" since it represents the active tab; use the existing
tab_class(`@filters`[:type] == ...) logic to determine the condition and ensure
both link and non-link states expose the same active semantics.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 431392c4-3f3f-4ca9-8589-5a0ac543f5c6

📥 Commits

Reviewing files that changed from the base of the PR and between 9d72112 and 6742178.

📒 Files selected for processing (2)
  • lib/ksef_hub_web/live/invoice_live/index.ex
  • test/ksef_hub_web/live/invoice_live/index_test.exs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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