// surgx-browse-v3.jsx — SurgX Browse / dictionary index, design v3. // Mounts #root; exposes window.SurgxBrowseV3Root. // Blank-over-wrong throughout: every honest-empty is designed, never fabricated. // Reuses the .sxv3 / .sx token root and component classes from surgx-pdp-v3.css. (() => { const { useState, useEffect, useMemo, useRef } = React; const SX_SPECIALTY_FORK = true; /* ---- shared search core (surgx-search-core.js, loaded before this script) ---- */ const { str, normalizeVendor, matchesQuery } = window.SurgxSearchCore; /* ---- tiny helpers (mirror pattern from pdp-v3) ---- */ const initials = n => { const w = String(n || '?').split(/\s+/).filter(Boolean); return ((w[0]?.[0] || '?') + (w[1]?.[0] || '')).toUpperCase(); }; const Icon = ({ d, size = 14 }) => ( ); const ICON = { back: 'M10 13 5 8l5-5', chevDown: 'M4 6l4 4 4-4' }; const Glyph = ({ name, size = 40 }) => (
{initials(name)}
); const Empty = ({ title, detail }) => (
{title}
{detail ?
{detail}
: null}
); /* ---- clearance chip (same 3-state vocabulary as pdp-v3) ---- */ function ClearanceChip({ fda_510k_primary }) { const k = (str(fda_510k_primary).match(/^(K\d{6})$/) || [])[1]; if (k) return FDA 510(k) {k}; return no K-number on record; } /* ---- derive top-20 vendors by count from curated systems list ---- */ function topVendors(systems) { const counts = {}; for (const s of systems) { const v = normalizeVendor(s.vendor); if (v) counts[v] = (counts[v] || 0) + 1; } return Object.entries(counts) .sort((a, b) => b[1] - a[1]) .slice(0, 20) .map(([name, count]) => ({ name, count })); } /* ---- Facet panel (desktop rail list or mobile chip row) ---- */ function FacetPanel({ label, items, value, onChange }) { return (
{label}
{items.map(item => ( ))}
); } /* ---- single browse card ---- */ function BrowseCard({ slug, display_name, vendor, subspecialty, fda_510k_primary, tier }) { const name = display_name || slug; const vendorShort = normalizeVendor(vendor); const subspec = str(subspecialty).slice(0, 60); return (
{name}
{vendorShort ?
{vendorShort}
: null} {subspec ?
{subspec}
: null}
{tier === 'curated' ? curated · grounded : FDA records only}
); } function CompactRow({ slug, display_name, vendor, subspecialty, fda_510k_primary, tier }) { const name = display_name || slug; const vendorShort = normalizeVendor(vendor); const subspec = str(subspecialty).slice(0, 72); return (
{name}
{vendorShort ? {vendorShort} : null} {subspec ? {subspec} : null}
{tier === 'generic' ? FDA records only : null}
); } function GroupedSection({ node, onOpen }) { const ref = useRef(null); const [loaded, setLoaded] = useState(false); const [state, setState] = useState({ loading: false, error: null, data: null }); useEffect(() => { const el = ref.current; if (!el || loaded) return; const load = () => { setLoaded(true); setState({ loading: true, error: null, data: null }); const qs = new URLSearchParams({ specialty: node.slug, page: '1', page_size: '6' }); fetch('/api/r2/systems?' + qs.toString()) .then(r => { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); }) .then(data => setState({ loading: false, error: null, data })) .catch(e => setState({ loading: false, error: e.message, data: null })); }; if (!('IntersectionObserver' in window)) { load(); return; } const io = new IntersectionObserver(entries => { if (entries.some(e => e.isIntersecting)) { io.disconnect(); load(); } }, { rootMargin: '420px 0px' }); io.observe(el); return () => io.disconnect(); }, [loaded, node.slug]); const items = state.data ? (state.data.items || []) : []; const remaining = Math.max(0, node.count - items.length); return (

{node.display}

{node.has_children ?
subspecialty tiers available
: null}
{node.count}
{state.loading ? (
loading section…
) : state.error ? ( ) : items.length ? (
{items.map(s => )}
) : loaded ? ( ) : (
ready to load…
)} {Array.isArray(node.children) && node.children.length ? (
{node.children.map(child => ( ))}
) : null} {items.length ? (
) : null}
); } function PagedRows({ endpoint, emptyTitle }) { const [page, setPage] = useState(1); const [state, setState] = useState({ loading: true, error: null, data: null }); useEffect(() => { setPage(1); }, [endpoint]); useEffect(() => { const sep = endpoint.indexOf('?') === -1 ? '?' : '&'; fetch(endpoint + sep + 'page=' + page + '&page_size=24') .then(r => { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); }) .then(data => setState(prev => ({ loading: false, error: null, data: page === 1 ? data : { ...data, items: [...((prev.data && prev.data.items) || []), ...(data.items || [])] }, }))) .catch(e => setState({ loading: false, error: e.message, data: null })); }, [endpoint, page]); if (state.loading && page === 1) return
loading systems…
; if (state.error) return ; const data = state.data || { items: [], total: 0, has_more: false }; const items = data.items || []; if (!items.length) return ; return ( <>
{items.length} of {data.total} systems
{items.map(s => )}
{data.has_more ? (
) : null} ); } function specialtySystemCount(node) { return Number(node && node.count) || 0; } function vendorRowsFromSystems(systems) { const counts = {}; for (const s of systems || []) { const v = normalizeVendor(s.vendor); if (v) counts[v] = (counts[v] || 0) + 1; } return Object.entries(counts) .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])) .map(([name, count]) => ({ name, count })); } function fetchAllSpecialtySystems(slug) { const items = []; let page = 1; const MAX_PAGES = 60; // hard cap (60*100=6000 systems, far above any specialty) — runaway guard const loadPage = () => { const qs = new URLSearchParams({ tier: 'curated', specialty: slug, page: String(page), page_size: '100', }); return fetch('/api/r2/systems?' + qs.toString()) .then(r => { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); }) .then(data => { const got = data.items || []; items.push(...got); // Termination guards (M3 review): stop on no-more, on an empty page // (zero progress — defends a backend has_more:true bug from infinite-looping), // or at the page cap. page_size=100 is the backend clamp. if (data.has_more && got.length > 0 && page < MAX_PAGES) { page += 1; return loadPage(); } return items; }); }; return loadPage(); } function SpecialtyMoreWaysIn() { return (
MORE WAYS IN
Supplies & item categories
cross-catalogcoming soon
Procedure search
mapping in progresscoming soon
); } function SpecialtyFork({ specialty, node, systemsState, onPickVendor, onPickSearch, onBack }) { const systemCount = specialtySystemCount(node); const vendorCount = systemsState && systemsState.items ? vendorRowsFromSystems(systemsState.items).length : null; const vendorBadge = vendorCount == null ? '...' : vendorCount.toLocaleString(); return ( <>

{specialty}

BROWSE THIS SPECIALTY {systemCount.toLocaleString()} curated
{systemsState && systemsState.error ? (
) : null}
both lead cards reach the same curated systems — pick your entry axis.
); } function SpecialtyVendorBranch({ specialty, systemsState, onBack }) { const [vendorFilter, setVendorFilter] = useState(null); const systems = systemsState && systemsState.items ? systemsState.items : []; const vendors = useMemo(() => vendorRowsFromSystems(systems), [systems]); const vendorSystems = useMemo(() => ( vendorFilter ? systems.filter(s => normalizeVendor(s.vendor) === vendorFilter) : [] ), [systems, vendorFilter]); return ( <>

{vendorFilter || ('Vendors — ' + specialty)}

{systemsState && systemsState.loading ? (
loading vendors...
) : systemsState && systemsState.error ? ( ) : vendorFilter ? ( <>
{vendorSystems.length} systems
{vendorSystems.map(s => )}
) : vendors.length ? (
{vendors.map(v => ( ))}
) : ( )} ); } function SpecialtySearchBranch({ specialty, node, onBack }) { const [q, setQ] = useState(''); const [debouncedQ, setDebouncedQ] = useState(''); const slug = node && node.slug ? node.slug : ''; useEffect(() => { const t = setTimeout(() => setDebouncedQ(q.trim()), 200); return () => clearTimeout(t); }, [q]); const endpoint = useMemo(() => { const params = new URLSearchParams({ tier: 'curated', specialty: slug }); if (debouncedQ) params.set('q', debouncedQ); return '/api/r2/systems?' + params.toString(); }, [slug, debouncedQ]); return ( <>

Search systems

setQ(e.target.value)} placeholder="Search by system name, K-number, or vendor" aria-label="Search by system name, K-number, or vendor" style={{ width: '100%', marginBottom: 12 }} /> ); } function GroupedBrowseV3Root({ initialSearch, initialSpecialty, initialTier }) { const [tree, setTree] = useState(null); const [treeErr, setTreeErr] = useState(null); const [search, setSearch] = useState(initialSearch || ''); const [specialtyFilter, setSpecialtyFilter] = useState(initialSpecialty || null); const [branchMode, setBranchMode] = useState(null); const [specialtySystemsState, setSpecialtySystemsState] = useState({ slug: null, loading: false, error: null, items: null }); const tierFilter = initialTier === 'generic' ? 'generic' : 'curated'; useEffect(() => { fetch('/api/r2/taxonomy/tree?tier=curated') .then(r => { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); }) .then(setTree) .catch(e => setTreeErr(e.message)); }, []); const nodes = useMemo(() => tree && tree.nodes ? tree.nodes : [], [tree]); const specialtyItems = useMemo(() => [ { value: '__all__', label: 'All systems', count: null }, ...nodes.map(n => ({ value: n.slug, label: n.display, count: n.count })), ], [nodes]); const currentNode = nodes.find(n => n.slug === specialtyFilter); const specialtyNode = currentNode || (specialtyFilter ? { slug: specialtyFilter, display: specialtyFilter, count: 0 } : null); const currentSpecialty = specialtyNode ? specialtyNode.display : specialtyFilter; const activeSearch = search.trim(); const searchEndpoint = '/api/r2/search?' + new URLSearchParams({ q: activeSearch, tier: tierFilter }).toString(); const specialtyEndpoint = '/api/r2/systems?' + new URLSearchParams({ tier: 'curated', specialty: specialtyFilter || '' }).toString(); const clearSpecialty = () => { setSpecialtyFilter(null); setBranchMode(null); }; const openSpecialty = slug => { setSpecialtyFilter(slug); setBranchMode(null); }; useEffect(() => { if (!SX_SPECIALTY_FORK || !specialtyFilter) return; let cancelled = false; setSpecialtySystemsState(prev => ({ slug: specialtyFilter, loading: true, error: null, items: prev.slug === specialtyFilter ? prev.items : null, })); fetchAllSpecialtySystems(specialtyFilter) .then(items => { if (!cancelled) setSpecialtySystemsState({ slug: specialtyFilter, loading: false, error: null, items }); }) .catch(e => { if (!cancelled) setSpecialtySystemsState({ slug: specialtyFilter, loading: false, error: e.message, items: null }); }); return () => { cancelled = true; }; }, [specialtyFilter]); return (
SurgX surgical supply, with receipts setSearch(e.target.value)} placeholder="Search systems, vendors, K-numbers…" aria-label="Search systems" />
{treeErr ? : null}
{tree ? ( v === '__all__' ? clearSpecialty() : openSpecialty(v)} /> ) : null}
{activeSearch ? ( <>
relevance-ranked results
) : SX_SPECIALTY_FORK && specialtyFilter && branchMode === null ? ( setBranchMode('vendor')} onPickSearch={() => setBranchMode('search')} onBack={clearSpecialty} /> ) : SX_SPECIALTY_FORK && specialtyFilter && branchMode === 'vendor' ? ( setBranchMode(null)} /> ) : SX_SPECIALTY_FORK && specialtyFilter && branchMode === 'search' ? ( setBranchMode(null)} /> ) : specialtyFilter ? ( <>

{currentNode ? currentNode.display : specialtyFilter}

) : tree ? ( <>
{tree.tiers.curated.toLocaleString()} curated systems · {nodes.length} specialties
{nodes.map(node => ( ))} ) : (
loading topology…
)}
); } /* ---- root component ---- */ function SurgxBrowseV3Root() { /* URL param seeding — initial state, not a useEffect (avoids unfiltered flash) */ const _params = new URLSearchParams(location.search); const _tierParam = _params.get('tier'); /* ACTIVATED 2026-06-11: grouped topology is the default. Rollback hatch: ?group=0 forces the legacy flat list (revert this one line — flip back to === '1' — to dark again). */ if (_params.get('group') !== '0') { return ( ); } /* fetch state */ const [taxonomy, setTaxonomy] = useState(null); const [systems, setSystems] = useState(null); const [genericIndex, setGenericIndex] = useState(null); // null = loading, false = failed/404 const [taxErr, setTaxErr] = useState(null); const [sysErr, setSysErr] = useState(null); /* filter/UI state */ const [search, setSearch] = useState(_params.get('q') || ''); const [specialtyFilter, setSpecialtyFilter] = useState(_params.get('specialty') || null); const [vendorFilter, setVendorFilter] = useState(null); /* ?tier= deep-link (landing receipts cards); only the two known values seed */ const [tierFilter, setTierFilter] = useState( _tierParam === 'curated' || _tierParam === 'generic' ? _tierParam : null ); // null=all, 'curated', 'generic' const [curatedPage, setCuratedPage] = useState(1); const [genericPage, setGenericPage] = useState(1); const [genericOpen, setGenericOpen] = useState(false); /* parallel fetch on mount */ useEffect(() => { fetch('/api/r2/taxonomy') .then(r => { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); }) .then(setTaxonomy) .catch(e => setTaxErr(e.message)); fetch('/api/r2/systems') .then(r => { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); }) .then(setSystems) .catch(e => setSysErr(e.message)); fetch('/api/r2/generic-index') .then(r => { if (!r.ok) return false; return r.json(); }) .then(d => setGenericIndex(d || false)) .catch(() => setGenericIndex(false)); }, []); /* reset pagination when filters/search change */ useEffect(() => { setCuratedPage(1); setGenericPage(1); }, [search, specialtyFilter, vendorFilter, tierFilter]); /* derived vendor list from curated systems */ const vendors = useMemo(() => { if (!systems) return []; return topVendors(systems.items || []); }, [systems]); /* specialty facet items from taxonomy */ const specialtyItems = useMemo(() => { if (!taxonomy) return []; return [ { value: '__all__', label: 'All systems', count: null }, ...(taxonomy.specialties || []).map(s => ({ value: s.slug, label: s.display, count: s.count })), ]; }, [taxonomy]); /* vendor facet items */ const vendorItems = useMemo(() => vendors.map(v => ({ value: v.name, label: v.name, count: v.count })), [vendors]); /* tier facet items */ const tierItems = [ { value: '__all__', label: 'All', count: null }, { value: 'curated', label: 'Curated', count: null }, { value: 'generic', label: 'FDA records only', count: null }, ]; /* filter curated list */ const curatedItems = useMemo(() => { const raw = systems ? (systems.items || []) : []; const q = search.trim().toLowerCase(); return raw.filter(s => { if (specialtyFilter && specialtyFilter !== '__all__' && s.specialty_canonical !== specialtyFilter) return false; if (vendorFilter && normalizeVendor(s.vendor) !== vendorFilter) return false; if (tierFilter === 'generic') return false; if (!matchesQuery(s, q)) return false; return true; }); }, [systems, search, specialtyFilter, vendorFilter, tierFilter]); /* filter generic list */ const genericItems = useMemo(() => { if (!genericIndex || !genericIndex.items) return []; const q = search.trim().toLowerCase(); return genericIndex.items.filter(s => { if (specialtyFilter && specialtyFilter !== '__all__' && s.specialty_canonical !== specialtyFilter) return false; if (tierFilter === 'curated') return false; if (!matchesQuery(s, q)) return false; return true; }); }, [genericIndex, search, specialtyFilter, tierFilter]); /* pagination */ const CURATED_PAGE_SIZE = 60; const GENERIC_PAGE_SIZE = 50; const shownCurated = curatedItems.slice(0, curatedPage * CURATED_PAGE_SIZE); const shownGeneric = genericItems.slice(0, genericPage * GENERIC_PAGE_SIZE); const hasMoreCurated = shownCurated.length < curatedItems.length; const hasMoreGeneric = shownGeneric.length < genericItems.length; /* generic total from index or systems fallback */ const genericTotal = genericIndex && genericIndex.count != null ? genericIndex.count : genericIndex && genericIndex.items ? genericIndex.items.length : 0; /* loading state */ const loading = taxonomy === null || systems === null; if (loading && taxErr === null && sysErr === null) { return (
loading the dictionary…
); } return (
{/* topbar */}
SurgX surgical supply, with receipts setSearch(e.target.value)} placeholder="Search systems, vendors, K-numbers…" aria-label="Search systems" />
{/* per-source fetch errors rendered inline — never a blank page */} {taxErr ? ( ) : null} {sysErr ? (
) : null}
{/* ---- facet rail / mobile chip row ---- */}
{/* SPECIALTY */} {taxonomy && !taxErr ? ( setSpecialtyFilter(v === '__all__' ? null : v)} /> ) : null} {/* VENDOR */} {vendors.length > 0 && tierFilter !== 'generic' ? ( setVendorFilter(v)} /> ) : null} {/* TIER */} setTierFilter(v === '__all__' ? null : v)} /> {/* PROCEDURE — honest non-interactive empty */}
Procedure
{/* ---- main content ---- */}
{/* count bar */}
{!sysErr ? shownCurated.length + ' of ' + curatedItems.length + ' curated' + (genericIndex !== null ? ' · ' + (genericIndex ? shownGeneric.length + ' of ' + genericItems.length + ' generic' : '') : ' · loading generic…') : null}
{/* curated cards */} {!sysErr && curatedItems.length > 0 ? (
{shownCurated.map(s => ( ))}
) : !sysErr && systems ? ( ) : null} {/* show more — curated */} {hasMoreCurated ? (
) : null} {/* generic band */} {genericIndex === false ? ( /* 404 / failed — honest empty, never blank */
) : genericIndex && genericIndex.items && tierFilter !== 'curated' ? (
{genericOpen ? ( <> {vendorFilter ? (
vendor facet applies to curated tier — generic band shows all specialty-matched results
) : null} {genericItems.length > 0 ? (
{shownGeneric.map(s => ( ))}
) : ( )} {hasMoreGeneric ? (
) : null} ) : null}
) : null}
); } window.SurgxBrowseV3Root = SurgxBrowseV3Root; // Mount from inside the transpiled script (same pattern as surgx-pdp-entry.jsx): // Babel-standalone fetches text/babel src scripts asynchronously, so a separate // DOMContentLoaded mount races transpilation and blanks the page. ReactDOM.createRoot(document.getElementById('root')).render(); })();