Compare commits

..
3 Commits
Author SHA1 Message Date
DenozordecandClaude Sonnet 5.5 53420b49a1 feat(web): dashboard modules grid and activity feed as on mockup boards
quality / changes (push) Successful in 8s
quality / go (push) Skipped
quality / bird2 (push) Skipped
quality / commitlint (push) Skipped
quality / docker-check (push) Skipped
quality / openapi (push) Successful in 23s
quality / web (push) Successful in 1m32s
CD / quality (push) Successful in 2m11s
CD / publish (push) Successful in 3m7s
Заголовки страниц приведены к доскам (20px). На панели добавлены таблица первых 5 модулей и лента активности по последним задачам.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-23 19:47:08 +07:00
DenozordecandClaude Sonnet 5.5 55ddce43f7 fix(web): отступы контента как в MikrotikManager (gap-5, p-6)
quality / changes (push) Successful in 11s
quality / go (push) Skipped
quality / bird2 (push) Skipped
quality / commitlint (push) Skipped
quality / docker-check (push) Skipped
quality / openapi (push) Successful in 35s
quality / web (push) Successful in 1m27s
CD / quality (push) Successful in 3m50s
CD / publish (push) Successful in 3m4s
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-23 19:40:47 +07:00
DenozordecandClaude Sonnet 5.5 bcb1c48997 fix(web): MikrotikManager palette and full-width content
Токены цвета в packages/ui синхронизированы с app/globals.css MikrotikManager (main): сине-серая палитра, тёмный сайдбар, статусные и chart-токены, шкала радиусов. Ограничение max-w 1480px у контента оболочки снято, отступ как у MikrotikManager (p-6).

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-23 19:37:43 +07:00
5 changed files with 110 additions and 55 deletions

No files matched your search

