09 — User Behaviour (Settings Module)
- 1. Expected behaviour (server-contract-driven)
- 2. Common mistakes & countermeasures
- 3. Power & new users
- 4. Mobile & a11y users
- 5. Interrupted sessions & retries
- 6. Abandonment
- 7. Frustration points → UX treatments (summary)
- 8. Cross-device & conflict
- 9. Adoption path (holistic)
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
| Mistake | Related | UX countermeasure |
|---|---|---|
| Editing an object and losing nested fields | full-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 breaks | value: unknown (update-setting.dto.ts:10) | type chip on every row; typed editors per value (06 §S2) |
| Batch save with unrelated edits mixed in | sequential 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 string | no coercion server-side | number editor for num values; string stays string — chip shows type so it's discoverable |
| Searching for a group word in search box | no server q param | group chips do the filtering; search box says "search keys" in hint |
| Deleting then recreating a key | unique index {tenantId,key} (setting.schema.ts:38) + soft delete | confirm copy warns; error banner on E11000 explains soft-delete state (OQ-5) |
| Editing org settings in the wrong surface | dual 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+Fsearch,Entersave,Nnew 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
AppOrgSettingsLinkbanner 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
monowraps (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)
| Frustration | Treatment |
|---|---|
| "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;versionexists 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).