Dashboard Behaviors
Overview
The Dashboard is an admin-only, SSR-based overview page that displays financial summary cards across selected clients. It provides at-a-glance visibility into bank account balances, outstanding tasks, recent sales trends, expense breakdowns, and profit/loss summaries. Cards are loaded lazily via HTMX for progressive rendering.
Testing Philosophy
- Prefer unit tests for pure business logic (calculations, formatting, client trimming)
- Use integration tests for database interactions and cross-system flows (Datomic queries, GraphQL calls)
- Use UI tests only for end-to-end happy paths and visual states
- Every behavior must be user-visible; no tests for implementation details
Testing Patterns
Pattern: Progressive Card Loading
Dashboard cards follow a progressive loading pattern:
- SSR renders stub cards with loading spinners
- Each stub triggers an independent HTMX request on load
- Card endpoints return HTML fragments that replace the stub content
- Cards load independently without blocking each other
Test implications: Unit test the card query logic. Integration test each card endpoint returns valid HTML. UI test only needs to verify the page loads and cards appear.
Pattern: Client Context Propagation
All dashboard operations depend on selected clients:
- Client selection triggers a page-wide refresh via HTMX
wrap-trim-clients middleware limits to 20 clients before queries execute
- All card endpoints receive the same trimmed client set
Test implications: Integration test that changing clients updates all cards. Unit test the trimming logic.
Pattern: Admin Permission Gating
The dashboard is restricted to admin users:
wrap-admin middleware checks user role before any data access
- Non-admin users are redirected before reaching handlers
- Middleware is applied consistently to all card endpoints
Test implications: Integration test each endpoint independently for permission enforcement.
Dashboard Page
Page Loading Behaviors
| # |
Behavior |
Test Strategy |
Status |
| 1.1 |
It should render the main dashboard page with navigation, client selector, and "Dashboard" breadcrumb for admin users |
UI |
[ ] |
| 1.2 |
It should display six stub cards with loading spinners for progressive rendering |
UI |
[ ] |
| 1.3 |
It should trigger independent HTMX requests to load each card's content on page load |
Integration |
[ ] |
| 1.4 |
It should progressively replace stub cards with actual data as responses arrive |
UI |
[ ] |
Bank Accounts Card
Display Behaviors
| # |
Behavior |
Test Strategy |
Status |
| 2.1 |
It should display each client's name, account name, ledger balance, and last sync time |
UI |
[ ] |
| 2.2 |
It should exclude bank accounts with cash type from the display |
Integration |
[ ] |
| 2.3 |
It should format ledger balances as currency ($X,XXX.XX) |
Unit + UI |
[ ] |
| 2.4 |
It should display the last sync timestamp in standard time format when present |
Unit + UI |
[ ] |
| 2.5 |
It should display Intuit balance and sync time for Intuit-linked accounts |
UI |
[ ] |
| 2.6 |
It should display Yodlee available balance, sync time, and pending balance for Yodlee-linked accounts |
UI |
[ ] |
| 2.7 |
It should display Plaid balance and sync time for Plaid-linked accounts |
UI |
[ ] |
| 2.8 |
It should display $0.00 for missing or null balances |
Unit + UI |
[ ] |
Sales Card
Display Behaviors
| # |
Behavior |
Test Strategy |
Status |
| 3.1 |
It should display a bar chart of gross sales for the last 14 days |
UI |
[ ] |
| 3.2 |
It should render an empty bar chart when no sales orders exist in the date range |
UI |
[ ] |
Data Behaviors
| # |
Behavior |
Test Strategy |
Status |
| 3.3 |
It should query and sum sales order totals by date for the selected clients |
Integration |
[ ] |
Expense Card
Display Behaviors
| # |
Behavior |
Test Strategy |
Status |
| 4.1 |
It should display a pie chart of the top 5 expense accounts for the last month |
UI |
[ ] |
| 4.2 |
It should render an empty pie chart when no invoices with expense accounts exist in the date range |
UI |
[ ] |
Data Behaviors
| # |
Behavior |
Test Strategy |
Status |
| 4.3 |
It should sum expense amounts by account name for the selected clients |
Integration |
[ ] |
Profit & Loss Card
Display Behaviors
| # |
Behavior |
Test Strategy |
Status |
| 5.1 |
It should display income and expenses aggregated by category (sales, COGS, payroll, controllable, fixed overhead, ownership controllable) |
UI |
[ ] |
| 5.2 |
It should show $0.00 for both income and expenses when no data exists for the period |
UI |
[ ] |
Data Behaviors
| # |
Behavior |
Test Strategy |
Status |
| 5.3 |
It should query P&L data via GraphQL for the selected clients and last month |
Integration |
[ ] |
Tasks Card
Display Behaviors
| # |
Behavior |
Test Strategy |
Status |
| 6.1 |
It should display the count of unpaid invoices when the count is non-zero |
UI |
[ ] |
| 6.2 |
It should display the count of uncategorized transactions requiring feedback when the count is non-zero |
UI |
[ ] |
| 6.3 |
It should provide a "Pay now" link for unpaid invoices linking to the unpaid invoices page with year date range |
UI |
[ ] |
| 6.4 |
It should provide a "Review now" link for uncategorized transactions linking to the requires-feedback page |
UI |
[ ] |
| 6.5 |
It should hide task sections entirely when their respective counts are zero |
Integration |
[ ] |
Data Behaviors
| # |
Behavior |
Test Strategy |
Status |
| 6.6 |
It should query Datomic for invoices with unpaid status for the selected clients |
Integration |
[ ] |
| 6.7 |
It should query Datomic for transactions with requires-feedback approval status for the selected clients |
Integration |
[ ] |
Expense Breakdown Card
Display Behaviors
| # |
Behavior |
Test Strategy |
Status |
| 7.1 |
It should display a bar chart breaking down expenses by account |
UI |
[ ] |
| 7.2 |
It should render an empty chart when no expense data exists |
UI |
[ ] |
| 7.3 |
It should provide Vendor and Account typeahead filters |
UI |
[ ] |
Data Behaviors
| # |
Behavior |
Test Strategy |
Status |
| 7.4 |
It should reload the chart with filtered data when filter selections change |
Integration |
[ ] |
| 7.5 |
It should update the URL with filter query parameters via hx-push-url |
Integration |
[ ] |
| 7.6 |
It should exclude voided invoices from the breakdown |
Integration |
[ ] |
Filtering Behaviors
Expense Breakdown Filters
| # |
Behavior |
Test Strategy |
Status |
| 8.1 |
It should filter the expense breakdown chart by vendor selection |
Integration |
[ ] |
| 8.2 |
It should filter the expense breakdown chart by expense account selection |
Integration |
[ ] |
| 8.3 |
It should trigger an HTMX request to reload the chart when any filter changes |
Integration |
[ ] |
Client Selection Behaviors
| # |
Behavior |
Test Strategy |
Status |
| 9.1 |
It should update the dashboard content when the user selects different clients from the dropdown |
UI |
[ ] |
| 9.2 |
It should trigger a clientSelected event on the body when client selection changes |
Integration |
[ ] |
| 9.3 |
It should swap the dashboard content area with fresh content for the newly selected clients |
Integration |
[ ] |
| 9.4 |
It should re-fetch all card data with the new client context |
Integration |
[ ] |
| 9.5 |
It should limit reports to the first 20 selected clients from the valid set |
Unit + Integration |
[ ] |
| 9.6 |
It should display a yellow warning banner when more than 20 clients are selected |
UI |
[ ] |
| 9.7 |
It should persist the warning banner across client selection changes until fewer than 21 clients are selected |
UI |
[ ] |
| 9.8 |
It should trim the client set before executing any card data queries |
Integration |
[ ] |
Error Handling Behaviors
| # |
Behavior |
Test Strategy |
Status |
| 10.1 |
It should load each card independently via separate HTMX requests |
Integration |
[ ] |
| 10.2 |
It should not prevent other cards from loading when one card endpoint fails |
Integration |
[ ] |
| 10.3 |
It should display a loading spinner on stub cards until data loads or a timeout occurs |
UI |
[ ] |
| 10.4 |
It should return appropriate HTTP status codes for card endpoint errors without breaking the page layout |
Integration |
[ ] |
Cross-Cutting Behaviors
Permission Behaviors
| # |
Behavior |
Test Strategy |
Status |
| 11.1 |
It should allow only admin users to access the dashboard page and card endpoints |
Integration |
[ ] |
| 11.2 |
It should redirect non-admin authenticated users to /login with a 302 status |
Integration |
[ ] |
| 11.3 |
It should redirect unauthenticated users to /login with a redirect-to parameter |
Integration |
[ ] |
| 11.4 |
It should verify admin role via middleware before executing any data queries |
Integration |
[ ] |
Empty State Behaviors
| # |
Behavior |
Test Strategy |
Status |
| 12.1 |
It should render the dashboard page when no clients are selected, with all cards showing empty states |
UI |
[ ] |
| 12.2 |
It should display an empty bank accounts list when no clients are selected |
UI |
[ ] |
| 12.3 |
It should display an empty sales chart when no clients are selected |
UI |
[ ] |
| 12.4 |
It should display an empty expense pie chart when no clients are selected |
UI |
[ ] |
| 12.5 |
It should show $0.00 income and expenses in the P&L card when no clients are selected |
UI |
[ ] |
| 12.6 |
It should hide all task sections when no clients are selected |
UI |
[ ] |
Test Data Requirements
| Entity |
Requirements |
| Users |
Admin user with access to multiple clients; non-admin user for permission denial |
| Clients |
Minimum 2, ideally 25+; mix with/without bank accounts |
| Bank Accounts |
Various types (checking, savings, cash); some linked to Intuit, Yodlee, Plaid; with/without balances and sync timestamps |
| Sales Orders |
Orders within and outside the 14-day window with totals |
| Invoices |
With expense accounts and unpaid status; voided invoices to test exclusion |
| Transactions |
With requires-feedback approval status |
| Chart of Accounts |
Categories: sales, COGS, payroll, controllable, fixed overhead, ownership controllable |
Existing Tests to Preserve
- No existing dashboard-specific tests have been identified in the current test suite. Any tests covering dashboard routes or card handlers should be preserved during refactoring.
Dependencies
- Datomic (primary data store for all card queries)
- GraphQL/Lacinia (P&L data via get-profit-and-loss-raw)
- HTMX (progressive card loading via hx-get/hx-trigger/hx-swap)
- Chart.js (canvas-based charts)
- Alpine.js (chart data binding)