import { useEffect, useState } from "react"; import { createRoot } from "react-dom/client"; import { ActivityIndicator, AdminNavigationPanel, AppHeader, ApplicationPanel, ApplicationShell, Button, HeaderNavigation, HeaderProfile, Icon, SettingsCard, ToastStack, UserProfileMenu, useApplicationWorkspace } from "@nodedc/ui-react"; import "@nodedc/tokens/tokens.css"; import "@nodedc/tokens/themes.css"; import "@nodedc/ui-core/styles.css"; import { desktopLogin } from "./api"; import { useNode } from "./useNode"; import { roots, views, type RootId, type ViewId } from "./nodeModel"; import { NodeOverview } from "./NodeOverview"; import { USBView, DiagnosticsView, NetworkView } from "./InventoryViews"; import { SystemAccess } from "./SystemAccess"; import { TailnetAccess } from "./TailnetAccess"; import { useEnvironment } from "./useEnvironment"; import { EnvironmentView } from "./EnvironmentView"; import { CoreConnectionView } from "./CoreConnectionView"; import "./node.css"; import { NodeSensors } from "./NodeSensors"; function App() { const node = useNode(); const { value, pending, locked, refresh, failure } = node; const environment = useEnvironment(!!value, failure); const refreshAll = () => {if(!environment.running) {void refresh();void environment.refresh();}}; const [root, setRoot] = useState("system"); const workspace = useApplicationWorkspace({ activeView: "environment" }); const [adding, setAdding] = useState(false); const [theme, setTheme] = useState(() => localStorage.getItem("node-theme") === "light" ? "light" : "dark"); // Theme applies to body portals as well as the application shell. useEffect(() => { document.documentElement.dataset.nodedcTheme = theme; }, [theme]); const currentRoot = roots.find(item => item.id === root)!; const currentView = views.find(item => item.id === workspace.activeView); function openView(id: ViewId) { if(environment.running) return; setAdding(false); setRoot(views.find(item => item.id === id)!.root); workspace.openView(id); } function selectRoot(id: RootId) { const first = roots.find(item => item.id === id)!.first; if (first) openView(first); } const content = !value ? null : workspace.activeView === "environment" ? : workspace.activeView === "overview" ? : workspace.activeView === "sensors" ? : workspace.activeView === "network" ? : workspace.activeView === "usb" ? : workspace.activeView === "core" ? : workspace.activeView === "diagnostics" ? : workspace.activeView === "tailscale" ? : workspace.activeView === "ssh" ? setAdding(false)} /> : null; return <> } brandLabel="Mission Core Node" center={ ({ value: item.id, label: item.label, disabled: !value || !item.first || environment.running }))} onChange={selectRoot} />} right={ { const next = theme === "dark" ? "light" : "dark"; setTheme(next); localStorage.setItem("node-theme", next); } }]} />} />} navigationOpen={!!value && workspace.navigationOpen} contentOpen={!!value && workspace.contentOpen} contentExpanded={workspace.contentExpanded} navigation={, onSelect: () => openView("overview") }] : []} items={views.filter(item => item.root === root).map(item => ({ id: item.id, label: item.label, icon: }))} activeId={workspace.activeView ?? undefined} onItemChange={id => openView(id as ViewId)} footer={Mission Core Node · {value?.version}} />} content={currentView && setAdding(true) }] : []), { label: "Обновить сведения", icon: "refresh", disabled: pending || environment.running, onClick: refreshAll }]}>{content}} stage={
{value ?
{roots.map(item => )}
: {pending ? :

{locked ? "Подтвердите доступ в системном окне." : "Не удалось связаться со службой. Повторите подключение."}

}
}
} /> ; } createRoot(document.getElementById("root")!).render();