whisper-money/resources
Víctor Falcón 8d58e0bf81
refactor(settings): dedupe the categories and labels pages (#770)
## Why

`settings/categories.tsx` and `settings/labels.tsx` were the last big
duplication cluster after the landing page (#768): 6 clone pairs between
and within them, ~100 duplicated lines.

They are the same page with a different record type:

- the trailing `...` dropdown with Edit / Delete
- the right-click context menu on each row, with the same two items
- the pair of dialog mounts wired to two `useState` flags — written
**twice per page**, once for the dropdown and once for the context menu
- 39 lines of table markup (header groups, body, empty row) differing
only in the row component and the empty message

## What changed

Three shared pieces:

| component | replaces |
|---|---|
| `RowActionsDropdown` | the `...` cell + its two dialogs, in both pages
|
| `RowWithActionsContextMenu` | the row + context menu + its two
dialogs, in both pages |
| `SettingsTable` | the table markup, in both pages |

The dialogs come in as render props (`renderEditDialog` /
`renderDeleteDialog`), so each page keeps its own dialog components and
their extra props — `categories` needs the full category list, labels
needs nothing — while the menus own the open state.

`categories.tsx` −86 lines, `labels.tsx` −100 lines.

### Why not the existing DataTable

`components/ui/data-table.tsx` already renders a table, but it is
**virtualized**: its `renderRow` hands back a `VirtualItem` and the
virtualizer, and rows are expected to attach `measureElement`. These
settings lists are short and their rows are wrapped in a
`ContextMenuTrigger asChild`, so adopting it would mean threading
measurement through the context menu to gain nothing. `SettingsTable` is
the plain version, and `accounts.tsx` / `automation-rules.tsx` (which
repeat the same markup) can move onto it next.

## Metrics

Cumulative with the other refactor PRs in flight; measured against
`main`:

| | before | after |
|---|---|---|
| duplicated lines (tsx) | 5.82% | 5.40% |
| duplicated lines (total) | 5.34% | 5.11% |
| clones | 322 | 314 |

## Testing

ESLint and `tsc --noEmit` clean. No behaviour change: same markup, same
handlers, same dialogs — only their location moved.

The browser suite is what actually exercises these pages
(`CategoriesTest` covers viewing, creating, filtering, the empty state
and cell alignment). I could not run it locally without a production
asset build, so it is verified here by CI's `browser-tests-matrix`.
2026-08-11 14:37:23 +02:00
..
css fix(charts): improve contrast for chart colors 9-10 (#614) 2026-06-30 10:28:03 +00:00
js refactor(settings): dedupe the categories and labels pages (#770) 2026-08-11 14:37:23 +02:00
views feat(deps): upgrade Inertia.js from v2 to v3 (#769) 2026-08-11 14:33:31 +02:00