Skip to content

feat: add accounts and transactions tabs to finance dashboard - #61

Open
reuel88 wants to merge 5 commits into
arhamkhnz:mainfrom
reuel88:feat/finance-accounts-transactions-tabs
Open

feat: add accounts and transactions tabs to finance dashboard#61
reuel88 wants to merge 5 commits into
arhamkhnz:mainfrom
reuel88:feat/finance-accounts-transactions-tabs

Conversation

@reuel88

@reuel88 reuel88 commented May 18, 2026

Copy link
Copy Markdown

Summary

Builds out the two currently-empty tabs on the Finance dashboard (/dashboard/finance) — Accounts and Transactions — with content that matches the existing Dashboard tab's card-heavy aesthetic.

What's included

Accounts tab

  • KPI strip: total balance, account count breakdown by type, largest account, 30-day net flow.
  • "Allocation by type" stacked bar (Bank / Savings / Investment / Crypto / Reserve).
  • Full account list using the existing Item primitives, with brand logos, monthly delta chips, and a 7-day balance sparkline per row.
  • Trailing strip: recent per-account activity, upcoming inflows, and an "Add account" empty-state CTA.

Transactions tab

  • Monthly inflow / outflow / net KPIs.
  • TanStack-backed table mirroring opportunities-section.tsx: ~80 mock rows, search + Account/Category/Type dropdown filters, sortable Date and Amount columns, 10-rows-per-page pagination, row selection, and a per-row actions menu.

Cross-tab UX

  • Tabs are controlled in page.tsx. Clicking the chevron on an account row switches to the Transactions tab and seeds the Account filter; a `ref` on the Transactions tab trigger moves keyboard focus there so SR / keyboard users land in a sensible place.

Accessibility

  • `role="img"` + descriptive `aria-label` on sparklines and the allocation bar (chart data is otherwise invisible to AT).
  • `aria-sort` on the `TableHead` for Date/Amount, plus sort buttons that announce the current direction.
  • `aria-hidden` on decorative brand logos, chevrons, action icons, and filter-button glyphs (they all sit next to redundant text).
  • `aria-label="Search transactions"` on the search input.
  • Tightened `dark:text-amber-200` on the Pending status badge for contrast.

Consistency

  • File layout matches the rest of the repo: flat `_components/` with a single `transactions-table/` subdir for the TanStack column / schema / data trio (mirrors `opportunities-table/` and `recent-orders-table/`).
  • No shared primitives modified.

Screenshots

Accounts tab

screencapture-localhost-3000-dashboard-finance-2026-05-18-15_24_47

Transactions tab

screencapture-localhost-3000-dashboard-finance-2026-05-18-15_24_59

Test plan

  • Visit `/dashboard/finance` and cycle through all three tabs.
  • Dashboard tab renders unchanged.
  • Accounts tab renders KPI strip, allocation bar, account list with sparklines, and the trailing 3-column strip.
  • Clicking a chevron on an account row jumps to the Transactions tab with that account pre-filtered.
  • Transactions search, filters (Account / Category / Type), sort (Date / Amount), and pagination all work.
  • Toggle dark mode: green/red deltas, chart colors, and the amber Pending badge stay legible.
  • Keyboard: Tab into account-row chevron, Enter, focus lands on the Transactions tab trigger.

Greptile Summary

This PR builds out the two previously empty tabs on the Finance dashboard — Accounts and Transactions — with KPI strips, an accounts list with sparklines, an allocation bar, a TanStack-backed transactions table with filtering/sorting/pagination, and several supporting cards. The implementation is thorough and closely follows the existing repo conventions.

  • Accounts tab adds AccountKpis, AccountsList, AllocationByType, RecentAccountActivity, UpcomingInflows, and AddAccountCard, all wired through page.tsx with a cross-tab navigation callback that seeds the account filter in the Transactions tab.
  • Transactions tab introduces a full TanStack Table with global search, three dropdown column filters, sortable Date/Amount columns, row selection, per-row actions, and a custom paginator — mirroring the existing opportunities-section.tsx pattern.
  • upcoming-transactions.tsx is refactored to move new Date() calls client-side via useState(null) + useEffect, fixing a potential SSR/CSR hydration mismatch; page.tsx applies the same pattern for the formatted header date.

Confidence Score: 5/5

Safe to merge — all changes are additive UI components backed by static mock data, and the cross-tab navigation logic is straightforward and well-guarded.

The changes introduce new client components, a TanStack Table integration, and a useEffect-based cross-tab filter handoff, none of which touch shared primitives or existing routes. The table-in-deps effect runs more often than needed but the early-return guard keeps it correct. All other findings are style-level cleanups with no runtime impact.

No files require special attention; transactions-section.tsx has the useEffect dependency concern worth a second glance, but it does not affect correctness.

Important Files Changed

