diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index d956b4dc1..53fe3ea4c 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -797,3 +797,4 @@ This file is append-only. Never rewrite or delete an existing review record; app | 2026-07-25 | codex/search-results-filters-20260725 | 88131e7267efd33059766dec80355a9246fbb2bf | Search result filters and document Sources merge-readiness review | APPROVE. No P0-P2 finding after current-main sync. Documents open Sources as an on-screen filtering surface with source-type controls; the shared results ribbon is applied across search pages. Highest residual risk: unusual real-content combinations may alter perceived density, while responsive, forced-colors, focus, and overflow paths are browser-covered. RAG impact: no retrieval behaviour change - UI controls and source browsing only. | `npm run verify:ui` pass 268/268; `npm run verify:cheap` pass (377 files, 3340 passed, 1 skipped); post-sync `npm run verify:pr-local` pass (378 files, 3349 passed, 1 skipped, production build, bundle-secret scan, offline RAG fixtures); `npm run check:production-readiness` pass with OPENAI_SAFETY_IDENTIFIER_SECRET warning; no live/provider-backed app checks run. | | 2026-07-25 | `codex/therapy-page-polish-ad78b4` | `157559aa0678f02de09c14f66d544b62a5138c4a` | Targeted release review: Therapy naming, centred navigation, and white canvas | APPROVE. No P0-P3 findings. The production Therapy route consistently uses the title Therapy, the shared page background token, and a centred overflow-safe section navigation. The latest `origin/main` merge was clean and retained both upstream responsive/home-composer assertions. Highest residual risk is visual drift at an untested browser engine; exact desktop and phone Chromium measurements were stable. | Focused Vitest 40/40; pre-sync `verify:cheap` 378 files / 3342 passed / 1 skipped; pre-sync `verify:ui` passed; integrated runtime, Prettier, lint, and typecheck passed; integrated Vitest was interrupted by the shared heavyweight-test queue after an independent 378-file / 3342-pass run. Required hosted checks must pass on the published exact head before merge. No clinical/provider workflow ran. | | 2026-07-25 | codex/search-results-filters-20260725 (PR #1184) | 8f74d8bd40810ede34ad4b155973b598c1be0101 | Superseding merge-readiness review after Sources focus repair | APPROVE. Supersedes the 88131e72 row: the automated P2 showed a transient Daily Actions menu item could disconnect before Sources restored focus. Closing Sources now falls back after unmount to the currently rendered action trigger, and the regression requires the visible Documents trigger to own focus. No P0-P2 finding remains. RAG impact: no retrieval behaviour change - UI focus restoration only. | Post-fix isolated production Chromium 1/1; post-current-main local Chromium 1/1; `npm run verify:cheap` pass (378 files, 3350 passed, 1 skipped); targeted Prettier and ESLint pass; required hosted checks must rerun on the published exact head; no live clinical/provider workflow ran. | +| 2026-07-25 | `codex/mobile-search-filter-dropdowns-dcbb32` | `d9f0051ef8335817e7fa29aeb02ee6324331df39` | Release review: responsive search-result filter dropdowns across production modes | APPROVE with verification note. No P0-P2 finding. Phone result-type rails are replaced by page-specific native selects while desktop controls remain intact; shared controls own a real 44px interactive target and retain forced-colors/focus behavior. Highest residual risk is browser-specific native-select rendering outside Chromium. RAG impact: no retrieval behaviour change - result filtering and sort presentation only. | `npm run verify:cheap` pass twice (378 files, 3351 passed, 1 skipped); focused production Chromium routes and accessibility/stress guards pass; production `npm run build` and client-bundle secret scan pass; offline RAG fixtures 36/36 pass. `verify:pr-local` reached unit tests but local infrastructure tests timed out under heavy worktree contention; two remained timeout-only on focused retry. Required hosted checks must pass on the exact published head before merge. No live clinical/provider workflow ran. | diff --git a/src/components/applications-launcher-page.tsx b/src/components/applications-launcher-page.tsx index f7770bc72..009abe5c5 100644 --- a/src/components/applications-launcher-page.tsx +++ b/src/components/applications-launcher-page.tsx @@ -24,7 +24,10 @@ import { import { type FormEvent, useMemo, useState } from "react"; import { ModeHomeHero, ModeHomeVerificationFooter } from "@/components/mode-home-template"; -import { SearchResultsHeaderBand } from "@/components/clinical-dashboard/search-results-header-band"; +import { + MobileResultFilterControl, + SearchResultsHeaderBand, +} from "@/components/clinical-dashboard/search-results-header-band"; import { useSearchCommand } from "@/components/clinical-dashboard/search-command-context"; import { useFavouritesAccess } from "@/components/clinical-dashboard/use-favourites-access"; import { cn, toneInfo, toneSuccess, toneWarning } from "@/components/ui-primitives"; @@ -392,34 +395,15 @@ function FilterTabs({ ); })} -
- {mobileFilters.map((filter) => { - const active = filter.id === activeFilter || (filter.id === "all" && activeFilter === "saved"); - return ( - - ); - })} -
+ ({ value: filter.id, label: filter.label }))} + onChange={onFilterChange} + /> ); } diff --git a/src/components/clinical-dashboard/differentials-home.tsx b/src/components/clinical-dashboard/differentials-home.tsx index 03b353317..246d1cc43 100644 --- a/src/components/clinical-dashboard/differentials-home.tsx +++ b/src/components/clinical-dashboard/differentials-home.tsx @@ -24,7 +24,10 @@ import { } from "lucide-react"; import { ModeHomeTemplate, ModeHomeVerificationFooter } from "@/components/mode-home-template"; -import { SearchResultsHeaderBand } from "@/components/clinical-dashboard/search-results-header-band"; +import { + MobileResultFilterControl, + SearchResultsHeaderBand, +} from "@/components/clinical-dashboard/search-results-header-band"; import { UniversalSearchAlsoMatches } from "@/components/clinical-dashboard/universal-search-also-matches"; import { useDifferentialSearch } from "@/components/clinical-dashboard/use-differential-catalog"; import { useResultSort } from "@/components/use-result-sort"; @@ -908,6 +911,20 @@ function SearchResultsView({ sortValue={sortValue} onSortChange={setSortValue} filterLabel="Filter differential result type" + mobileControls={ + + } filterControls={ onResultTypeChange(tab.key)} className={cn( - "inline-flex min-h-tap shrink-0 items-center gap-1.5 rounded-lg border px-2.5 text-2xs font-bold transition motion-reduce:transition-none sm:min-h-9 sm:text-xs", + "inline-flex min-h-tap shrink-0 items-center gap-1.5 rounded-lg border px-2.5 text-2xs font-bold transition motion-reduce:transition-none sm:text-xs", "focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)]", active ? "border-[color:var(--clinical-accent-border)] bg-[color:var(--clinical-accent-soft)] text-[color:var(--clinical-accent)] shadow-[var(--shadow-inset)]" @@ -903,6 +906,21 @@ function DocumentSearchResultsPanelImpl({ ) : null } filterLabel="Filter documents by source type" + mobileControls={ + showResultsControls && resultTabs.length > 1 ? ( + ({ + value: tab.key, + label: `${tab.label} (${tab.count})`, + }))} + onChange={setActiveResultType} + /> + ) : null + } filterControls={ showResultsControls && resultTabs.length > 1 ? ( ({ + value: filter.id, + label: `${filter.label} (${counts[filter.id]})`, + }))} + onChange={setActiveFilter} + /> + } filterControls={} /> diff --git a/src/components/clinical-dashboard/search-results-header-band.tsx b/src/components/clinical-dashboard/search-results-header-band.tsx index 02fd49504..272d19a7f 100644 --- a/src/components/clinical-dashboard/search-results-header-band.tsx +++ b/src/components/clinical-dashboard/search-results-header-band.tsx @@ -23,6 +23,7 @@ export function SearchResultsHeaderBand({ onSortChange, onSaveSearch, utilityControls, + mobileControls, filterControls, filterLabel = "Filter search results", headingLevel = 2, @@ -39,6 +40,8 @@ export function SearchResultsHeaderBand({ onSaveSearch?: () => void; /** Page-specific actions that belong beside sort/view controls. */ utilityControls?: ReactNode; + /** Compact page-specific controls shown in the utility row below `sm`. */ + mobileControls?: ReactNode; /** Page-specific filters rendered as a full-width row within the shared ribbon. */ filterControls?: ReactNode; filterLabel?: string; @@ -56,7 +59,8 @@ export function SearchResultsHeaderBand({ const displayQuery = query.trim() || "All"; const statusLabel = loading ? "Searching…" : `${matchCount} ${matchCount === 1 ? "match" : "matches"}`; const hasUtilities = - visibleScopes.length > 0 || Boolean(onSortChange || onViewChange || onSaveSearch || utilityControls); + visibleScopes.length > 0 || + Boolean(onSortChange || onViewChange || onSaveSearch || utilityControls || mobileControls); const QueryHeading = headingLevel === 1 ? "h1" : "h2"; return ( @@ -133,14 +137,43 @@ export function SearchResultsHeaderBand({ ))} - {onSortChange ? ( - - ) : null} + {onSortChange && mobileControls ? ( +
+ +
+ {mobileControls} +
+
+ ) : ( + <> + {onSortChange ? ( + + ) : null} + {mobileControls ? ( +
+ {mobileControls} +
+ ) : null} + + )} {utilityControls} {onViewChange ? (
{filterControls}
@@ -225,7 +261,7 @@ export function ResultSortControl({ return (