Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53420b49a1 |
No files matched your search
@@ -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>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in new issue
Block a user