diff --git a/CLAUDE.md b/CLAUDE.md index 49d4691..8c65743 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -140,6 +140,28 @@ records.data → apply_transformations() → - Server.js has global error handler - Database functions return JSON with `success` boolean +## UI (React + Vite) + +The frontend lives in `ui/src/` and is built to `public/` via `npm run build` from the `ui/` directory. **Always run `npm run build` from `ui/` after any changes to `ui/src/` files.** + +### Pages + +- **Sources / Rules / Mappings / Records** — standard CRUD pages +- **Pivot** (`ui/src/pages/Pivot.jsx`) — interactive pivot/crosstab powered by Perspective (`@perspective-dev` v4.4.0, loaded from CDN). See `docs/perspective-pivot.md` for the full Perspective API reference. +- **Stacks** — multi-source union views with running balance +- **Log** — import audit trail + +### Pivot inspector panel + +Clicking a data cell opens a right-hand inspector panel showing the underlying transactions for that cell. Key behaviors: + +- **Toggle**: clicking the same cell again closes the panel. The toggle key is `JSON.stringify({ p: row.__ROW_PATH__, c: column_names })` — stable across source and stack views. +- **Listener cleanup**: the `perspective-click` handler is stored in `perspClickHandlerRef` and removed via `removeEventListener` on effect cleanup. Without this, switching views accumulates duplicate listeners that fire multiple times per click. +- **split_by filter derivation**: `detail.config.filter` from the click event may omit split_by column constraints. They are derived from `column_names` positionally (`column_names[i]` matches `config.split_by[i]`) and appended to the filter before querying. +- **Row filtering**: a temporary `table.view({ filter, expressions })` is used so Perspective evaluates expression/computed columns correctly. Falls back to JS-side `filterRowsByConfig` on error (which skips filters for fields not in raw data). +- The panel is resizable via a drag handle on its left edge (`paneWidth` state, min 240px). +- The transaction table is sortable (click header) and shows column totals for all-numeric columns. + ## File Structure ``` @@ -154,6 +176,14 @@ dataflow/ │ ├── rules.js │ ├── mappings.js │ └── records.js +├── ui/ +│ ├── src/ +│ │ ├── pages/ # One file per page +│ │ └── api.js # API client +│ └── package.json +├── public/ # Built UI (gitignored, generated by npm run build) +├── docs/ +│ └── perspective-pivot.md # Perspective API reference ├── examples/ │ ├── GETTING_STARTED.md # Tutorial │ └── bank_transactions.csv