fix(web): review round 1: optimistic edits, friendly errors, preferences, Gelasio
- Tags and custom fields update the cached entry optimistically and build from the latest cache; one entry's PATCHes run in order (mutation scope). - The review textarea follows server changes (Restore) unless edited. - friendlyError() maps validation reasons and 413 to plain copy everywhere. - Rating scale from me/preferences; /login and /register use auth/status; Attachments lists from the API. - Gelasio vendored (official OFL variable TTFs); minors from the review. - Docs: DESIGN (AA on orange, faint labels), DECISIONS, TODO, ARCHITECTURE. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
d204775235
commit
5fa59310f3
@@ -4,6 +4,32 @@ Append-only. Never edit or delete an entry. Supersede it with a new one that lin
|
||||
One entry per decision a future maintainer would otherwise have to reverse-engineer.
|
||||
No index: `grep -n '^## ' docs/DECISIONS.md` lists every entry (date, title, line number).
|
||||
|
||||
## 2026-10-30: Web app (Task 17): preferences, first run, optimistic edits, AA on orange, self-hosted fonts
|
||||
**Status:** accepted
|
||||
**Context:** Task 17 and its fix round 1 (controller rulings).
|
||||
**Decision:**
|
||||
- **Three small API routes for the web app.** `GET auth/status` (no auth) → `{ hasUsers }`: `/login` redirects a fresh
|
||||
instance to `/register`, which shows "Create the admin account", or invite mode when users exist (also on
|
||||
`invite_required`, and with `?invite=` prefilled). `GET attachments?entity=&entityId=` lists an owned record's
|
||||
attachments with their links (404 when the record is not the caller's). `GET/PATCH me/preferences` →
|
||||
`{ ratingScale: "5half"|"5"|"10"|"100", defaultVisibility }`, stored in `users.preferences` under the same keys
|
||||
`defaultVisibility()` already reads (`packages/core/src/preferences.ts`); unknown stored keys are kept.
|
||||
- **Rating scale** comes from preferences (default five stars with halves). Stored ratings stay integers 0–100; the web
|
||||
formats them with `formatRating(value, scale)`.
|
||||
- **Entry edits are optimistic and serial.** `useEntryPatch` updates the cached entry in `onMutate` (rollback on error),
|
||||
runs one entry's PATCHes in order through a TanStack mutation `scope`, and `edit(cur => patch)` builds from the latest
|
||||
cache, so two quick tag adds keep both. Only the last queued edit writes the server's answer back.
|
||||
- **Keyboard rating saves once:** arrow steps update locally and save 500 ms after the last key, on blur, or on unmount,
|
||||
so stepping does not write a history row per keypress.
|
||||
- **Errors in the product's voice:** every error display goes through `friendlyError()` (`apps/web/src/format.ts`), which maps
|
||||
`validation` field/reason pairs and 413 to plain copy; other API errors keep the server's message.
|
||||
- **AA contrast on the approved palette:** dark text on `--warn`, `--text-faint` group labels, `--text-muted` placeholders on
|
||||
`--surface-3` (recorded in DESIGN.md).
|
||||
- **Fonts are vendored** (Inter woff2 from fontsource; Gelasio as the official variable TTFs from github.com/google/fonts
|
||||
`ofl/gelasio`, unmodified, because no woff2 tooling was available offline) with their OFL texts.
|
||||
- **The web app mirrors wire types** in `apps/web/src/api.ts` instead of importing `@sttracker/core` (apps/web has no
|
||||
workspace dependency on it, and the browser bundle must not pull server code).
|
||||
|
||||
## 2026-10-30: Token actor in history, connection deleted_at, CSRF on every browser write
|
||||
**Status:** accepted. Supersedes the **Actor**, **CSRF** and **Deleted connections** bullets of
|
||||
"HTTP API: auth modes, CSRF header, error mapping, actor" below.
|
||||
|
||||
Reference in New Issue
Block a user