From 39d779c7d998168344cdffb6b59cdfe49e8aed64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 15 May 2026 18:05:03 +0200 Subject: [PATCH] docs(planning): mobile audit report for settings and admin --- .../v1427-r3c-mobile-settings-admin.md | 198 ++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 .planning/research/v1427-r3c-mobile-settings-admin.md diff --git a/.planning/research/v1427-r3c-mobile-settings-admin.md b/.planning/research/v1427-r3c-mobile-settings-admin.md new file mode 100644 index 000000000..db31260c8 --- /dev/null +++ b/.planning/research/v1427-r3c-mobile-settings-admin.md @@ -0,0 +1,198 @@ +--- +file: .planning/research/v1427-r3c-mobile-settings-admin.md +purpose: Mobile capability audit — Settings + Admin surface +created: 2026-05-15 +auditor: MA6 +--- + +# Mobile audit — Settings and Admin + +## Summary + +Reviewed 23 settings components and 23 admin components across `/settings/[section]` and `/admin/[section]`. The two shells (`settings-shell.tsx`, `admin-shell.tsx`) already carry the mobile chip strip with 44px tap floors, and several wide tables (`account` passkeys, `admin/users`, `admin/api-tokens`) already ship `md:hidden` mobile card-list fallbacks. The remaining mobile-hostile patterns are concentrated in (a) the user-facing `/settings/api` token tables, which still force horizontal scroll, (b) the admin tables that never received the card-list treatment (`login-overview`, `feedback`, `backups`, `app-logs`, `ai-quality`, `coach-feedback`), and (c) input fields that universally lack `inputMode` / `enterKeyHint` mobile-keyboard hints. 19 findings; 0 Critical, 7 High, 7 Medium, 5 Low. + +## Findings + +### F1 — `/settings/api` tables force horizontal scroll on mobile +- Severity: High +- Axis: visual +- File: `src/components/settings/api-section.tsx:73-104,239-345,362-403` +- Symptom: Three tables (endpoint docs, active tokens, revoked tokens) carry hard `min-w-[760px]` / `min-w-[860px]` widths with no `md:hidden` card-list fallback. At 320-414 px the user has to swipe horizontally to see the "Last used", "Created", and "Revoke" cells; the Trash2 revoke button sits in the rightmost column and is invisible on first paint. +- Evidence: grep — `api-section.tsx:74 className="w-full min-w-[760px] text-xs md:min-w-0"`; `:240 min-w-[860px]`; `:363 min-w-[760px]`. No `md:hidden` mobile list, unlike the admin `api-token-overview` and `user-management` sections which already adopted the dual-table pattern. +- Recommended fix: Mirror `admin/api-token-overview-section.tsx`'s pattern — keep the `hidden md:block` desktop table, add a `md:hidden` card list (name + permissions + status badge + last-used line + revoke icon button per card). +- Effort: M + +### F2 — Admin tables without card-list fallback +- Severity: High +- Axis: visual +- File: `src/components/admin/login-overview-section.tsx:371`, `feedback-inbox-section.tsx:123`, `backups-section.tsx:471`, `app-log-preview-section.tsx:207`, `ai-quality-section.tsx:134`, `coach-feedback-section.tsx:119` +- Symptom: Six admin tables sit inside `overflow-x-auto` with no mobile card-list. `login-overview` has 7 columns (status / user / action / provider / IP / location / timestamp), `feedback` 5, `backups` 5, `app-logs` 5, `ai-quality` 7, `coach-feedback` 7. At 393 px the user must horizontally scroll to see anything past the second column. The pattern was already adopted in three other admin tables (`users`, `api-tokens`, `account/passkeys`) — it's a symmetry gap. +- Evidence: grep `overflow-x-auto`+`` finds 6 admin sections without the matching `md:hidden
` has no `min-w-0`, so on a 320 px viewport (which forces horizontal scroll anyway per F2) the carrier label `Telekom`/`Vodafone` adds a second line to the row, increasing row height by ~14 px and pushing the table taller than expected. When the eventual mobile card-list lands per F2, the carrier chip needs to be planned as a sibling chip, not a second line, so the card stays compact. +- Evidence: `login-overview-section.tsx:455-462`: `{carrierShortLabel(entry.carrier)}`. The provider cell already has 3-4 lines worth of content; adding a fourth pushes row height past the 44 px tap-comfort floor. +- Recommended fix: When implementing the F2 card-list, render carrier as an inline secondary chip next to the provider chip (gap-1, both `text-[10px]`) instead of a stacked block. On desktop, the existing block layout is fine because the row already has 7 columns of horizontal real estate. +- Effort: S (folds into F2 patch) + +### F4 — CSV-export and pagination buttons hidden behind table scroll on mobile +- Severity: High +- Axis: logic +- File: `src/components/admin/login-overview-section.tsx:330-339,478-509` +- Symptom: The Export-CSV button sits inside the same `overflow-x-auto` wrapper as the table, but the toolbar row (`mt-3 flex flex-wrap items-center justify-between`) only renders after the empty-state check — so on a mobile viewport the user scrolls horizontally through 7 columns to reach the prev/next pagination at the table's right edge. The CSV download itself works (it's above the table), but the per-page selector and pagination disappear off-screen. +- Evidence: `login-overview-section.tsx:478-509`: pagination lives inside the same `overflow-x-auto` wrapper as the 7-column table. At 393 px the wrapper paints a scrollbar; the pagination row is pushed to the wrapper's intrinsic width. +- Recommended fix: Move the `mt-3 flex flex-wrap` pagination row out of the `overflow-x-auto` wrapper so it always renders at viewport width. Same fix applies to `app-log-preview-section.tsx:254-261`. +- Effort: S + +### F5 — Native `` height +- Severity: Medium +- Axis: code +- File: `src/components/settings/account-section.tsx:63-64`, `timezone-picker.tsx:30-31`, `admin/general-settings-section.tsx:9-10` +- Symptom: Three sections rely on a hand-rolled `NATIVE_SELECT_CLASS` string (`h-9` + custom border tokens) for the native `` falls back to the system picker (good UX), but the visual contract drifts between two cards on the same Account page. +- Evidence: Three near-identical `NATIVE_SELECT_CLASS` constants — `account-section.tsx:63`, `timezone-picker.tsx:30`, `general-settings-section.tsx:9`. The two settings strings differ from the admin string at the focus-ring spec (`ring-2` vs `ring-[3px]`). +- Recommended fix: Single source of truth — extract a `` primitive into `src/components/ui/native-select.tsx`. Three call sites import it instead of pasting class strings. Same focus token across all surfaces. +- Effort: M + +### F6 — `` and email/URL fields lack `inputMode` / `enterKeyHint` +- Severity: High +- Axis: code +- File: every settings/admin section +- Symptom: Across every input we audited (height in `account`, telegram chat ID, telegram bot token, ntfy server URL/topic, moodLog URL/API key, withings client-id, threshold min/max, dashboard widget reorder labels, admin reminder minutes, search filters in `login-overview`, password fields) there is zero `inputMode` or `enterKeyHint` attribute. iOS / Android keyboards fall back to the default text keyboard for `type="number"` (Android Chrome partially uses the numeric keypad, iOS Safari sometimes doesn't) and the Enter key shows "go" instead of "next"/"send". Touch typing flow on every form is slower than it should be. +- Evidence: `grep -n "inputMode\|enterKeyHint"` over both component directories returns zero matches. `grep -n "autoComplete"` shows ~13 hits, so the attribute style is camelCase React — the team knows the pattern, just hasn't applied it to keyboard-hint attributes. +- Recommended fix: Add `inputMode="numeric"` to every `type="number"` Input (height, chat ID if numeric, reminder minutes, threshold min/max). Add `inputMode="email"` to email inputs. Add `inputMode="url"` to URL inputs. Add `enterKeyHint="next"` to fields followed by another input in the same form, `enterKeyHint="done"` to the last field. The shared `Input` primitive could grow a `type`-derived default and let callers override. +- Effort: M + +### F7 — Password-input toggle button is 16×16, below 44px tap floor +- Severity: High +- Axis: visual +- File: `src/components/settings/password-input.tsx:21-33` +- Symptom: The eye/eye-off show-password toggle is an absolutely-positioned `