"use client"; import { useEffect, useState } from "react"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { ALL_SITES, resolveActiveSiteFrom, siteIdFromPathname, withoutSiteParam, } from "../lib/activeSite"; import { useSiteScope } from "./SiteScopeProvider"; export type SiteScopeOption = { siteId: string; siteTitle: string }; // The single global site selector shown in the sidebar. The stored selection is // a cookie, read by the root layout and supplied by SiteScopeProvider, so this // renders the right value on the FIRST paint, on the server and on every // navigation — it has no effect that rewrites the URL and reads no storage. See // app/lib/activeSite.ts. // // What it shows, first match wins: the choice just made on this URL (until the // navigation it starts lands), the site a /sites//… path names, a valid // `?site=` on the URL (a link's scope for that page), the stored selection. export function SiteScopeSelect({ sites }: { sites: SiteScopeOption[] }) { const [hydrated, setHydrated] = useState(false); useEffect(() => setHydrated(true), []); const router = useRouter(); const pathname = usePathname(); const searchParams = useSearchParams(); const { siteIds, stored, choose } = useSiteScope(); const search = searchParams.toString(); const param = searchParams.get("site"); const onSite = siteIdFromPathname(pathname); // A controlled void onChange(e.target.value)} aria-label="Active site" // A TESTING AFFORDANCE, as on the operation lanes: a change that lands // before React hydrates is lost (the server-rendered value already // reads right), so a spec waits for this before it picks a site. data-hydrated={hydrated ? "true" : undefined} className="rounded border border-border bg-card px-2 py-1 text-sm" > {multi && } {sites.map((s) => ( ))} ); }