кодировка
This commit is contained in:
parent
ecb929d780
commit
b282de5355
1 changed files with 47 additions and 46 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
|
|
@ -24,10 +24,10 @@ import { useTags } from '../api/tags'
|
|||
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 == 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)}м`
|
||||
}
|
||||
|
||||
function scopeToSearch(scope: DashboardScope): string {
|
||||
|
|
@ -91,9 +91,9 @@ export function HomePage() {
|
|||
const handleRunNow = async () => {
|
||||
try {
|
||||
await runNow.mutateAsync({ ...scope, force: true })
|
||||
message.success('Ìîíèòîðèíã çàïóùåí')
|
||||
message.success('Мониторинг запущен')
|
||||
} catch {
|
||||
message.error('Íå óäàëîñü çàïóñòèòü ìîíèòîðèíã')
|
||||
message.error('Не удалось запустить мониторинг')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -203,13 +203,13 @@ export function HomePage() {
|
|||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
NOC Dashboard
|
||||
</Typography.Title>
|
||||
<Typography.Text type="secondary">SLA, ïîêðûòèå ìîíèòîðèíãîì, èíöèäåíòû è î÷åðåäü äåéñòâèé</Typography.Text>
|
||||
<Typography.Text type="secondary">SLA, покрытие мониторингом, инциденты и очередь действий</Typography.Text>
|
||||
</Col>
|
||||
<Col>
|
||||
<Space wrap>
|
||||
<Button onClick={() => navigate(`/home/settings${scopeToSearch(scope)}`)}>Íàñòðîéêè ìîíèòîðèíãà</Button>
|
||||
<Button onClick={() => navigate(`/home/settings${scopeToSearch(scope)}`)}>Настройки мониторинга</Button>
|
||||
<Button type="primary" onClick={handleRunNow} loading={runNow.isPending}>
|
||||
Çàïóñòèòü ñåé÷àñ
|
||||
Запустить сейчас
|
||||
</Button>
|
||||
</Space>
|
||||
</Col>
|
||||
|
|
@ -219,7 +219,7 @@ export function HomePage() {
|
|||
<Row gutter={[8, 8]}>
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<Select
|
||||
placeholder="Ãîðîä"
|
||||
placeholder="Город"
|
||||
allowClear
|
||||
style={{ width: '100%' }}
|
||||
options={(meta?.cities ?? []).map((c) => ({ value: c, label: c }))}
|
||||
|
|
@ -229,7 +229,7 @@ export function HomePage() {
|
|||
</Col>
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<Select
|
||||
placeholder="Êëèåíò"
|
||||
placeholder="Клиент"
|
||||
allowClear
|
||||
style={{ width: '100%' }}
|
||||
options={(meta?.clients ?? []).map((c) => ({ value: c, label: c }))}
|
||||
|
|
@ -240,7 +240,7 @@ export function HomePage() {
|
|||
<Col xs={24} sm={12} lg={6}>
|
||||
<Select
|
||||
mode="multiple"
|
||||
placeholder="Îáúåêòû"
|
||||
placeholder="Объекты"
|
||||
allowClear
|
||||
style={{ width: '100%' }}
|
||||
value={objectIds}
|
||||
|
|
@ -251,7 +251,7 @@ export function HomePage() {
|
|||
<Col xs={24} sm={12} lg={6}>
|
||||
<Select
|
||||
mode="multiple"
|
||||
placeholder="Òåãè"
|
||||
placeholder="Теги"
|
||||
allowClear
|
||||
style={{ width: '100%' }}
|
||||
value={tagIds}
|
||||
|
|
@ -263,12 +263,12 @@ export function HomePage() {
|
|||
</Card>
|
||||
|
||||
<Row gutter={[12, 12]} style={{ marginBottom: 12 }}>
|
||||
<Col xs={12} md={8} lg={4}><Card size="small" loading={isLoading}><Statistic title="Îáúåêòîâ â ñêîóïå" value={totals.objects} /></Card></Col>
|
||||
<Col xs={12} md={8} lg={4}><Card size="small" loading={isLoading}><Statistic title="Óñòðîéñòâ" value={totals.devices} /></Card></Col>
|
||||
<Col xs={12} md={8} lg={4}><Card size="small" loading={isLoading}><Statistic title="Èíöèäåíòíûõ îáúåêòîâ" value={incidentObjects.length} /></Card></Col>
|
||||
<Col xs={12} md={8} lg={4}><Card size="small" loading={isLoading}><Statistic title="Ñèãíàëîâ ïðîáëåì" value={signalCount} /></Card></Col>
|
||||
<Col xs={12} md={8} lg={4}><Card size="small" loading={isLoading}><Statistic title="Ïðîñðî÷åííûå îáúåêòû" value={stalePingObjects.length} /></Card></Col>
|
||||
<Col xs={12} md={8} lg={4}><Card size="small" loading={isLoading}><Statistic title="Ñðåäíèé health" value={avgHealth ?? '—'} /></Card></Col>
|
||||
<Col xs={12} md={8} lg={4}><Card size="small" loading={isLoading}><Statistic title="Объектов в скоупе" value={totals.objects} /></Card></Col>
|
||||
<Col xs={12} md={8} lg={4}><Card size="small" loading={isLoading}><Statistic title="Устройств" value={totals.devices} /></Card></Col>
|
||||
<Col xs={12} md={8} lg={4}><Card size="small" loading={isLoading}><Statistic title="Инцидентных объектов" value={incidentObjects.length} /></Card></Col>
|
||||
<Col xs={12} md={8} lg={4}><Card size="small" loading={isLoading}><Statistic title="Сигналов проблем" value={signalCount} /></Card></Col>
|
||||
<Col xs={12} md={8} lg={4}><Card size="small" loading={isLoading}><Statistic title="Просроченные объекты" value={stalePingObjects.length} /></Card></Col>
|
||||
<Col xs={12} md={8} lg={4}><Card size="small" loading={isLoading}><Statistic title="Средний health" value={avgHealth ?? '—'} /></Card></Col>
|
||||
</Row>
|
||||
|
||||
{monitorStatus?.last_error ? <Alert type="error" showIcon style={{ marginBottom: 12 }} message={monitorStatus.last_error} /> : null}
|
||||
|
|
@ -276,9 +276,9 @@ export function HomePage() {
|
|||
<Row gutter={[12, 12]}>
|
||||
<Col xs={24} xl={16}>
|
||||
<Space direction="vertical" size={12} style={{ width: '100%' }}>
|
||||
<Card title="Îáúåêòû ñ èíöèäåíòàìè" size="small" style={{ borderRadius: 12 }}>
|
||||
<Card title="Объекты с инцидентами" size="small" style={{ borderRadius: 12 }}>
|
||||
{incidentObjects.length === 0 ? (
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="Êðèòè÷íûõ îáúåêòîâ íåò" />
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="Критичных объектов нет" />
|
||||
) : (
|
||||
<List
|
||||
dataSource={incidentObjects.slice(0, 12)}
|
||||
|
|
@ -293,7 +293,7 @@ export function HomePage() {
|
|||
<List.Item
|
||||
actions={[
|
||||
<Button key="open" type="link" onClick={() => navigate(`/home/objects/${item.object_id}${scopeToSearch(scope)}`)}>
|
||||
Îòêðûòü
|
||||
Открыть
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
|
|
@ -301,17 +301,17 @@ export function HomePage() {
|
|||
title={
|
||||
<Space wrap size={8}>
|
||||
<Typography.Text strong>{item.object_name}</Typography.Text>
|
||||
<Tag>{item.city ?? '—'}</Tag>
|
||||
<Tag color="blue">{item.client ?? '—'}</Tag>
|
||||
<Tag color={healthColor(item.health_score)}>health {item.health_score ?? '—'}</Tag>
|
||||
<Tag>{item.city ?? '—'}</Tag>
|
||||
<Tag color="blue">{item.client ?? '—'}</Tag>
|
||||
<Tag color={healthColor(item.health_score)}>health {item.health_score ?? '—'}</Tag>
|
||||
</Space>
|
||||
}
|
||||
description={
|
||||
<Space split={<span>•</span>} wrap>
|
||||
<Space split={<span>•</span>} wrap>
|
||||
<span>offline: {offline}</span>
|
||||
<span>unknown: {unknown}</span>
|
||||
<span>disk warn/failed: {diskWarn}/{diskFailed}</span>
|
||||
<span>óñòðîéñòâ: {item.device_count}</span>
|
||||
<span>устройств: {item.device_count}</span>
|
||||
<span>age ping: {formatAge(ping?.max_age_sec)}</span>
|
||||
</Space>
|
||||
}
|
||||
|
|
@ -323,15 +323,15 @@ export function HomePage() {
|
|||
)}
|
||||
</Card>
|
||||
|
||||
<Card title="Îáúåêòû ñ íåñâåæèìè äàííûìè ìîíèòîðèíãà" size="small" style={{ borderRadius: 12 }}>
|
||||
<Card title="Объекты с несвежими данными мониторинга" size="small" style={{ borderRadius: 12 }}>
|
||||
{stalePingObjects.length === 0 ? (
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="Âñå îáúåêòû îáíîâëÿþòñÿ â ïðåäåëàõ SLA" />
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="Все объекты обновляются в пределах SLA" />
|
||||
) : (
|
||||
<List
|
||||
size="small"
|
||||
dataSource={stalePingObjects}
|
||||
renderItem={(item) => (
|
||||
<List.Item actions={[<Button key="open" type="link" onClick={() => navigate(`/home/objects/${item.obj.object_id}${scopeToSearch(scope)}`)}>Îòêðûòü</Button>]}>
|
||||
<List.Item actions={[<Button key="open" type="link" onClick={() => navigate(`/home/objects/${item.obj.object_id}${scopeToSearch(scope)}`)}>Открыть</Button>]}>
|
||||
<Space direction="vertical" size={0} style={{ width: '100%' }}>
|
||||
<Row justify="space-between">
|
||||
<Typography.Text>{item.obj.object_name}</Typography.Text>
|
||||
|
|
@ -351,28 +351,28 @@ export function HomePage() {
|
|||
|
||||
<Col xs={24} xl={8}>
|
||||
<Space direction="vertical" size={12} style={{ width: '100%' }}>
|
||||
<Card title="SLA è ïîêðûòèå" size="small" style={{ borderRadius: 12 }}>
|
||||
<Card title="SLA и покрытие" size="small" style={{ borderRadius: 12 }}>
|
||||
<Space direction="vertical" size={10} style={{ width: '100%' }}>
|
||||
<div>
|
||||
<Row justify="space-between">
|
||||
<Typography.Text>Äîñòóïíîñòü (online)</Typography.Text>
|
||||
<Typography.Text strong>{availabilityPct != null ? `${availabilityPct}%` : '—'}</Typography.Text>
|
||||
<Typography.Text>Доступность (online)</Typography.Text>
|
||||
<Typography.Text strong>{availabilityPct != null ? `${availabilityPct}%` : '—'}</Typography.Text>
|
||||
</Row>
|
||||
<Progress percent={availabilityPct ?? 0} size="small" showInfo={false} strokeColor={availabilityPct != null && availabilityPct < 95 ? '#ff4d4f' : '#52c41a'} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Row justify="space-between">
|
||||
<Typography.Text>Ïîêðûòèå ìîíèòîðèíãîì</Typography.Text>
|
||||
<Typography.Text strong>{coveragePct != null ? `${coveragePct}%` : '—'}</Typography.Text>
|
||||
<Typography.Text>Покрытие мониторингом</Typography.Text>
|
||||
<Typography.Text strong>{coveragePct != null ? `${coveragePct}%` : '—'}</Typography.Text>
|
||||
</Row>
|
||||
<Progress percent={coveragePct ?? 0} size="small" showInfo={false} strokeColor="#1677ff" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Row justify="space-between">
|
||||
<Typography.Text>Ñâåæåñòü äàííûõ</Typography.Text>
|
||||
<Typography.Text strong>{freshnessPct != null ? `${freshnessPct}%` : '—'}</Typography.Text>
|
||||
<Typography.Text>Свежесть данных</Typography.Text>
|
||||
<Typography.Text strong>{freshnessPct != null ? `${freshnessPct}%` : '—'}</Typography.Text>
|
||||
</Row>
|
||||
<Progress percent={freshnessPct ?? 0} size="small" showInfo={false} strokeColor={freshnessPct != null && freshnessPct < 90 ? '#faad14' : '#52c41a'} />
|
||||
</div>
|
||||
|
|
@ -381,16 +381,16 @@ export function HomePage() {
|
|||
<Tag color="green">online: {onlineTotal}</Tag>
|
||||
<Tag color="red">offline: {offlineTotal}</Tag>
|
||||
<Tag color="orange">unknown: {unknownTotal}</Tag>
|
||||
<Tag>ïî ping: {monitoredTotal}</Tag>
|
||||
<Tag>по ping: {monitoredTotal}</Tag>
|
||||
</Space>
|
||||
</Space>
|
||||
</Card>
|
||||
|
||||
<Card title="Ñîñòîÿíèå öèêëà ìîíèòîðèíãà" size="small" style={{ borderRadius: 12 }}>
|
||||
<Card title="Состояние цикла мониторинга" size="small" style={{ borderRadius: 12 }}>
|
||||
<Space direction="vertical" size={8} style={{ width: '100%' }}>
|
||||
<Row justify="space-between"><Typography.Text>Ñòàòóñ</Typography.Text><Tag color={runStatusColor(monitorStatus?.status)}>{monitorStatus?.status ?? '-'}</Tag></Row>
|
||||
<Row justify="space-between"><Typography.Text>Îáúåêòû â ìîíèòîðèíãå</Typography.Text><Typography.Text strong>{summary?.included_count ?? totals.allowed_objects}</Typography.Text></Row>
|
||||
<Row justify="space-between"><Typography.Text>Ïîñëåäíèé óñïåõ</Typography.Text><Typography.Text strong>{monitorStatus?.last_success_at ? dayjs(monitorStatus.last_success_at).format('DD.MM HH:mm:ss') : '—'}</Typography.Text></Row>
|
||||
<Row justify="space-between"><Typography.Text>Статус</Typography.Text><Tag color={runStatusColor(monitorStatus?.status)}>{monitorStatus?.status ?? '-'}</Tag></Row>
|
||||
<Row justify="space-between"><Typography.Text>Объекты в мониторинге</Typography.Text><Typography.Text strong>{summary?.included_count ?? totals.allowed_objects}</Typography.Text></Row>
|
||||
<Row justify="space-between"><Typography.Text>Последний успех</Typography.Text><Typography.Text strong>{monitorStatus?.last_success_at ? dayjs(monitorStatus.last_success_at).format('DD.MM HH:mm:ss') : '—'}</Typography.Text></Row>
|
||||
|
||||
{lastCounts ? (
|
||||
<Space direction="vertical" size={2} style={{ width: '100%' }}>
|
||||
|
|
@ -405,7 +405,7 @@ export function HomePage() {
|
|||
|
||||
<Card title="Action queue" size="small" style={{ borderRadius: 12 }}>
|
||||
{actionQueue.length === 0 ? (
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="Êðèòè÷íûõ äåéñòâèé ñåé÷àñ íåò" />
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="Критичных действий сейчас нет" />
|
||||
) : (
|
||||
<List
|
||||
size="small"
|
||||
|
|
@ -414,7 +414,7 @@ export function HomePage() {
|
|||
<List.Item
|
||||
actions={[
|
||||
<Button key="open" type="link" onClick={() => navigate(`/home/objects/${row.obj.object_id}${scopeToSearch(scope)}`)}>
|
||||
Îòêðûòü
|
||||
Открыть
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
|
|
@ -427,7 +427,7 @@ export function HomePage() {
|
|||
</Col>
|
||||
</Row>
|
||||
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
||||
{row.reasons.join(' • ')}
|
||||
{row.reasons.join(' • ')}
|
||||
</Typography.Text>
|
||||
</List.Item>
|
||||
)}
|
||||
|
|
@ -440,3 +440,4 @@ export function HomePage() {
|
|||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue