Skip to content

feat(scanner): desktop UX polish round 2 — 10 improvements#951

Merged
ericsocrat merged 4 commits intomainfrom
feat/scanner-ux-polish-r2
Mar 18, 2026
Merged

feat(scanner): desktop UX polish round 2 — 10 improvements#951
ericsocrat merged 4 commits intomainfrom
feat/scanner-ux-polish-r2

Conversation

@ericsocrat
Copy link
Owner

Summary

Implements all 10 recommendations (R1–R10) from the desktop UX audit conducted against the live production site (tryvit.vercel.app). These are targeted polish improvements to the scanner flow — camera page, not-found result view, and product submission form.

Changes by Recommendation

Scanner Page (scan/page.tsx)

  • R1 — Timeout guard on feedActive: The 15-second timeout banner now only fires after the camera feed is confirmed active (feedActive === true), preventing false "camera not responding" warnings during initial camera negotiation.
  • R5 — Camera hint priority: The "hold steady" hint paragraph now renders above the timeout banner, ensuring the helpful hint is seen first before any error messaging.

Scan Result — Not Found (ScanResultView.tsx)

  • R2 — Centered content on wide viewports: ScanNotFoundView wrapped in max-w-md mx-auto to prevent the content from stretching across the full width on desktop.
  • R6 — PackageSearch icon: Replaced generic Search icon with PackageSearch from lucide-react for better semantic match (product not found ≠ search).
  • R7 — GS1 coverage note: Added explanatory text below the GS1 country hint: "Coverage varies by region — the product may still exist in another database."

Submit Page (submit/page.tsx)

  • R3 — Country explainer: Added hint text below the country badge: "Based on your profile or the barcode's GS1 prefix."
  • R4 — Optional field labels: Brand, Category, Photo, and Notes labels now show "(Optional)" suffix via t("common.optional").
  • R8 — Notes character counter: Live {n}/500 counter below the notes textarea.
  • R9 — Enlarged photo drop zone: Upload area enlarged with py-8 padding and centered flex layout for easier target acquisition.
  • R10 — Step indicator: "Step 2 of 2 — Add product details" shown below the page subtitle for orientation.

i18n

  • 4 new keys added across all 3 locales (en.json, de.json, pl.json):
    • common.optional — "(Optional)" / "(Optional)" / "(Opcjonalne)"
    • scan.gs1CoverageNote — regional coverage explanation
    • submit.countryExplainer — country selection explanation
    • submit.stepIndicator — step 2 of 2 indicator

Docs

  • docs/DESIGN_REFRESH_SPEC.md — Audit planning artifact documenting the broader design refresh spec.

Test Coverage

New tests (7):

  • R1: Timeout test updated — simulates feedActive=true (video readyState=3, videoWidth=640, playing event) before asserting setTimeout(fn, 15_000)
  • R3: Country explainer text visible below country badge
  • R4: All 4 optional labels present (getAllByText("(Optional)") ≥ 4)
  • R7: GS1 coverage note appears with recognized EAN prefix
  • R7: GS1 coverage note absent with unrecognized EAN prefix
  • R8: Character counter shows "0/500" initially, "5/500" after typing
  • R10: Step indicator text "Step 2 of 2" visible

Updated tests (8 selectors):

  • All getByLabelText("Brand") / getByLabelText("Category") / getByLabelText("Notes") updated to regex (/^Brand/, /^Category/, /^Notes/) to accommodate the new "(Optional)" suffix.

Verification

npx tsc --noEmit           → 0 errors
npx vitest run             → 5804 passed, 0 failed (349 files, 196.92s)

Files Changed

10 files changed, +820 / -26 lines

  • 3 source components modified (scan/page.tsx, submit/page.tsx, ScanResultView.tsx)
  • 3 i18n dictionaries updated (en.json, de.json, pl.json)
  • 3 test files updated (page.test.tsx ×2, ScanResultView.test.tsx)
  • 1 new doc (DESIGN_REFRESH_SPEC.md)

Copilot AI review requested due to automatic review settings March 18, 2026 13:25
@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@vercel
Copy link

vercel bot commented Mar 18, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tryvit Ready Ready Preview, Comment Mar 18, 2026 2:25pm

Scanner page:
- R1: timeout banner only fires after camera feed is confirmed active
- R5: camera hint renders above timeout banner for visual priority

Scan result (not found):
- R2: max-w-md wrapper centers content on wide viewports
- R6: PackageSearch icon replaces generic Search icon
- R7: GS1 coverage note explains regional database gaps

Submit page:
- R3: country explainer text below country badge
- R4: optional labels on Brand, Category, Photo, Notes fields
- R8: character counter (0/500) on notes textarea
- R9: enlarged photo upload drop zone with centered layout
- R10: step indicator (Step 2 of 2) below page subtitle