@@ -215,7 +215,7 @@ export function PortalShell({ children }: { children: ReactNode }) {
<PortalSidebar />
<SidebarInset id="main-content" className="min-w-0">
<PortalHeader />
<div className="mx-auto flex w-full min-w-0 max-w-[1480px] flex-1 flex-col gap-4 px-4 py-4 md:gap-6 md:px-6 md:py-5">{children}</div>
<div className="flex w-full min-w-0 flex-1 flex-col gap-5 p-4 md:p-6">{children}</div>
</SidebarInset>
<CommandPalette />
</SidebarProvider>
+8 -11
View File
@@ -2,22 +2,19 @@ import type { ReactNode } from 'react'
interface PageHeaderProps {
title: string
description?: string
description?: ReactNode
actions?: ReactNode
}
/** Заголовок страницы как на досках макета: название 20px, описание 14px muted, действия справа. */
export function PageHeader({ title, description, actions }: PageHeaderProps) {
return (
<div className="flex min-w-0 flex-col gap-2 md:flex-row md:items-start md:justify-between">
<div className="flex min-w-0 flex-col gap-1">
<h1 className="text-2xl font-semibold tracking-tight">{title}</h1>
{description ? (
<p className="text-muted-foreground max-w-prose text-sm break-words">{description}</p>
) : null}
<header className="flex min-w-0 flex-wrap items-end justify-between gap-3">
<div className="flex min-w-0 flex-col gap-0.5">
<h1 className="text-xl font-semibold tracking-[-0.01em]">{title}</h1>
{description ? <p className="text-muted-foreground text-sm text-pretty">{description}</p> : null}
</div>
{actions ? (
<div className="flex shrink-0 flex-wrap items-center justify-end gap-2">{actions}</div>
) : null}
</div>
{actions ? <div className="flex shrink-0 flex-wrap items-center justify-end gap-2">{actions}</div> : null}
</header>
)
}
+26
View File
@@ -9,6 +9,10 @@ import { buildDashboardKpiCards } from '@/components/dashboard/dashboard-kpi-gri
import { DashboardNetworkHealth } from '@/components/dashboard/dashboard-network-health'
import { DashboardOperationsBreakdown } from '@/components/dashboard/dashboard-operations-breakdown'
import { DashboardQuickLinks } from '@/components/dashboard/dashboard-quick-links'
import { ModulesListGrid } from '@/components/modules/modules-list-grid'
import { ActivityFeed } from '@/components/portal/canvas-blocks'
import { fmtWhen } from '@/components/portal/format'
import type { Tone } from '@/components/portal/tone'
import { OpsDashboard } from '@/components/reui-kit'
import { chartPanelGridClassName } from '@/lib/ui-surface'
import {
@@ -69,6 +73,18 @@ function DashboardComponent() {
const speakers = speakersQ.data?.items ?? []
const jobs = jobsQ.data?.items ?? []
const kpiCards = buildDashboardKpiCards({ modules, peers, speakers, jobs })
const activity = jobs.slice(0, 4).map((j) => {
const failed = ['failed', 'error', 'cancelled'].includes(j.status.toLowerCase())
const running = j.status === 'running' || j.status === 'queued'
const tone: Tone = failed ? 'er' : running ? 'in' : 'ok'
return {
t: fmtWhen(j.finished_at ?? j.started_at ?? j.created_at),
title: j.kind,
text: j.error ?? `Статус: ${j.status}`,
label: failed ? 'Error' : running ? 'Running' : 'Info',
tone,
}
})
return (
<OpsDashboard
@@ -87,6 +103,16 @@ function DashboardComponent() {
kpiCards={kpiCards}
isLoading={initialLoading}
afterKpi={showQuickActions ? <DashboardQuickLinks /> : null}
queue={
<div className="flex min-w-0 flex-col gap-5">
<ModulesListGrid items={modules.slice(0, 5)} isLoading={modulesQ.isLoading} title="Модули" />
{activity.length > 0 ? (
<ActivityFeed title="Активность" description="Последние события плоскости управления" items={activity} />
) : null}
</div>
}
queueTitle=""
queueDescription=""
charts={
<div className={chartPanelGridClassName}>
<DashboardNetworkHealth
+1 -1
View File
@@ -1 +1 @@
{"root":["./src/main.tsx","./src/routetree.gen.ts","./src/vite-env.d.ts","./src/components/app-switcher.tsx","./src/components/category-badge.tsx","./src/components/confirm-dialog.tsx","./src/components/data-grid-cell.tsx","./src/components/empty-state.tsx","./src/components/form-drawer.tsx","./src/components/loading-button.tsx","./src/components/page-header.tsx","./src/components/page-shell.tsx","./src/components/panel-card.tsx","./src/components/query-state.tsx","./src/components/segmented-tabs.tsx","./src/components/select-field.tsx","./src/components/skeletons.tsx","./src/components/status-badge.tsx","./src/components/status-toggle-group.tsx","./src/components/theme-provider.tsx","./src/components/truncated-text.tsx","./src/components/access/access-api-keys-card.tsx","./src/components/access/access-api-keys-grid.tsx","./src/components/access/api-key-create-dialog.tsx","./src/components/access/api-key-token-dialog.tsx","./src/components/analytics/analytics-activity-list.tsx","./src/components/analytics/analytics-card-shell.tsx","./src/components/analytics/analytics-kpi-row.tsx","./src/components/analytics/analytics-progress.tsx","./src/components/analytics/analytics-segment-control.tsx","./src/components/analytics/chart-bar-strip.tsx","./src/components/analytics/chart-donut-metric.tsx","./src/components/analytics/dashboard-network-capacity-card.tsx","./src/components/analytics/dashboard-operations-flow-card.tsx","./src/components/analytics/dashboard-platform-card.tsx","./src/components/analytics/index.ts","./src/components/analytics/monitoring-health-card.tsx","./src/components/analytics/network-overview-analytics-card.tsx","./src/components/dashboard/card-dot-field.tsx","./src/components/dashboard/dashboard-kpi-grid.tsx","./src/components/dashboard/dashboard-network-health.tsx","./src/components/dashboard/dashboard-network-panel.tsx","./src/components/dashboard/dashboard-operations-breakdown.tsx","./src/components/dashboard/dashboard-quick-links.tsx","./src/components/directories/community-create-dialog.tsx","./src/components/directories/directories-communities-grid.tsx","./src/components/directories/directories-doh-grid.tsx","./src/components/directories/directories-row-actions.tsx","./src/components/directories/doh-profile-create-dialog.tsx","./src/components/layout/app-shell.tsx","./src/components/layout/apps-menu.tsx","./src/components/layout/command-palette.tsx","./src/components/layout/nav-user.tsx","./src/components/layout/system-monitor-popover.tsx","./src/components/lookup/lookup-add-step.tsx","./src/components/lookup/lookup-matches-grid.tsx","./src/components/lookup/lookup-search-form.tsx","./src/components/lookup/lookup-status-banner.tsx","./src/components/lookup/lookup-wizard.tsx","./src/components/modules/community-select.tsx","./src/components/modules/module-as-entry-dialog.tsx","./src/components/modules/module-cdn-source-dialog.tsx","./src/components/modules/module-create-dialog.tsx","./src/components/modules/module-domain-entry-dialog.tsx","./src/components/modules/module-edit-dialog.tsx","./src/components/modules/module-entries-grid.tsx","./src/components/modules/module-entries-section.tsx","./src/components/modules/module-ip-range-entry-dialog.tsx","./src/components/modules/module-kpi-cards.tsx","./src/components/modules/modules-list-grid.tsx","./src/components/monitoring/monitoring-ready-grid.tsx","./src/components/network/network-discovered-peers-card.tsx","./src/components/network/network-kpi.tsx","./src/components/network/network-peers-card.tsx","./src/components/network/network-peers-grid.tsx","./src/components/network/network-speakers-card.tsx","./src/components/network/network-speakers-grid.tsx","./src/components/network/peer-form-dialog.tsx","./src/components/network/speaker-form-dialog.tsx","./src/components/operations/job-timeline.tsx","./src/components/operations/operations-jobs-card.tsx","./src/components/operations/operations-jobs-grid.tsx","./src/components/operations/operations-jobs-kanban.tsx","./src/components/operations/operations-revisions-grid.tsx","./src/components/patterns/donut-breakdown-card.tsx","./src/components/patterns/illustrated-empty-state.tsx","./src/components/patterns/index.ts","./src/components/patterns/kpi-sparkline-card.tsx","./src/components/patterns/metric-tone-styles.ts","./src/components/patterns/panel-corners.tsx","./src/components/patterns/projects-empty-state.tsx","./src/components/patterns/segmented-progress-card.tsx","./src/components/reui/alert.tsx","./src/components/reui/autocomplete.tsx","./src/components/reui/badge.tsx","./src/components/reui/date-selector.tsx","./src/components/reui/frame.tsx","./src/components/reui/icon-stack.tsx","./src/components/reui/icon-tile.tsx","./src/components/reui/kanban.tsx","./src/components/reui/number-field.tsx","./src/components/reui/rating.tsx","./src/components/reui/sortable.tsx","./src/components/reui/stepper.tsx","./src/components/reui/timeline.tsx","./src/components/reui/cascader/cascader-async.tsLine truncated
{"root":["./src/main.tsx","./src/routetree.gen.ts","./src/vite-env.d.ts","./src/components/app-switcher.tsx","./src/components/category-badge.tsx","./src/components/confirm-dialog.tsx","./src/components/data-grid-cell.tsx","./src/components/empty-state.tsx","./src/components/form-drawer.tsx","./src/components/loading-button.tsx","./src/components/page-header.tsx","./src/components/page-shell.tsx","./src/components/panel-card.tsx","./src/components/query-state.tsx","./src/components/segmented-tabs.tsx","./src/components/select-field.tsx","./src/components/skeletons.tsx","./src/components/status-badge.tsx","./src/components/status-toggle-group.tsx","./src/components/theme-provider.tsx","./src/components/truncated-text.tsx","./src/components/access/access-api-keys-card.tsx","./src/components/access/access-api-keys-grid.tsx","./src/components/access/api-key-create-dialog.tsx","./src/components/access/api-key-token-dialog.tsx","./src/components/analytics/analytics-activity-list.tsx","./src/components/analytics/analytics-card-shell.tsx","./src/components/analytics/analytics-kpi-row.tsx","./src/components/analytics/analytics-progress.tsx","./src/components/analytics/analytics-segment-control.tsx","./src/components/analytics/chart-bar-strip.tsx","./src/components/analytics/chart-donut-metric.tsx","./src/components/analytics/dashboard-network-capacity-card.tsx","./src/components/analytics/dashboard-operations-flow-card.tsx","./src/components/analytics/dashboard-platform-card.tsx","./src/components/analytics/index.ts","./src/components/analytics/monitoring-health-card.tsx","./src/components/analytics/network-overview-analytics-card.tsx","./src/components/dashboard/card-dot-field.tsx","./src/components/dashboard/dashboard-kpi-grid.tsx","./src/components/dashboard/dashboard-network-health.tsx","./src/components/dashboard/dashboard-network-panel.tsx","./src/components/dashboard/dashboard-operations-breakdown.tsx","./src/components/dashboard/dashboard-quick-links.tsx","./src/components/directories/community-create-dialog.tsx","./src/components/directories/directories-communities-grid.tsx","./src/components/directories/directories-doh-grid.tsx","./src/components/directories/directories-row-actions.tsx","./src/components/directories/doh-profile-create-dialog.tsx","./src/components/layout/portal-shell.tsx","./src/components/lookup/lookup-add-step.tsx","./src/components/lookup/lookup-matches-grid.tsx","./src/components/lookup/lookup-search-form.tsx","./src/components/lookup/lookup-status-banner.tsx","./src/components/lookup/lookup-wizard.tsx","./src/components/modules/community-select.tsx","./src/components/modules/module-as-entry-dialog.tsx","./src/components/modules/module-cdn-source-dialog.tsx","./src/components/modules/module-create-dialog.tsx","./src/components/modules/module-domain-entry-dialog.tsx","./src/components/modules/module-edit-dialog.tsx","./src/components/modules/module-entries-grid.tsx","./src/components/modules/module-entries-section.tsx","./src/components/modules/module-ip-range-entry-dialog.tsx","./src/components/modules/module-kpi-cards.tsx","./src/components/modules/modules-list-grid.tsx","./src/components/monitoring/monitoring-ready-grid.tsx","./src/components/network/network-discovered-peers-card.tsx","./src/components/network/network-kpi.tsx","./src/components/network/network-peers-card.tsx","./src/components/network/network-peers-grid.tsx","./src/components/network/network-speakers-card.tsx","./src/components/network/network-speakers-grid.tsx","./src/components/network/peer-form-dialog.tsx","./src/components/network/speaker-form-dialog.tsx","./src/components/operations/job-timeline.tsx","./src/components/operations/operations-jobs-card.tsx","./src/components/operations/operations-jobs-grid.tsx","./src/components/operations/operations-jobs-kanban.tsx","./src/components/operations/operations-revisions-grid.tsx","./src/components/patterns/donut-breakdown-card.tsx","./src/components/patterns/illustrated-empty-state.tsx","./src/components/patterns/index.ts","./src/components/patterns/kpi-sparkline-card.tsx","./src/components/patterns/metric-tone-styles.ts","./src/components/patterns/panel-corners.tsx","./src/components/patterns/projects-empty-state.tsx","./src/components/patterns/segmented-progress-card.tsx","./src/components/portal/bits.tsx","./src/components/portal/canvas-blocks.tsx","./src/components/portal/chrome-context.tsx","./src/components/portal/chrome-store.ts","./src/components/portal/chrome.tsx","./src/components/portal/data-page-layout.ts","./src/components/portal/data-page.tsx","./src/components/portal/format.ts","./src/components/portal/kpi-strip.tsx","./src/components/portal/kpi.tsx","./src/components/portal/section-grid.tsx","./src/components/portal/tone.ts","./src/components/reui/alert.tsx","./src/components/reui/autocomplete.tsx","./src/components/reui/badge.tsx","./src/components/reui/date-selector.tsx","./src/components/reui/frame.tsx","./src/components/reui/icon-stack.tsx","./src/Line truncated
+74 -42
View File
@@ -9,37 +9,37 @@
:root {
--radius: 0.625rem;
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--background: oklch(0.973 0.004 247.858);
--foreground: oklch(0.141 0.006 264.0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--card-foreground: oklch(0.141 0.006 264.0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--popover-foreground: oklch(0.141 0.006 264.0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary: oklch(0.958 0.005 247.858);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--muted: oklch(0.958 0.005 247.858);
--muted-foreground: oklch(0.520 0.010 264.0);
--accent: oklch(0.942 0.016 264.376);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--border: oklch(0.904 0.006 264.0);
--input: oklch(0.904 0.006 264.0);
--ring: oklch(0.488 0.243 264.376);
--chart-1: oklch(0.546 0.215 264.376);
--chart-2: oklch(0.527 0.154 150.069);
--chart-3: oklch(0.625 0.162 41.500);
--chart-4: oklch(0.591 0.224 296.400);
--chart-5: oklch(0.577 0.245 27.325);
--sidebar: oklch(0.152 0.010 264.376);
--sidebar-foreground: oklch(0.920 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
--sidebar-accent: oklch(0.240 0.012 264.376);
--sidebar-accent-foreground: oklch(0.950 0 0);
--sidebar-border: oklch(1 0 0 / 8%);
--sidebar-ring: oklch(0.488 0.243 264.376);
--destructive-foreground: var(--color-red-800);
--success: var(--color-emerald-500);
--success-foreground: var(--color-emerald-900);
@@ -51,40 +51,56 @@
--invert-foreground: var(--color-zinc-50);
--focus: var(--color-blue-500);
--focus-foreground: var(--color-blue-900);
/* Токены MikrotikManager (main) */
--chart-line-1: oklch(0.527 0.154 150.069);
--chart-line-2: oklch(0.546 0.215 264.376);
--chart-line-3: oklch(0.625 0.162 41.500);
--chart-line-4: oklch(0.591 0.224 296.400);
--chart-rx: oklch(0.527 0.154 150.069);
--chart-tx: oklch(0.546 0.215 264.376);
--status-online: oklch(0.527 0.154 150.069);
--status-offline: oklch(0.577 0.245 27.325);
--status-degraded: oklch(0.666 0.179 58.318);
--status-online-fg: oklch(0.437 0.149 150.069);
--status-online-bg: oklch(0.527 0.154 150.069 / 0.10);
--status-offline-fg: oklch(0.505 0.213 27.325);
--status-offline-bg: oklch(0.577 0.245 27.325 / 0.10);
--status-degraded-fg: oklch(0.555 0.163 58.318);
--status-degraded-bg: oklch(0.666 0.179 58.318 / 0.10);
}
.dark {
--background: oklch(0.145 0 0);
--background: oklch(0.133 0.011 264.376);
--foreground: oklch(0.985 0 0);
--card: oklch(0.205 0 0);
--card: oklch(0.191 0.011 264.376);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.205 0 0);
--popover: oklch(0.191 0.011 264.376);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary: oklch(0.252 0.010 264.376);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--muted: oklch(0.252 0.010 264.376);
--muted-foreground: oklch(0.648 0.008 264.376);
--accent: oklch(0.285 0.022 264.376);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.205 0 0);
--ring: oklch(0.488 0.243 264.376);
--chart-1: oklch(0.637 0.222 264.376);
--chart-2: oklch(0.696 0.170 151.328);
--chart-3: oklch(0.713 0.176 47.600);
--chart-4: oklch(0.714 0.173 303.900);
--chart-5: oklch(0.704 0.191 22.216);
--sidebar: oklch(0.191 0.011 264.376);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent: oklch(0.262 0.014 264.376);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.556 0 0);
--sidebar-ring: oklch(0.488 0.243 264.376);
--destructive-foreground: var(--color-red-600);
--success: var(--color-emerald-500);
--success-foreground: var(--color-emerald-600);
@@ -96,6 +112,22 @@
--invert-foreground: var(--color-zinc-50);
--focus: var(--color-blue-500);
--focus-foreground: var(--color-blue-600);
/* Токены MikrotikManager (main) */
--chart-line-1: oklch(0.696 0.170 151.328);
--chart-line-2: oklch(0.637 0.222 264.376);
--chart-line-3: oklch(0.713 0.176 47.600);
--chart-line-4: oklch(0.714 0.173 303.900);
--chart-rx: oklch(0.696 0.170 151.328);
--chart-tx: oklch(0.637 0.222 264.376);
--status-online: oklch(0.527 0.154 150.069);
--status-offline: oklch(0.577 0.245 27.325);
--status-degraded: oklch(0.666 0.179 58.318);
--status-online-fg: oklch(0.696 0.170 151.328);
--status-online-bg: oklch(0.527 0.154 150.069 / 0.12);
--status-offline-fg: oklch(0.704 0.191 22.216);
--status-offline-bg: oklch(0.577 0.245 27.325 / 0.12);
--status-degraded-fg: oklch(0.769 0.149 70.080);
--status-degraded-bg: oklch(0.666 0.179 58.318 / 0.12);
}
@theme inline {
@@ -130,10 +162,10 @@
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-sm: calc(var(--radius) * 0.6);
--radius-md: calc(var(--radius) * 0.8);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--radius-xl: calc(var(--radius) * 1.4);
--color-invert-foreground: var(--invert-foreground);
--color-invert: var(--invert);
--color-warning-foreground: var(--warning-foreground);