Filename Overview
src/app/(main)/dashboard/finance/_components/account-sparkline.tsx New sparkline component using Recharts; has a redundant sr-only span inside a role="img" element and the chartConfig color declaration is never used (stroke is always overridden).
src/app/(main)/dashboard/finance/_components/transactions-section.tsx Client-side transactions table with TanStack Table; useEffect includes table object in deps which changes every render — the early-return guard prevents loops but the effect fires on every re-render while initialAccountFilter is set.
src/app/(main)/dashboard/finance/page.tsx Converted to client component to support tab state, cross-tab account navigation, and hydration-safe date display; tab constants correctly renamed from time-range semantics to content semantics.
src/app/(main)/dashboard/finance/_components/accounts-list.tsx Account list with sparklines grouped by type; correct use of asChild pattern, aria-labels, and optional callback for cross-tab navigation.
src/app/(main)/dashboard/finance/_components/transactions-kpis.tsx Monthly inflow/outflow/net KPIs computed at module level; thisMonth predicate now uses new Date() but monthRows is still a module-level constant.
src/app/(main)/dashboard/finance/_components/transactions-table/columns.tsx TanStack Table column definitions with proper aria-labels, sort state announcements, and account lookup by ID.
src/app/(main)/dashboard/finance/_components/accounts.ts Static mock account data with type definitions, derived totals, and helper functions; well-structured.
src/app/(main)/dashboard/finance/_components/allocation-by-type.tsx Stacked bar allocation chart with ARIA label; correctly handles zero total balance edge case.
src/app/(main)/dashboard/finance/_components/transactions-table/schema.ts Zod schema for transaction rows; clean enum-constrained fields for category, type, and status.
src/app/(main)/dashboard/finance/_components/recent-account-activity.tsx Recent activity card with static entries; correctly handles missing account lookup.
src/app/(main)/dashboard/finance/_components/upcoming-inflows.tsx Upcoming inflows card with hardcoded date strings (mock data); correctly guards against missing account IDs.
src/app/(main)/dashboard/finance/_components/upcoming-transactions.tsx Hydration-safe refactor: dates are now computed client-side via useState(null)+useEffect, eliminating the SSR/CSR mismatch from direct new Date() at module level.
src/app/(main)/dashboard/finance/_components/add-account-card.tsx Static CTA card with three option buttons and a primary action; straightforward and consistent with existing patterns.
src/app/(main)/dashboard/finance/_components/transactions-table/data.json ~80 mock transaction rows across 2026-04 and 2026-05; all account IDs reference valid accounts and all categories/types match the schema.
src/app/(main)/dashboard/finance/_components/account-kpis.tsx KPI strip over static accounts data; uses a length guard before reduce to avoid crashes on empty arrays.

Sequence Diagram

sequenceDiagram
    participant User
    participant AccountsList
    participant Page as page.tsx (state)
    participant TransactionsSection
    participant TanStackTable

    User->>AccountsList: Click chevron on account row
    AccountsList->>Page: onSelectAccount(accountId)
    Page->>Page: setAccountFilterSeed(accountId)
    Page->>Page: setActiveTab("transactions")
    Page->>Page: requestAnimationFrame → focus Transactions trigger
    Page->>TransactionsSection: "render with initialAccountFilter=accountId"
    TransactionsSection->>TanStackTable: useState initializer sets columnFilters
    TransactionsSection->>TanStackTable: useEffect → setFilterValue(accountId) + setPageIndex(0)
    TransactionsSection->>Page: onAccountFilterConsumed()
    Page->>Page: setAccountFilterSeed(undefined)
    Page->>TransactionsSection: "re-render with initialAccountFilter=undefined"
    TransactionsSection->>TanStackTable: filter persists in internal state
Loading
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
src/app/(main)/dashboard/finance/_components/account-sparkline.tsx:8-10
The `chartConfig` declares `color: "var(--chart-1)"` for the `value` series, but the `Line` component always receives an explicit `stroke` prop (`var(--chart-2)` or `var(--chart-4)`) that overrides it. Because there is no tooltip or legend in this sparkline, the `chartConfig` color is never applied to anything visible, making the declaration misleading for anyone trying to understand which chart token the sparkline uses.

```suggestion
const chartConfig = {
  value: { label: "Balance" },
} satisfies ChartConfig;
```

### Issue 2 of 3
src/app/(main)/dashboard/finance/_components/account-sparkline.tsx:29-32
The outer `div` already carries `role="img"` and `aria-label={summary}`, which is the correct and sufficient way to give the element an accessible name under the WAI-ARIA spec. When `role="img"` has an `aria-label`, AT treats the element as opaque — inner content is not announced — so the nested `<span className="sr-only">` is dead code from an accessibility perspective and can be removed.

```suggestion
  return (
    <div role="img" aria-label={summary} className={className ?? "h-8 w-24"}>
      <ChartContainer config={chartConfig} className="h-full w-full" initialDimension={{ width: 96, height: 32 }}>
```

### Issue 3 of 3
src/app/(main)/dashboard/finance/_components/transactions-section.tsx:863-868
**`table` in `useEffect` deps re-runs the effect on every render**