i18n: 4 new keys added to en/de/pl dictionaries
Tests: 7 new tests + 8 label selectors updated for optional suffix
Docs: DESIGN_REFRESH_SPEC.md added (audit planning artifact)
@github-actions
Copy link

github-actions bot commented Mar 18, 2026

Bundle Size Report

Metric Value
Main baseline 0 KB
This PR 0 KB
Delta +0 KB (+0%)
JS chunks 0
Hard limit 4000 KB

✅ Bundle size is within acceptable limits.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Polishes the desktop scanner UX across the scan page, not-found scan result, and product submission flow, and adds supporting i18n keys + test updates.

Changes:

  • Refines scanner timeout behavior (only starts once the camera feed is active) and adjusts hint ordering.
  • Improves not-found view desktop layout/iconography and adds a GS1 coverage note.
  • Enhances submit form UX (optional markers, country explainer, notes counter, larger photo drop zone, step indicator) with updated tests and locale strings.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
frontend/src/components/scan/ScanResultView.tsx Desktop layout constraint for not-found view; icon swap; GS1 coverage note rendering.
frontend/src/components/scan/ScanResultView.test.tsx Adds assertions for GS1 coverage note presence/absence.
frontend/src/app/app/scan/submit/page.tsx Adds step indicator, optional labels, country explainer, larger photo drop zone, notes counter.
frontend/src/app/app/scan/submit/page.test.tsx Updates selectors for optional suffix; adds tests for new submit-page UX elements.
frontend/src/app/app/scan/page.tsx Gates 15s timeout on feedActive; reorders camera hint vs timeout banner.
frontend/src/app/app/scan/page.test.tsx Updates timeout test to simulate feedActive=true before asserting timer registration.
frontend/messages/en.json Adds common.optional, scan.gs1CoverageNote, submit.countryExplainer, submit.stepIndicator.
frontend/messages/de.json Same new keys for DE locale.
frontend/messages/pl.json Same new keys for PL locale.
docs/DESIGN_REFRESH_SPEC.md Adds design/UX refresh planning spec document.

Comment on lines +481 to +486
- `common.retry` — Shared button label

