фикс разметки и кодировки
This commit is contained in:
parent
3feb05ce0f
commit
ecb3f6b524
3 changed files with 163 additions and 144 deletions
|
|
@ -10,19 +10,19 @@ import { useDashboardMonitorSSE } from '../hooks/useDashboardMonitorSSE'
|
|||
|
||||
function formatAge(seconds: number | null | undefined): string {
|
||||
if (seconds == null) return '-'
|
||||
if (seconds < 60) return `${seconds}ñ`
|
||||
if (seconds < 3600) return `${Math.floor(seconds / 60)}ì`
|
||||
return `${Math.floor(seconds / 3600)}÷ ${Math.floor((seconds % 3600) / 60)}ì`
|
||||
if (seconds < 60) return `${seconds}с`
|
||||
if (seconds < 3600) return `${Math.floor(seconds / 60)}м`
|
||||
return `${Math.floor(seconds / 3600)}ч ${Math.floor((seconds % 3600) / 60)}м`
|
||||
}
|
||||
|
||||
function reasonLabel(reason: string): string {
|
||||
const map: Record<string, string> = {
|
||||
object_include_policy: 'Âêëþ÷åí ïîëèòèêîé îáúåêòà',
|
||||
object_exclude_policy: 'Èñêëþ÷åí ïîëèòèêîé îáúåêòà',
|
||||
inherit_default: 'Íàñëåäîâàíèå ïî óìîë÷àíèþ',
|
||||
tag_policy_enabled: 'Âêëþ÷åí ïîëèòèêîé òåãîâ',
|
||||
tag_policy_disabled: 'Èñêëþ÷åí ïîëèòèêîé òåãîâ',
|
||||
scope_filtered: 'Âíå òåêóùåãî scope',
|
||||
object_include_policy: 'Включен политикой объекта',
|
||||
object_exclude_policy: 'Исключен политикой объекта',
|
||||
inherit_default: 'Наследование по умолчанию',
|
||||
tag_policy_enabled: 'Включен политикой тегов',
|
||||
tag_policy_disabled: 'Исключен политикой тегов',
|
||||
scope_filtered: 'Вне текущего scope',
|
||||
}
|
||||
return map[reason] ?? reason
|
||||
}
|
||||
|
|
@ -72,9 +72,9 @@ export function HomePage() {
|
|||
const handleRunNow = async () => {
|
||||
try {
|
||||
await runNow.mutateAsync({ ...scope, force: true })
|
||||
message.success('Ìîíèòîðèíã çàïóùåí')
|
||||
message.success('Мониторинг запущен')
|
||||
} catch {
|
||||
message.error('Íå óäàëîñü çàïóñòèòü ìîíèòîðèíã')
|
||||
message.error('Не удалось запустить мониторинг')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ export function HomePage() {
|
|||
|
||||
const columns = [
|
||||
{
|
||||
title: 'Îáúåêò',
|
||||
title: 'Объект',
|
||||
key: 'object',
|
||||
render: (_: unknown, row: any) => (
|
||||
<Space>
|
||||
|
|
@ -99,12 +99,12 @@ export function HomePage() {
|
|||
),
|
||||
},
|
||||
{
|
||||
title: 'Ïðè÷èíà',
|
||||
title: 'Причина',
|
||||
key: 'reason',
|
||||
render: (_: unknown, row: any) => <Typography.Text>{reasonLabel(row.inclusion_reason)}</Typography.Text>,
|
||||
},
|
||||
{
|
||||
title: 'Óñòðîéñòâ',
|
||||
title: 'Устройств',
|
||||
dataIndex: 'device_count',
|
||||
key: 'device_count',
|
||||
},
|
||||
|
|
@ -137,22 +137,26 @@ export function HomePage() {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<Space style={{ width: '100%', justifyContent: 'space-between', marginBottom: 12 }} align="center">
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
NOC Äåøáîðä
|
||||
</Typography.Title>
|
||||
<Space>
|
||||
<Button onClick={() => navigate(`/home/settings${scopeToSearch(scope)}`)}>Íàñòðîéêè ìîíèòîðèíãà</Button>
|
||||
<Button type="primary" onClick={handleRunNow} loading={runNow.isPending}>
|
||||
Çàïóñòèòü ñåé÷àñ
|
||||
</Button>
|
||||
</Space>
|
||||
</Space>
|
||||
<Row justify="space-between" align="middle" gutter={[8, 8]} style={{ marginBottom: 12 }}>
|
||||
<Col>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
NOC Дешборд
|
||||
</Typography.Title>
|
||||
</Col>
|
||||
<Col>
|
||||
<Space wrap>
|
||||
<Button onClick={() => navigate(`/home/settings${scopeToSearch(scope)}`)}>Настройки мониторинга</Button>
|
||||
<Button type="primary" onClick={handleRunNow} loading={runNow.isPending}>
|
||||
Запустить сейчас
|
||||
</Button>
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row gutter={12} style={{ marginBottom: 12 }}>
|
||||
<Col span={5}>
|
||||
<Row gutter={[12, 12]} style={{ marginBottom: 12 }}>
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<Select
|
||||
placeholder="Ãîðîä"
|
||||
placeholder="Город"
|
||||
allowClear
|
||||
style={{ width: '100%' }}
|
||||
options={(meta?.cities ?? []).map((c) => ({ value: c, label: c }))}
|
||||
|
|
@ -160,9 +164,9 @@ export function HomePage() {
|
|||
onChange={setCity}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={5}>
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<Select
|
||||
placeholder="Êëèåíò"
|
||||
placeholder="Клиент"
|
||||
allowClear
|
||||
style={{ width: '100%' }}
|
||||
options={(meta?.clients ?? []).map((c) => ({ value: c, label: c }))}
|
||||
|
|
@ -170,10 +174,10 @@ export function HomePage() {
|
|||
onChange={setClient}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={7}>
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<Select
|
||||
mode="multiple"
|
||||
placeholder="Îáúåêòû"
|
||||
placeholder="Объекты"
|
||||
allowClear
|
||||
style={{ width: '100%' }}
|
||||
value={objectIds}
|
||||
|
|
@ -181,10 +185,10 @@ export function HomePage() {
|
|||
options={(objects ?? []).map((o) => ({ value: o.id, label: o.name }))}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={7}>
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<Select
|
||||
mode="multiple"
|
||||
placeholder="Òåãè"
|
||||
placeholder="Теги"
|
||||
allowClear
|
||||
style={{ width: '100%' }}
|
||||
value={tagIds}
|
||||
|
|
@ -197,9 +201,9 @@ export function HomePage() {
|
|||
<Row gutter={12} style={{ marginBottom: 16 }}>
|
||||
<Col>
|
||||
<Typography.Text type="secondary">
|
||||
Ñòàòóñ: {monitorStatus?.status ?? '-'}
|
||||
Статус: {monitorStatus?.status ?? '-'}
|
||||
{monitorStatus?.last_success_at
|
||||
? ` · óñïåøíûé çàïóñê ${dayjs(monitorStatus.last_success_at).format('DD.MM HH:mm:ss')}`
|
||||
? ` · успешный запуск ${dayjs(monitorStatus.last_success_at).format('DD.MM HH:mm:ss')}`
|
||||
: ''}
|
||||
</Typography.Text>
|
||||
</Col>
|
||||
|
|
@ -209,22 +213,22 @@ export function HomePage() {
|
|||
<Alert type="error" showIcon style={{ marginBottom: 12 }} message={monitorStatus.last_error} />
|
||||
)}
|
||||
|
||||
<Row gutter={12} style={{ marginBottom: 16 }}>
|
||||
<Col span={6}>
|
||||
<Card><Statistic title="Îáúåêòû" value={totals.objects} /></Card>
|
||||
<Row gutter={[12, 12]} style={{ marginBottom: 16 }}>
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<Card><Statistic title="Объекты" value={totals.objects} /></Card>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Card><Statistic title="Â ìîíèòîðèíãå" value={summary?.included_count ?? totals.allowed_objects} /></Card>
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<Card><Statistic title="В мониторинге" value={summary?.included_count ?? totals.allowed_objects} /></Card>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Card><Statistic title="Âíå ìîíèòîðèíãà" value={summary?.excluded_count ?? totals.excluded_objects} /></Card>
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<Card><Statistic title="Вне мониторинга" value={summary?.excluded_count ?? totals.excluded_objects} /></Card>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Card><Statistic title="Óñòðîéñòâà" value={totals.devices} /></Card>
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<Card><Statistic title="Устройства" value={totals.devices} /></Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Card title="Ïðîáëåìíûå îáúåêòû" style={{ marginBottom: 16 }}>
|
||||
<Card title="Проблемные объекты" style={{ marginBottom: 16 }}>
|
||||
<Table
|
||||
size="small"
|
||||
dataSource={summary?.problem_objects ?? []}
|
||||
|
|
@ -232,12 +236,13 @@ export function HomePage() {
|
|||
columns={columns}
|
||||
pagination={false}
|
||||
loading={isLoading}
|
||||
scroll={{ x: 900 }}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Row gutter={12}>
|
||||
<Col span={12}>
|
||||
<Card title="Â ìîíèòîðèíãå" style={{ marginBottom: 16 }}>
|
||||
<Row gutter={[12, 12]}>
|
||||
<Col xs={24} xl={12}>
|
||||
<Card title="В мониторинге" style={{ marginBottom: 16 }}>
|
||||
<Table
|
||||
size="small"
|
||||
dataSource={summary?.included_objects ?? []}
|
||||
|
|
@ -245,11 +250,12 @@ export function HomePage() {
|
|||
columns={columns}
|
||||
pagination={{ pageSize: 8 }}
|
||||
loading={isLoading}
|
||||
scroll={{ x: 900 }}
|
||||
/>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Card title="Âíå ìîíèòîðèíãà" style={{ marginBottom: 16 }}>
|
||||
<Col xs={24} xl={12}>
|
||||
<Card title="Вне мониторинга" style={{ marginBottom: 16 }}>
|
||||
<Table
|
||||
size="small"
|
||||
dataSource={summary?.excluded_objects ?? []}
|
||||
|
|
@ -257,6 +263,7 @@ export function HomePage() {
|
|||
columns={columns}
|
||||
pagination={{ pageSize: 8 }}
|
||||
loading={isLoading}
|
||||
scroll={{ x: 900 }}
|
||||
/>
|
||||
</Card>
|
||||
</Col>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { ArrowLeftOutlined } from '@ant-design/icons'
|
|||
import { Alert, Button, Card, Col, Row, Select, Space, Table, Tag, Typography } from 'antd'
|
||||
import dayjs from 'dayjs'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { useNavigate, useParams, useSearchParams } from 'react-router-dom'
|
||||
import {
|
||||
useDashboardObjectAvailability,
|
||||
|
|
@ -12,16 +13,15 @@ import {
|
|||
type DashboardScope,
|
||||
} from '../api/dashboard'
|
||||
import { useDashboardMonitorSSE } from '../hooks/useDashboardMonitorSSE'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
|
||||
function reasonLabel(reason: string): string {
|
||||
const map: Record<string, string> = {
|
||||
object_include_policy: 'Âêëþ÷åí ïîëèòèêîé îáúåêòà',
|
||||
object_exclude_policy: 'Èñêëþ÷åí ïîëèòèêîé îáúåêòà',
|
||||
inherit_default: 'Íàñëåäîâàíèå ïî óìîë÷àíèþ',
|
||||
tag_policy_enabled: 'Âêëþ÷åí ïîëèòèêîé òåãîâ',
|
||||
tag_policy_disabled: 'Èñêëþ÷åí ïîëèòèêîé òåãîâ',
|
||||
scope_filtered: 'Âíå òåêóùåãî scope',
|
||||
object_include_policy: 'Включен политикой объекта',
|
||||
object_exclude_policy: 'Исключен политикой объекта',
|
||||
inherit_default: 'Наследование по умолчанию',
|
||||
tag_policy_enabled: 'Включен политикой тегов',
|
||||
tag_policy_disabled: 'Исключен политикой тегов',
|
||||
scope_filtered: 'Вне текущего scope',
|
||||
}
|
||||
return map[reason] ?? reason
|
||||
}
|
||||
|
|
@ -93,24 +93,28 @@ export function HomeObjectDetailPage() {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<Space style={{ width: '100%', justifyContent: 'space-between', marginBottom: 12 }} align="center">
|
||||
<Space>
|
||||
<Button icon={<ArrowLeftOutlined />} onClick={() => navigate('/home')}>
|
||||
Íàçàä
|
||||
</Button>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
{object?.object_name ?? `Îáúåêò #${objectId}`}
|
||||
</Typography.Title>
|
||||
</Space>
|
||||
<Space>
|
||||
{object?.allowed ? <Tag color="green">Â ìîíèòîðèíãå</Tag> : <Tag color="red">Âíå ìîíèòîðèíãà</Tag>}
|
||||
{object?.health_score != null && (
|
||||
<Tag color={object.health_score >= 80 ? 'green' : object.health_score >= 60 ? 'orange' : 'red'}>
|
||||
Health {object.health_score}
|
||||
</Tag>
|
||||
)}
|
||||
</Space>
|
||||
</Space>
|
||||
<Row justify="space-between" align="middle" gutter={[8, 8]} style={{ marginBottom: 12 }}>
|
||||
<Col>
|
||||
<Space wrap>
|
||||
<Button icon={<ArrowLeftOutlined />} onClick={() => navigate('/home')}>
|
||||
Назад
|
||||
</Button>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
{object?.object_name ?? `Объект #${objectId}`}
|
||||
</Typography.Title>
|
||||
</Space>
|
||||
</Col>
|
||||
<Col>
|
||||
<Space wrap>
|
||||
{object?.allowed ? <Tag color="green">В мониторинге</Tag> : <Tag color="red">Вне мониторинга</Tag>}
|
||||
{object?.health_score != null && (
|
||||
<Tag color={object.health_score >= 80 ? 'green' : object.health_score >= 60 ? 'orange' : 'red'}>
|
||||
Health {object.health_score}
|
||||
</Tag>
|
||||
)}
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{object && (
|
||||
<Typography.Text type="secondary" style={{ display: 'block', marginBottom: 12 }}>
|
||||
|
|
@ -123,43 +127,43 @@ export function HomeObjectDetailPage() {
|
|||
type="info"
|
||||
showIcon
|
||||
style={{ marginBottom: 12 }}
|
||||
message="Íåäîñòàòî÷íî äàííûõ äëÿ ãðàôèêà äîñòóïíîñòè â âûáðàííîì îêíå"
|
||||
message="Недостаточно данных для графика доступности в выбранном окне"
|
||||
/>
|
||||
)}
|
||||
|
||||
<Row gutter={12} style={{ marginBottom: 16 }}>
|
||||
<Col span={8}>
|
||||
<Row gutter={[12, 12]} style={{ marginBottom: 16 }}>
|
||||
<Col xs={24} sm={12} lg={8}>
|
||||
<Card loading={summaryLoading}>
|
||||
<Typography.Text type="secondary">Óñòðîéñòâ</Typography.Text>
|
||||
<Typography.Text type="secondary">Устройств</Typography.Text>
|
||||
<Typography.Title level={3} style={{ margin: 0 }}>{summary?.totals.devices ?? 0}</Typography.Title>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Col xs={24} sm={12} lg={8}>
|
||||
<Card loading={summaryLoading}>
|
||||
<Typography.Text type="secondary">Ïðîáëåìíûõ</Typography.Text>
|
||||
<Typography.Text type="secondary">Проблемных</Typography.Text>
|
||||
<Typography.Title level={3} style={{ margin: 0 }}>{summary?.totals.problem_devices ?? 0}</Typography.Title>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Col xs={24} sm={12} lg={8}>
|
||||
<Card loading={availLoading}>
|
||||
<Typography.Text type="secondary">Äîñòóïíîñòü ({window})</Typography.Text>
|
||||
<Typography.Text type="secondary">Доступность ({window})</Typography.Text>
|
||||
<Typography.Title level={3} style={{ margin: 0 }}>
|
||||
{availability?.overall_availability_pct != null ? `${availability.overall_availability_pct}%` : '—'}
|
||||
{availability?.overall_availability_pct != null ? `${availability.overall_availability_pct}%` : '—'}
|
||||
</Typography.Title>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Card
|
||||
title="Ãðàôèê äîñòóïíîñòè ïî Ping"
|
||||
title="График доступности по Ping"
|
||||
extra={
|
||||
<Select
|
||||
value={window}
|
||||
onChange={(v) => setWindow(v as DashboardAvailabilityWindow)}
|
||||
options={[
|
||||
{ value: '24h', label: '24÷' },
|
||||
{ value: '3d', label: '3ä' },
|
||||
{ value: '7d', label: '7ä' },
|
||||
{ value: '24h', label: '24ч' },
|
||||
{ value: '3d', label: '3д' },
|
||||
{ value: '7d', label: '7д' },
|
||||
]}
|
||||
style={{ width: 100 }}
|
||||
/>
|
||||
|
|
@ -169,13 +173,14 @@ export function HomeObjectDetailPage() {
|
|||
<AvailabilityChart points={availability?.overall_series ?? []} />
|
||||
</Card>
|
||||
|
||||
<Card title="Ñòàòóñ óñòðîéñòâ" style={{ marginBottom: 16 }}>
|
||||
<Card title="Статус устройств" style={{ marginBottom: 16 }}>
|
||||
<Table
|
||||
loading={devicesLoading}
|
||||
dataSource={devices?.devices ?? []}
|
||||
rowKey="device_id"
|
||||
size="small"
|
||||
pagination={{ pageSize: 20 }}
|
||||
scroll={{ x: 1000 }}
|
||||
columns={[
|
||||
{
|
||||
title: 'IP / Hostname',
|
||||
|
|
@ -183,11 +188,11 @@ export function HomeObjectDetailPage() {
|
|||
render: (_: unknown, row: any) => (
|
||||
<Space direction="vertical" size={0}>
|
||||
<code>{row.ip}</code>
|
||||
<Typography.Text type="secondary" style={{ fontSize: 12 }}>{row.hostname ?? '—'}</Typography.Text>
|
||||
<Typography.Text type="secondary" style={{ fontSize: 12 }}>{row.hostname ?? '—'}</Typography.Text>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{ title: 'Êàòåãîðèÿ', dataIndex: 'category', key: 'category' },
|
||||
{ title: 'Категория', dataIndex: 'category', key: 'category' },
|
||||
{
|
||||
title: 'Ping',
|
||||
key: 'ping',
|
||||
|
|
@ -197,8 +202,8 @@ export function HomeObjectDetailPage() {
|
|||
{row.ping_status}
|
||||
</Tag>
|
||||
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
||||
{row.ping_checked_at ? dayjs(row.ping_checked_at).format('DD.MM HH:mm:ss') : '—'}
|
||||
{row.last_ping_ms != null ? ` · ${row.last_ping_ms}ms` : ''}
|
||||
{row.ping_checked_at ? dayjs(row.ping_checked_at).format('DD.MM HH:mm:ss') : '—'}
|
||||
{row.last_ping_ms != null ? ` · ${row.last_ping_ms}ms` : ''}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
),
|
||||
|
|
@ -211,37 +216,38 @@ export function HomeObjectDetailPage() {
|
|||
<Tag color={row.disk_status === 'ok' ? 'green' : row.disk_status === 'warn' ? 'orange' : 'red'}>
|
||||
{row.disk_status}{row.disk_max_pct != null ? ` (${row.disk_max_pct}%)` : ''}
|
||||
</Tag>
|
||||
) : '—',
|
||||
) : '—',
|
||||
},
|
||||
{
|
||||
title: 'Ïðîáëåìà',
|
||||
title: 'Проблема',
|
||||
key: 'problem',
|
||||
render: (_: unknown, row: any) => row.has_problem ? <Tag color="red">Äà</Tag> : <Tag color="green">Íåò</Tag>,
|
||||
render: (_: unknown, row: any) => row.has_problem ? <Tag color="red">Да</Tag> : <Tag color="green">Нет</Tag>,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card title="Per-device äîñòóïíîñòü (âûáîðêà)">
|
||||
<Card title="Per-device доступность (выборка)">
|
||||
<Table
|
||||
loading={availLoading}
|
||||
dataSource={availability?.devices ?? []}
|
||||
rowKey="device_id"
|
||||
size="small"
|
||||
pagination={{ pageSize: 10 }}
|
||||
scroll={{ x: 900 }}
|
||||
columns={[
|
||||
{ title: 'IP', dataIndex: 'ip', key: 'ip', render: (v: string) => <code>{v}</code> },
|
||||
{ title: 'Hostname', dataIndex: 'hostname', key: 'hostname', render: (v: string | null) => v ?? '—' },
|
||||
{ title: 'Hostname', dataIndex: 'hostname', key: 'hostname', render: (v: string | null) => v ?? '—' },
|
||||
{
|
||||
title: 'Availability',
|
||||
key: 'availability_pct',
|
||||
render: (_: unknown, row: any) => row.availability_pct != null ? `${row.availability_pct}%` : '—',
|
||||
render: (_: unknown, row: any) => row.availability_pct != null ? `${row.availability_pct}%` : '—',
|
||||
},
|
||||
{ title: 'Samples', dataIndex: 'samples', key: 'samples' },
|
||||
{
|
||||
title: 'Ïîñëåäíèé ñòàòóñ',
|
||||
title: 'Последний статус',
|
||||
key: 'last_status',
|
||||
render: (_: unknown, row: any) => row.last_status ? <Tag>{row.last_status}</Tag> : '—',
|
||||
render: (_: unknown, row: any) => row.last_status ? <Tag>{row.last_status}</Tag> : '—',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -110,32 +110,36 @@ export function HomeSettingsPage() {
|
|||
const handleRunNow = async () => {
|
||||
try {
|
||||
await runNow.mutateAsync({ force: true })
|
||||
message.success('Ìîíèòîðèíã çàïóùåí')
|
||||
message.success('Мониторинг запущен')
|
||||
} catch {
|
||||
message.error('Íå óäàëîñü çàïóñòèòü ìîíèòîðèíã')
|
||||
message.error('Не удалось запустить мониторинг')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Space style={{ width: '100%', justifyContent: 'space-between', marginBottom: 12 }} align="center">
|
||||
<Space>
|
||||
<Button icon={<ArrowLeftOutlined />} onClick={() => navigate('/home')}>
|
||||
Íàçàä
|
||||
<Row justify="space-between" align="middle" gutter={[8, 8]} style={{ marginBottom: 12 }}>
|
||||
<Col>
|
||||
<Space wrap>
|
||||
<Button icon={<ArrowLeftOutlined />} onClick={() => navigate('/home')}>
|
||||
Назад
|
||||
</Button>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
Настройки мониторинга
|
||||
</Typography.Title>
|
||||
</Space>
|
||||
</Col>
|
||||
<Col>
|
||||
<Button type="primary" onClick={handleRunNow} loading={runNow.isPending}>
|
||||
Запустить сейчас
|
||||
</Button>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
Íàñòðîéêè ìîíèòîðèíãà
|
||||
</Typography.Title>
|
||||
</Space>
|
||||
<Button type="primary" onClick={handleRunNow} loading={runNow.isPending}>
|
||||
Çàïóñòèòü ñåé÷àñ
|
||||
</Button>
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Typography.Text type="secondary" style={{ display: 'block', marginBottom: 12 }}>
|
||||
Ñòàòóñ: {monitorStatus?.status ?? '-'}
|
||||
Статус: {monitorStatus?.status ?? '-'}
|
||||
{monitorStatus?.last_success_at
|
||||
? ` · óñïåøíûé çàïóñê ${dayjs(monitorStatus.last_success_at).format('DD.MM HH:mm:ss')}`
|
||||
? ` · успешный запуск ${dayjs(monitorStatus.last_success_at).format('DD.MM HH:mm:ss')}`
|
||||
: ''}
|
||||
</Typography.Text>
|
||||
|
||||
|
|
@ -143,12 +147,12 @@ export function HomeSettingsPage() {
|
|||
<Alert type="error" showIcon style={{ marginBottom: 12 }} message={monitorStatus.last_error} />
|
||||
)}
|
||||
|
||||
<Card title="Ðàñïèñàíèå ïðîâåðîê" style={{ marginBottom: 16 }}>
|
||||
<Card title="Расписание проверок" style={{ marginBottom: 16 }}>
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
{(checksData?.checks ?? []).map((check) => (
|
||||
<Row key={check.check_key} gutter={12} align="middle">
|
||||
<Col span={4}><Typography.Text strong>{check.check_key}</Typography.Text></Col>
|
||||
<Col span={3}>
|
||||
<Row key={check.check_key} gutter={[12, 12]} align="middle">
|
||||
<Col xs={24} md={6}><Typography.Text strong>{check.check_key}</Typography.Text></Col>
|
||||
<Col xs={24} md={4}>
|
||||
<Switch
|
||||
checked={check.enabled}
|
||||
onChange={async (enabled) => {
|
||||
|
|
@ -156,7 +160,7 @@ export function HomeSettingsPage() {
|
|||
}}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={5}>
|
||||
<Col xs={24} md={6}>
|
||||
<InputNumber
|
||||
min={30}
|
||||
value={check.interval_sec_default}
|
||||
|
|
@ -168,7 +172,7 @@ export function HomeSettingsPage() {
|
|||
}}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={10}>
|
||||
<Col xs={24} md={8}>
|
||||
<Select
|
||||
mode="multiple"
|
||||
allowClear
|
||||
|
|
@ -185,10 +189,10 @@ export function HomeSettingsPage() {
|
|||
</Space>
|
||||
</Card>
|
||||
|
||||
<Card title="Ïîëèòèêà ïî òåãàì" style={{ marginBottom: 16 }}>
|
||||
<Card title="Политика по тегам" style={{ marginBottom: 16 }}>
|
||||
<Row gutter={[12, 8]}>
|
||||
{(tags ?? []).map((tag) => (
|
||||
<Col span={8} key={tag.id}>
|
||||
<Col xs={24} md={12} xl={8} key={tag.id}>
|
||||
<Space>
|
||||
<Tag color={tag.color}>{tag.name}</Tag>
|
||||
<Switch
|
||||
|
|
@ -204,16 +208,17 @@ export function HomeSettingsPage() {
|
|||
</Row>
|
||||
</Card>
|
||||
|
||||
<Card title="Ïîëèòèêà îáúåêòîâ è overrides" style={{ marginBottom: 16 }}>
|
||||
<Card title="Политика объектов и overrides" style={{ marginBottom: 16 }}>
|
||||
<Table
|
||||
size="small"
|
||||
rowKey="id"
|
||||
pagination={{ pageSize: 8 }}
|
||||
dataSource={objects ?? []}
|
||||
scroll={{ x: 650 }}
|
||||
columns={[
|
||||
{ title: 'Îáúåêò', dataIndex: 'name', key: 'name' },
|
||||
{ title: 'Объект', dataIndex: 'name', key: 'name' },
|
||||
{
|
||||
title: 'Ðåæèì',
|
||||
title: 'Режим',
|
||||
key: 'mode',
|
||||
render: (_: unknown, row: any) => (
|
||||
<Select
|
||||
|
|
@ -237,14 +242,14 @@ export function HomeSettingsPage() {
|
|||
|
||||
<Space wrap>
|
||||
<Select
|
||||
placeholder="Îáúåêò"
|
||||
placeholder="Объект"
|
||||
style={{ width: 220 }}
|
||||
value={overrideObjectId}
|
||||
onChange={setOverrideObjectId}
|
||||
options={(objects ?? []).map((o) => ({ value: o.id, label: o.name }))}
|
||||
/>
|
||||
<Select
|
||||
placeholder="Ïðîâåðêà"
|
||||
placeholder="Проверка"
|
||||
style={{ width: 160 }}
|
||||
value={overrideCheckKey}
|
||||
onChange={setOverrideCheckKey}
|
||||
|
|
@ -262,13 +267,13 @@ export function HomeSettingsPage() {
|
|||
]}
|
||||
/>
|
||||
<InputNumber
|
||||
placeholder="Èíòåðâàë (ñåê)"
|
||||
placeholder="Интервал (сек)"
|
||||
min={30}
|
||||
value={overrideInterval}
|
||||
onChange={(v) => setOverrideInterval(typeof v === 'number' ? v : undefined)}
|
||||
/>
|
||||
<Select
|
||||
placeholder="Ðåæèì êàòåãîðèé"
|
||||
placeholder="Режим категорий"
|
||||
style={{ width: 170 }}
|
||||
value={overrideCategoriesMode}
|
||||
onChange={(v) => setOverrideCategoriesMode(v as OverrideCategoriesMode)}
|
||||
|
|
@ -281,7 +286,7 @@ export function HomeSettingsPage() {
|
|||
mode="multiple"
|
||||
allowClear
|
||||
disabled={overrideCategoriesMode !== 'custom'}
|
||||
placeholder="Êàòåãîðèè override"
|
||||
placeholder="Категории override"
|
||||
style={{ width: 280 }}
|
||||
value={overrideCategories}
|
||||
onChange={setOverrideCategories}
|
||||
|
|
@ -291,7 +296,7 @@ export function HomeSettingsPage() {
|
|||
type="primary"
|
||||
onClick={async () => {
|
||||
if (!overrideObjectId || !overrideCheckKey) {
|
||||
message.warning('Âûáåðèòå îáúåêò è ïðîâåðêó')
|
||||
message.warning('Выберите объект и проверку')
|
||||
return
|
||||
}
|
||||
await patchOverrides.mutateAsync([
|
||||
|
|
@ -309,15 +314,15 @@ export function HomeSettingsPage() {
|
|||
: null,
|
||||
},
|
||||
])
|
||||
message.success('Override îáíîâëåí')
|
||||
message.success('Override обновлен')
|
||||
}}
|
||||
>
|
||||
Ñîõðàíèòü override
|
||||
Сохранить override
|
||||
</Button>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
if (!overrideObjectId || !overrideCheckKey) {
|
||||
message.warning('Âûáåðèòå îáúåêò è ïðîâåðêó')
|
||||
message.warning('Выберите объект и проверку')
|
||||
return
|
||||
}
|
||||
await patchOverrides.mutateAsync([
|
||||
|
|
@ -329,10 +334,10 @@ export function HomeSettingsPage() {
|
|||
categories_override: null,
|
||||
},
|
||||
])
|
||||
message.success('Override î÷èùåí')
|
||||
message.success('Override очищен')
|
||||
}}
|
||||
>
|
||||
Î÷èñòèòü override
|
||||
Очистить override
|
||||
</Button>
|
||||
</Space>
|
||||
|
||||
|
|
@ -343,14 +348,15 @@ export function HomeSettingsPage() {
|
|||
rowKey={(row) => `${row.object_id}:${row.check_key}`}
|
||||
pagination={{ pageSize: 6 }}
|
||||
dataSource={checksData?.object_overrides ?? []}
|
||||
scroll={{ x: 900 }}
|
||||
columns={[
|
||||
{
|
||||
title: 'Îáúåêò',
|
||||
title: 'Объект',
|
||||
key: 'object',
|
||||
render: (_: unknown, row: DashboardObjectOverride) =>
|
||||
objects?.find((o) => o.id === row.object_id)?.name ?? row.object_id,
|
||||
},
|
||||
{ title: 'Ïðîâåðêà', dataIndex: 'check_key', key: 'check_key' },
|
||||
{ title: 'Проверка', dataIndex: 'check_key', key: 'check_key' },
|
||||
{
|
||||
title: 'Enabled',
|
||||
key: 'enabled_override',
|
||||
|
|
@ -358,13 +364,13 @@ export function HomeSettingsPage() {
|
|||
row.enabled_override == null ? 'inherit' : row.enabled_override ? 'enabled' : 'disabled',
|
||||
},
|
||||
{
|
||||
title: 'Èíòåðâàë',
|
||||
title: 'Интервал',
|
||||
key: 'interval_sec_override',
|
||||
render: (_: unknown, row: DashboardObjectOverride) =>
|
||||
row.interval_sec_override == null ? 'inherit' : `${row.interval_sec_override}s`,
|
||||
},
|
||||
{
|
||||
title: 'Êàòåãîðèè',
|
||||
title: 'Категории',
|
||||
key: 'categories_override',
|
||||
render: (_: unknown, row: DashboardObjectOverride) =>
|
||||
row.categories_override == null
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue