// Shared workspace structure. Uses the same primitives and brand tokens as app.jsx. function Icon({ name, size = 20 }) { const paths = { home: ( <> ), folder: ( ), server: ( <> ), search: ( <> ), audit: ( <> ), timeline: ( <> ), truck: ( <> ), settings: ( <> ), menu: , close: , plus: , arrow: , logout: ( <> ), link: ( <> ), }; return ( ); } function Brand() { return ( Watson by{" "} StoreConnect ); } const workspaceLinks = [ { key: "dashboard", label: "Overview", icon: "home", group: "Workspace" }, { key: "list", label: "Investigations", icon: "folder" }, { key: "environments", label: "Environments", icon: "server" }, { key: "lookup", label: "Record lookup", icon: "search", group: "Investigation tools", }, { key: "audit", label: "Payment audit", icon: "audit" }, { key: "timeline", label: "Timeline", icon: "timeline" }, { key: "shipments", label: "Shipments", icon: "truck" }, { key: "integrations", label: "Setup & connections", icon: "settings", group: "Account", }, ]; function Nav({ page, user, onNav, onLogout, children }) { const [menuOpen, setMenuOpen] = React.useState(false); const dialog = React.useRef(null); const menuButton = React.useRef(null); const active = ["show", "new"].includes(page) ? "list" : ["env-show", "env-new"].includes(page) ? "environments" : page; const pageName = workspaceLinks.find((l) => l.key === active)?.label || "Overview"; const navigate = (key) => { closeMenu(false); onNav(key); }; const closeMenu = (restore = true) => { dialog.current?.close(); setMenuOpen(false); if (restore) menuButton.current?.focus(); }; React.useEffect(() => { const mq = window.matchMedia("(min-width: 1100px)"); const resize = () => { if (mq.matches) closeMenu(false); }; mq.addEventListener("change", resize); return () => mq.removeEventListener("change", resize); }, []); React.useEffect(() => { if (!menuOpen) return; const previous = document.body.style.overflow; document.body.style.overflow = "hidden"; return () => { document.body.style.overflow = previous; }; }, [menuOpen]); const links = (mobile = false) => ( <> { e.preventDefault(); navigate("dashboard"); }} aria-label="Watson overview" > {mobile && ( )}
{user?.name} {user?.salesforce_session ? "Salesforce session" : "Watson account"}
); return (
{ e.preventDefault(); document.getElementById("main-content")?.focus(); }} > Skip to main content { if (e.key !== "Tab") return; const controls = Array.from( dialog.current.querySelectorAll("a[href], button:not(:disabled)"), ).filter((el) => el.getClientRects().length); const first = controls[0], last = controls[controls.length - 1]; if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last?.focus(); } else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first?.focus(); } }} ref={dialog} className="mobile-nav" aria-label="Navigation" onCancel={(e) => { e.preventDefault(); closeMenu(); }} onClose={() => setMenuOpen(false)} onClick={(e) => { if (e.target === dialog.current) closeMenu(); }} >
{links(true)}
Workspace {" "} {pageName}
{children}
Watson StoreConnect support workspace
); } function PageHeading({ eyebrow, title, description, children }) { return (
{eyebrow &&

{eyebrow}

}

{title}

{description &&

{description}

}
{children &&
{children}
}
); } function InvestigationRow({ inv, onNav }) { const record = primarySalesforceRecord(inv); return (
  • { e.preventDefault(); onNav("show", inv.id); }} > {inv.title}
    {inv.environment || "Default environment"} {record && ( {salesforceRecordLabel(record.type)} {record.number} )} {inv.user?.name || "Investigation"}
    {timeAgo(inv.updated_at)}
  • ); } function EmptyState({ title, children }) { return (

    {title}

    {children}

    ); } function Dashboard({ user, investigations, environments, onNav }) { const open = investigations.filter((i) => i.status === "open").length; const resolved = investigations.filter((i) => i.status === "resolved").length; return ( <> onNav("new")}> New investigation

    Connect the dots

    From first question
    to a clear resolution.

    Cases, bugs, tickets and tools. One place to find the answer.

    onNav("list")} /> onNav("list")} /> onNav("list")} /> onNav("environments")} />

    Recent investigations

    Your team's latest work

    onNav("list")}> View all
    {investigations.length ? (
      {investigations.slice(0, 6).map((inv) => ( ))}
    ) : ( Create an investigation to keep the subject, evidence and findings together. )}

    Start with a tool

    Get straight to the evidence

    {[ { key: "lookup", icon: "search", title: "Record lookup", text: "Find an order, payment or record", }, { key: "audit", icon: "audit", title: "Payment audit", text: "Trace transactions and mismatches", }, { key: "timeline", icon: "timeline", title: "Build a timeline", text: "Follow the sequence of events", }, { key: "shipments", icon: "truck", title: "Check shipments", text: "Review fulfillment and sync", }, ].map((t) => ( { e.preventDefault(); onNav(t.key); }} > {t.title} {t.text} ))}

    Keep the subject connected

    Search Salesforce from an investigation and link the right Case, Bug or Ticket using your own access.

    onNav("new")}> Start an investigation
    ); } function InvestigationList({ investigations, onNav }) { const [filter, setFilter] = React.useState("all"); const [query, setQuery] = React.useState(""); const filtered = investigations.filter( (i) => (filter === "all" || i.status === filter) && [ i.title, primarySalesforceRecord(i)?.number, salesforceRecordLabel(primarySalesforceRecord(i)?.type), i.environment, i.user?.name, ] .join(" ") .toLowerCase() .includes(query.toLowerCase()), ); return ( <> onNav("new")}> New investigation
    {["all", "open", "resolved", "stale"].map((s) => ( ))}

    {filtered.length} investigation{filtered.length === 1 ? "" : "s"}

    {filtered.length ? (
      {filtered.map((inv) => ( ))}
    ) : ( Try another search or status, or create a new investigation. )}
    ); } function EnvironmentList({ environments, onNav }) { const [query, setQuery] = React.useState(""); const visible = environments.filter((env) => [env.name, env.rails_app, env.db_app] .join(" ") .toLowerCase() .includes(query.toLowerCase()), ); return ( <> onNav("env-new")}> Add environment

    {visible.length} environment{visible.length === 1 ? "" : "s"}

    {visible.map((env) => (

    { e.preventDefault(); onNav("env-show", env.id); }} > {env.name}

    Application
    {env.rails_app || env.db_app || "Not set"}
    Database
    {env.db_app || "Not set"}
    onNav("env-show", env.id)}> View environment
    ))}
    {!visible.length && ( Try another search or add an environment to get started. )} ); }