**Rules:**
- 3-segment keys minimum. Never `retry` alone — always `common.retry`.
- Boolean conditions use `{key}Yes` / `{key}No` suffix, not separate keys.
- Counts use ICU `{count, plural, one {# product} other {# products}}` (via `next-intl` or manual interpolation).
Comment on lines 273 to 303
<label
htmlFor="photo"
className="mb-1 block text-sm font-medium text-foreground-secondary"
>
{t("submit.photoLabel")}
{t("submit.photoLabel")}{" "}
<span className="font-normal text-foreground-muted">{t("common.optional")}</span>
</label>
{photoPreview && photoPreview.startsWith("blob:") ? (
<div className="relative inline-block">
<img
src={photoPreview}
alt=""
className="h-32 w-32 rounded-lg border border-border object-cover"
/>
<button
type="button"
onClick={removePhoto}
className="absolute -right-2 -top-2 rounded-full bg-red-500 p-0.5 text-white shadow-sm hover:bg-red-600"
aria-label={t("submit.photoRemove")}
>
<X size={14} />
</button>
</div>
) : (
<label
htmlFor="photo"
className="flex cursor-pointer items-center gap-2 rounded-lg border border-dashed border-border px-4 py-3 text-sm text-foreground-secondary hover:border-brand hover:text-brand"
className="flex cursor-pointer flex-col items-center justify-center gap-2 rounded-lg border border-dashed border-border px-4 py-8 text-sm text-foreground-secondary hover:border-brand hover:text-brand"
>
<Camera size={18} aria-hidden="true" />
<Camera size={24} aria-hidden="true" />
{t("submit.photoHint")}
</label>
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Desktop UX polish pass for the scanner flow (camera scan page, “not found” results, and product submission), plus supporting i18n/test updates and an added design refresh spec document.

Changes:

  • Adjusts scanner timeout behavior to only start after the camera feed is confirmed active, and tweaks hint/banner ordering on the scan page.
  • Improves “not found” result layout on desktop and adds a GS1 coverage explanatory note (with tests).
  • Polishes the submit flow UI (optional field indicators, country explainer, notes counter, photo dropzone sizing, step indicator) with updated translations and tests.

Reviewed changes

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

Show a summary per file
File Description
frontend/src/components/scan/ScanResultView.tsx Centers not-found content on desktop, swaps icon, adds GS1 coverage note copy.
frontend/src/components/scan/ScanResultView.test.tsx Adds assertions for GS1 coverage note presence/absence.
frontend/src/app/app/scan/submit/page.tsx Adds step indicator, optional field suffixes, country explainer, notes counter, and larger photo dropzone.
frontend/src/app/app/scan/submit/page.test.tsx Updates label selectors and adds tests for new submit-page UX elements.
frontend/src/app/app/scan/page.tsx Gates timeout on feedActive and reorders camera hint vs timeout banner.
frontend/src/app/app/scan/page.test.tsx Updates timeout test to simulate feedActive=true before asserting timer registration.
frontend/messages/en.json Adds new i18n keys for optional label, GS1 coverage note, country explainer, step indicator.
frontend/messages/de.json Same i18n additions for German locale.
frontend/messages/pl.json Same i18n additions for Polish locale.
frontend/e2e/auth.setup.ts Raises waitForURL timeout to reduce CI flakiness; minor import formatting change.
docs/INDEX.md Adds the new design refresh spec to the docs index.
docs/DESIGN_REFRESH_SPEC.md New design/UX planning spec document for the broader refresh effort.

Comment on lines +471 to +486
All user-facing strings live in `/messages/{locale}.json`. Keys follow:

```
{domain}.{subject}.{action/descriptor}
```

Examples:
- `product.score.excellent` — Score band label
- `scanner.error.cameraBlocked` — Scanner error message
- `onboarding.step.region` — Onboarding step title
- `common.retry` — Shared button label

**Rules:**
- 3-segment keys minimum. Never `retry` alone — always `common.retry`.
- Boolean conditions use `{key}Yes` / `{key}No` suffix, not separate keys.
- Counts use ICU `{count, plural, one {# product} other {# products}}` (via `next-intl` or manual interpolation).
Comment on lines +7 to 12
import { expect, test as setup } from "@playwright/test";
import {
TEST_EMAIL,
TEST_PASSWORD,
ensureTestUser,
TEST_EMAIL,
TEST_PASSWORD,
ensureTestUser,
} from "./helpers/test-user";
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Implements a second round of desktop UX polish for the scanner flow (camera page, not-found result view, and product submission form), along with i18n updates, test adjustments, and supporting documentation.

Changes:

  • Scanner page: gate the 15s timeout banner behind feedActive, and reorder the camera hint above the timeout banner.
  • Not-found + submit UX: improve desktop layout/semantics (centering, icon swap, GS1 coverage note) and add submission form affordances (optional labels, country explainer, notes counter, step indicator, larger photo dropzone).
  • Localization + tests + docs: add new i18n keys across locales, update/add unit tests, and add/index a design refresh spec document.

Reviewed changes

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

Show a summary per file
File Description
frontend/src/components/scan/ScanResultView.tsx Centers not-found view on desktop, swaps icon, adds GS1 coverage note text
frontend/src/components/scan/ScanResultView.test.tsx Adds tests for GS1 coverage note presence/absence
frontend/src/app/app/scan/submit/page.tsx Adds step indicator, optional markers, country explainer, notes counter, and larger upload drop zone
frontend/src/app/app/scan/submit/page.test.tsx Updates label selectors for optional suffix + adds tests for new submit-page UI elements
frontend/src/app/app/scan/page.tsx Starts scan-timeout timer only after feedActive; reorders camera hint above timeout messaging
frontend/src/app/app/scan/page.test.tsx Updates timeout test to simulate feedActive=true before asserting timeout registration
frontend/messages/en.json Adds new keys: common.optional, scan.gs1CoverageNote, submit.countryExplainer, submit.stepIndicator
frontend/messages/de.json Adds same 4 i18n keys in German
frontend/messages/pl.json Adds same 4 i18n keys in Polish
frontend/e2e/auth.setup.ts Increases auth setup robustness via longer timeouts (and minor refactor)
docs/INDEX.md Adds the new design refresh spec to docs index
docs/DESIGN_REFRESH_SPEC.md Adds a design/UX refresh spec document for future planning

Comment on lines +484 to +486
- 3-segment keys minimum. Never `retry` alone — always `common.retry`.
- Boolean conditions use `{key}Yes` / `{key}No` suffix, not separate keys.
- Counts use ICU `{count, plural, one {# product} other {# products}}` (via `next-intl` or manual interpolation).
Comment on lines +9 to +11
TEST_EMAIL,
TEST_PASSWORD,
ensureTestUser,
Build/seed steps use staging fallback (SUPABASE_URL_STAGING ||
NEXT_PUBLIC_SUPABASE_URL) but Mobile/Desktop Audit steps used only
production secrets. When staging secrets exist, ensureTestUser()
created the user on production while the app was built against
staging — login silently failed because the user didn't exist on
the staging Supabase instance.

Fix: use the same staging-first fallback pattern in audit steps.
@ericsocrat ericsocrat merged commit 25acdf7 into main Mar 18, 2026
17 checks passed
@ericsocrat ericsocrat deleted the feat/scanner-ux-polish-r2 branch March 18, 2026 14:32
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