Compare commits

...
1 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
2 changed files with 34 additions and 11 deletions

No files matched your search

+8 -11
View File
@@ -2,22 +2,19 @@ import type { ReactNode } from 'react'
interface PageHeaderProps { interface PageHeaderProps {
title: string title: string
description?: string description?: ReactNode
actions?: ReactNode actions?: ReactNode
} }
/** Заголовок страницы как на досках макета: название 20px, описание 14px muted, действия справа. */
export function PageHeader({ title, description, actions }: PageHeaderProps) { export function PageHeader({ title, description, actions }: PageHeaderProps) {
return ( return (
<div className="flex min-w-0 flex-col gap-2 md:flex-row md:items-start md:justify-between"> <header className="flex min-w-0 flex-wrap items-end justify-between gap-3">
<div className="flex min-w-0 flex-col gap-1"> <div className="flex min-w-0 flex-col gap-0.5">
<h1 className="text-2xl font-semibold tracking-tight">{title}</h1> <h1 className="text-xl font-semibold tracking-[-0.01em]">{title}</h1>
{description ? ( {description ? <p className="text-muted-foreground text-sm text-pretty">{description}</p> : null}
<p className="text-muted-foreground max-w-prose text-sm break-words">{description}</p>
) : null}
</div> </div>
{actions ? ( {actions ? <div className="flex shrink-0 flex-wrap items-center justify-end gap-2">{actions}</div> : null}
<div className="flex shrink-0 flex-wrap items-center justify-end gap-2">{actions}</div> </header>
) : null}
</div>
) )
} }
+26
View File
@@ -9,6 +9,10 @@ import { buildDashboardKpiCards } from '@/components/dashboard/dashboard-kpi-gri
import { DashboardNetworkHealth } from '@/components/dashboard/dashboard-network-health' import { DashboardNetworkHealth } from '@/components/dashboard/dashboard-network-health'
import { DashboardOperationsBreakdown } from '@/components/dashboard/dashboard-operations-breakdown' import { DashboardOperationsBreakdown } from '@/components/dashboard/dashboard-operations-breakdown'
import { DashboardQuickLinks } from '@/components/dashboard/dashboard-quick-links' 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 { OpsDashboard } from '@/components/reui-kit'
import { chartPanelGridClassName } from '@/lib/ui-surface' import { chartPanelGridClassName } from '@/lib/ui-surface'
import { import {
@@ -69,6 +73,18 @@ function DashboardComponent() {
const speakers = speakersQ.data?.items ?? [] const speakers = speakersQ.data?.items ?? []
const jobs = jobsQ.data?.items ?? [] const jobs = jobsQ.data?.items ?? []
const kpiCards = buildDashboardKpiCards({ modules, peers, speakers, jobs }) 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 ( return (
<OpsDashboard <OpsDashboard
@@ -87,6 +103,16 @@ function DashboardComponent() {
kpiCards={kpiCards} kpiCards={kpiCards}
isLoading={initialLoading} isLoading={initialLoading}
afterKpi={showQuickActions ? <DashboardQuickLinks /> : null} 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={ charts={
<div className={chartPanelGridClassName}> <div className={chartPanelGridClassName}>
<DashboardNetworkHealth <DashboardNetworkHealth