From 3606707b65a82f5ace23f8a162f9b229f83b7019 Mon Sep 17 00:00:00 2001
From: BigSimmo <87357024+BigSimmo@users.noreply.github.com>
Date: Fri, 14 Aug 2026 22:44:18 +0800
Subject: [PATCH 1/3] feat(auth): refine responsive account setup
---
.../account-setup-dialog.tsx | 252 +++++++++++-------
tests/favourites-auth-gate.dom.test.tsx | 28 +-
tests/ui-smoke.spec.ts | 42 ++-
3 files changed, 198 insertions(+), 124 deletions(-)
diff --git a/src/components/clinical-dashboard/account-setup-dialog.tsx b/src/components/clinical-dashboard/account-setup-dialog.tsx
index 4f004e3533..0a176c50e9 100644
--- a/src/components/clinical-dashboard/account-setup-dialog.tsx
+++ b/src/components/clinical-dashboard/account-setup-dialog.tsx
@@ -1,7 +1,18 @@
"use client";
+import Link from "next/link";
import { type FormEvent, useState } from "react";
-import { Clock3, Heart, Loader2, Mail, ShieldCheck, SlidersHorizontal, type LucideIcon } from "lucide-react";
+import {
+ ArrowRight,
+ Clock3,
+ Heart,
+ Loader2,
+ LockKeyhole,
+ Mail,
+ ShieldCheck,
+ SlidersHorizontal,
+ type LucideIcon,
+} from "lucide-react";
import { BrandMark } from "@/components/clinical-dashboard/brand";
import { ProviderBrandIcon, type SsoProvider } from "@/components/clinical-dashboard/provider-brand-icons";
@@ -10,31 +21,31 @@ import { TextField } from "@/components/ui/text-field";
import { AsyncButton, cn, floatingControl, InlineNotice, primaryControl } from "@/components/ui-primitives";
import { useAuthSession, type OAuthProvider } from "@/lib/supabase/client";
-const storageBenefits = [
+const workspaceBenefits = [
{
- label: "Saved favourites",
- detail: "Available whenever you sign in on another device.",
- scope: "Account",
+ label: "Save favourites",
+ mobileLabel: "Favourites sync",
+ detail: "Reopen trusted resources on any device.",
icon: Heart,
},
{
- label: "Clinical defaults",
- detail: "Your jurisdiction and answer style follow your account.",
- scope: "Account",
+ label: "Keep your clinical defaults",
+ mobileLabel: "Preferences sync",
+ detail: "Your jurisdiction and answer style follow you.",
icon: SlidersHorizontal,
},
{
- label: "Recent searches",
- detail: "Stay in this browser session and do not sync.",
- scope: "This device",
+ label: "Recent searches stay here",
+ mobileLabel: "Searches stay here",
+ detail: "Browser activity does not sync to your account.",
icon: Clock3,
},
] as const;
-type StorageBenefit = {
+type WorkspaceBenefit = {
label: string;
+ mobileLabel: string;
detail: string;
- scope: "Account" | "This device";
icon: LucideIcon;
};
@@ -60,10 +71,10 @@ export function AccountSetupDialog({
const busy = auth.status === "loading";
const actionBusy = busy || pendingProvider !== null;
const isFavouritesIntent = intent === "favourites";
- const title = isFavouritesIntent ? "Sign up to save favourites" : "Set up your workspace";
+ const title = isFavouritesIntent ? "Sign up to save favourites" : "Continue to your workspace";
const description = isFavouritesIntent
? "Sign in or create an account to save favourites and reopen them on any device."
- : "Sign in or create an account to keep favourites and clinical defaults with you.";
+ : "Sign in or create an account in one step.";
const error = actionAttempted ? auth.error : null;
const notice = actionAttempted ? auth.notice : null;
@@ -91,59 +102,61 @@ export function AccountSetupDialog({
-
-
- }
- headerClassName="bg-[color:var(--surface-lux)]"
- titleClassName="text-base sm:text-lg"
- bodyClassName="bg-[color:var(--surface)] p-4 sm:p-6"
- contentClassName="account-setup-dialog max-h-[calc(100dvh-0.5rem)] sm:max-h-[calc(100dvh-2rem)] sm:max-w-[44rem]"
+ headerClassName="absolute right-3 top-3 z-30 w-auto border-0 bg-transparent p-0 sm:right-4 sm:top-4 sm:p-0"
+ titleClassName="sr-only"
+ bodyClassName="bg-[color:var(--surface)] p-0 sm:p-0"
+ contentClassName="account-setup-dialog relative max-h-[calc(100dvh-0.5rem)] sm:max-h-[calc(100dvh-2rem)] sm:max-w-[68rem]"
portal
>
-
+ );
+}
+
+function AccountOrientationPanel() {
+ return (
+
+
+
-
+
+
+
+
-
-
- What’s saved where
-
-
- Account data follows you; recent activity stays private to this browser.
-
-
+ Your workspace, wherever you work.
+
+
-
- {storageBenefits.map((benefit) => (
-
- ))}
-
+
+ {workspaceBenefits.map((benefit) => (
+
+ ))}
+
-
-
-
- No PHI required. Do not enter
- patient-identifying information during sign-in.
-
-
-
-
-
+
+
);
}
-function StorageBenefitRow({ benefit }: { benefit: StorageBenefit }) {
+function WorkspaceBenefitRow({ benefit }: { benefit: WorkspaceBenefit }) {
const Icon = benefit.icon;
return (
-
-
-
+
+
+
- {benefit.label}
- {benefit.detail}
-
-
- {benefit.scope}
+
+ {benefit.mobileLabel}
+
+
+ {benefit.label}
+
+
+ {benefit.detail}
+
);
}
+function PrivacyFooter({ className }: { className?: string }) {
+ return (
+
+
+
+ Do not enter patient-identifiable information.
+
+ Privacy and data processing
+
+
+
+
+ );
+}
+
function ProviderButton({
provider,
busy,
@@ -240,7 +294,7 @@ function ProviderButton({
data-provider={provider.toLowerCase()}
className={cn(
floatingControl,
- "w-full min-w-0 justify-center gap-2.5 bg-[color:var(--surface-lux)] px-3 shadow-[var(--shadow-inset)]",
+ "min-h-tap w-full min-w-0 justify-center gap-2.5 bg-[color:var(--surface-lux)] px-3 shadow-[var(--shadow-inset)]",
)}
>
{pending ? (
diff --git a/tests/favourites-auth-gate.dom.test.tsx b/tests/favourites-auth-gate.dom.test.tsx
index 681453a3cb..0534422e19 100644
--- a/tests/favourites-auth-gate.dom.test.tsx
+++ b/tests/favourites-auth-gate.dom.test.tsx
@@ -154,18 +154,24 @@ describe("favourites auth gate DOM", () => {
expect(screen.getByRole("heading", { name: "Sign up to save favourites" })).toBeVisible();
expect(screen.getByText(/Sign in or create an account to save favourites/i)).toBeVisible();
- expect(screen.getByText("Saved favourites")).toBeVisible();
+ expect(screen.getByText("Save favourites")).toBeVisible();
});
it("separates account-synced data from device-only recents", () => {
render( undefined} />);
- expect(screen.getByRole("heading", { name: "What’s saved where" })).toBeVisible();
- expect(screen.getAllByText("Account")).toHaveLength(2);
- expect(screen.getByText("This device")).toBeVisible();
- expect(screen.getByText(/Recent searches/i)).toBeVisible();
- expect(screen.getByText(/Stay in this browser session and do not sync/i)).toBeVisible();
- expect(screen.getByText(/No PHI required\./i)).toBeVisible();
+ expect(screen.getByRole("heading", { name: "Your workspace, wherever you work." })).toBeVisible();
+ expect(screen.getByText("Save favourites")).toBeVisible();
+ expect(screen.getByText(/Reopen trusted resources on any device/i)).toBeVisible();
+ expect(screen.getByText("Keep your clinical defaults")).toBeVisible();
+ expect(screen.getByText(/Your jurisdiction and answer style follow you/i)).toBeVisible();
+ expect(screen.getByText("Recent searches stay here")).toBeVisible();
+ expect(screen.getByText(/Browser activity does not sync to your account/i)).toBeVisible();
+ expect(screen.getAllByText("Do not enter patient-identifiable information.")).toHaveLength(2);
+ expect(screen.getAllByRole("link", { name: "Privacy and data processing" })).toHaveLength(2);
+ for (const privacyLink of screen.getAllByRole("link", { name: "Privacy and data processing" })) {
+ expect(privacyLink).toHaveAttribute("href", "/privacy");
+ }
expect(screen.queryByText(/Everything syncs across your devices/i)).toBeNull();
expect(screen.queryByText(/never shared/i)).toBeNull();
expect(screen.queryByText("Account-scoped saves")).toBeNull();
@@ -178,7 +184,7 @@ describe("favourites auth gate DOM", () => {
const apple = screen.getByRole("button", { name: "Continue with Apple" });
const google = screen.getByRole("button", { name: "Continue with Google" });
const microsoft = screen.getByRole("button", { name: "Continue with Microsoft" });
- const email = screen.getByLabelText(/Email address/);
+ const email = screen.getByLabelText(/Work email/);
expect(apple.compareDocumentPosition(google) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
expect(google.compareDocumentPosition(microsoft) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
@@ -206,16 +212,16 @@ describe("favourites auth gate DOM", () => {
for (const provider of ["Apple", "Google", "Microsoft"]) {
expect(screen.getByRole("button", { name: `Continue with ${provider}` })).toBeDisabled();
}
- expect(screen.getByRole("button", { name: "Continue with email" })).toBeDisabled();
+ expect(screen.getByRole("button", { name: "Continue securely" })).toBeDisabled();
});
it("submits email and announces success and failure feedback", async () => {
const user = userEvent.setup();
const { rerender } = render( undefined} />);
- const submit = screen.getByRole("button", { name: "Continue with email" });
+ const submit = screen.getByRole("button", { name: "Continue securely" });
expect(submit).toBeDisabled();
- const email = screen.getByLabelText(/Email address/);
+ const email = screen.getByLabelText(/Work email/);
expect(email).toHaveAttribute("data-sheet-autofocus", "true");
await user.type(email, "clinician@clinic.example");
await user.click(submit);
diff --git a/tests/ui-smoke.spec.ts b/tests/ui-smoke.spec.ts
index f574c5100f..e675520546 100644
--- a/tests/ui-smoke.spec.ts
+++ b/tests/ui-smoke.spec.ts
@@ -904,7 +904,7 @@ function accountSettingsDialog(page: Page) {
}
function accountSetupDialog(page: Page) {
- return page.getByRole("dialog", { name: "Set up your workspace" });
+ return page.getByRole("dialog", { name: "Account setup" });
}
async function expectAccountSettingsSurface(settings: Locator) {
@@ -981,19 +981,31 @@ async function expectMobileSettingsLayout(settings: Locator) {
}
async function expectAccountSetupSurface(setup: Locator) {
- await expect(setup.getByRole("heading", { name: "Set up your workspace" })).toBeVisible();
- await expect(setup.getByLabel("Email address")).toBeVisible();
- await expect(setup.getByRole("button", { name: "Continue with email" })).toBeVisible();
+ await expect(setup.getByRole("heading", { name: "Continue to your workspace" })).toBeVisible();
+ await expect(setup.getByRole("heading", { name: "Your workspace, wherever you work." })).toBeVisible();
+ await expect(setup.getByLabel("Work email")).toBeVisible();
+ await expect(setup.getByRole("button", { name: "Continue securely" })).toBeVisible();
await expect(setup.getByRole("button", { name: "Continue with Apple" })).toBeEnabled();
await expect(setup.getByRole("button", { name: "Continue with Google" })).toBeEnabled();
await expect(setup.getByRole("button", { name: "Continue with Microsoft" })).toBeEnabled();
await expect(setup.getByText(/Apple sign-in is not available/i)).toHaveCount(0);
- await expect(setup.getByRole("heading", { name: "What’s saved where" })).toBeVisible();
- await expect(setup.getByText("Account", { exact: true })).toHaveCount(2);
- await expect(setup.getByText("This device", { exact: true })).toBeVisible();
- await expect(setup.getByText(/Stay in this browser session and do not sync/i)).toBeVisible();
- await expect(setup.getByText(/No PHI required\./i)).toBeVisible();
- await expect(setup).toContainText("Do not enter patient-identifying information during sign-in.");
+ const accountSetupViewportWidth = await setup.evaluate(() => window.innerWidth);
+ if (accountSetupViewportWidth >= 1024) {
+ await expect(setup.getByText("Save favourites", { exact: true })).toBeVisible();
+ await expect(setup.getByText(/Reopen trusted resources on any device/i)).toBeVisible();
+ await expect(setup.getByText("Keep your clinical defaults", { exact: true })).toBeVisible();
+ await expect(setup.getByText(/Your jurisdiction and answer style follow you/i)).toBeVisible();
+ await expect(setup.getByText("Recent searches stay here", { exact: true })).toBeVisible();
+ await expect(setup.getByText(/Browser activity does not sync to your account/i)).toBeVisible();
+ } else {
+ await expect(setup.getByText("Favourites sync", { exact: true })).toBeVisible();
+ await expect(setup.getByText("Preferences sync", { exact: true })).toBeVisible();
+ await expect(setup.getByText("Searches stay here", { exact: true })).toBeVisible();
+ }
+ const privacyLink = setup.getByRole("link", { name: "Privacy and data processing" });
+ await expect(privacyLink).toBeVisible();
+ await expect(privacyLink).toHaveAttribute("href", "/privacy");
+ await expect(privacyLink.locator("xpath=..")).toContainText("Do not enter patient-identifiable information.");
}
async function expectAccountProviderLayout(setup: Locator, layout: "row" | "stack") {
@@ -1588,7 +1600,7 @@ test.describe("Clinical KB UI smoke coverage", () => {
await expect(setup).toBeVisible();
await expectAccountSetupSurface(setup);
await expectAccountProviderLayout(setup, "stack");
- await expect(setup.getByLabel("Email address")).toBeFocused();
+ await expect(setup.getByLabel("Work email")).toBeFocused();
const setupBox = await setup.boundingBox();
expect(setupBox).not.toBeNull();
expect(setupBox!.x).toBeGreaterThanOrEqual(-1);
@@ -1597,8 +1609,10 @@ test.describe("Clinical KB UI smoke coverage", () => {
await page.setViewportSize({ width: 320, height: 700 });
const setupClose = setup.getByRole("button", { name: "Close account setup" });
- await expect(setup.getByLabel("Email address")).toBeInViewport();
- await expect(setup.getByRole("button", { name: "Continue with email" })).toBeInViewport();
+ const setupEmail = setup.getByLabel("Work email");
+ await setupEmail.scrollIntoViewIfNeeded();
+ await expect(setupEmail).toBeInViewport();
+ await expect(setup.getByRole("button", { name: "Continue securely" })).toBeInViewport();
await expect(setupClose).toBeInViewport();
await expectNoPageHorizontalOverflow(page);
@@ -1606,7 +1620,7 @@ test.describe("Clinical KB UI smoke coverage", () => {
await setupScrollPort.evaluate((element) => {
element.scrollTop = element.scrollHeight;
});
- await expect(setup.getByText("No PHI required")).toBeInViewport();
+ await expect(setup.getByRole("link", { name: "Privacy and data processing" })).toBeInViewport();
await expect(setupClose).toBeInViewport();
await page.emulateMedia({ reducedMotion: "reduce", forcedColors: "active" });
From e79e5d5900e031bd0e8ad6a47c6a23343411daea Mon Sep 17 00:00:00 2001
From: BigSimmo <87357024+BigSimmo@users.noreply.github.com>
Date: Fri, 14 Aug 2026 22:47:40 +0800
Subject: [PATCH 2/3] chore(review): record account setup review
---
...82663cf239be31aa3a5eb4df2c4e9b49b249ce251af596c2038.record.md | 1 +
1 file changed, 1 insertion(+)
create mode 100644 docs/branch-review-records/9ef3a3c00e3f782663cf239be31aa3a5eb4df2c4e9b49b249ce251af596c2038.record.md
diff --git a/docs/branch-review-records/9ef3a3c00e3f782663cf239be31aa3a5eb4df2c4e9b49b249ce251af596c2038.record.md b/docs/branch-review-records/9ef3a3c00e3f782663cf239be31aa3a5eb4df2c4e9b49b249ce251af596c2038.record.md
new file mode 100644
index 0000000000..5c925c86a1
--- /dev/null
+++ b/docs/branch-review-records/9ef3a3c00e3f782663cf239be31aa3a5eb4df2c4e9b49b249ce251af596c2038.record.md
@@ -0,0 +1 @@
+| 2026-08-14 | codex/account-setup-polish-20260814 | 3606707b65a82f5ace23f8a162f9b229f83b7019 | account setup responsive auth privacy UI | No reproducible P0-P3 findings; local dev-server stale chunk was cleared by repository-safe restart and did not reproduce | live desktop and 390px phone review; axe WCAG A/AA 0 violations; focused DOM 17 passed; focused Chromium desktop and phone passed; typecheck, formatting, production-readiness passed; verify:pr-local timed out after 15 minutes without decisive output |
From 9c3d1b9dbf4484205c0d636337f72b4e9d1bbc4c Mon Sep 17 00:00:00 2001
From: BigSimmo <87357024+BigSimmo@users.noreply.github.com>
Date: Sun, 16 Aug 2026 02:27:11 +0800
Subject: [PATCH 3/3] fix(services): remove quick-search suggestions from
service filters
---
docs/filter-contract.md | 9 +++---
.../services/services-navigator-page.tsx | 29 -------------------
tests/ui-tools.spec.ts | 15 +++-------
3 files changed, 9 insertions(+), 44 deletions(-)
diff --git a/docs/filter-contract.md b/docs/filter-contract.md
index 0469e77946..342de4b41a 100644
--- a/docs/filter-contract.md
+++ b/docs/filter-contract.md
@@ -28,10 +28,11 @@ does today — tells the reader they cannot hold two domains at once, which is f
### There is no `navigate` kind, and that is the point
-Services' quick filters do not filter. They call `router.push` and **replace the query**, so
-choosing one discards the search and its results with no warning and no undo. A control labelled
-"Filter" must not do that. Query-replacing presets belong beside the composer as suggested
-searches (`AnswerSuggestionChips`), not inside the filter sheet.
+Services' former quick filters did not filter. They called `router.push` and **replaced the query**,
+so choosing one discarded the search and its results with no warning and no undo. A control labelled
+"Filter" must not do that. The presets were removed because they also competed visually with the
+service-group navigation; query-replacing suggestions should only return if they have a distinct,
+clearly labelled home beside the composer.
Factsheets' category dimension is not this pattern, despite an earlier draft of this section
grouping it with services' quick filters: `filterFactsheets(query, category)` ANDs the two, so
diff --git a/src/components/services/services-navigator-page.tsx b/src/components/services/services-navigator-page.tsx
index b02e528d70..e6443dfbcf 100644
--- a/src/components/services/services-navigator-page.tsx
+++ b/src/components/services/services-navigator-page.tsx
@@ -13,7 +13,6 @@ import {
SearchResultsSkeleton,
} from "@/components/clinical-dashboard/search-results-header-band";
import { UniversalSearchAlsoMatches } from "@/components/clinical-dashboard/universal-search-also-matches";
-import { AnswerSuggestionChips } from "@/components/clinical-dashboard/answer-suggestion-chips";
import {
ResultFilterSheet,
ResultFilterTrigger,
@@ -55,16 +54,6 @@ import { useRegistryRecords } from "@/lib/use-registry-records";
type ServiceResultScope = "results" | "all";
-const bestFitQuery = "13YARN crisis Aboriginal Torres Strait Islander phone";
-const serviceQuickFilters = [
- { label: "Best fit", query: bestFitQuery },
- { label: "Crisis", query: "crisis" },
- { label: "Culturally safe", query: "Aboriginal Torres Strait Islander" },
- { label: "Phone referral", query: "phone referral" },
- { label: "Free", query: "free" },
- { label: "WA", query: "WA" },
-] as const;
-
function displayText(value: string | null | undefined, fallback = "Confirm locally") {
return value?.trim() ? value.trim() : fallback;
}
@@ -661,24 +650,6 @@ export function ServicesNavigatorPage() {
) : undefined
}
/>
-
- {/* Evicted from the filter sheet: every quick filter called
- `router.push` and replaced the query outright, discarding the
- search and its results with no warning and no undo — exactly
- what docs/filter-contract.md section 1 forbids inside a control
- labelled "Filter". Framed as a new search, which is what
- picking one does. */}
- filter.label)}
- onPick={(label) => {
- const filter = serviceQuickFilters.find((item) => item.label === label);
- if (filter) applyServiceQuery(filter.query);
- }}
- />
>
}
>
diff --git a/tests/ui-tools.spec.ts b/tests/ui-tools.spec.ts
index ee790159a6..8e1cbf323e 100644
--- a/tests/ui-tools.spec.ts
+++ b/tests/ui-tools.spec.ts
@@ -1132,8 +1132,7 @@ test.describe("Clinical KB tools launcher", () => {
const quickFilter = page.getByTestId("service-filter-trigger-phone");
await expect(quickFilter).toBeVisible();
await expect(quickFilter).toHaveAccessibleName(/No filters active/);
- await page.getByTestId("service-quick-search-suggestions").getByRole("button", { name: "Crisis" }).click();
- await expect(page).toHaveURL(/\/services\?.*q=crisis/);
+ await expect(page.getByText("Try a focused search", { exact: true })).toHaveCount(0);
// Phones keep the full search results in the page instead of opening a
// command sheet over the small viewport.
@@ -1330,6 +1329,7 @@ test.describe("Clinical KB tools launcher", () => {
await gotoLauncher(page, "/services?focus=1");
await expect(page.getByTestId("services-home").getByTestId("global-search-input")).toBeVisible();
await expect(page.getByTestId("services-home").getByTestId("global-search-input")).toBeFocused();
+ await expect(page.getByText("Try a focused search", { exact: true })).toHaveCount(0);
await gotoLauncher(page, "/forms?focus=1");
await expect(visibleByTestId(page, "forms-home").getByTestId("global-search-input")).toBeVisible();
@@ -1370,16 +1370,9 @@ test.describe("Clinical KB tools launcher", () => {
await expect(page.getByRole("navigation", { name: "Service groups" })).toBeVisible();
await expect(page.getByTestId("services-shortlist-bar")).toHaveCount(0);
- const culturallySafe = page
- .getByTestId("service-quick-search-suggestions")
- .getByRole("button", { name: "Culturally safe" });
- await expect(culturallySafe).toBeVisible();
- await waitForReactEventHandler(culturallySafe);
- await culturallySafe.click();
- await expect(page).toHaveURL(/q=Aboriginal\+Torres\+Strait\+Islander/);
+ await expect(page.getByText("Try a focused search", { exact: true })).toHaveCount(0);
await expect(page.getByTestId("service-search-result-13yarn")).toBeVisible();
- // Quick search suggestions and facet clearing are separate contracts.
// Exercise a real facet, then clear only that facet while preserving q.
await page.getByTestId("service-filter-trigger-desktop").click();
const filterPanel = page.getByTestId("service-filter-panel");
@@ -1389,7 +1382,7 @@ test.describe("Clinical KB tools launcher", () => {
await crisisFacet.click();
await expect(page).toHaveURL(/acuity_flags=crisis_high/);
await filterPanel.getByTestId("service-filter-panel-clear").click();
- await expect(page).toHaveURL(/q=Aboriginal\+Torres\+Strait\+Islander/);
+ await expect(page).toHaveURL(/q=13YARN/);
await expect(page.getByTestId("service-search-result-13yarn")).toBeVisible();
await filterPanel.getByRole("button", { name: "Close", exact: true }).click();