Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

09 — User Behaviour (Settings Module)

Expected behaviour (derived from server semantics + shared interaction rules), the common mistakes users make with these screens, and the UX countermeasures. Purpose: hand the UI designer a playbook of what the user does so the flow never surprises.


1. Expected behaviour (server-contract-driven)

  • List is always server-sorted: {group: 1, key: 1} (settings.service.ts:11) — the UI must never re-sort into a different order than the API returns; grouping is presentational.
  • Group chip = server filter: tapping "Attendance" refetches (settings.controller.ts:26-28) — users expect the list to shrink server-authoritatively.
  • Save = full value replacement (setting.repository.ts:37): editing an object and forgetting a key inside it loses that nested key. UI: JSON editor starts from the current serialized value; never from an empty box.
  • Boolean toggles save instantly; text/number/JSON need an explicit save — users learn the pattern from the chip (BOOL rows show no Save button).
  • Delete is final-looking: soft delete means the row disappears but the key is burned (recreate → 500, OQ-5) — the confirm dialog must say "cannot be re-created".
  • Permissions are client-only today (OQ-2): a user without settings.* sees nothing (guard-hidden), but the server would serve them if called directly.

2. Common mistakes & countermeasures

MistakeRelatedUX countermeasure
Editing an object and losing nested fieldsfull-replace $set {value} (setting.repository.ts:37)JSON editor always prefilled from server value; never blank; "replace whole value" helper text
Not knowing a value's type until it breaksvalue: unknown (update-setting.dto.ts:10)type chip on every row; typed editors per value (06 §S2)
Batch save with unrelated edits mixed insequential loop (settings.service.ts:28-34)dirty-dot per row; "Save all (N)" counts only dirty; per-row retry on partial failure
Typing "5" expecting number, saved as stringno coercion server-sidenumber editor for num values; string stays string — chip shows type so it's discoverable
Searching for a group word in search boxno server q paramgroup chips do the filtering; search box says "search keys" in hint
Deleting then recreating a keyunique index {tenantId,key} (setting.schema.ts:38) + soft deleteconfirm copy warns; error banner on E11000 explains soft-delete state (OQ-5)
Editing org settings in the wrong surfacedual surface (organization.schema.ts:96-112 vs settings collection)AppOrgSettingsLink banner → /organization (06 §S5)

3. Power & new users

  • Power user (admin): keyboard-first on desktop — Ctrl+F search, Enter save, N new setting; batch-selects a group and edits in one pass; uses group chips to narrow.
  • New user (first-time admin): lands on the list; the AppOrgSettingsLink banner is the first thing they see; empty state teaches "add your first key" with the FAB.

4. Mobile & a11y users

  • One-thumb phone: FAB reachable; row menus on tap (not long-press-first); editors keyboard-avoidance.
  • TalkBack/VoiceOver: rows announced with key + type + value; dirty state announced; JSON parse errors announced on focus.
  • Dynamic type 200%: key mono wraps (ellipsis only on preview subtitle — never on the key itself).
  • Reduced motion: no stagger/shake; fades only.

5. Interrupted sessions & retries

  • Suspend/resume: list cache re-shown (stale-while-revalidate); editors restore last server value (never partial draft on relaunch — drafts are in-memory only).
  • Mid-save interruption (app kill): upsert is idempotent — on next open the value is whatever the server has; the row reconciles from a refetch.
  • Retry after 5xx: form kept, "Retry" snackbar action re-submits the same payload.
  • 429: countdown; no auto-retry (00-shared/07 §4).

6. Abandonment

  • Where people quit and what to do:
    • Dirty explicit editor + back → "Discard changes?" dialog.
    • Batch mode + back → Cancel restores last-server values (dirty dots clear).
    • New-setting sheet half-filled → close = discard (no draft).
  • No server drafts exist — everything in-memory is forfeit on close; the list itself is safe to re-fetch.

7. Frustration points → UX treatments (summary)

FrustrationTreatment
"Which surface owns attendance?"cross-link banner (S5) + group copy
"My JSON got mangled"pretty-print on save; parse-error blocked before send; never blank prefill
"It saved half my batch"per-row status in AppSaveBar, retry per row (03 §3)
"Key disappeared after delete"warning copy + E11000 banner (OQ-5)
"I can't find the key"client search + group chips + server-sorted groups

8. Cross-device & conflict

  • Two admins editing the same key: last write wins (no optimistic-lock check on the upsert path — setting.repository.ts:35-39; version exists on the schema (base.schema.ts:30-31) but the upsert never compares it, OQ-9). UI: refetch on screen focus shows the latest value; no merge, no conflict dialog (config is last-write-wins by design).
  • Concurrent batch saves on different keys: both succeed (per-key docs).

9. Adoption path (holistic)

P0: list + group filter + single save + delete. P1: batch save, JSON editor polish, client search. P2 (planned): settings history (OQ-7), encrypted values (OQ-6), settings registry with defaults, permission enforcement wiring (OQ-2), COACHING group (IMPLEMENTATION_PLAN.md:773).