diff --git a/data/repo-awareness-snapshot.json b/data/repo-awareness-snapshot.json index aa974f511f..c40604a2c4 100644 --- a/data/repo-awareness-snapshot.json +++ b/data/repo-awareness-snapshot.json @@ -1,7 +1,7 @@ { "version": "repo-awareness-snapshot-v3", "captured_revision": { - "committed_at": "2026-10-11" + "committed_at": "2026-10-12" }, "routes": { "modes": [ @@ -1393,11 +1393,6 @@ "file": "src/app/mockups/search-band-directions/page.tsx", "area": "mockup" }, - { - "path": "/on-call/refer", - "file": "src/app/(search-app)/on-call/refer/page.tsx", - "area": "product" - }, { "path": "/psychiatry", "file": "src/app/(search-app)/psychiatry/page.tsx", @@ -1825,6 +1820,11 @@ "file": "src/app/(search-app)/dsm/page.tsx", "target": "/?mode=dsm" }, + { + "path": "/on-call/refer", + "file": "src/app/(search-app)/on-call/refer/page.tsx", + "target": "/on-call/call?show=refer" + }, { "path": "/specifiers", "file": "src/app/(search-app)/specifiers/page.tsx", diff --git a/docs/on-call/design/mockup-conformance.md b/docs/on-call/design/mockup-conformance.md index 15a705324a..fe1f9a6da8 100644 --- a/docs/on-call/design/mockup-conformance.md +++ b/docs/on-call/design/mockup-conformance.md @@ -77,13 +77,14 @@ gate, and none was visible in the source: | Board | Element | Disposition | Where | Note | | -------------- | ------------------------------------------------------------------------------------- | ----------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 01 Home | Universal header — page-actions ellipsis (hub) | deviation | on-call-page-menu-trigger | Replaced by the signed-in Needs you bell (`needs-you-bell`) before Search my work. Tools moved to More for this shift and in-page More on section pages. Signed-out homes hide the bell. | -| 01 Home | My shifts in More for this shift | built | on-call-now-footer-shifts | Relocated from the hub ellipsis. | -| 01 Home | Pocket card in More for this shift | built | on-call-now-footer-card | Relocated from the hub ellipsis. Also listed in the pages sheet. | -| 01 Home | Calendar in More for this shift | built | on-call-now-footer-calendar | Relocated from the hub ellipsis. | +| 01 Home | My shifts in More for this shift | deviation | on-call-now-footer-shifts | Left Now in the Work mode simplify plan (owner approval 12 Oct 2026): each row lives on the page that owns it (Roster, the Handbook, Starting here). | +| 01 Home | Pocket card in More for this shift | deviation | on-call-now-footer-card | Left Now in the Work mode simplify plan (owner approval 12 Oct 2026): each row lives on the page that owns it (Roster, the Handbook, Starting here). | +| 01 Home | Calendar in More for this shift | deviation | on-call-now-footer-calendar | Left Now in the Work mode simplify plan (owner approval 12 Oct 2026): each row lives on the page that owns it (Roster, the Handbook, Starting here). | | 01 Home | Hospital line — which hospital's numbers are on screen | built | on-call-now-hospital | Added after the drawing (v6 Now, plan C25). Board 01 Home is Now: the safety order leads with the hospital line, before anything else on the page. | | 01 Home | Emergency pin — the hospital's pinned Emergency: route | built | on-call-now-emergency | Added after the drawing (v6 Now, plan C25). Only site-named, clinical Emergency: entries qualify (at most three); with none recorded, one quiet `on-call-now-emergency-not-set-up` row says so (an editor's link opens that hospital in Service; a member is told to ask an editor) and the app never invents a number. Its space is reserved while a previously loaded hospital loads. | | 01 Home | Right now — the dark hero answering who covers this hour | built | on-call-now-right-now | Added after the drawing (v6 figure 03, plan C25). The hospital's switchboard until a hospital sets after-hours times (a Stage B field); "All roles" opens Call. | -| 01 Home | Footer group — Who do I call now, shift lists, Systems down, First night, On site | built | on-call-now-footer | Added after the drawing (v6 Now, plan C25). Shift lists and Systems down moved behind the mode button in the drawing; the page menu has no link slot yet, so they sit here instead. | +| 01 Home | Footer group — Who do I call now, shift lists, Systems down, First night, On site | deviation | on-call-now-footer | Left Now in the Work mode simplify plan (owner approval 12 Oct 2026): each row lives on the page that owns it (Roster, the Handbook, Starting here). | +| 01 Home | Shift tools — Log a call, Handover and the shift lists in one card | built | on-call-now-shift-tools | Work mode simplify plan (owner approval 12 Oct 2026): the shift lists, the one row kept from the footer, share the tools card. | | 01 Home | Section rail across the top | deviation | on-call-home-sections | The rail is gone. It listed the same nine pages the mode pill above it already opens — two controls doing one job — while nothing helped a reader move around the long page in front of them. The second row now navigates WITHIN the current page; the pill navigates between pages. | | 01 Home | Slot counts on the rail (`Contacts 42`) | deviation | — | `ModeNavItem.count` is documented as state, not size — never a catalogue total. There is no rail to carry them now either; the counts appear where the drawing also puts them, on the Home tiles. | | 01 Home | Shift module — site, hours, wards, progress bar | change-b | B: on_call_sites with shift_start, shift_end, wards | | diff --git a/docs/site-map.md b/docs/site-map.md index 54d3d538a9..2b49edc2b3 100644 --- a/docs/site-map.md +++ b/docs/site-map.md @@ -1619,6 +1619,7 @@ This file is generated by `npm run docs:update` (or `npm run sitemap:update` dir - `/dictionary/browse` - Redirects to `/dictionary/search`. Source: `src/app/(search-app)/dictionary/browse/page.tsx`. - `/sources` - Redirects to `/?mode=sources`. Source: `src/app/(search-app)/sources/page.tsx`. - `/dsm` - Redirects to `/?mode=dsm`. Source: `src/app/(search-app)/dsm/page.tsx`. +- `/on-call/refer` - Redirects to `/on-call/call?show=refer`. Source: `src/app/(search-app)/on-call/refer/page.tsx`. - `/specifiers` - Redirects to `/?mode=specifiers`. Source: `src/app/(search-app)/specifiers/page.tsx`. - `/services` - Redirects to `/?mode=services`. Source: `src/app/(search-app)/services/page.tsx`. - `/my-day` - Redirects to `/my-day/notifications`. Source: `src/app/(search-app)/my-day/page.tsx`. diff --git a/src/app/(search-app)/on-call/refer/page.tsx b/src/app/(search-app)/on-call/refer/page.tsx index eb8da0faac..22f05933de 100644 --- a/src/app/(search-app)/on-call/refer/page.tsx +++ b/src/app/(search-app)/on-call/refer/page.tsx @@ -1,12 +1,18 @@ import type { Metadata } from "next"; +import { redirect } from "next/navigation"; import { OnCallReferPage } from "@/components/on-call/refer/refer-page"; +import { getWorkModeLaunch } from "@/lib/work-mode-launch/server"; export const metadata: Metadata = { title: "Refer | On Call | PsychSift", description: "How to refer to each service at your hospital, and your own referral notes.", }; -export default function OnCallReferRoute() { +// Refer is a tab of Contacts in the current work mode (Work mode simplify plan, owner approval +// 12 Oct 2026), so one search covers numbers and referrals. The classic work mode (the rollback) +// keeps its own Refer page. +export default async function OnCallReferRoute() { + if ((await getWorkModeLaunch()).newWorkMode) redirect("/on-call/call?show=refer"); return ; } diff --git a/src/components/on-call/call/call-page.tsx b/src/components/on-call/call/call-page.tsx index 20edbc365f..1504274ac5 100644 --- a/src/components/on-call/call/call-page.tsx +++ b/src/components/on-call/call/call-page.tsx @@ -2,11 +2,11 @@ import { currentCover } from "@/lib/on-call/service-availability"; import { useHospitalClock } from "@/components/on-call/use-hospital-clock"; -import { ListChecks, Phone, Plus, Printer, Shield, Users } from "lucide-react"; +import { ListChecks, Phone, Plus, Shield } from "lucide-react"; import dynamic from "next/dynamic"; import Link from "next/link"; -import { useRouter } from "next/navigation"; -import { useEffect, useMemo, useState, useSyncExternalStore, type ReactNode } from "react"; +import { useRouter, useSearchParams } from "next/navigation"; +import { Suspense, useCallback, useEffect, useMemo, useState, useSyncExternalStore, type ReactNode } from "react"; import { useOptionalAccountData } from "@/components/account-data-provider"; import { focusRing } from "@/components/card-recipes"; @@ -38,11 +38,12 @@ import { modeInsetHairline, modeRowHeight } from "@/components/mode-kit/recipes" import { modeSecondaryText } from "@/components/mode-kit/type"; import { onCallGroupAnchorId } from "@/components/on-call/on-call-page-anchors"; import { useHospitalHandbook, type HospitalHandbookState } from "@/components/on-call/use-hospital-handbook"; +import { OnCallReferSections, onCallReferralMatchCount } from "@/components/on-call/refer/refer-page"; import { SegmentedControl } from "@/components/ui/segmented-control"; import { SearchField } from "@/components/ui/text-field"; import { cn } from "@/components/ui-primitives"; import { RosterWhosOnEntryLink } from "@/components/on-call/roster-whos-on/roster-whos-on-entry-link"; -import { NewWorkModeOnly } from "@/components/work-mode-launch/work-mode-launch-provider"; +import { NewWorkModeOnly, useNewWorkMode } from "@/components/work-mode-launch/work-mode-launch-provider"; import { onCallDetailsSchemaFor, type OnCallEntry } from "@/lib/on-call/entry-model"; import { searchOnCallEntries } from "@/lib/on-call/entry-search"; import { cacheOnCallEntries, useOnCallEntries } from "@/lib/on-call/entry-store"; @@ -71,14 +72,33 @@ export const ON_CALL_OLD_HANDOVER_HASH = "#on-call-handover-heading"; const noSubscription = () => () => {}; -type PeopleTab = "hospital" | "outside" | "mine"; +type PeopleTab = "hospital" | "refer" | "outside" | "mine"; +// Contacts holds Refer as a tab (Work mode simplify plan, owner approval 12 Oct 2026): one place for +// "who and where", with one search box across numbers and referrals. Short labels fit four at 320px. const PEOPLE_TABS: readonly { value: PeopleTab; label: string }[] = [ { value: "hospital", label: "Hospital" }, - { value: "outside", label: "Outside lines" }, + { value: "refer", label: "Refer" }, + { value: "outside", label: "Outside" }, { value: "mine", label: "Mine" }, ]; +/** + * Follows `?show=` whenever the address changes, including a redirect from the old Refer page onto an already + * mounted Contacts page. Its own Suspense boundary keeps the rest of the page server-rendered. + */ +function PeopleTabFromAddress({ onAsked }: { readonly onAsked: (asked: string | null) => void }) { + const asked = useSearchParams()?.get("show") ?? null; + useEffect(() => onAsked(asked), [asked, onAsked]); + return null; +} + +/** The tab an address asks for: `/on-call/call?show=refer` opens Refer (the old Refer page lands there). */ +function peopleTabFor(asked: string | null, withRefer = true): PeopleTab { + if (asked === "refer" && !withRefer) return "hospital"; + return PEOPLE_TABS.some((option) => option.value === asked) ? (asked as PeopleTab) : "hospital"; +} + function hospitalName(handbook: HospitalHandbookState): string | null { return handbook.siteName ?? handbook.serviceName; } @@ -264,7 +284,25 @@ export function OnCallCallPage() { const entries = useOnCallEntries(); const hospitalPhone = useOnCallHospitalPhone(); const [query, setQuery] = useState(""); - const [tab, setTab] = useState("hospital"); + // Refer inside Contacts is a new work mode screen; classic readers keep the standalone Refer page. + const withRefer = useNewWorkMode(); + const peopleTabs = withRefer ? PEOPLE_TABS : PEOPLE_TABS.filter((option) => option.value !== "refer"); + const [tab, setTabState] = useState("hospital"); + const followAddress = useCallback((asked: string | null) => setTabState(peopleTabFor(asked, withRefer)), [withRefer]); + // Keep the address in step with the chosen tab, so a refresh reopens the same one. + const setTab = useCallback( + (next: PeopleTab) => { + setTabState(next); + const params = new URLSearchParams(window.location.search); + if (next === "hospital") params.delete("show"); + else params.set("show", next); + const search = params.toString(); + router.replace(`${window.location.pathname}${search ? `?${search}` : ""}${window.location.hash}`, { + scroll: false, + }); + }, + [router], + ); const [adding, setAdding] = useState(false); const [clock, setClock] = useState(() => new Date()); // The time in "Cover as of 21:40" is drawn after hydration only, so the server's minute never mismatches the phone's. @@ -341,14 +379,22 @@ export function OnCallCallPage() { !entries.loading && !signedOut && !entries.sample && entries.loadError === null && checkQueue.assessed > 0; const hospitalCount = groups.reduce((sum, group) => sum + group.items.length, 0); - const resultCount = cover.length + hospitalCount + external.length + (signedOut ? 0 : mine.length); + // Referral routes are searched here too (Refer lives inside Contacts), so they count towards the result. + const referralCount = onCallReferralMatchCount({ + items: ready && withRefer ? handbook.items : [], + entries: withRefer ? entries.entries : [], + query, + signedOut, + }); + const resultCount = cover.length + hospitalCount + external.length + (signedOut ? 0 : mine.length) + referralCount; const showHospital = searching || tab === "hospital"; const showOutside = searching || tab === "outside"; const showMine = searching ? !signedOut && mine.length > 0 : tab === "mine"; // The crisis lines show whenever the hospital's numbers are not on screen, // unless the Outside lines list (which holds them) already is. - const showCrisis = !showOutside && (!ready || tab === "mine" || hospitalCount + cover.length === 0); + const showCrisis = + !showOutside && (!ready || tab === "mine" || tab === "refer" || hospitalCount + cover.length === 0); const canAdd = Boolean(account?.isAuthenticated) && !signedOut && !entries.demoMode; @@ -405,11 +451,14 @@ export function OnCallCallPage() { return ( }> + + +
setQuery(event.target.value)} onClear={() => setQuery("")} @@ -426,7 +475,7 @@ export function OnCallCallPage() { layout="equal" value={tab} onChange={setTab} - options={PEOPLE_TABS} + options={peopleTabs} ariaControls="on-call-people-list" /> )} @@ -549,6 +598,8 @@ export function OnCallCallPage() { ) : null} + + {withRefer && (searching || tab === "refer") ? : null}
{searching ? null : ( @@ -559,20 +610,6 @@ export function OnCallCallPage() {
    -