From 8f38c76f7977bb02e16e7015a54d891a27468394 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 13 Aug 2026 11:51:08 +0300 Subject: [PATCH] fix(device-manager): align shell with NODE.DC UX canon --- apps/device-manager/public/nodedc-logo.svg | 1 + apps/device-manager/public/nodedc-mark.svg | 1 + apps/device-manager/src/DeviceManagerApp.tsx | 172 ++++++++----------- apps/device-manager/src/styles.css | 79 ++++----- 4 files changed, 108 insertions(+), 145 deletions(-) create mode 100644 apps/device-manager/public/nodedc-logo.svg create mode 100644 apps/device-manager/public/nodedc-mark.svg diff --git a/apps/device-manager/public/nodedc-logo.svg b/apps/device-manager/public/nodedc-logo.svg new file mode 100644 index 0000000..92b19d8 --- /dev/null +++ b/apps/device-manager/public/nodedc-logo.svg @@ -0,0 +1 @@ + diff --git a/apps/device-manager/public/nodedc-mark.svg b/apps/device-manager/public/nodedc-mark.svg new file mode 100644 index 0000000..866cfe4 --- /dev/null +++ b/apps/device-manager/public/nodedc-mark.svg @@ -0,0 +1 @@ + diff --git a/apps/device-manager/src/DeviceManagerApp.tsx b/apps/device-manager/src/DeviceManagerApp.tsx index dce37ec..50bd4db 100644 --- a/apps/device-manager/src/DeviceManagerApp.tsx +++ b/apps/device-manager/src/DeviceManagerApp.tsx @@ -6,10 +6,10 @@ import { ApplicationShell, Button, GlassSurface, - HeaderNavigation, HeaderProfile, HeaderWorkspace, Icon, + IconButton, Select, SettingsCard, StatusBadge, @@ -129,10 +129,9 @@ export function DeviceManagerApp() { [activeOwnerRef, projects], ); const activeProject = projects.find((project) => project.projectRef === activeProjectRef) ?? null; + const creatableOwnerScopes = session?.actor.ownerScopes ?? []; const capabilities = new Set(activeProject?.access.capabilities ?? []); - const canCreateProject = Boolean( - session?.actor.ownerScopes.some((scope) => scope.ownerRef === activeOwnerRef), - ); + const canCreateProject = creatableOwnerScopes.length > 0; const canManageCollections = capabilities.has("collection.manage"); const canEnroll = capabilities.has("device.enroll"); const canClaim = capabilities.has("device.claim"); @@ -182,26 +181,18 @@ export function DeviceManagerApp() { NODE.DC} + brand={NODE.DC} brandHref="/" - left={ + center={ } - center={ - shell.openNavigation()} - /> - } right={ setProjectDialogOpen(true)} onOpenProject={openProject} error={error} onDismissError={() => setError(null)} @@ -234,27 +220,44 @@ export function DeviceManagerApp() { navigation={ , - }] : []} + title="Проекты" + headerActions={ + setProjectDialogOpen(true)} + > + + + } contextSlot={ ownerScopes.length ? ( - ({ + value: scope.ownerRef, + label: scope.displayName, + description: scope.scopeKind === "company" ? "Компания" : "Личный контур", + }))} + onChange={setActiveOwnerRef} + searchable={ownerScopes.length > 6} + /> + {visibleProjects.length ? ( + ({ - value: scope.ownerRef, - label: scope.displayName, - description: scope.scopeKind === "company" ? "Компания" : "Личный контур", - }))} - onChange={onOwnerChange} - /> - ) : null} - - - - {error ? (
@@ -412,14 +373,13 @@ function DeviceStage({
) : null} -
- - - - -
- -
+ +
+ NODEDC / DEVICE CORE +

Device Core

+

Единый контур подключения, учёта и управления устройствами.

+
+
DEVICE PROJECTS

Рабочие проекты

Доступ определяется Hub и project grants @@ -451,7 +411,8 @@ function DeviceStage({

Создание доступно только в owner scope, подтверждённом Hub.

)} -
+
+ ); } @@ -573,9 +534,10 @@ function EntityList({ items, empty }: { items: Array<{ id: string; title: string ))}; } -function ProjectDialog({ open, ownerScope, onClose, onCreated, onError }: { +function ProjectDialog({ open, ownerScopes, initialOwnerRef, onClose, onCreated, onError }: { open: boolean; - ownerScope: OwnerScopeClaim | null; + ownerScopes: OwnerScopeClaim[]; + initialOwnerRef: string; onClose: () => void; onCreated: () => Promise; onError: (reason: unknown) => void; @@ -583,7 +545,15 @@ function ProjectDialog({ open, ownerScope, onClose, onCreated, onError }: { const [name, setName] = useState(""); const [key, setKey] = useState(""); const [description, setDescription] = useState(""); + const [ownerRef, setOwnerRef] = useState(initialOwnerRef); const [pending, setPending] = useState(false); + useEffect(() => { + if (!open) return; + setOwnerRef(ownerScopes.some((scope) => scope.ownerRef === initialOwnerRef) + ? initialOwnerRef + : ownerScopes[0]?.ownerRef ?? ""); + }, [initialOwnerRef, open, ownerScopes]); + const ownerScope = ownerScopes.find((scope) => scope.ownerRef === ownerRef) ?? null; const submit = async (event: FormEvent) => { event.preventDefault(); if (!ownerScope) return; @@ -605,6 +575,16 @@ function ProjectDialog({ open, ownerScope, onClose, onCreated, onError }: { }>
+