// GUDID match approval view - Phase B.2.6 UI surface.
const { useEffect: useEffectGudid, useMemo: useMemoGudid, useState: useStateGudid, useRef: useRefGudid } = React;
const ADD_TO_LIBRARY_TOOLTIP = "GUDID covers about 30% of OR supplies. Items here are normal - flag them to expand the catalog.";
const GUDID_EMBEDDED_APP_SHELL = new URLSearchParams(window.location.search).get("embed") === "shell";
// Live card header helpers:
function firstLiveCardText(...values) {
for (const value of values) {
const text = String(value || "").replace(/\s+/g, " ").trim();
if (text) return text;
}
return "";
}
function cardWithLiveHeader(card = {}) {
const live = window.PREFCARD_LIVE_CARD || {};
return {
...card,
name: firstLiveCardText(live.procedure_name, card.name),
maintainer: firstLiveCardText(live.surgeon_name, card.maintainer, "Maintainer unknown"),
maintainerRole: firstLiveCardText(live.surgeon_specialty, live.surgeon_title, card.maintainerRole),
maintainerInst: firstLiveCardText(live.facility_name, card.maintainerInst),
lastEdited: firstLiveCardText(String(live.last_updated || "").slice(0, 10), card.lastEdited),
};
}
// Live card header helpers end.
const MATCH_COPY = {
block: {
label: "Add to library",
detail: "Catalog expansion needed",
fg: "var(--ss-match-reject)",
bg: "var(--ss-match-reject-bg)",
bd: "#DAB5AC",
icon: "alert",
},
reject: {
label: "Reject",
detail: "Manual catalog entry required",
fg: "var(--ss-match-reject)",
bg: "var(--ss-match-reject-bg)",
bd: "#DAB5AC",
icon: "alert",
},
no_match: {
label: "No match",
detail: "Add to library",
fg: "var(--ss-match-nomatch)",
bg: "var(--ss-match-nomatch-bg)",
bd: "#D6D0C5",
icon: "x",
},
review: {
label: "Amber review",
detail: "Verify before save",
fg: "var(--ss-match-review)",
bg: "var(--ss-match-review-bg)",
bd: "#DEC2B2",
icon: "review",
},
auto: {
label: "Confirm badge",
detail: "Auto-accept",
fg: "var(--ss-match-auto)",
bg: "var(--ss-match-auto-bg)",
bd: "#B9D0BE",
icon: "check",
},
auto_pending: {
label: "Confirm badge",
detail: "Auto-pending UI confirm",
fg: "var(--ss-match-auto)",
bg: "var(--ss-match-auto-bg)",
bd: "#B9D0BE",
icon: "check",
},
verified: {
label: "GUDID verified",
detail: "High confidence match",
fg: "var(--ss-match-verified)",
bg: "var(--ss-match-verified-bg)",
bd: "#B9D0BE",
icon: "check",
},
};
const GUDID_ROOM_DEFAULT_RULES = [
{
id: "esu",
label: "ESU",
defaultLabel: "ESU assumed in room",
accessoryPattern: /\b(bovie|cautery|electrosurgical)\b.*\b(pencil|tip|cord|pad|blade)\b|\b(pencil|tip)\b.*\b(bovie|cautery|electrosurgical)\b/i,
equipmentPattern: /\b(esu|electrosurgical unit|cautery generator|bovie generator|valleylab|force triad)\b/i,
targetLabels: ["boom", "equipment cart", "Bovie tower"],
bodyTemplate: (sourceName) =>
`This item (${sourceName}) is an ESU accessory, but no electrosurgical unit is listed on the card. The ESU is assumed to be a room default.`,
},
{
id: "suction",
label: "Suction",
defaultLabel: "Suction assumed in room",
accessoryPattern: /\b(yankauer|poole|suction tubing|suction tip|suction canister|suction liner)\b/i,
equipmentPattern: /\b(suction machine|suction pump|suction tower|suction regulator|wall suction)\b/i,
targetLabels: ["boom", "equipment cart", "suction tower"],
bodyTemplate: (sourceName) =>
`This item (${sourceName}) is a suction accessory, but no suction equipment is listed on the card. Suction is assumed to be a room default.`,
},
];
function itemSearchText(item) {
return [item?.item_name, item?.name, item?.raw_text].filter(Boolean).join(" ").toLowerCase();
}
function itemDisplayName(item) {
return item?.item_name || item?.name || item?.raw_text || "this item";
}
function itemRoomDefault(item, cardItems) {
if (!item || !Array.isArray(cardItems)) return null;
const itemText = itemSearchText(item);
for (const rule of GUDID_ROOM_DEFAULT_RULES) {
if (!rule.accessoryPattern.test(itemText)) continue;
const hasExplicitEquipment = cardItems.some((other) =>
other !== item && rule.equipmentPattern.test(itemSearchText(other))
);
if (hasExplicitEquipment) continue;
const sourceName = itemDisplayName(item);
return { rule, sourceName, body: rule.bodyTemplate(sourceName) };
}
return null;
}
function getGudidBand(item) {
const match = item.gudid_match;
if (!match) return "no_match";
// Scanner-bound items have gudid_match populated from FDA GUDID lookup but
// never ran through the text-matcher, so confidence is null. Trust the
// barcode binding (user explicitly scanned) and treat as verified. Anchored
// in Ada's PR #1164 edge-case 2026-05-22T~17:35Z.
if (match.match_confidence == null) return "verified";
if (match.band) return match.band;
if (match.match_confidence < 0.5) return "block";
if (match.match_confidence < 0.85) return "review";
if (match.match_confidence < 0.9) return "auto_pending";
return "verified";
}
function percent(value) {
if (typeof value !== "number") return "0%";
return `${Math.round(value * 100)}%`;
}
function prettifyPath(path) {
return (path || "no_match").replaceAll("_", " ");
}
// CAT grouping helpers: keep this block JSX-free so node tests can eval it.
const PREFCARD_SECTION_ORDER = [
"prep",
"position",
"drapes",
"sterile_supplies",
"instruments",
"rep_trays",
"equipment",
"meds",
"suture",
"dressing",
"hold",
"notes",
];
const LEGACY_PHASE_SECTION = { setup: "sterile_supplies", incision: "instruments", close: "dressing" };
const CAT_COLLAPSE_THRESHOLD = 5;
function labelForCardSection(section) {
return (section || "notes").replaceAll("_", " ").replace(/\b\w/g, (char) => char.toUpperCase());
}
function quantityValue(value) {
const qty = Number(value);
return Number.isFinite(qty) ? qty : 0;
}
function sectionMapFromCardPayload(payload) {
const byId = new Map();
for (const [section, sectionItems] of Object.entries(payload?.sections || {})) {
if (!Array.isArray(sectionItems)) continue;
for (const item of sectionItems) {
if (item?.id == null) continue;
byId.set(String(item.id), section);
byId.set(`card-item-${item.id}`, section);
}
}
return byId;
}
function sectionKeyForItem(item, sectionMap = new Map()) {
const mapped = sectionMap.get(String(item?.card_item_id)) || sectionMap.get(String(item?.id));
const raw = mapped || item?.section_key || item?.section_enum || item?.card_section || item?.section || "notes";
if (PREFCARD_SECTION_ORDER.includes(raw)) return raw;
return LEGACY_PHASE_SECTION[raw] || "notes";
}
function itemAppearsOpen(item) {
const open = quantityValue(item?.qty_open);
const hold = quantityValue(item?.qty_hold);
return open > 0 || open + hold === 0;
}
function itemAppearsHold(item) {
return quantityValue(item?.qty_hold) > 0;
}
function sortSectionKeys(a, b) {
const ai = PREFCARD_SECTION_ORDER.indexOf(a);
const bi = PREFCARD_SECTION_ORDER.indexOf(b);
return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi) || a.localeCompare(b);
}
function buildCategoryGroups(items, cardPayload) {
const sectionMap = sectionMapFromCardPayload(cardPayload);
const buckets = { open: new Map(), hold: new Map() };
for (const item of items || []) {
const section = sectionKeyForItem(item, sectionMap);
const targets = [];
if (itemAppearsOpen(item)) targets.push("open");
if (itemAppearsHold(item)) targets.push("hold");
for (const target of targets) {
if (!buckets[target].has(section)) buckets[target].set(section, []);
buckets[target].get(section).push(item);
}
}
const finalize = (map) => ({
total: Array.from(map.values()).reduce((sum, sectionItems) => sum + sectionItems.length, 0),
sections: Array.from(map.keys()).sort(sortSectionKeys).map((section) => ({
key: section,
label: labelForCardSection(section),
items: map.get(section),
defaultCollapsed: map.get(section).length > CAT_COLLAPSE_THRESHOLD,
})),
});
return { open: finalize(buckets.open), hold: finalize(buckets.hold) };
}
// CAT grouping helpers end.
function GudidMatchApprovalView({ mobile = false, viewTabs = null }) {
// Live source built from real /api/cards/{id} payload (PR #1164 + post-PR
// projection in data.js::mapApiToGudidMatchLive). The companion
// PREFCARD_GUDID_MATCH_RESPONSE is retained for view-primitive.jsx's
// design-system band illustration only.
const [cardSnapshot, setCardSnapshot] = useStateGudid(window.PREFCARD);
const [liveResponse, setLiveResponse] = useStateGudid(window.PREFCARD_GUDID_MATCH_LIVE || window.PREFCARD_GUDID_MATCH_RESPONSE);
const [selectedFiles, setSelectedFiles] = useStateGudid([]);
const [uploadResult, setUploadResult] = useStateGudid(window.PREFCARD_UPLOAD_RESULT || null);
const [uploadState, setUploadState] = useStateGudid({ phase: "idle", message: "" });
const [approveState, setApproveState] = useStateGudid({ phase: "idle", message: "" });
const response = liveResponse || window.PREFCARD_GUDID_MATCH_RESPONSE;
const items = response.matched_items || [];
const [filter, setFilter] = useStateGudid("all");
const [detailItem, setDetailItem] = useStateGudid(null);
const [verifiedIds, setVerifiedIds] = useStateGudid(new Set());
const [manualNames, setManualNames] = useStateGudid({});
const [driftChoice, setDriftChoice] = useStateGudid({});
const [activeQtyTab, setActiveQtyTab] = useStateGudid("open");
const stats = useMemoGudid(() => summarizeGudidItems(items), [items]);
const visibleItems = useMemoGudid(() => items.filter((item) => {
const band = getGudidBand(item);
if (filter === "manual") return band === "block" || band === "reject" || band === "no_match";
if (filter === "review") return band === "review";
if (filter === "auto") return band === "auto" || band === "auto_pending" || band === "verified";
return true;
}), [items, filter]);
const categoryGroups = useMemoGudid(
() => buildCategoryGroups(visibleItems, window.PREFCARD_LAST_PAYLOAD),
[visibleItems, cardSnapshot]
);
const headerCard = cardWithLiveHeader(cardSnapshot);
function verifyItem(id) {
setVerifiedIds((prev) => {
const next = new Set(prev);
next.add(id);
return next;
});
}
function setManualName(id, value) {
setManualNames((prev) => ({ ...prev, [id]: value }));
}
async function matchUploadedItems(result) {
const extractionItems = result?.extraction?.items || [];
const supplyItems = extractionItems
.filter((item) => item.section !== "notes" && (item.item_name || item.raw_text))
.slice(0, 200);
if (!supplyItems.length) return null;
const response = await fetch(window.prefcardApiPath("/api/match-items"), {
method: "POST",
credentials: "same-origin",
headers: { "Content-Type": "application/json", "Accept": "application/json" },
body: JSON.stringify({
items: supplyItems.map((item) => ({
item_name: item.item_name || item.raw_text,
catalog_num: item.catalog_num || null,
vendor: item.vendor || null,
})),
require_active_distribution: true,
}),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
async function uploadSelectedFiles() {
if (!selectedFiles.length) return;
setUploadState({
phase: "uploading",
message: `Combining ${selectedFiles.length} page${selectedFiles.length === 1 ? "" : "s"} into one preference card.`,
});
setApproveState({ phase: "idle", message: "" });
const form = new FormData();
selectedFiles.forEach((file) => form.append("files", file));
form.append("source_type", "prefcard_regen_canvas");
try {
const response = await fetch(window.prefcardApiPath("/api/upload/card"), {
method: "POST",
credentials: "same-origin",
body: form,
});
const result = await response.json();
if (!response.ok) throw new Error(result.detail || `HTTP ${response.status}`);
let matchResponse = null;
try {
matchResponse = await matchUploadedItems(result);
} catch (matchErr) {
console.warn("[prefcard-multi-view] match-items probe failed; showing upload extraction only:", matchErr);
}
window.applyUploadResult(result, matchResponse);
setCardSnapshot(window.PREFCARD);
setLiveResponse(window.PREFCARD_GUDID_MATCH_LIVE);
setUploadResult(result);
setFilter("all");
setUploadState({
phase: "done",
message: `Merged ${result.page_count || result.extraction?.page_count || selectedFiles.length} page${selectedFiles.length === 1 ? "" : "s"}; ${result.items_count || result.extraction?.items?.length || 0} extracted rows ready for review.`,
});
} catch (err) {
setUploadState({ phase: "error", message: err.message || "Upload failed" });
}
}
async function approveUpload() {
const uploadId = uploadResult?.upload_id || cardSnapshot.uploadId;
if (!uploadId) return;
setApproveState({ phase: "saving", message: "Approving merged extraction." });
try {
const response = await fetch(window.prefcardApiPath(`/api/upload/card/${uploadId}/approve`), {
method: "POST",
credentials: "same-origin",
headers: { "Content-Type": "application/json", "Accept": "application/json" },
body: JSON.stringify({
items: uploadResult?.extraction?.items || [],
}),
});
const result = await response.json();
if (!response.ok) throw new Error(result.detail || `HTTP ${response.status}`);
if (result.card_id) {
const cardResponse = await fetch(window.prefcardApiPath(`/api/cards/${result.card_id}`), {
credentials: "same-origin",
headers: { "Accept": "application/json" },
});
if (cardResponse.ok) {
window.applyPrefcardPayload(await cardResponse.json());
setCardSnapshot(window.PREFCARD);
setLiveResponse(window.PREFCARD_GUDID_MATCH_LIVE);
}
}
setApproveState({
phase: "done",
message: `${result.items_added || 0} rows approved; ${result.items_needs_review || 0} need human review.`,
});
} catch (err) {
setApproveState({ phase: "error", message: err.message || "Approve failed" });
}
}
function applyScanResolved(item, result) {
const nextMatch = {
primary_di: result.gudid_di,
// Precedence: most-specific first. gudid_description carries the full canonical
// product (e.g. "Stryker Performance Series Sagittal Blade") while gudid_brand
// is a generic family label (e.g. "Stryker Performance Series"). Surgeon's
// item_name preserves their familiar text + size + catalog# context — second
// choice when GUDID description is missing. gudid_brand is last-resort fallback.
canonical_name: result.gudid_description || item.item_name || result.gudid_brand || "-",
brand_name: result.gudid_brand || "-",
company_name: result.gudid_company || "-",
device_description: result.gudid_description || "",
match_confidence: result.match_confidence ?? 1,
match_path: result.match_path || "scan",
match_pin_source: "scan_barcode",
band: "verified",
};
setLiveResponse((prev) => ({
...prev,
matched_items: (prev?.matched_items || []).map((row) => (
row.id === item.id || row.card_item_id === item.card_item_id
? { ...row, needs_human_review: 0, gudid_match: nextMatch }
: row
)),
}));
setDetailItem((prev) => (prev ? { ...prev, needs_human_review: 0, gudid_match: nextMatch } : prev));
}
return (
{!GUDID_EMBEDDED_APP_SHELL && (mobile ?
:
)}
{viewTabs}
setDriftChoice((prev) => ({ ...prev, [id]: value }))}
/>
{!mobile && (
getGudidBand(item) === "review")}
onDetail={setDetailItem}
/>
)}
{detailItem && (
setDetailItem(null)}
onScanResolved={applyScanResolved}
/>
)}
);
}
function GudidMobileHeader({ card }) {
const maintainer = card?.maintainer || "Maintainer";
return (
);
}
function GudidUploadPanel({
mobile,
files,
uploadState,
approveState,
uploadResult,
onFiles,
onUpload,
onApprove,
}) {
const pageCount = uploadResult?.page_count || uploadResult?.extraction?.page_count;
const itemCount = uploadResult?.items_count || uploadResult?.extraction?.items?.length;
const busy = uploadState.phase === "uploading" || approveState.phase === "saving";
return (
Multi-page upload
onFiles(Array.from(event.target.files || []))}
style={{
maxWidth: mobile ? "100%" : 260,
fontFamily: "var(--ss-sans)",
fontSize: 12.5,
color: "var(--ss-gray-900)",
}}
/>
{(uploadResult || uploadState.phase === "done") && (
)}
{files.length > 0 && (
{files.map((file, index) => (
Page {index + 1}
{file.name}
))}
)}
{(uploadState.message || approveState.message) && (
{approveState.message || uploadState.message}
)}
);
}
function GudidNotesPanel({ card, mobile }) {
const historyNotes = card.historyNotes || [];
const clinicalNotes = card.clinicalNotes || [];
if (!historyNotes.length && !clinicalNotes.length) return null;
return (
{historyNotes.length > 0 && (
{historyNotes.map((note) => (
{note.note_text}
))}
)}
{clinicalNotes.length > 0 && (
{clinicalNotes.map((note) => (
{note.item_name}
{note.raw_text || "(none captured)"}
))}
)}
);
}
function NoteBucket({ title, count, children, mobile }) {
return (
{title}
{count}
);
}
function summarizeGudidItems(items) {
return items.reduce((acc, item) => {
const band = getGudidBand(item);
acc.total += 1;
if (band === "block" || band === "reject" || band === "no_match") acc.manual += 1;
if (band === "review") acc.review += 1;
if (band === "auto" || band === "auto_pending" || band === "verified") acc.auto += 1;
if (item.gudid_match) acc.matched += 1;
return acc;
}, { total: 0, matched: 0, auto: 0, review: 0, manual: 0, noMatch: 0 });
}
function GudidPageHeader({ mobile, card, stats, pendingUploadId, approveState, onApprove }) {
const canApprove = Boolean(pendingUploadId) && approveState.phase !== "saving";
return (
Prefcard regen approval
{card.name} with GUDID match review
{card.maintainer}
{card.maintainerInst}
{pendingUploadId ? `upload ${pendingUploadId}` : `card ${window.PREFCARD_CONFIG.cardId}`}
);
}
function HeaderMetric({ label, value }) {
return (
);
}
function GudidStatsBar({ stats, filter, onFilter, mobile }) {
const segments = [
{ id: "all", label: "Total", value: stats.total, tone: "neutral" },
{ id: "auto", label: "Auto-accept", value: stats.auto, tone: "verified" },
{ id: "review", label: "Amber review", value: stats.review, tone: "review" },
{ id: "manual", label: "Add to library", value: stats.manual, tone: "reject", title: ADD_TO_LIBRARY_TOOLTIP },
];
return (
{segments.map((segment) => (
))}
);
}
function statButtonTone(tone) {
if (tone === "verified") return { color: "var(--ss-match-verified)", background: "var(--ss-match-verified-bg)" };
if (tone === "review") return { color: "var(--ss-match-review)", background: "var(--ss-match-review-bg)" };
if (tone === "reject") return { color: "var(--ss-match-reject)", background: "var(--ss-match-reject-bg)" };
return { color: "var(--ss-gray-900)", background: "var(--ss-cream)" };
}
function GudidCategorizedRows({
groups,
activeTab,
cardId,
mobile,
verifiedIds,
manualNames,
driftChoice,
onTab,
onManualName,
onVerify,
onDetail,
onDriftChoice,
}) {
const activeGroup = groups[activeTab] || groups.open;
const tabs = [
{ id: "open", label: "Open", group: groups.open },
{ id: "hold", label: "Hold", group: groups.hold },
];
return (
{tabs.map((tab) => (
))}
{activeGroup.sections.length ? activeGroup.sections.map((section) => (
)) : (
No {activeTab} items match the current GUDID filter.
)}
);
}
function GudidSectionAccordion({
section,
cardId,
mobile,
verifiedIds,
manualNames,
driftChoice,
onManualName,
onVerify,
onDetail,
onDriftChoice,
}) {
const storageKey = `prefcard-cat:${cardId}:${section.key}`;
const [collapsed, setCollapsed] = useStateGudid(() => {
try {
const saved = window.sessionStorage.getItem(storageKey);
if (saved != null) return saved === "1";
} catch (_) {}
return section.defaultCollapsed;
});
useEffectGudid(() => {
try {
const saved = window.sessionStorage.getItem(storageKey);
setCollapsed(saved != null ? saved === "1" : section.defaultCollapsed);
} catch (_) {
setCollapsed(section.defaultCollapsed);
}
}, [storageKey, section.defaultCollapsed]);
function toggle() {
setCollapsed((prev) => {
const next = !prev;
try { window.sessionStorage.setItem(storageKey, next ? "1" : "0"); } catch (_) {}
return next;
});
}
return (
{!collapsed && (
{section.items.map((item) => (
))}
)}
);
}
function GudidMatchRow({
item,
sectionKey,
mobile,
verified,
manualName,
driftChoice,
onManualName,
onVerify,
onDetail,
onDriftChoice,
}) {
const band = getGudidBand(item);
const match = item.gudid_match;
const meta = MATCH_COPY[band];
const needsManual = band === "block" || band === "reject" || band === "no_match";
const needsVerify = band === "review" && !verified;
return (
{mobile && }
{match && }
qty {item.qty_open} open / {item.qty_hold} hold
{item.notes && (
{item.notes}
)}
{band === "review" && (
)}
{needsManual && (
)}
{match?.drift_detected && (
)}
{!mobile && }
{cardItemIdForProductInfo(item) && }
{needsVerify && }
{verified && verified}
);
}
function SectionChip({ section }) {
return (
{labelForCardSection(section)}
);
}
function supplyGlyphSeed(item) {
const kind = item.section === "incision" ? "implant" : item.section === "prep" ? "equipment" : "consumable";
return { id: item.id, kind };
}
function MatchConfidenceChip({ item, verified = false, onOpen, compact = false }) {
const rawBand = getGudidBand(item);
const band = verified ? "verified" : rawBand;
const match = item.gudid_match;
const meta = MATCH_COPY[band];
const silentVerified = rawBand === "verified" && !verified;
const label = band === "block" || band === "reject"
? `Reject ${percent(match.match_confidence)}`
: band === "no_match"
? "No match"
: band === "verified"
? (match && match.match_confidence == null ? "Scanner" : "Verified")
: `${band === "review" ? "Amber" : "Confirm"} ${percent(match.match_confidence)}`;
return (
);
}
function CanonicalNameBlock({ item, manualName }) {
const band = getGudidBand(item);
const match = item.gudid_match;
const showDiff = band === "review";
const title = band === "block" || band === "reject" || band === "no_match"
? (manualName || item.item_name)
: match.canonical_name;
return (
{showDiff && (
{item.item_name}
)}
{title}
{match ? `${match.brand_name} / ${match.company_name}` : `${item.vendor || "Unknown vendor"} / no GUDID match`}
);
}
function DiffPanel({ item, verified }) {
const match = item.gudid_match;
return (
Vision
{item.item_name}
GUDID
{match.canonical_name}
{percent(match.match_confidence)} through {prettifyPath(match.match_path)}
);
}
function ManualEntryPanel({ item, value, onChange }) {
return (
);
}
function DriftPanel({ item, choice, onChoice }) {
const match = item.gudid_match;
return (
Cerner card has {match.canonical_name}, discontinued {match.distribution_end_date}. Active successor: {match.successor_pin}.
);
}
function GudidSidePanel({ stats, selected, onDetail }) {
return (
);
}
function MiniStat({ label, value }) {
return (
);
}
function GudidDetailModal({ item, cardItems = [], onClose, onScanResolved }) {
const [productInfo, setProductInfo] = useStateGudid({ phase: "idle", enrichment: null, imageUrl: null, error: "" });
const [scanOpen, setScanOpen] = useStateGudid(false);
const cardItemId = cardItemIdForProductInfo(item);
useEffectGudid(() => {
let cancelled = false;
if (!cardItemId) {
setProductInfo({ phase: "unavailable", enrichment: null, imageUrl: null, error: "" });
return () => { cancelled = true; };
}
setProductInfo({ phase: "loading", enrichment: null, imageUrl: null, error: "" });
Promise.all([
fetch(window.prefcardApiPath(`/api/items/${cardItemId}/enrich`), {
method: "POST",
credentials: "same-origin",
headers: { "Accept": "application/json" },
}).then(async (response) => {
const payload = await response.json().catch(() => ({}));
if (!response.ok) throw new Error(payload.detail || `HTTP ${response.status}`);
return payload;
}),
fetch(window.prefcardApiPath(`/api/product-image/${cardItemId}`), {
credentials: "same-origin",
headers: { "Accept": "application/json" },
}).then((response) => response.ok ? response.json() : { image_url: null }).catch(() => ({ image_url: null })),
]).then(([enrichment, image]) => {
if (cancelled) return;
setProductInfo({ phase: "ready", enrichment, imageUrl: image?.image_url || null, error: "" });
}).catch((err) => {
if (cancelled) return;
setProductInfo({ phase: "error", enrichment: null, imageUrl: null, error: err.message || "Lookup failed" });
});
return () => { cancelled = true; };
}, [cardItemId]);
return (
{
if (event.target === event.currentTarget) onClose();
}}
style={{
position: "fixed",
inset: 0,
zIndex: 20,
background: "rgba(27,26,24,0.28)",
display: "flex",
alignItems: "center",
justifyContent: "center",
padding: 24,
}}
>
{scanOpen && (
setScanOpen(false)}
onResolved={(result) => {
onScanResolved?.(item, result);
}}
/>
)}
{cardItemId && }
);
}
function ScanBarcodeModal({ item, cardItemId, onClose, onResolved }) {
const [barcode, setBarcode] = useStateGudid("");
const [barcodeType, setBarcodeType] = useStateGudid("unknown");
const [phase, setPhase] = useStateGudid("idle");
const [message, setMessage] = useStateGudid("");
const [fileName, setFileName] = useStateGudid("");
const [cameraOn, setCameraOn] = useStateGudid(false);
const videoRef = useRefGudid(null);
const streamRef = useRefGudid(null);
const detectorSupported = typeof window !== "undefined" && "BarcodeDetector" in window;
const cardId = window.PREFCARD_CONFIG?.cardId;
async function resolveBarcodeValue(value, type) {
const clean = (value || "").trim();
if (!clean) return;
try {
setPhase("resolving");
const response = await fetch(window.prefcardApiPath(`/api/cards/${cardId}/items/${cardItemId}/scan-barcode`), {
method: "POST",
credentials: "same-origin",
headers: { "Content-Type": "application/json", "Accept": "application/json" },
body: JSON.stringify({
barcode_value: clean,
barcode_type: type || "unknown",
raw_item_name: item.item_name || item.raw_text || "",
}),
});
const result = await response.json().catch(() => ({}));
if (!response.ok) throw new Error(result.detail || `HTTP ${response.status}`);
if (!result.resolved) {
setPhase("not-found");
setMessage(result.reason || "No GUDID record found for that barcode.");
return;
}
setPhase("success");
setMessage(`Linked ${result.gudid_brand || "GUDID device"} and saved this facility mapping.`);
onResolved?.(result);
} catch (err) {
setPhase("error");
setMessage(err.message || "Barcode scan failed.");
}
}
useEffectGudid(() => {
if (!cameraOn || !detectorSupported) return undefined;
let cancelled = false;
let rafId = null;
let detector = null;
(async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } });
if (cancelled) { stream.getTracks().forEach((t) => t.stop()); return; }
streamRef.current = stream;
if (videoRef.current) {
videoRef.current.srcObject = stream;
await videoRef.current.play().catch(() => {});
}
detector = new window.BarcodeDetector({ formats: ["data_matrix", "qr_code", "ean_13", "code_128", "upc_a"] });
setPhase("scanning");
setMessage("Point camera at the barcode. Auto-detects once visible.");
const scanFrame = async () => {
if (cancelled || !videoRef.current || videoRef.current.readyState < 2) {
if (!cancelled) rafId = requestAnimationFrame(scanFrame);
return;
}
try {
const codes = await detector.detect(videoRef.current);
// PREFER DATA MATRIX over 1D when both are visible: GS1 DataMatrix carries
// primary DI + lot + expiry that matches GUDID DB; 1D Code 128 often only
// carries the GTIN with AIM identifier prefix that needs server-side parsing.
const dataMatrix = codes && codes.find && codes.find((c) => c.format === "data_matrix");
const first = dataMatrix || (codes && codes[0]);
if (first && first.rawValue) {
setBarcode(first.rawValue);
setBarcodeType(first.format || "live");
setMessage(`Detected ${first.format || "barcode"} — resolving...`);
setCameraOn(false);
await resolveBarcodeValue(first.rawValue, first.format || "live");
return;
}
} catch (e) {
// transient frame error — keep scanning
}
if (!cancelled) rafId = requestAnimationFrame(scanFrame);
};
scanFrame();
} catch (err) {
setPhase("error");
setMessage(err.message || "Camera unavailable. Use file upload or type the code.");
setCameraOn(false);
}
})();
return () => {
cancelled = true;
if (rafId) cancelAnimationFrame(rafId);
if (streamRef.current) {
streamRef.current.getTracks().forEach((t) => t.stop());
streamRef.current = null;
}
};
}, [cameraOn, detectorSupported]);
async function detectFromFile(event) {
const file = event.target.files?.[0];
if (!file) return;
setFileName(file.name);
setMessage("");
if (!detectorSupported) {
setPhase("fallback");
setMessage("This browser cannot decode uploaded barcode images; enter the barcode printed on the package.");
return;
}
try {
setPhase("scanning");
const bitmap = await createImageBitmap(file);
const detector = new window.BarcodeDetector({ formats: ["data_matrix", "qr_code", "ean_13", "code_128", "upc_a"] });
const codes = await detector.detect(bitmap);
const first = codes?.[0];
if (!first) {
setPhase("idle");
setMessage("No barcode detected in that image.");
return;
}
setBarcode(first.rawValue || "");
setBarcodeType(first.format || "image");
setPhase("ready");
setMessage(`Detected ${first.format || "barcode"} from ${file.name}.`);
} catch (err) {
setPhase("error");
setMessage(err.message || "Could not decode that image.");
}
}
async function resolveBarcode() {
const clean = barcode.trim();
if (!clean) {
setPhase("error");
setMessage("Enter or upload a barcode first.");
return;
}
try {
setPhase("resolving");
const response = await fetch(window.prefcardApiPath(`/api/cards/${cardId}/items/${cardItemId}/scan-barcode`), {
method: "POST",
credentials: "same-origin",
headers: { "Content-Type": "application/json", "Accept": "application/json" },
body: JSON.stringify({
barcode_value: clean,
barcode_type: barcodeType,
raw_item_name: item.item_name || item.raw_text || "",
}),
});
const result = await response.json().catch(() => ({}));
if (!response.ok) throw new Error(result.detail || `HTTP ${response.status}`);
if (!result.resolved) {
setPhase("not-found");
setMessage(result.reason || "No GUDID record found for that barcode.");
return;
}
setPhase("success");
setMessage(`Linked ${result.gudid_brand || "GUDID device"} and saved this facility mapping.`);
onResolved?.(result);
} catch (err) {
setPhase("error");
setMessage(err.message || "Barcode scan failed.");
}
}
return (
Scan to learn
Bind barcode to this item
Future cards at this facility will try this scanned GUDID match before generic text matching.
{detectorSupported && (
{cameraOn && (
)}
)}
{!detectorSupported && (
Live camera barcode scan requires BarcodeDetector API (Chrome/Edge on Android). On unsupported browsers, use file upload or type the code below.
)}
{fileName &&
{fileName}
}
{message && (
{message}
)}
);
}
function cardItemIdForProductInfo(item) {
const raw = item?.card_item_id || item?.backend_id;
if (raw && Number.isFinite(Number(raw))) return Number(raw);
// Fallback: items from /api/cards/{id} responses have item.id = card_items.id (numeric).
// The /api/match-items preview path uses synthesized string ids — caller falls through to the regex below.
if (item?.id && Number.isFinite(Number(item.id))) return Number(item.id);
const match = String(item?.id || "").match(/card-item-(\d+)$/);
return match ? Number(match[1]) : null;
}
function ProductInfoLookupPanel({ item, cardItems = [], lookup }) {
if (lookup.phase === "loading" || lookup.phase === "idle") {
return (
Looking up product...
);
}
if (lookup.phase === "unavailable") {
return (
Saved item needed
This row is not persisted yet, so live openFDA and product-image lookup are unavailable.
);
}
if (lookup.phase === "error") {
return (
Lookup unavailable
{lookup.error}
);
}
const enrichment = lookup.enrichment || {};
const match = item.gudid_match;
const marketplace = enrichment.marketplace_match || {};
const safety = enrichment.safety || {};
const rep = enrichment.surgx_rep;
const manufacturer = enrichment.company || marketplace.manufacturer_name || match?.company_name || item.vendor || "";
const imageUrl = lookup.imageUrl;
const roomDefault = itemRoomDefault(item, cardItems);
return (
{(enrichment.description || marketplace.device_description || match?.device_description) && (
Description
{enrichment.description || marketplace.device_description || match?.device_description}
)}
{!item.gudid_match &&
}
);
}
function disruptionPrimaryDi(item) {
return item?.gudid_di || item?.gudid_match?.primary_di || item?.gudid_match?.di || "";
}
function disruptionTierStyle(severity) {
const tier = String(severity || "low").toLowerCase();
if (tier === "critical") {
return { background: "var(--ss-match-reject-bg)", border: "#DAB5AC", color: "var(--ss-match-reject)" };
}
if (tier === "moderate") {
return { background: "var(--ss-open-bg)", border: "#E1CC9E", color: "#7A5615" };
}
return { background: "var(--ss-cream)", border: "var(--ss-line-soft)", color: "var(--ss-gray-700)" };
}
function disruptionTopAlert(alerts) {
const rank = { critical: 3, moderate: 2, low: 1 };
return [...alerts].sort((left, right) => {
const rightRank = rank[String(right.severity || "low").toLowerCase()] || 0;
const leftRank = rank[String(left.severity || "low").toLowerCase()] || 0;
return rightRank - leftRank;
})[0];
}
function disruptionFirmLabel(alerts, topAlert) {
const firms = [...new Set(alerts.map((alert) => alert.recalling_firm).filter(Boolean))];
if (firms.length > 1) return "Multiple manufacturers";
return firms[0] || topAlert?.recalling_firm || "Manufacturer not listed";
}
function substitutesForItem(substitutesList, itemName) {
if (!Array.isArray(substitutesList) || !substitutesList.length) return { alternatives: [], label: "" };
const target = String(itemName || "").trim().toLowerCase();
const exact = target
? substitutesList.find((group) => String(group.for_item || "").trim().toLowerCase() === target)
: null;
const group = exact || substitutesList[0] || {};
return {
alternatives: Array.isArray(group.alternatives) ? group.alternatives : [],
label: group.for_item || itemName || "",
};
}
function SubstitutesPanel({ state, itemName, onRetry, onMarkSubstitute, substitutedProductId, disabled = false }) {
if (state.phase === "loading") {
return (
Looking up substitutes...
);
}
if (state.phase === "error") {
return (
Substitute lookup failed. Try again.
);
}
const { alternatives, label } = substitutesForItem(state.detail?.substitutes, itemName);
if (!alternatives.length) {
return (
No substitute matches in catalog.
Contact materials team for sourcing options.
);
}
return (
Alternatives for {label || itemName}
{alternatives.slice(0, 3).map((alt, idx) => {
const isSubstituted = substitutedProductId != null && String(substitutedProductId) === String(alt.id);
return (
{alt.name || "Unnamed substitute"}
{[alt.manufacturer, alt.vendor_name, alt.price != null && alt.price !== "" ? String(alt.price) : ""].filter(Boolean).join(" / ") || "Vendor details not listed"}
{isSubstituted ? (
Substituted
) : (
)}
);
})}
);
}
function SubstituteConfirmDialog({ itemName, substitute, onCancel, onConfirm }) {
const [reason, setReason] = useStateGudid("");
const [phase, setPhase] = useStateGudid("idle");
const [error, setError] = useStateGudid("");
const dialogRef = React.useRef(null);
const textareaRef = React.useRef(null);
const cleanReason = reason.trim();
const canSubmit = cleanReason.length >= 50 && phase !== "saving";
useEffectGudid(() => {
textareaRef.current?.focus();
}, []);
const close = () => {
if (phase !== "saving") onCancel();
};
const submit = async () => {
if (!canSubmit) return;
setPhase("saving");
setError("");
try {
await onConfirm(cleanReason);
setPhase("done");
} catch (err) {
setPhase("idle");
setError(err.message || "Could not mark substitute. Try again.");
}
};
const onKeyDown = (event) => {
if (event.key === "Escape") {
event.preventDefault();
close();
return;
}
if (event.key !== "Tab" || !dialogRef.current) return;
const focusable = Array.from(dialogRef.current.querySelectorAll("button, textarea")).filter((node) => !node.disabled);
if (!focusable.length) return;
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (event.shiftKey && document.activeElement === first) {
event.preventDefault();
last.focus();
} else if (!event.shiftKey && document.activeElement === last) {
event.preventDefault();
first.focus();
}
};
return (
{ if (event.target === event.currentTarget) close(); }} style={substituteDialogOverlayStyle}>
Mark as substituted
This records that {substitute?.name || "Unnamed substitute"} is replacing {itemName || "this item"}.
{substitute?.name || "Unnamed substitute"}
{[substitute?.manufacturer, substitute?.vendor_name || substitute?.vendor].filter(Boolean).join(" / ") || "Vendor details not listed"}
= 50 ? "var(--ss-match-verified)" : "var(--ss-match-reject)", fontSize: 12 }}>{cleanReason.length}/50 characters
{error && {error}}
);
}
const substitutesPanelStyle = {
marginTop: 4,
padding: 10,
border: "1px solid var(--ss-line-soft)",
borderRadius: 5,
background: "rgba(255,255,255,0.58)",
color: "var(--ss-gray-800)",
fontSize: 12.5,
lineHeight: 1.35,
};
const substitutedBadgeStyle = {
alignSelf: "start",
border: "1px solid var(--ss-match-verified)",
borderRadius: 999,
color: "var(--ss-match-verified)",
background: "rgba(28, 95, 72, 0.08)",
fontSize: 11,
fontWeight: 800,
padding: "4px 8px",
};
const substituteDialogOverlayStyle = {
position: "fixed",
inset: 0,
zIndex: 90,
background: "rgba(17, 24, 39, 0.36)",
display: "grid",
placeItems: "center",
padding: 18,
};
const substituteDialogStyle = {
width: "min(520px, 100%)",
background: "var(--ss-paper)",
border: "1px solid var(--ss-line)",
borderRadius: 8,
boxShadow: "0 18px 60px rgba(17,24,39,0.24)",
padding: 18,
display: "grid",
gap: 12,
};
const substituteTextareaStyle = {
width: "100%",
border: "1px solid var(--ss-line)",
borderRadius: 5,
background: "var(--ss-paper)",
padding: 10,
minHeight: 92,
resize: "vertical",
font: "inherit",
};
function DisruptionBadge({ item }) {
const gudidDi = disruptionPrimaryDi(item);
const [status, setStatus] = useStateGudid({ phase: gudidDi ? "loading" : "none", alerts: [], error: "" });
const [substitutes, setSubstitutes] = useStateGudid({ expanded: false, phase: "idle", alertId: null, detail: null, error: "" });
const [pendingSubstitute, setPendingSubstitute] = useStateGudid(null);
const [substitution, setSubstitution] = useStateGudid(item.intent_status === "substituted" ? { productId: item.marketplace_product_id || null, name: item.substituted_product_name || "" } : null);
const substitutesRequestId = React.useRef(0);
useEffectGudid(() => {
let cancelled = false;
if (!gudidDi) {
setStatus({ phase: "none", alerts: [], error: "" });
return () => { cancelled = true; };
}
setStatus({ phase: "loading", alerts: [], error: "" });
fetch(window.prefcardApiPath(`/api/disruptions/check/${encodeURIComponent(gudidDi)}`), {
credentials: "same-origin",
headers: { "Accept": "application/json" },
}).then(async (response) => {
const payload = await response.json().catch(() => ({}));
if (!response.ok) throw new Error(payload.detail || `HTTP ${response.status}`);
return payload;
}).then((payload) => {
if (cancelled) return;
const alerts = Array.isArray(payload.alerts) ? payload.alerts : [];
setStatus({ phase: payload.affected && alerts.length ? "affected" : "clean", alerts, error: "" });
}).catch((err) => {
if (cancelled) return;
setStatus({ phase: "error", alerts: [], error: err.message || "Disruption check failed" });
});
return () => { cancelled = true; };
}, [gudidDi]);
if (status.phase !== "affected") return null;
const alerts = status.alerts;
const topAlert = disruptionTopAlert(alerts);
const style = disruptionTierStyle(topAlert?.severity);
const count = alerts.length;
const itemName = item.item_name || item.name || item.description || "";
const cardItemId = cardItemIdForProductInfo(item);
const cardId = window.PREFCARD_CONFIG?.cardId;
const submitMarketplaceSubstitute = async (substitute, reason) => {
if (!cardId || !cardItemId) throw new Error("Card item id missing for substitute update.");
const response = await fetch(window.prefcardApiPath(`/api/cards/${cardId}/items/${cardItemId}/substitute`), {
method: "POST",
credentials: "same-origin",
headers: { "Content-Type": "application/json", "Accept": "application/json" },
body: JSON.stringify({
marketplace_product_id: substitute.id,
marketplace_product_name: substitute.name || "Unnamed substitute",
marketplace_manufacturer: substitute.manufacturer || null,
marketplace_vendor: substitute.vendor_name || substitute.vendor || null,
intent_reason: reason,
}),
});
const payload = await response.json().catch(() => ({}));
if (!response.ok) {
const authMessage = response.status === 401 || response.status === 403 ? "Sign-in required to substitute." : "";
throw new Error(authMessage || payload.detail || `HTTP ${response.status}`);
}
setSubstitution({ productId: substitute.id, name: substitute.name || "Unnamed substitute", reason });
setPendingSubstitute(null);
return payload;
};
const loadSubstitutes = (force = false) => {
if (!topAlert?.id) return;
if (substitutes.expanded && !force) {
substitutesRequestId.current += 1;
setSubstitutes((prev) => ({ ...prev, expanded: false }));
return;
}
if (!force && substitutes.detail && substitutes.alertId === topAlert.id) {
setSubstitutes((prev) => ({ ...prev, expanded: true }));
return;
}
const requestId = substitutesRequestId.current + 1;
substitutesRequestId.current = requestId;
setSubstitutes({ expanded: true, phase: "loading", alertId: topAlert.id, detail: null, error: "" });
fetch(window.prefcardApiPath(`/api/disruptions/${encodeURIComponent(topAlert.id)}`), {
credentials: "same-origin",
headers: { "Accept": "application/json" },
}).then(async (response) => {
const payload = await response.json().catch(() => ({}));
if (!response.ok) throw new Error(payload.detail || `HTTP ${response.status}`);
return payload;
}).then((payload) => {
if (substitutesRequestId.current !== requestId) return;
setSubstitutes({ expanded: true, phase: "loaded", alertId: topAlert.id, detail: payload, error: "" });
}).catch((err) => {
if (substitutesRequestId.current !== requestId) return;
setSubstitutes({ expanded: true, phase: "error", alertId: topAlert.id, detail: null, error: err.message || "Substitute lookup failed" });
});
};
return (
Supply disruption
! {count} active FDA disruption{count === 1 ? "" : "s"}
{topAlert?.reason || "Active FDA disruption is linked to this device."}
{disruptionFirmLabel(alerts, topAlert)}
{topAlert?.severity ? ` / ${String(topAlert.severity).toUpperCase()} severity` : ""}
{substitution && (
Substituted - see notes
{substitution.name}
{itemName || "Original item"}
)}
{topAlert?.id && (
Open raw alert ->
)}
{substitutes.expanded && (
loadSubstitutes(true)}
onMarkSubstitute={setPendingSubstitute}
substitutedProductId={substitution?.productId}
disabled={!cardId || !cardItemId}
/>
)}
{pendingSubstitute && (
setPendingSubstitute(null)}
onConfirm={(reason) => submitMarketplaceSubstitute(pendingSubstitute, reason)}
/>
)}
);
}
function FallbackDetailGrid({ item }) {
const match = item.gudid_match;
return (
{match ? (
<>
>
) : (
)}
);
}
function RoomDefaultEducationChip({ item, roomDefault }) {
if (!roomDefault) return null;
const itemId = cardItemIdForProductInfo(item);
const openLayout = () => {
const url = new URL(window.location.href);
url.searchParams.set("view", "layout");
if (itemId) url.searchParams.set("highlight_item", String(itemId));
window.location.href = url.toString();
};
return (
Room-default assumption
{roomDefault.rule.defaultLabel}
{roomDefault.body}
Suggested placement: {roomDefault.rule.targetLabels.join(", ")}.
);
}
function ProductInfoBand({ item, enrichment }) {
const match = item.gudid_match;
const conf = enrichment._match_confidence ?? match?.match_confidence;
const band = getGudidBand(item);
const meta = MATCH_COPY[band];
const path = prettifyPath(enrichment._match_path || match?.match_path || "no_match");
const title = band === "no_match"
? "No GUDID match - add to library"
: band === "review"
? "Amber review - verify before save"
: band === "block" || band === "reject"
? "Low confidence - add to library"
: "GUDID verified";
return (
{title}
{conf == null ? "Scanner or existing GUDID binding; no text-confidence score needed." : `${percent(conf)} through ${path}.`}
{(enrichment._match_pin_source || match?.match_pin_source) && (
pin {enrichment._match_pin_source || match.match_pin_source}
)}
{conf == null ? "GUDID" : percent(conf)}
);
}
function ProductImageFrame({ imageUrl, item }) {
return (
{imageUrl ? (
}`)})
{ event.currentTarget.style.display = "none"; }}
/>
) : (
No product image returned
)}
);
}
const SAFETY_EDUCATION = {
open_recalls: {
label: "Open recalls",
zero: {
tier: "sage",
headline: "No active FDA recalls",
body: "No open recall actions on this device or its product family. Continue normal verification.",
},
nonzero: (n) => ({
tier: "terra",
headline: `${n} open FDA recall${n === 1 ? "" : "s"}`,
body: "FDA has an active recall on this device or product family. Verify lot/serial against the recall notice with the manufacturer or your supply-chain lead before using.",
}),
},
total_recalls: {
label: "Total recalls",
zero: {
tier: "sage",
headline: "No recall history",
body: "No historical recalls for this product family.",
},
nonzero: (n) => ({
tier: "neutral",
headline: `${n} historical recall${n === 1 ? "" : "s"}`,
body: "Past recall actions for this product family. Not all are active - open count above is what matters today.",
}),
},
clearance_count: {
label: "510(k)",
zero: {
tier: "amber",
headline: "No 510(k) clearances found",
body: "No 510(k) premarket notifications for this manufacturer's device family in our records. The device may be cleared under PMA, exempt, or our coverage is incomplete. Verify FDA pathway separately if needed.",
},
nonzero: (n) => ({
tier: "sage",
headline: `Cleared ${n} time${n === 1 ? "" : "s"} via 510(k)`,
body: "510(k) is the FDA pathway for devices that are substantially equivalent to a previously cleared device. Multiple clearances suggest an established product line.",
}),
},
adverse_event_count: {
label: "MAUDE",
zero: {
tier: "sage",
headline: "No MAUDE adverse-event reports",
body: "No adverse event reports filed with FDA against this manufacturer. Continue normal verification.",
},
low: (n) => ({
tier: "amber",
headline: `${n} MAUDE report${n === 1 ? "" : "s"}`,
body: "Manufacturer and User Facility Device Experience - adverse event reports filed with FDA. Review device labeling and monitor patient closely.",
}),
high: (n) => ({
tier: "terra",
headline: `${n} MAUDE reports - elevated`,
body: "Manufacturer and User Facility Device Experience - elevated count of FDA-filed adverse event reports for this manufacturer. Surface to the surgeon if substitute is available.",
}),
},
};
function safetyEducationFor(metric, value) {
const n = Number(value || 0);
const config = SAFETY_EDUCATION[metric];
if (!config) return null;
if (metric === "adverse_event_count") {
if (n <= 0) return config.zero;
return n >= 10 ? config.high(n) : config.low(n);
}
return n > 0 ? config.nonzero(n) : config.zero;
}
function SafetySummary({ safety }) {
const [openMetric, setOpenMetric] = useStateGudid(null);
const recallCount = Number(safety.recall_count || 0);
const openRecalls = Number(safety.open_recalls || 0);
const clearances = Number(safety.clearance_count || 0);
const adverse = Number(safety.adverse_event_count || 0);
const metrics = [
{ id: "open_recalls", label: "Open recalls", value: openRecalls, tone: openRecalls > 0 ? "terra" : "sage" },
{ id: "total_recalls", label: "Total recalls", value: recallCount, tone: "neutral" },
{ id: "clearance_count", label: "510(k)", value: clearances, tone: clearances > 0 ? "sage" : "amber" },
{ id: "adverse_event_count", label: "MAUDE", value: adverse, tone: adverse >= 10 ? "terra" : adverse > 0 ? "amber" : "sage" },
];
const activeMetric = metrics.find((metric) => metric.id === openMetric);
const activeEducation = activeMetric ? safetyEducationFor(activeMetric.id, activeMetric.value) : null;
const toggleMetric = (id) => setOpenMetric((current) => current === id ? null : id);
return (
{metrics.map((metric) => (
toggleMetric(metric.id)}
/>
))}
{activeEducation && (
)}
);
}
function SafetyEducationPanel({ education }) {
const toneMap = {
sage: { bg: "var(--ss-match-verified-bg)", fg: "var(--ss-match-verified)", bd: "#B9D0BE" },
terra: { bg: "var(--ss-match-reject-bg)", fg: "var(--ss-match-reject)", bd: "#DAB5AC" },
amber: { bg: "var(--ss-open-bg)", fg: "#7A5615", bd: "#E1CC9E" },
neutral: { bg: "var(--ss-cream)", fg: "var(--ss-gray-900)", bd: "var(--ss-line-soft)" },
};
const t = toneMap[education.tier] || toneMap.neutral;
return (
{education.headline}
{education.body}
);
}
function SafetyMetric({ label, value, tone = "neutral", expanded = false, onToggle }) {
const toneMap = {
sage: { bg: "var(--ss-match-verified-bg)", fg: "var(--ss-match-verified)", bd: "#B9D0BE" },
terra: { bg: "var(--ss-match-reject-bg)", fg: "var(--ss-match-reject)", bd: "#DAB5AC" },
amber: { bg: "var(--ss-open-bg)", fg: "#7A5615", bd: "#E1CC9E" },
neutral: { bg: "var(--ss-cream)", fg: "var(--ss-gray-900)", bd: "var(--ss-line-soft)" },
};
const t = toneMap[tone] || toneMap.neutral;
const handleKeyDown = (event) => {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
onToggle?.();
}
};
return (
{expanded ? "^" : "?"}
{value}
{label}
);
}
const ALLERGEN_SIGNAL_COPY = {
latex: {
tier: "high",
label: "LATEX",
text: "Verify patient latex allergy before opening package.",
},
mri_unsafe: {
tier: "high",
label: "MRI UNSAFE",
text: "Do not use in MRI suite under any field strength.",
},
nickel: {
tier: "medium",
label: "NICKEL",
text: "Check pre-op chart for nickel sensitivity.",
},
iodine: {
tier: "medium",
label: "IODINE",
text: "Verify iodine allergy before use.",
},
mri_conditional: {
tier: "medium",
label: "MRI CONDITIONAL",
text: "Verify scanner field strength and conditions before use.",
},
formaldehyde: {
tier: "medium",
label: "FORMALDEHYDE",
text: "Wear appropriate PPE and ensure ventilation.",
},
ethylene_oxide: {
tier: "medium",
label: "ETHYLENE-OXIDE STERILIZED",
text: "Verify aeration time before opening.",
},
silicone: { tier: "low", label: "silicone", text: "Contains silicone" },
pvc: { tier: "low", label: "PVC", text: "Contains PVC" },
dehp: { tier: "low", label: "DEHP", text: "Contains DEHP; consider neonatal exposure." },
bpa: { tier: "low", label: "BPA", text: "Contains BPA" },
adhesive_acrylic: {
tier: "low",
label: "acrylic adhesive",
text: "Acrylic adhesive; possible contact-allergen.",
},
};
function activeAllergenSignals(allergens) {
const flags = allergens?.flags;
if (!flags || typeof flags !== "object") return [];
return Object.entries(flags).reduce((signals, [key, value]) => {
if (!value) return signals;
const signal = ALLERGEN_SIGNAL_COPY[key];
if (!signal) {
console.warn("Unknown allergen flag skipped", key);
return signals;
}
signals.push({ key, ...signal });
return signals;
}, []);
}
function AllergenEducationBlock({ allergens }) {
const signals = activeAllergenSignals(allergens);
if (!signals.length) return null;
const high = signals.filter((signal) => signal.tier === "high");
const medium = signals.filter((signal) => signal.tier === "medium");
const low = signals.filter((signal) => signal.tier === "low");
const confidence = Number(allergens?.confidence);
const hasConfidence = Number.isFinite(confidence);
const lowConfidence = hasConfidence && confidence < 0.7;
return (
Allergen / Material Safety
{high.map((signal) => (
))}
{medium.map((signal) => (
))}
{low.length > 0 && (
{low.map((signal) => signal.text).join("; ")}
)}
{(hasConfidence || lowConfidence) && (
{hasConfidence && {Math.round(confidence * 100)}% confidence}
{lowConfidence && (
{hasConfidence ? " - " : ""}Low confidence - verify against package labeling.
)}
)}
);
}
function AllergenSignalRow({ signal, tier }) {
const tone = tier === "high"
? { bg: "var(--ss-match-reject-bg)", fg: "var(--ss-match-reject)", bd: "#DAB5AC" }
: { bg: "var(--ss-open-bg)", fg: "#7A5615", bd: "#E1CC9E" };
return (
{signal.label}
{signal.text}
);
}
function VendorRepBlock({ manufacturer, rep, repCount }) {
if (isDisplayableCanvasRep(rep)) {
const location = [rep.home_base_city, rep.home_base_state].filter(Boolean).join(", ");
return (
Vendor rep {repCount > 1 ? `(${repCount} on file)` : ""}
verified {rep.verified}
{rep.full_name || "Unnamed rep"}
{[rep.title, rep.company_name, rep.rep_type, location].filter(Boolean).join(" / ")}
);
}
if (!manufacturer) return null;
return (
Vendor contact
No verified rep is on file for {manufacturer}. Use the manufacturer contact path for now.
);
}
function NoMatchEducation() {
return (
Why no match?
GUDID covers about 30% of OR supplies. This is normal for commodity supplies; add it to the library when the item matters.
);
}
function MiniSpinner() {
return (
);
}
function productInfoNotice(background) {
return {
marginTop: 16,
background,
border: "1px solid var(--ss-line)",
borderRadius: 6,
padding: 14,
display: "flex",
alignItems: "center",
gap: 10,
color: "var(--ss-gray-900)",
fontSize: 13,
};
}
function riskTextForClass(fdaClass) {
const key = String(fdaClass || "").trim().toUpperCase();
if (key === "I") return "Low risk / general controls";
if (key === "II") return "Moderate risk / special controls";
if (key === "III") return "High risk / PMA pathway";
return "Not classified";
}
function isDisplayableCanvasRep(rep) {
if (!rep || Number(rep.verified || 0) <= 0) return false;
const email = String(rep.email || "").toLowerCase();
const name = String(rep.full_name || "").toLowerCase();
const company = String(rep.company_name || "").toLowerCase();
const localPart = email.split("@")[0] || "";
const label = `${rep.full_name || ""} ${rep.title || ""}`.toLowerCase();
if (email.includes(".demo@") || email.includes("@demo.") || /\bdemo\b/.test(name) || /\bdemo\b/.test(company)) return false;
if (localPart.includes("customerservice") || localPart.includes("customer.service") || localPart.includes("orders")) return false;
if (["info", "support", "service", "sales"].includes(localPart)) return false;
return !(/\bcustomer service\b|\border(s|ing)?\b|\bsupport\b|\bservice desk\b/.test(label));
}
const repChipStyle = {
display: "inline-flex",
alignItems: "center",
padding: "4px 8px",
borderRadius: 99,
background: "var(--ss-warm-white)",
border: "1px solid var(--ss-line)",
color: "var(--ss-ink)",
fontSize: 12,
fontWeight: 600,
textDecoration: "none",
};
function DetailTile({ label, value, mono = false }) {
return (
);
}
function DetailPair({ label, value, mono = false }) {
return (
{label}
{value}
);
}
function MatchGlyph({ kind, size = 12 }) {
const props = { width: size, height: size, viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.7, strokeLinecap: "round", strokeLinejoin: "round" };
if (kind === "check") return ;
if (kind === "x") return ;
if (kind === "alert") return ;
if (kind === "review") return ;
return ;
}
const gudidGhostButton = {
padding: "6px 10px",
borderRadius: 5,
background: "transparent",
border: "1px solid var(--ss-line)",
color: "var(--ss-gray-900)",
fontSize: 12,
fontWeight: 500,
cursor: "pointer",
fontFamily: "var(--ss-sans)",
};
const gudidPrimaryButton = {
padding: "6px 10px",
borderRadius: 5,
background: "var(--ss-ink)",
border: "1px solid var(--ss-ink)",
color: "var(--ss-cream)",
fontSize: 12,
fontWeight: 500,
cursor: "pointer",
fontFamily: "var(--ss-sans)",
};
Object.assign(window, {
GudidMatchApprovalView,
MatchConfidenceChip,
getGudidBand,
buildCategoryGroups,
sectionKeyForItem,
});