In TanStack Table v8, `useReactTable` returns a new table object reference on every render. Including `table` in the dependency array means this effect fires after every re-render of `TransactionsSection`, not only when `initialAccountFilter` changes. During the brief window between the parent setting `accountFilterSeed` and `onAccountFilterConsumed` propagating back, the effect will fire repeatedly — calling `setPageIndex(0)` and `onAccountFilterConsumed` multiple times. Both calls are idempotent so there is no wrong outcome, but it produces unnecessary extra renders. Removing `table` from the dependency array (and silencing the lint rule with a comment if needed) or using a `ref` to hold the table would avoid the repeated runs.

Reviews (5): Last reviewed commit: "fix(finance): defer date-relative render..." | Re-trigger Greptile

Populates the two empty tabs on /dashboard/finance with native-feeling
content that mirrors the existing Dashboard tab's card aesthetic.

Accounts tab: account KPI strip, allocation breakdown, full account list
with brand logos and 7-day balance sparklines, recent activity, upcoming
inflows, and an "Add account" CTA card.

Transactions tab: monthly inflow/outflow/net KPIs above a filterable,
sortable, paginated TanStack table (~80 mock rows; search +
account/category/type filters).

Cross-tab UX: clicking the chevron on an account row switches to the
Transactions tab, pre-filters by that account, and moves focus to the
Transactions tab trigger.

a11y: role=img + descriptive labels on sparklines and the allocation
bar, aria-sort on sortable headers, aria-hidden on decorative icons and
brand logos that sit next to redundant text, an explicit label on the
search input, and tightened amber contrast on the Pending status badge.
Comment thread src/app/(main)/dashboard/finance/_components/transactions-kpis.tsx
Comment thread src/app/(main)/dashboard/finance/page.tsx Outdated
Comment thread src/app/(main)/dashboard/finance/_components/account-kpis.tsx Outdated
- Derive transactions-kpis month prefix from the current date instead
  of the hardcoded "2026-05" string so the KPI cards don't permanently
  zero out once the calendar month rolls over.
- Rename Tabs value constants from stale time-range strings
  ("30-days" / "12-months" / "custom") to content-descriptive ones
  ("dashboard" / "accounts" / "transactions").
- Null-guard the largest-account reduce + render so the KPI card
  no longer assumes a non-empty accounts list (shows "—" / "No linked
  accounts" when the list is empty).
@reuel88

reuel88 commented May 18, 2026

Copy link
Copy Markdown
Author

Thanks for the review — pushed 1a98c40 addressing all three threads:

  1. transactions-kpis.tsxthisMonth now derives YYYY-MM from new Date() instead of the hardcoded "2026-05". Heads-up that the mock dataset only covers April–May 2026, so once the system clock advances past May 2026 the KPIs will still drop to zero on their own — happy to refresh the dataset in a follow-up, or to switch the predicate to anchor on the dataset's max date if you'd prefer the demo stay populated indefinitely.

  2. page.tsx — renamed the tab-value constants to "dashboard" / "accounts" / "transactions".

  3. account-kpis.tsx — guarded the reduce seed AND the render. When accounts is empty the card now shows with a "No linked accounts" subtitle instead of crashing on largest.balance.

pnpm exec tsc --noEmit and pnpm lint both clean.

@arhamkhnz

Copy link
Copy Markdown
Owner

Thanks for the PR! The overall direction looks good, but the design needs a bit more polish for consistency.

On the Accounts tab, there’s a lot of empty space below the Allocation by type section, and the icons in the All accounts cards don’t appear to be aligned consistently. I’d suggest revisiting the layout for both tabs and exploring a few different layout options to make the UI feel more balanced and polished.

reuel88 and others added 3 commits May 22, 2026 21:24
…n mismatch

Module-scope `new Date()` in `upcoming-transactions` and an unconditional
`format(new Date(), ...)` in the finance page produced different day strings
on the server and client when the two runtimes' local timezones straddled
midnight, causing a React hydration error. Compute "now" inside the
components via useState + useEffect so SSR and the first client render emit
matching placeholders.

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

reuel88 commented May 23, 2026

Copy link
Copy Markdown
Author

Made changes to layout based on feedback.

screencapture-localhost-3000-dashboard-finance-2026-05-23-11_14_01

xdroberto added a commit to xdroberto/nova-analytics that referenced this pull request Jul 8, 2026
…lesson

UptimeRobot monitor live (last Phase-4 item) -> Phase 4 FULLY CLOSED: ADR-004 monitoring section, ROADMAP milestones (4325946), BRAIN current position. Disk builder-prune recorded honestly: 87%->74% (~4GB real, not the 21GB docker advertised - shared layers overcount). gh default-repo gotcha logged as a lesson (set-default now fork; was the arhamkhnz#59/arhamkhnz#61 misread cause). Post-merge push-to-deploy verified from the PR merge (~3m09s, health ok).
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