You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
117 lines
8.2 KiB
Markdown
117 lines
8.2 KiB
Markdown
# "Ideal Web App" Pass — Edit Everywhere, Dialogs, Toasts, Settings Hub
|
|
|
|
> **STATUS: DELIVERED 2026-07-17.** All 10 plan tasks landed (dialog/toast kit, edit
|
|
> dialogs everywhere, send composer, /settings hub, favicon/titles); e2e-walked live and
|
|
> hardened by a 63-agent adversarial review — 27 confirmed findings fixed (2 deferred:
|
|
> billing-terms edit UI, dynamic record tab titles). Suite green at 346 tests.
|
|
|
|
*Approved 2026-07-17 (Approach A). Fixes the six gaps that keep the console feeling like a
|
|
wireframe despite the D17 restyle: no record editing, browser `window.prompt/confirm`
|
|
popups, silent success, no Settings home, inline wireframe forms, no favicon. Visual
|
|
reference: Pinterest's product chrome — centered rounded modals with generous padding and
|
|
pill buttons, and a left-rail settings page.*
|
|
|
|
## 1. Goal & scope
|
|
|
|
Make every flow feel like a finished web application. Mostly UI; two **small additive
|
|
backend slices** (reminder-schedule endpoints, sharing default). No schema changes beyond
|
|
zero (settings ride the existing `setting` table; cadence changes are new dated
|
|
`reminder_schedule` rows per rule 3). `@sims/ui` stays generic.
|
|
|
|
**Key server fact:** `PATCH /clients/:id` already updates name / gstin / stateCode /
|
|
address / contacts / status / notes (`repos-clients.ts:85`) — client editing is pure UI.
|
|
`updateAmc`, `updateRecurringPlan`, `updateInteraction`, `patchModule`, `patchEmployee`
|
|
likewise exist and are under-used by the UI.
|
|
|
|
## 2. App-chrome kit (`packages/ui` — generic only)
|
|
|
|
| Piece | Contract |
|
|
|---|---|
|
|
| `Dialog` | Pinterest modal. Props: `open, onClose, title, size?: 'sm'\|'md'\|'lg'` (420/560/720px max-width), `footer?: ReactNode`, children = body (scrolls if tall). 16px radius, `--shadow-lg`, scrim (blur + dim) closes on click; Esc closes; **focus trap + focus restore** (same technique as `CommandPalette`); `role="dialog" aria-modal aria-label={title}`; ✕ button in the title row. Replaces the legacy `Modal` (currently zero call sites — delete it). |
|
|
| `ConfirmDialog` | On top of Dialog (`size='sm'`): `open, onClose, onConfirm, title, body?, confirmLabel?, tone?: 'danger'`. Confirm button is ink (or danger red); busy state while `onConfirm`'s promise resolves. Kills every `window.confirm`. |
|
|
| `Toaster` | `ToastProvider` (mounted once in `main.tsx`) + `useToast()` → `toast.ok(msg)`, `toast.err(msg)`. Bottom-right stack, max 3 visible, 4s auto-dismiss + ✕, `aria-live="polite"`, ok/err tones from tokens. Pure state logic in an exported reducer (`toast-store.ts`) so it's unit-testable without a DOM. |
|
|
| `FormGrid` | `display:grid; grid-template-columns: 1fr 1fr; gap 12px` (single column ≤560px); fields span-2 via a `wide` prop on a `FormField` wrapper (thin wrapper over existing `Field`). |
|
|
| `Button pill` | `pill?: boolean` prop → 999px radius (Pinterest button shape) — used in dialog footers only. |
|
|
|
|
CSS: new `wf-dialog*`, `wf-toast*`, `wf-formgrid` blocks in `tokens.css`; z-order — drawer
|
|
40 < modal-back 50 < palette 60 < toasts 70.
|
|
|
|
## 3. Edit everywhere (all modals; toast on success; ConfirmDialog on destructive)
|
|
|
|
| Surface | Modal content | Endpoint (exists) |
|
|
|---|---|---|
|
|
| Client 360 header → **Edit** | name · GSTIN · state · address · notes + **contacts repeater** (name/email/phone/role rows, add/remove, ≥0 rows) | `PATCH /clients/:id` |
|
|
| Clients → New client | same form, empty; `?new=1` opens it | `POST /clients` |
|
|
| AMC row → Edit / new | coverage, period, amount ₹, reminder days | `PATCH /amc/:id`, create |
|
|
| Plan row → Edit / new | module, cadence, amount, next run, policy | `PATCH /recurring/:id`, create |
|
|
| Interaction row → Edit / log | type, date, outcome, follow-up, notes | `PATCH /interactions/:id`, create |
|
|
| Employees add / edit / reset-pw | as today's inline forms | existing |
|
|
| Modules new / add-price | as today's inline forms | existing |
|
|
| Deactivate (AMC/plan/employee) | ConfirmDialog, danger tone | existing |
|
|
|
|
Quote-content editor stays inline (it has the live preview pane). Inline `Notice` remains
|
|
only for in-form validation; all mutation success/failure also lands a toast.
|
|
|
|
## 4. Send-by-email modal + document confirms
|
|
|
|
- **Send** (DocumentView): Dialog with **To** — `<select>` of the client's contacts that
|
|
have emails + an "other…" free-text option — and **Subject** prefilled
|
|
`${docType} ${docNo}`. Gmail state inline: connected → send enabled; disconnected/dead →
|
|
disabled with the reconnect hint. Calls the existing `documentAction(id,'send',{to,subject})`.
|
|
- Convert-to-invoice-and-send, supersede, cancel, credit-note: ConfirmDialog with the real
|
|
consequence copy (number consumption, email dispatch). All six `window.*` popups die.
|
|
|
|
## 5. Settings hub — `/settings` (Pinterest left rail + one panel)
|
|
|
|
Owner-only except Appearance. Rail sections; active section in `?s=` (deep-linkable).
|
|
|
|
| Section | Panel | Backend |
|
|
|---|---|---|
|
|
| Company | profile form (GSTIN checksum + state code validated server-side) | `GET/PUT /settings/company` (exists) |
|
|
| Document template | today's `DocumentTemplate` editor moved in whole (form + live preview); old route `/settings/template` → `Navigate` to `/settings?s=template` | exists |
|
|
| Reminders | read-only table of `reminder_schedule` rows (rule kind · effective from/to · day offsets · subject) + **“New dated row” modal** (rule kind, effective_from, offsets CSV validated `3,7,14`-style, subject, body). Rows are never edited or deleted — a change is a new row (rule 3). Also two number fields: overdue days / renewal days. | **new** `GET /settings/reminders` → `{ overdueDays, renewalDays, schedules[] }`; `PUT /settings/reminders` → the two day settings; `POST /settings/schedules` → insert row. Owner-gated, audited, transactional. |
|
|
| Sharing | default share-link expiry days (blank = never) | **new** `GET/PUT /settings/sharing` → setting row `share.default_expiry_days`; `createShare` reads it as the default when the caller passes nothing (fallback 30 unchanged). |
|
|
| Integrations | Gmail: `/email/status` (connected/address/dead + reconnect hint). AWS: last month's ranking total via existing `GET /aws/ranking` (or "no data"). Read-only. | exists |
|
|
| Appearance | theme mode + accent swatches (moves the full picker here; the compact top-bar toggle stays) | none |
|
|
|
|
Nav: ADMIN group = **Settings** (owner-only; replaces the Document Template item) +
|
|
Employees. `Doc Template` breadcrumb/palette entries update accordingly.
|
|
|
|
## 6. Polish
|
|
|
|
- **Favicon**: teal rounded-square SVG with "HQ" mark (`apps/hq-web/public/favicon.svg` +
|
|
link in `index.html`); same mark reused as the login brand accent.
|
|
- **`document.title` per route**: `"<Page> · SiMS HQ"` set in `Layout` from the nav map
|
|
(record pages: doc no / client name once loaded).
|
|
- Errors from mutations surface as `toast.err`; page-level fetch failures keep
|
|
`ErrorState` (unchanged).
|
|
|
|
## 7. Explicitly not doing
|
|
|
|
Masonry/card re-skins of tables (Tables Rule); editing issued documents (immutability);
|
|
schedule-row edit/delete (dated rows are append-only); WhatsApp/send-channel changes;
|
|
password-protected shares; drawer pattern (modal chosen); i18n.
|
|
|
|
## 8. Testing & verification
|
|
|
|
- Kit: `toast-store` reducer + offsets-CSV validator get pure vitest tests; Dialog focus
|
|
logic reuses the proven palette pattern (no DOM env in repo — behavior verified e2e).
|
|
- Backend: `settings-reminders.test.ts` + `settings-sharing.test.ts` in `apps/hq/test`
|
|
(real-DB style like siblings): schedule insert appends + audits + never mutates, day
|
|
settings round-trip, `resolveSchedule` picks the new dated row from its effective date,
|
|
`createShare` honors the sharing default and the explicit param still wins.
|
|
- Gates: typecheck + full suite (293 + new) green; e2e walk — edit client (name + contact
|
|
add), send modal both Gmail states, one ConfirmDialog per document action, every settings
|
|
section round-trip, toasts, favicon/tab titles, dark mode + 420px pass.
|
|
|
|
## 9. Build order
|
|
|
|
1. Kit (Dialog/Confirm/Toaster/FormGrid/pill + CSS + toast tests)
|
|
2. Backend slices (reminders + sharing endpoints, tests)
|
|
3. Send modal + document ConfirmDialogs
|
|
4. Client edit + New-client modal
|
|
5. Remaining edit/create modals (AMC, plans, interactions, employees, modules)
|
|
6. Settings hub + nav/route changes
|
|
7. Favicon + titles + toast sweep
|
|
8. Verification pass (§8)
|