# 2026-07-18 — P1-4 modal migration: finance/* complete + new `` 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 ``: - `open`, `onClose`, `title`, `icon`, `maxWidth`, `zIndex`, `footer`, `children` — same shape as `` props. - Body uses `` (sticky top) and `` (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 ``. ### `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 `);}` 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).