# 2026-07-18 — P1-4 modal migration: finance/* complete + new `<Drawer>` component

Branch: `feat/p1-4-modals-finance`
Worktree: `.worktrees/feat-p1-4-modals-finance`

## Why

Closes the remaining finance/* modals that the prior partial PR
couldn't reach because they needed either a centered modal or a side
drawer.

## Changes

### `client/src/components/ui/Drawer.tsx` (new)

Right-side slide-in panel component. Symmetric to `<Modal>`:

- `open`, `onClose`, `title`, `icon`, `maxWidth`, `zIndex`, `footer`,
  `children` — same shape as `<Modal>` props.
- Body uses `<DrawerHeader>` (sticky top) and `<DrawerFooter>`
  (sticky bottom) slots.
- Default `maxWidth` is `max-w-2xl` (vs Modal's `max-w-xl`).
- Default `zIndex` is `110` (same as Modal).
- Backdrop click-outside + Escape-to-close + scroll-lock behaviour
  matches Modal.

### `client/src/pages/finance/StudentGroups.tsx` (3 modals migrated)

- **Delete group?** (confirm) — red destructive dialog.
- **New student group / Edit X** — name + description + type +
  colour form.
- **Add members to X** — mode dropdown (all / by class / by IDs)
  with conditional UID input.

### `client/src/pages/finance/StudentGroupDetail.tsx` (1 modal migrated)

- **Remove member?** — confirm dialog.

### `client/src/pages/finance/Invoices.tsx` (1 drawer migrated)

- **Invoice detail** (right-side drawer, max-w-2xl) — replaced the
  inline `fixed inset-0 ... slide-in-from-right` pattern with
  `<Drawer>`.

### `client/src/pages/finance/Payslips.tsx` (1 drawer migrated)

- **Payslip detail** (right-side drawer, max-w-md) — summary tile +
  Earnings/Deductions rows + "Open full payslip view" footer action.

### Housekeeping

- Cleaned up an orphan `</div>);}` left over from the previous
  `Discounts.tsx` migration (commit `4f8c33f`) — that dangling
  closure was a pre-existing TS error and is now fixed.

## Verification

- `node scripts/lint-no-inline-modals.js` → `finance/*`:
  6 → 0 (StudentGroups, StudentGroupDetail, Invoices, Payslips all
  clean).
- Whole-tree lint: only `Messages.tsx` and other out-of-scope files
  remain (already covered by other PRs).
- `npx tsc --noEmit` for finance/* is clean except for two
  pre-existing errors that are not introduced by this PR:
  `StudentGroup` is exported from the store via a different path
  (consistency fix, deferred), and `Trips.tsx:118` references
  `Wallet` without importing it (one-line import, deferred).
