Skip to content

v0.6.5.0 ux: hardening pass for non-technical users (7 of 20 findings)#154

Merged
jayzalowitz merged 1 commit into
mainfrom
jayzalowitz/ux-hardening
May 5, 2026
Merged

v0.6.5.0 ux: hardening pass for non-technical users (7 of 20 findings)#154
jayzalowitz merged 1 commit into
mainfrom
jayzalowitz/ux-hardening

Conversation

@jayzalowitz

Copy link
Copy Markdown
Owner

Summary

UX hardening driven by a browser-agent visual review of every SPA route — focused on what would confuse a non-technical user. 7 of 20 findings closed in this PR (the load-bearing ones); polish items (Settings consolidation, theme switcher relocation, auto-save, date input theming, etc.) queued for a follow-up. Full review with severity grades + screenshots in .context/ux-review/FINDINGS.md.

The single biggest finding from the review — Hosted OAuth (P0 #1) — needs a verified Google OAuth app + production hosting and isn't shippable from a worktree alone. Tracked separately.

What ships in this PR

1. Centralized friendly errors + Try Again (P0 #4)

New `ApiError` class with `kind` discriminant (`offline | auth | not-found | bad-request | server | unknown`) and a `friendlyMessage` field. The web dev proxy returns `{error: 'API proxy error'}` when the upstream API is down — pre-fix that string leaked verbatim to users on Chat / Decisions / Audit pages. Now translated to "Can't reach SkyTwin right now. We'll keep trying."

`renderApiError(err, { context, retry })` and `wireApiRetry(container, retry)` give every page the same calm error card with a "Try again" button.

2. Approvals stops lying (P0 #5)

Pre-fix the Approvals page showed "0 waiting / You're all caught up" when the API was down — indistinguishable from genuinely having nothing to do, so a user could miss real approvals. Now: a loaded-empty list looks different from an offline list, and Try again is one click away. Same treatment for Decisions, Audit, Chat.

3. UUID badge → friendly fallback (P0 #2)

Header user badge and Settings footer used to show raw `11111111-2222-…` when the user record couldn't be loaded. Now shows `You (1111…)` with the first 4 chars in a tooltip for devs.

4. Connection status as a header banner (P1 #12)

Calm yellow banner with animated dot + "Retry now" button, rendered below the page header when the API is unreachable. Promoted from the bottom-left footer (where most users wouldn't notice). Hidden when connected so the happy path has no extra chrome. Respects `prefers-reduced-motion`.

5. Onboarding works even when the API is down (P1 #6)

Pre-fix the "Try one — see how it thinks" preview card was rendered with `display: none` and only revealed by JS after a successful demo-info fetch. With the API down, the card never appeared — onboarding step 1 became a wall of text. Now: card always renders; clicks return pre-canned sample answers (recruiter / subscription / dinner) with the same visual treatment as the live engine, plus a small "Live preview offline — showing a sample answer" caveat.

6. Mobile bottom-nav fixes (P0 #3) — three at once

  • Single-letter icons (H/!/D/T/S) replaced with inline-SVG icons (home / chat-bubble / checkmark-circle / hamburger / gear).
  • Chat link added to mobile bottom-nav (v0.6.0.0 shipped Chat in the desktop sidebar but missed mobile entirely).
  • Bottom-nav no longer overlaps page content (`.content` padding-bottom `4rem` → `5.5rem + safe-area-inset`).

7. Voice mismatch (P1 #13)

Sidebar "My learnings" → "What I've learned", matches the page header.

Visual verification

Before/after screenshots in `.context/ux-review/`:

  • `04-dashboard-noapi.png` (before) vs `14-after-pr1-dashboard.png` (after)
  • `05-chat.png` (before, "API proxy error") vs `15-after-pr1-chat-offline.png` (after, "Can't reach SkyTwin right now")
  • `06-approvals.png` (before, "0 waiting") vs `16-after-pr1-approvals-offline.png` (after, "Couldn't load your approvals")
  • `12-mobile-home.png` (before, single-letter icons) vs `17-after-pr1-mobile-home.png` (after, real SVGs + Chat link)
  • `01-onboarding-step1.png` (before, no demo card) vs `18-after-pr1-onboarding-fallback.png` (after, demo card visible) → click → `19-after-pr1-onboarding-static-preview.png` (sample answer)

Test plan

  • `pnpm build` — 20 packages green
  • `pnpm test` — 40 packages, 1300+ tests, all green
  • Browser-agent walkthrough at `localhost:3210` (web only, no API) — verified each finding's fix renders correctly
  • Manual: bring up the full stack (`pnpm dev` with CockroachDB), verify the connection banner disappears when API is up
  • Manual: shrink browser to mobile width, verify the new bottom-nav icons are crisp + clickable + no page-content overlap

What's queued for the next PR

From the same review (`FINDINGS.md` P1/P2 items):

🤖 Generated with Claude Code

Closes 7 of 20 findings from .context/ux-review/FINDINGS.md (the
load-bearing ones); polish queued for a follow-up. All visible to
non-technical users on first contact with the app.

Centralized friendly errors (P0 #4)
- New ApiError class with kind discriminant + friendlyMessage field.
- "API proxy error" no longer leaks to users; translated to
  "Can't reach SkyTwin right now. We'll keep trying."
- renderApiError(err, {context, retry}) + wireApiRetry give every
  page the same calm error card with a Try again button.

Approvals/Decisions/Audit/Chat use the helper (P0 #5)
- Pre-fix Approvals showed "0 waiting / You're all caught up" when
  API was down — indistinguishable from genuinely empty. Real
  approvals could be missed. Now: distinct offline state.

UUID badge → friendly fallback (P0 #2)
- Header user badge + Settings footer used to show raw
  "11111111-2222-..." when the user record couldn't load.
- Now shows "You (1111…)" with userId in tooltip for devs.

Connection status as actionable header banner (P1 #12)
- Promoted out of bottom-left footer where most users wouldn't see.
- Calm yellow banner with animated dot + Retry now button.
- Hidden when connected so happy path has no extra chrome.
- Respects prefers-reduced-motion.

Demo preview card static fallback (P1 #6)
- Pre-fix the "Try one — see how it thinks" card was hidden via
  display:none when /api/v1/demo/info failed. Onboarding step 1
  became a wall of text.
- Now: card always renders; clicks return pre-canned sample answers
  with the same visual treatment + "Live preview offline" caveat.

Mobile bottom-nav fixes (P0 #3)
- Replaced single-letter H/!/D/T/S icons with inline-SVG icons.
- Added Chat link (was missing entirely from mobile).
- Increased page-content padding-bottom 4rem → 5.5rem so the nav
  stops overlapping page content (composer hint was hidden under it).

Voice mismatch (P1 #13)
- Sidebar "My learnings" → "What I've learned", matches page header.

Full backend test suite still green across 40 packages. Browser-agent
visual verification screenshots in .context/ux-review/14- through 19-.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 5, 2026 21:23

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the web SPA for non-technical users by replacing raw/ambiguous failure states with friendlier UI, improving mobile navigation, and making onboarding/demo states degrade more gracefully when the API is unavailable.

Changes:

  • Introduces centralized API error classification/rendering and applies it to Approvals, Decisions, Audit, and Assistant.
  • Adds a header-level connection banner, friendlier user badge fallbacks, and onboarding static preview fallbacks.
  • Reworks the mobile bottom nav with SVG icons, adds Chat there, and adjusts page padding to avoid nav overlap.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
VERSION Bumps app version to 0.6.5.0.
CHANGELOG.md Documents the UX hardening changes shipped in this release.
apps/web/public/js/pages/settings.js Updates Settings sign-in footer to show a friendlier identity fallback.
apps/web/public/js/pages/onboarding.js Keeps demo preview visible and adds static fallback responses when the API is unavailable.
apps/web/public/js/pages/decisions.js Replaces raw error banners with centralized friendly API error UI.
apps/web/public/js/pages/audit.js Applies centralized friendly API error handling to audit loading.
apps/web/public/js/pages/assistant.js Applies centralized friendly API error handling to assistant loading.
apps/web/public/js/pages/approvals.js Stops rendering an empty approvals state when the pending approvals fetch fails.
apps/web/public/js/app.js Renames Twin page title, adds user badge fallback logic, and introduces the connection banner/retry flow.
apps/web/public/js/api-client.js Adds ApiError, HTTP/network error classification, shared error-card rendering, and retry button wiring.
apps/web/public/index.html Renames the Twin nav label, adds the connection banner, and replaces the mobile bottom nav icons/links.
apps/web/public/css/styles.css Styles the connection banner, SVG bottom-nav icons, and increases mobile content bottom padding.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

<span class="bottom-nav-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18M3 12h18M3 18h18"/></svg>
</span>Decisions
</a></li>
Comment thread apps/web/public/js/app.js
Comment on lines +280 to +287
// Triggers an immediate health check + re-renders the status. Skips
// the next backoff window the SSE client would otherwise wait through.
document.addEventListener('click', (e) => {
const target = e.target instanceof Element ? e.target : null;
if (!target) return;
if (target.getAttribute('data-action') !== 'connection-retry') return;
e.preventDefault();
updateConnectionStatus();
// Network unreachable (no DNS, no route, browser offline, etc.)
throw new ApiError({
kind: 'offline',
friendlyMessage: "You appear to be offline. Check your connection — we'll retry automatically.",
@jayzalowitz jayzalowitz merged commit b3131fc into main May 5, 2026
12 checks passed
@jayzalowitz jayzalowitz deleted the jayzalowitz/ux-hardening branch May 5, 2026 21:34
jayzalowitz added a commit that referenced this pull request May 5, 2026
Picks up the P1/P2 findings deferred from #154 (the hardening pass).
6 of the remaining UX review findings closed.

Settings cleanup (P1 #7, #9)
- Theme switcher relocated from page header (where it looked like a
  breadcrumb pill) to a dedicated "Visual theme" card in Settings.
- AI provider section now titled "AI brain — needed for Chat
  (optional otherwise)" instead of just "(optional)" — the Chat
  feature requires it, the old title was misleading.

Chat → Settings deep-link (P1 #9)
- When POST /messages returns 409 "no AI provider configured", the
  chat bubble explains the dependency + footer link to Settings →
  AI brain. Previously the user got "No AI provider configured"
  with no path forward.

Onboarding modal dimmer (P2 #15)
- Bumped overlay rgba(0,0,0,.85) → .92 + backdrop-filter: blur(4px)
  so the sidebar/page behind the modal is properly muted (was
  bleeding through the glass effect at .85).

Console error spam reduction (P2 #20)
- New isApiKnownOffline() in api-client.js. Badge-poll loop in
  app.js backs off 10s → 60s when API is known down. Pre-fix
  produced 110+ console errors/min against a dead server.

Date input theming on Audit (P1 #11)
- New .themed-date class so native date inputs match the dark glass
  aesthetic (background, border, color-scheme for picker icon).
- Webkit calendar-picker-indicator filter inverts the icon glyph
  on dark themes so it's actually visible.

Tests: no new unit tests (browser-only). Backend suite still green
across 40 packages.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants