// MDM SORP — App shell with sidebar + topbar + router.
const { useState, useEffect, useMemo, useRef, useCallback, createContext, useContext } = React;
function AppShell() {
const { t, lang, setLang } = useI18n();
const [route, setRoute] = useState(() => {
const h = location.hash.slice(1).split("/");
return { name: h[0] || "home", params: h[1] ? { id: decodeURIComponent(h[1]) } : {} };
});
const [cpOpen, setCpOpen] = useState(false);
const [collapsed, setCollapsed] = useState(false);
const navigate = useCallback((name, params = {}) => {
setRoute({ name, params });
const hash = params.id ? `${name}/${encodeURIComponent(params.id)}` : name;
history.replaceState(null, "", "#" + hash);
}, []);
// listen for external hash changes (e.g. design canvas iframes)
useEffect(() => {
const h = () => {
const seg = location.hash.slice(1).split("/");
setRoute({ name: seg[0] || "home", params: seg[1] ? { id: decodeURIComponent(seg[1]) } : {} });
};
window.addEventListener("hashchange", h);
return () => window.removeEventListener("hashchange", h);
}, []);
// hotkey: cmd-K
useEffect(() => {
const h = (e) => {
if ((e.metaKey || e.ctrlKey) && (e.key === "k" || e.key === "K")) {
e.preventDefault(); setCpOpen(true);
}
};
window.addEventListener("keydown", h);
return () => window.removeEventListener("keydown", h);
}, []);
const nav = [
{ sect: "nav_work", items: [
{ id: "home", icon: "home" },
]},
{ sect: "nav_data", items: [
{ id: "deals", icon: "deals", count: 38 },
{ id: "parties", icon: "parties", count: 2184 },
{ id: "nomenclature", icon: "box", count: 1248 },
{ id: "documents", icon: "docs", count: 1248 },
]},
{ sect: "nav_intake", items: [
{ id: "import", icon: "upload", count: 2 },
]},
{ sect: "nav_queues", items: [
{ id: "enrichment", icon: "queue", count: 13, hot: true },
{ id: "dedup", icon: "merge", count: 4 },
{ id: "normalize", icon: "list", count: 137 },
]},
{ sect: "nav_enrich", items: [
{ id: "sourcing", icon: "lightbulb" },
]},
{ sect: "nav_admin", items: [
{ id: "dashboards", icon: "bar" },
{ id: "schema", icon: "book" },
{ id: "integrations", icon: "plug" },
{ id: "audit", icon: "history" },
]},
];
const crumbs = computeCrumbs(route, t);
const fullBleed = route.name === "form";
if (fullBleed) {
return
{desc}