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 (
}>
+
+
+