# 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:
1. SSR renders stub cards with loading spinners
2. Each stub triggers an independent HTMX request on load
3. Card endpoints return HTML fragments that replace the stub content
4. 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:
1. Client selection triggers a page-wide refresh via HTMX
2. `wrap-trim-clients` middleware limits to 20 clients before queries execute
3. 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:
1. `wrap-admin` middleware checks user role before any data access
2. Non-admin users are redirected before reaching handlers
3. 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)
