fix(device-manager): align shell with NODE.DC UX canon
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
<svg id="nodedc-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 220.82 54.55"><defs><style>.cls-1{fill:#e2e1e1;}.cls-2{fill:#dbdbdb;stroke:#dbdbdb;stroke-miterlimit:10;stroke-width:0.75px;}</style></defs><path class="cls-1" d="M52.8,23.61,46.92,33.76,41.05,23.61H52.8m18-10.39H23.06L46.92,54.55Z"/><polygon class="cls-1" points="31.28 33.13 18.11 10.34 75.73 10.34 62.59 33.13 74.28 33.13 93.22 0 0 0 19.61 33.13 31.28 33.13"/><path class="cls-2" d="M116.35,18.49V1h1.27l10.34,15V1h1.33V18.49H128l-10.34-15v15Z"/><path class="cls-2" d="M140.43,18.64c-4.79,0-8.16-3.72-8.16-8.89S135.64.86,140.43.86s8.17,3.72,8.17,8.89S145.25,18.64,140.43,18.64Zm0-1.25c4,0,6.79-3.17,6.79-7.64s-2.77-7.64-6.79-7.64-6.77,3.17-6.77,7.64S136.44,17.39,140.43,17.39Z"/><path class="cls-2" d="M151.6,18.49V1h5.1c5.54,0,8.79,3.42,8.79,8.74s-3.25,8.74-8.79,8.74ZM153,17.24h3.75c4.77,0,7.42-2.92,7.42-7.49s-2.65-7.49-7.42-7.49H153Z"/><path class="cls-2" d="M168.49,1h10.77V2.26h-9.42V8.93h7.89v1.25h-7.89v7.06h9.74v1.25H168.49Z"/><path class="cls-2" d="M188.88,18.49V1H194c5.54,0,8.79,3.42,8.79,8.74s-3.25,8.74-8.79,8.74Zm1.35-1.25H194c4.77,0,7.41-2.92,7.41-7.49S198.75,2.26,194,2.26h-3.75Z"/><path class="cls-2" d="M205.15,9.75c0-5.24,3.19-8.89,8.11-8.89a6.8,6.8,0,0,1,7.1,5.52h-1.43a5.54,5.54,0,0,0-5.74-4.27c-4.05,0-6.64,3.17-6.64,7.64s2.54,7.64,6.59,7.64a5.46,5.46,0,0,0,5.74-4.29h1.43c-.75,3.52-3.4,5.54-7.15,5.54C208.27,18.64,205.15,15.05,205.15,9.75Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg id="nodedc-mark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 93.22 54.55"><path fill="#e2e1e1" d="M52.8 23.61 46.92 33.76 41.05 23.61H52.8m18-10.39H23.06l23.86 41.33Z"/><polygon fill="#e2e1e1" points="31.28 33.13 18.11 10.34 75.73 10.34 62.59 33.13 74.28 33.13 93.22 0 0 0 19.61 33.13 31.28 33.13"/></svg>
|
||||||
|
After Width: | Height: | Size: 315 B |
@@ -6,10 +6,10 @@ import {
|
|||||||
ApplicationShell,
|
ApplicationShell,
|
||||||
Button,
|
Button,
|
||||||
GlassSurface,
|
GlassSurface,
|
||||||
HeaderNavigation,
|
|
||||||
HeaderProfile,
|
HeaderProfile,
|
||||||
HeaderWorkspace,
|
HeaderWorkspace,
|
||||||
Icon,
|
Icon,
|
||||||
|
IconButton,
|
||||||
Select,
|
Select,
|
||||||
SettingsCard,
|
SettingsCard,
|
||||||
StatusBadge,
|
StatusBadge,
|
||||||
@@ -129,10 +129,9 @@ export function DeviceManagerApp() {
|
|||||||
[activeOwnerRef, projects],
|
[activeOwnerRef, projects],
|
||||||
);
|
);
|
||||||
const activeProject = projects.find((project) => project.projectRef === activeProjectRef) ?? null;
|
const activeProject = projects.find((project) => project.projectRef === activeProjectRef) ?? null;
|
||||||
|
const creatableOwnerScopes = session?.actor.ownerScopes ?? [];
|
||||||
const capabilities = new Set(activeProject?.access.capabilities ?? []);
|
const capabilities = new Set(activeProject?.access.capabilities ?? []);
|
||||||
const canCreateProject = Boolean(
|
const canCreateProject = creatableOwnerScopes.length > 0;
|
||||||
session?.actor.ownerScopes.some((scope) => scope.ownerRef === activeOwnerRef),
|
|
||||||
);
|
|
||||||
const canManageCollections = capabilities.has("collection.manage");
|
const canManageCollections = capabilities.has("collection.manage");
|
||||||
const canEnroll = capabilities.has("device.enroll");
|
const canEnroll = capabilities.has("device.enroll");
|
||||||
const canClaim = capabilities.has("device.claim");
|
const canClaim = capabilities.has("device.claim");
|
||||||
@@ -182,26 +181,18 @@ export function DeviceManagerApp() {
|
|||||||
<ApplicationShell
|
<ApplicationShell
|
||||||
header={
|
header={
|
||||||
<AppHeader
|
<AppHeader
|
||||||
brand={<span className="device-manager-brand">NODE.DC</span>}
|
brand={<img className="device-manager-brand" src="/nodedc-logo.svg" alt="NODE.DC" />}
|
||||||
brandHref="/"
|
brandHref="/"
|
||||||
left={
|
center={
|
||||||
<button
|
<button
|
||||||
className="device-manager-workspace-trigger"
|
className="device-manager-workspace-trigger"
|
||||||
type="button"
|
type="button"
|
||||||
aria-label="Открыть Device Core"
|
aria-label="Открыть навигацию Device Core"
|
||||||
onClick={shell.toggleNavigation}
|
onClick={shell.toggleNavigation}
|
||||||
>
|
>
|
||||||
<HeaderWorkspace label="Device Core" />
|
<HeaderWorkspace kind="mark" label="Device Core" imageUrl="/nodedc-mark.svg" />
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
center={
|
|
||||||
<HeaderNavigation
|
|
||||||
label="Device Core"
|
|
||||||
value="devices"
|
|
||||||
items={[{ value: "devices", label: "Device Core" }]}
|
|
||||||
onChange={() => shell.openNavigation()}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
right={
|
right={
|
||||||
<HeaderProfile>
|
<HeaderProfile>
|
||||||
<UserProfileMenu
|
<UserProfileMenu
|
||||||
@@ -219,12 +210,7 @@ export function DeviceManagerApp() {
|
|||||||
}
|
}
|
||||||
stage={
|
stage={
|
||||||
<DeviceStage
|
<DeviceStage
|
||||||
ownerScopes={ownerScopes}
|
|
||||||
activeOwnerRef={activeOwnerRef}
|
|
||||||
onOwnerChange={setActiveOwnerRef}
|
|
||||||
projects={visibleProjects}
|
projects={visibleProjects}
|
||||||
canCreateProject={canCreateProject}
|
|
||||||
onCreateProject={() => setProjectDialogOpen(true)}
|
|
||||||
onOpenProject={openProject}
|
onOpenProject={openProject}
|
||||||
error={error}
|
error={error}
|
||||||
onDismissError={() => setError(null)}
|
onDismissError={() => setError(null)}
|
||||||
@@ -234,27 +220,44 @@ export function DeviceManagerApp() {
|
|||||||
navigation={
|
navigation={
|
||||||
<AdminNavigationPanel
|
<AdminNavigationPanel
|
||||||
eyebrow="DEVICE CORE"
|
eyebrow="DEVICE CORE"
|
||||||
title="Устройства"
|
title="Проекты"
|
||||||
contexts={activeProject ? [{
|
headerActions={
|
||||||
id: activeProject.projectRef,
|
<IconButton
|
||||||
label: activeProject.name,
|
label="Новый проект"
|
||||||
description: activeProject.ownerScope.displayName,
|
disabled={!canCreateProject}
|
||||||
active: true,
|
onClick={() => setProjectDialogOpen(true)}
|
||||||
icon: <Icon name="apps" />,
|
>
|
||||||
}] : []}
|
<Icon name="plus" size={16} />
|
||||||
|
</IconButton>
|
||||||
|
}
|
||||||
contextSlot={
|
contextSlot={
|
||||||
ownerScopes.length ? (
|
ownerScopes.length ? (
|
||||||
<Select
|
<div className="device-manager-navigation-context">
|
||||||
value={activeOwnerRef}
|
<Select
|
||||||
label="Контур владельца"
|
value={activeOwnerRef}
|
||||||
options={ownerScopes.map((scope) => ({
|
label="Контур владельца"
|
||||||
value: scope.ownerRef,
|
options={ownerScopes.map((scope) => ({
|
||||||
label: scope.displayName,
|
value: scope.ownerRef,
|
||||||
description: scope.scopeKind === "company" ? "Компания" : "Личный контур",
|
label: scope.displayName,
|
||||||
}))}
|
description: scope.scopeKind === "company" ? "Компания" : "Личный контур",
|
||||||
onChange={setActiveOwnerRef}
|
}))}
|
||||||
searchable={ownerScopes.length > 6}
|
onChange={setActiveOwnerRef}
|
||||||
/>
|
searchable={ownerScopes.length > 6}
|
||||||
|
/>
|
||||||
|
{visibleProjects.length ? (
|
||||||
|
<Select
|
||||||
|
value={activeProjectRef}
|
||||||
|
label="Проект"
|
||||||
|
options={visibleProjects.map((project) => ({
|
||||||
|
value: project.projectRef,
|
||||||
|
label: project.name,
|
||||||
|
description: project.access.projectRole || "read",
|
||||||
|
}))}
|
||||||
|
onChange={(projectRef) => openProject(projectRef)}
|
||||||
|
searchable={visibleProjects.length > 6}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
items={activeProject ? visibleNavigationItems.map((item) => ({
|
items={activeProject ? visibleNavigationItems.map((item) => ({
|
||||||
@@ -262,7 +265,7 @@ export function DeviceManagerApp() {
|
|||||||
label: item.label,
|
label: item.label,
|
||||||
icon: <Icon name={item.icon} />,
|
icon: <Icon name={item.icon} />,
|
||||||
})) : []}
|
})) : []}
|
||||||
activeId={shell.activeView ?? undefined}
|
activeId={activeProject ? activeView : undefined}
|
||||||
footer={<span>{session.actor.hubRole} · {projects.length} проектов</span>}
|
footer={<span>{session.actor.hubRole} · {projects.length} проектов</span>}
|
||||||
onClose={shell.closeNavigation}
|
onClose={shell.closeNavigation}
|
||||||
onItemChange={(id) => shell.openView(id as ViewId)}
|
onItemChange={(id) => shell.openView(id as ViewId)}
|
||||||
@@ -306,7 +309,8 @@ export function DeviceManagerApp() {
|
|||||||
|
|
||||||
<ProjectDialog
|
<ProjectDialog
|
||||||
open={projectDialogOpen}
|
open={projectDialogOpen}
|
||||||
ownerScope={ownerScopes.find((scope) => scope.ownerRef === activeOwnerRef) ?? null}
|
ownerScopes={creatableOwnerScopes}
|
||||||
|
initialOwnerRef={activeOwnerRef}
|
||||||
onClose={() => setProjectDialogOpen(false)}
|
onClose={() => setProjectDialogOpen(false)}
|
||||||
onCreated={async () => {
|
onCreated={async () => {
|
||||||
setProjectDialogOpen(false);
|
setProjectDialogOpen(false);
|
||||||
@@ -349,61 +353,18 @@ export function DeviceManagerApp() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function DeviceStage({
|
function DeviceStage({
|
||||||
ownerScopes,
|
|
||||||
activeOwnerRef,
|
|
||||||
onOwnerChange,
|
|
||||||
projects,
|
projects,
|
||||||
canCreateProject,
|
|
||||||
onCreateProject,
|
|
||||||
onOpenProject,
|
onOpenProject,
|
||||||
error,
|
error,
|
||||||
onDismissError,
|
onDismissError,
|
||||||
}: {
|
}: {
|
||||||
ownerScopes: OwnerScopeClaim[];
|
|
||||||
activeOwnerRef: string;
|
|
||||||
onOwnerChange: (value: string) => void;
|
|
||||||
projects: ProjectSummary[];
|
projects: ProjectSummary[];
|
||||||
canCreateProject: boolean;
|
|
||||||
onCreateProject: () => void;
|
|
||||||
onOpenProject: (projectRef: string, view?: ViewId) => void;
|
onOpenProject: (projectRef: string, view?: ViewId) => void;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
onDismissError: () => void;
|
onDismissError: () => void;
|
||||||
}) {
|
}) {
|
||||||
const totals = projects.reduce(
|
|
||||||
(sum, project) => ({
|
|
||||||
devices: sum.devices + project.counts.devices,
|
|
||||||
collections: sum.collections + project.counts.collections,
|
|
||||||
discoveries: sum.discoveries + project.counts.discoveries,
|
|
||||||
}),
|
|
||||||
{ devices: 0, collections: 0, discoveries: 0 },
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<div className="device-manager-stage">
|
<div className="device-manager-stage">
|
||||||
<section className="device-manager-hero">
|
|
||||||
<div>
|
|
||||||
<span>UNIVERSAL DEVICE PLANE</span>
|
|
||||||
<h1>Все устройства — в одном управляемом контуре.</h1>
|
|
||||||
<p>Проекты, безопасное подключение, инвентарь и коллекции без привязки интерфейса к конкретному производителю.</p>
|
|
||||||
</div>
|
|
||||||
<div className="device-manager-hero__actions">
|
|
||||||
{ownerScopes.length ? (
|
|
||||||
<Select
|
|
||||||
value={activeOwnerRef}
|
|
||||||
label="Контур владельца"
|
|
||||||
options={ownerScopes.map((scope) => ({
|
|
||||||
value: scope.ownerRef,
|
|
||||||
label: scope.displayName,
|
|
||||||
description: scope.scopeKind === "company" ? "Компания" : "Личный контур",
|
|
||||||
}))}
|
|
||||||
onChange={onOwnerChange}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
<Button variant="primary" icon={<Icon name="plus" />} disabled={!canCreateProject} onClick={onCreateProject}>
|
|
||||||
Новый проект
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{error ? (
|
{error ? (
|
||||||
<div className="device-manager-alert" role="alert">
|
<div className="device-manager-alert" role="alert">
|
||||||
<Icon name="alert" />
|
<Icon name="alert" />
|
||||||
@@ -412,14 +373,13 @@ function DeviceStage({
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<section className="device-manager-metrics" aria-label="Состояние контура">
|
<GlassSurface className="device-manager-home" padding="lg" tone="soft">
|
||||||
<Metric label="Проекты" value={projects.length} detail="доступно пользователю" />
|
<section className="device-manager-hero">
|
||||||
<Metric label="Устройства" value={totals.devices} detail="в выбранном контуре" />
|
<span>NODEDC / DEVICE CORE</span>
|
||||||
<Metric label="Подключение" value={totals.discoveries} detail="ожидают решения" tone={totals.discoveries ? "warning" : "success"} />
|
<h1>Device Core</h1>
|
||||||
<Metric label="Коллекции" value={totals.collections} detail="логических групп" />
|
<p>Единый контур подключения, учёта и управления устройствами.</p>
|
||||||
</section>
|
</section>
|
||||||
|
<section className="device-manager-projects">
|
||||||
<section className="device-manager-projects">
|
|
||||||
<div className="device-manager-section-title">
|
<div className="device-manager-section-title">
|
||||||
<div><span>DEVICE PROJECTS</span><h2>Рабочие проекты</h2></div>
|
<div><span>DEVICE PROJECTS</span><h2>Рабочие проекты</h2></div>
|
||||||
<small>Доступ определяется Hub и project grants</small>
|
<small>Доступ определяется Hub и project grants</small>
|
||||||
@@ -451,7 +411,8 @@ function DeviceStage({
|
|||||||
<p>Создание доступно только в owner scope, подтверждённом Hub.</p>
|
<p>Создание доступно только в owner scope, подтверждённом Hub.</p>
|
||||||
</GlassSurface>
|
</GlassSurface>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
</GlassSurface>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -573,9 +534,10 @@ function EntityList({ items, empty }: { items: Array<{ id: string; title: string
|
|||||||
))}</div>;
|
))}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ProjectDialog({ open, ownerScope, onClose, onCreated, onError }: {
|
function ProjectDialog({ open, ownerScopes, initialOwnerRef, onClose, onCreated, onError }: {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
ownerScope: OwnerScopeClaim | null;
|
ownerScopes: OwnerScopeClaim[];
|
||||||
|
initialOwnerRef: string;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onCreated: () => Promise<void>;
|
onCreated: () => Promise<void>;
|
||||||
onError: (reason: unknown) => void;
|
onError: (reason: unknown) => void;
|
||||||
@@ -583,7 +545,15 @@ function ProjectDialog({ open, ownerScope, onClose, onCreated, onError }: {
|
|||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [key, setKey] = useState("");
|
const [key, setKey] = useState("");
|
||||||
const [description, setDescription] = useState("");
|
const [description, setDescription] = useState("");
|
||||||
|
const [ownerRef, setOwnerRef] = useState(initialOwnerRef);
|
||||||
const [pending, setPending] = useState(false);
|
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) => {
|
const submit = async (event: FormEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (!ownerScope) return;
|
if (!ownerScope) return;
|
||||||
@@ -605,6 +575,16 @@ function ProjectDialog({ open, ownerScope, onClose, onCreated, onError }: {
|
|||||||
<WindowFooterActions><Button variant="ghost" onClick={onClose}>Отмена</Button><Button type="submit" form="device-project-form" variant="primary" disabled={pending || !ownerScope}>{pending ? "Создаём…" : "Создать"}</Button></WindowFooterActions>
|
<WindowFooterActions><Button variant="ghost" onClick={onClose}>Отмена</Button><Button type="submit" form="device-project-form" variant="primary" disabled={pending || !ownerScope}>{pending ? "Создаём…" : "Создать"}</Button></WindowFooterActions>
|
||||||
}>
|
}>
|
||||||
<form id="device-project-form" className="device-manager-form" onSubmit={submit}>
|
<form id="device-project-form" className="device-manager-form" onSubmit={submit}>
|
||||||
|
<Select
|
||||||
|
label="Контур проекта"
|
||||||
|
value={ownerRef}
|
||||||
|
options={ownerScopes.map((scope) => ({
|
||||||
|
value: scope.ownerRef,
|
||||||
|
label: scope.displayName,
|
||||||
|
description: scope.scopeKind === "company" ? "Коммерческий контур" : "Личный контур",
|
||||||
|
}))}
|
||||||
|
onChange={setOwnerRef}
|
||||||
|
/>
|
||||||
<TextField label="Название" value={name} onChange={(event) => setName(event.target.value)} required maxLength={160} />
|
<TextField label="Название" value={name} onChange={(event) => setName(event.target.value)} required maxLength={160} />
|
||||||
<TextField label="Ключ проекта" value={key} onChange={(event) => setKey(event.target.value.toLowerCase())} required pattern="[a-z][a-z0-9-]{1,62}" description="Стабильный ключ: латиница, цифры и дефис." />
|
<TextField label="Ключ проекта" value={key} onChange={(event) => setKey(event.target.value.toLowerCase())} required pattern="[a-z][a-z0-9-]{1,62}" description="Стабильный ключ: латиница, цифры и дефис." />
|
||||||
<TextAreaField label="Описание" value={description} onChange={(event) => setDescription(event.target.value)} maxLength={2000} />
|
<TextAreaField label="Описание" value={description} onChange={(event) => setDescription(event.target.value)} maxLength={2000} />
|
||||||
|
|||||||
@@ -205,10 +205,9 @@ textarea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.device-manager-brand {
|
.device-manager-brand {
|
||||||
color: var(--nodedc-text-primary);
|
display: block;
|
||||||
font-size: 0.86rem;
|
width: 124px;
|
||||||
font-weight: 800;
|
height: auto;
|
||||||
letter-spacing: 0.15em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-manager-workspace-trigger {
|
.device-manager-workspace-trigger {
|
||||||
@@ -222,24 +221,26 @@ textarea {
|
|||||||
.device-manager-stage {
|
.device-manager-stage {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
padding: clamp(2.25rem, 5vw, 5.5rem);
|
padding: clamp(1rem, 2.5vw, 2.5rem);
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-manager-hero {
|
.device-manager-home {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
min-height: calc(100% - 1px);
|
||||||
max-width: 1420px;
|
max-width: 1420px;
|
||||||
align-items: end;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
justify-content: center;
|
||||||
gap: 2rem;
|
gap: clamp(3rem, 7vh, 6rem);
|
||||||
margin: 0 auto 2.5rem;
|
margin: 0 auto;
|
||||||
|
padding: clamp(2.5rem, 6vw, 6rem) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-manager-hero > div:first-child {
|
.device-manager-hero {
|
||||||
max-width: 780px;
|
max-width: 900px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-manager-hero span,
|
.device-manager-hero > span,
|
||||||
.device-manager-section-title span {
|
.device-manager-section-title span {
|
||||||
color: rgb(var(--nodedc-accent-rgb));
|
color: rgb(var(--nodedc-accent-rgb));
|
||||||
font-size: 0.68rem;
|
font-size: 0.68rem;
|
||||||
@@ -248,13 +249,12 @@ textarea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.device-manager-hero h1 {
|
.device-manager-hero h1 {
|
||||||
max-width: 760px;
|
|
||||||
margin: 0.7rem 0 0.9rem;
|
margin: 0.7rem 0 0.9rem;
|
||||||
color: var(--nodedc-text-primary);
|
color: var(--nodedc-text-primary);
|
||||||
font-size: clamp(2.25rem, 5vw, 5.4rem);
|
font-size: clamp(4rem, 8vw, 8.5rem);
|
||||||
font-weight: 520;
|
font-weight: 520;
|
||||||
letter-spacing: -0.065em;
|
letter-spacing: -0.065em;
|
||||||
line-height: 0.98;
|
line-height: 0.9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-manager-hero p {
|
.device-manager-hero p {
|
||||||
@@ -265,18 +265,6 @@ textarea {
|
|||||||
line-height: 1.55;
|
line-height: 1.55;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-manager-hero__actions {
|
|
||||||
display: flex;
|
|
||||||
min-width: min(100%, 390px);
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-end;
|
|
||||||
gap: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.device-manager-hero__actions .nodedc-select-anchor {
|
|
||||||
min-width: 210px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.device-manager-alert {
|
.device-manager-alert {
|
||||||
display: flex;
|
display: flex;
|
||||||
max-width: 1420px;
|
max-width: 1420px;
|
||||||
@@ -325,8 +313,12 @@ textarea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.device-manager-projects {
|
.device-manager-projects {
|
||||||
max-width: 1420px;
|
width: 100%;
|
||||||
margin: 0 auto;
|
}
|
||||||
|
|
||||||
|
.device-manager-navigation-context {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.65rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-manager-section-title {
|
.device-manager-section-title {
|
||||||
@@ -355,7 +347,7 @@ textarea {
|
|||||||
|
|
||||||
.device-project-card {
|
.device-project-card {
|
||||||
display: grid;
|
display: grid;
|
||||||
min-height: 210px;
|
min-height: 160px;
|
||||||
grid-template-columns: auto 1fr auto;
|
grid-template-columns: auto 1fr auto;
|
||||||
grid-template-rows: 1fr auto;
|
grid-template-rows: 1fr auto;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
@@ -890,16 +882,6 @@ textarea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1100px) {
|
@media (max-width: 1100px) {
|
||||||
.device-manager-hero {
|
|
||||||
align-items: start;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.device-manager-hero__actions {
|
|
||||||
width: 100%;
|
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.device-manager-metrics {
|
.device-manager-metrics {
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
@@ -909,25 +891,24 @@ textarea {
|
|||||||
body { overflow: auto; }
|
body { overflow: auto; }
|
||||||
|
|
||||||
.device-manager-stage {
|
.device-manager-stage {
|
||||||
padding: 2rem 1rem 4rem;
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-manager-home {
|
||||||
|
min-height: auto;
|
||||||
|
padding: 2.5rem 1.25rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-manager-hero h1 {
|
.device-manager-hero h1 {
|
||||||
font-size: clamp(2.25rem, 14vw, 3.5rem);
|
font-size: clamp(3.4rem, 17vw, 6rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-manager-hero__actions,
|
|
||||||
.device-manager-panel-toolbar,
|
.device-manager-panel-toolbar,
|
||||||
.device-manager-section-title {
|
.device-manager-section-title {
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-manager-hero__actions > *,
|
|
||||||
.device-manager-hero__actions .nodedc-select-anchor {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.device-manager-metrics,
|
.device-manager-metrics,
|
||||||
.device-manager-overview-grid {
|
.device-manager-overview-grid {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
|||||||
Reference in New Issue
Block a user