кодировка 2
This commit is contained in:
parent
b282de5355
commit
f58a6f13f9
1 changed files with 39 additions and 38 deletions
|
|
@ -18,12 +18,12 @@ import { useDashboardMonitorSSE } from '../hooks/useDashboardMonitorSSE'
|
||||||
|
|
||||||
function reasonLabel(reason: string): string {
|
function reasonLabel(reason: string): string {
|
||||||
const map: Record<string, string> = {
|
const map: Record<string, string> = {
|
||||||
object_include_policy: 'Âêëþ÷åí ïîëèòèêîé îáúåêòà',
|
object_include_policy: 'Включен политикой объекта',
|
||||||
object_exclude_policy: 'Èñêëþ÷åí ïîëèòèêîé îáúåêòà',
|
object_exclude_policy: 'Исключен политикой объекта',
|
||||||
inherit_default: 'Íàñëåäîâàíèå ïî óìîë÷àíèþ',
|
inherit_default: 'Наследование по умолчанию',
|
||||||
tag_policy_enabled: 'Âêëþ÷åí ïîëèòèêîé òåãîâ',
|
tag_policy_enabled: 'Включен политикой тегов',
|
||||||
tag_policy_disabled: 'Èñêëþ÷åí ïîëèòèêîé òåãîâ',
|
tag_policy_disabled: 'Исключен политикой тегов',
|
||||||
scope_filtered: 'Âíå òåêóùåãî ôèëüòðà',
|
scope_filtered: 'Вне текущего фильтра',
|
||||||
}
|
}
|
||||||
return map[reason] ?? reason
|
return map[reason] ?? reason
|
||||||
}
|
}
|
||||||
|
|
@ -58,9 +58,9 @@ function healthColor(score: number | null | undefined): string {
|
||||||
|
|
||||||
function formatAge(seconds: number | null | undefined): string {
|
function formatAge(seconds: number | null | undefined): string {
|
||||||
if (seconds == null) return '-'
|
if (seconds == null) return '-'
|
||||||
if (seconds < 60) return `${seconds}ñ`
|
if (seconds < 60) return `${seconds}с`
|
||||||
if (seconds < 3600) return `${Math.floor(seconds / 60)}ì`
|
if (seconds < 3600) return `${Math.floor(seconds / 60)}м`
|
||||||
return `${Math.floor(seconds / 3600)}÷ ${Math.floor((seconds % 3600) / 60)}ì`
|
return `${Math.floor(seconds / 3600)}ч ${Math.floor((seconds % 3600) / 60)}м`
|
||||||
}
|
}
|
||||||
|
|
||||||
function uptimeColor(point: DashboardAvailabilityPoint): string {
|
function uptimeColor(point: DashboardAvailabilityPoint): string {
|
||||||
|
|
@ -71,7 +71,7 @@ function uptimeColor(point: DashboardAvailabilityPoint): string {
|
||||||
|
|
||||||
function UptimeBars({ points, compact = false }: { points: DashboardAvailabilityPoint[]; compact?: boolean }) {
|
function UptimeBars({ points, compact = false }: { points: DashboardAvailabilityPoint[]; compact?: boolean }) {
|
||||||
if (!points.length) {
|
if (!points.length) {
|
||||||
return <Typography.Text type="secondary">Íåò òî÷åê â âûáðàííîì îêíå</Typography.Text>
|
return <Typography.Text type="secondary">Нет точек в выбранном окне</Typography.Text>
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -80,7 +80,7 @@ function UptimeBars({ points, compact = false }: { points: DashboardAvailability
|
||||||
{points.map((point, idx) => (
|
{points.map((point, idx) => (
|
||||||
<div
|
<div
|
||||||
key={`${point.bucket_start}-${idx}`}
|
key={`${point.bucket_start}-${idx}`}
|
||||||
title={`${dayjs(point.bucket_start).format('DD.MM HH:mm')} • ${point.availability_pct ?? '—'}%`}
|
title={`${dayjs(point.bucket_start).format('DD.MM HH:mm')} • ${point.availability_pct ?? '—'}%`}
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: compact ? 3 : 4,
|
minWidth: compact ? 3 : 4,
|
||||||
|
|
@ -104,7 +104,7 @@ function UptimeBars({ points, compact = false }: { points: DashboardAvailability
|
||||||
<Space size={12} style={{ marginTop: 6 }} wrap>
|
<Space size={12} style={{ marginTop: 6 }} wrap>
|
||||||
<Space size={4}><span style={{ width: 10, height: 10, borderRadius: 2, background: '#52c41a', display: 'inline-block' }} /><Typography.Text type="secondary" style={{ fontSize: 12 }}>online</Typography.Text></Space>
|
<Space size={4}><span style={{ width: 10, height: 10, borderRadius: 2, background: '#52c41a', display: 'inline-block' }} /><Typography.Text type="secondary" style={{ fontSize: 12 }}>online</Typography.Text></Space>
|
||||||
<Space size={4}><span style={{ width: 10, height: 10, borderRadius: 2, background: '#ff4d4f', display: 'inline-block' }} /><Typography.Text type="secondary" style={{ fontSize: 12 }}>offline</Typography.Text></Space>
|
<Space size={4}><span style={{ width: 10, height: 10, borderRadius: 2, background: '#ff4d4f', display: 'inline-block' }} /><Typography.Text type="secondary" style={{ fontSize: 12 }}>offline</Typography.Text></Space>
|
||||||
<Space size={4}><span style={{ width: 10, height: 10, borderRadius: 2, background: '#d9d9d9', display: 'inline-block' }} /><Typography.Text type="secondary" style={{ fontSize: 12 }}>íåò äàííûõ</Typography.Text></Space>
|
<Space size={4}><span style={{ width: 10, height: 10, borderRadius: 2, background: '#d9d9d9', display: 'inline-block' }} /><Typography.Text type="secondary" style={{ fontSize: 12 }}>нет данных</Typography.Text></Space>
|
||||||
</Space>
|
</Space>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -158,15 +158,15 @@ export function HomeObjectDetailPage() {
|
||||||
warn += 1
|
warn += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
const rackName = row.rack_name && row.rack_name.trim() ? row.rack_name : 'Áåç ñòîéêè'
|
const rackName = row.rack_name && row.rack_name.trim() ? row.rack_name : 'Без стойки'
|
||||||
if (!byRack.has(rackName)) byRack.set(rackName, [])
|
if (!byRack.has(rackName)) byRack.set(rackName, [])
|
||||||
byRack.get(rackName)?.push(row)
|
byRack.get(rackName)?.push(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
const rackGroups = Array.from(byRack.entries())
|
const rackGroups = Array.from(byRack.entries())
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
if (a[0] === 'Áåç ñòîéêè') return 1
|
if (a[0] === 'Без стойки') return 1
|
||||||
if (b[0] === 'Áåç ñòîéêè') return -1
|
if (b[0] === 'Без стойки') return -1
|
||||||
return a[0].localeCompare(b[0])
|
return a[0].localeCompare(b[0])
|
||||||
})
|
})
|
||||||
.map(([rack, rowsInRack]) => ({
|
.map(([rack, rowsInRack]) => ({
|
||||||
|
|
@ -194,13 +194,13 @@ export function HomeObjectDetailPage() {
|
||||||
<Space direction="vertical" size={0}>
|
<Space direction="vertical" size={0}>
|
||||||
<code>{row.ip}</code>
|
<code>{row.ip}</code>
|
||||||
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
||||||
{row.hostname ?? '—'}
|
{row.hostname ?? '—'}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
</Space>
|
</Space>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Êàòåãîðèÿ',
|
title: 'Категория',
|
||||||
dataIndex: 'category',
|
dataIndex: 'category',
|
||||||
key: 'category',
|
key: 'category',
|
||||||
width: 130,
|
width: 130,
|
||||||
|
|
@ -213,8 +213,8 @@ export function HomeObjectDetailPage() {
|
||||||
<Space direction="vertical" size={0}>
|
<Space direction="vertical" size={0}>
|
||||||
<Tag color={statusColor(row.ping_status)}>{row.ping_status}</Tag>
|
<Tag color={statusColor(row.ping_status)}>{row.ping_status}</Tag>
|
||||||
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
||||||
{row.ping_checked_at ? dayjs(row.ping_checked_at).format('DD.MM HH:mm:ss') : '—'}
|
{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.last_ping_ms != null ? ` • ${row.last_ping_ms}ms` : ''}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
</Space>
|
</Space>
|
||||||
),
|
),
|
||||||
|
|
@ -227,24 +227,24 @@ export function HomeObjectDetailPage() {
|
||||||
const availability = availabilityByDevice.get(Number(row.device_id))
|
const availability = availabilityByDevice.get(Number(row.device_id))
|
||||||
|
|
||||||
if (availabilityLoading) {
|
if (availabilityLoading) {
|
||||||
return <Typography.Text type="secondary">Çàãðóçêà...</Typography.Text>
|
return <Typography.Text type="secondary">Загрузка...</Typography.Text>
|
||||||
}
|
}
|
||||||
if (!availability?.points?.length) {
|
if (!availability?.points?.length) {
|
||||||
return <Typography.Text type="secondary">Íåò äàííûõ</Typography.Text>
|
return <Typography.Text type="secondary">Нет данных</Typography.Text>
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Space direction="vertical" size={2} style={{ width: '100%' }}>
|
<Space direction="vertical" size={2} style={{ width: '100%' }}>
|
||||||
<UptimeBars points={availability.points} compact />
|
<UptimeBars points={availability.points} compact />
|
||||||
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
||||||
{availability.availability != null ? `${availability.availability}%` : '—'}
|
{availability.availability != null ? `${availability.availability}%` : '—'}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
</Space>
|
</Space>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Ñâåæåñòü',
|
title: 'Свежесть',
|
||||||
key: 'freshness',
|
key: 'freshness',
|
||||||
width: 110,
|
width: 110,
|
||||||
render: (_, row) => <Tag color={row.ping_fresh ? 'green' : 'orange'}>{formatAge(row.ping_age_sec)}</Tag>,
|
render: (_, row) => <Tag color={row.ping_fresh ? 'green' : 'orange'}>{formatAge(row.ping_age_sec)}</Tag>,
|
||||||
|
|
@ -260,14 +260,14 @@ export function HomeObjectDetailPage() {
|
||||||
{row.disk_max_pct != null ? ` (${row.disk_max_pct}%)` : ''}
|
{row.disk_max_pct != null ? ` (${row.disk_max_pct}%)` : ''}
|
||||||
</Tag>
|
</Tag>
|
||||||
) : (
|
) : (
|
||||||
'—'
|
'—'
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Ïðîáëåìà',
|
title: 'Проблема',
|
||||||
key: 'problem',
|
key: 'problem',
|
||||||
width: 100,
|
width: 100,
|
||||||
render: (_, row) => (row.has_problem ? <Tag color="red">Äà</Tag> : <Tag color="green">Íåò</Tag>),
|
render: (_, row) => (row.has_problem ? <Tag color="red">Да</Tag> : <Tag color="green">Нет</Tag>),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -277,18 +277,18 @@ export function HomeObjectDetailPage() {
|
||||||
<Col>
|
<Col>
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
<Button icon={<ArrowLeftOutlined />} onClick={() => navigate('/home')}>
|
<Button icon={<ArrowLeftOutlined />} onClick={() => navigate('/home')}>
|
||||||
Íàçàä
|
Назад
|
||||||
</Button>
|
</Button>
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||||
{object?.object_name ?? `Îáúåêò #${objectId}`}
|
{object?.object_name ?? `Объект #${objectId}`}
|
||||||
</Typography.Title>
|
</Typography.Title>
|
||||||
</Space>
|
</Space>
|
||||||
</Col>
|
</Col>
|
||||||
<Col>
|
<Col>
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
{object?.allowed ? <Tag color="green">Â ìîíèòîðèíãå</Tag> : <Tag color="red">Âíå ìîíèòîðèíãà</Tag>}
|
{object?.allowed ? <Tag color="green">В мониторинге</Tag> : <Tag color="red">Вне мониторинга</Tag>}
|
||||||
<Tag color={healthColor(object?.health_score)}>health {object?.health_score ?? '-'}</Tag>
|
<Tag color={healthColor(object?.health_score)}>health {object?.health_score ?? '-'}</Tag>
|
||||||
<Tag color="blue">îêíî {window}</Tag>
|
<Tag color="blue">окно {window}</Tag>
|
||||||
</Space>
|
</Space>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
@ -304,14 +304,14 @@ export function HomeObjectDetailPage() {
|
||||||
type="info"
|
type="info"
|
||||||
showIcon
|
showIcon
|
||||||
style={{ marginBottom: 12 }}
|
style={{ marginBottom: 12 }}
|
||||||
message="Ïî ÷àñòè óñòðîéñòâ äàííûõ ïîêà íåäîñòàòî÷íî. Äëÿ íîâûõ îáúåêòîâ ýòî íîðìàëüíî."
|
message="По части устройств данных пока недостаточно. Для новых объектов это нормально."
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<Row gutter={[12, 12]} style={{ marginBottom: 12 }}>
|
<Row gutter={[12, 12]} style={{ marginBottom: 12 }}>
|
||||||
<Col xs={12} md={6}>
|
<Col xs={12} md={6}>
|
||||||
<Card size="small" loading={summaryLoading} style={{ borderRadius: 12 }}>
|
<Card size="small" loading={summaryLoading} style={{ borderRadius: 12 }}>
|
||||||
<Typography.Text type="secondary">Óñòðîéñòâ</Typography.Text>
|
<Typography.Text type="secondary">Устройств</Typography.Text>
|
||||||
<Typography.Title level={3} style={{ margin: 0 }}>
|
<Typography.Title level={3} style={{ margin: 0 }}>
|
||||||
{summary?.totals.devices ?? 0}
|
{summary?.totals.devices ?? 0}
|
||||||
</Typography.Title>
|
</Typography.Title>
|
||||||
|
|
@ -319,7 +319,7 @@ export function HomeObjectDetailPage() {
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={12} md={6}>
|
<Col xs={12} md={6}>
|
||||||
<Card size="small" loading={summaryLoading} style={{ borderRadius: 12 }}>
|
<Card size="small" loading={summaryLoading} style={{ borderRadius: 12 }}>
|
||||||
<Typography.Text type="secondary">Ïðîáëåìíûõ</Typography.Text>
|
<Typography.Text type="secondary">Проблемных</Typography.Text>
|
||||||
<Typography.Title level={3} style={{ margin: 0 }}>
|
<Typography.Title level={3} style={{ margin: 0 }}>
|
||||||
{summary?.totals.problem_devices ?? 0}
|
{summary?.totals.problem_devices ?? 0}
|
||||||
</Typography.Title>
|
</Typography.Title>
|
||||||
|
|
@ -344,7 +344,7 @@ export function HomeObjectDetailPage() {
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
title="Óñòðîéñòâà îáúåêòà"
|
title="Устройства объекта"
|
||||||
size="small"
|
size="small"
|
||||||
style={{ borderRadius: 12 }}
|
style={{ borderRadius: 12 }}
|
||||||
extra={
|
extra={
|
||||||
|
|
@ -352,16 +352,16 @@ export function HomeObjectDetailPage() {
|
||||||
value={window}
|
value={window}
|
||||||
onChange={(v) => setWindow(v as DashboardAvailabilityWindow)}
|
onChange={(v) => setWindow(v as DashboardAvailabilityWindow)}
|
||||||
options={[
|
options={[
|
||||||
{ value: '24h', label: '24÷' },
|
{ value: '24h', label: '24ч' },
|
||||||
{ value: '3d', label: '3ä' },
|
{ value: '3d', label: '3д' },
|
||||||
{ value: '7d', label: '7ä' },
|
{ value: '7d', label: '7д' },
|
||||||
]}
|
]}
|
||||||
style={{ width: 120 }}
|
style={{ width: 120 }}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{!grouped.rackGroups.length ? (
|
{!grouped.rackGroups.length ? (
|
||||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="Óñòðîéñòâà íå íàéäåíû" />
|
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="Устройства не найдены" />
|
||||||
) : (
|
) : (
|
||||||
<Space direction="vertical" size={12} style={{ width: '100%' }}>
|
<Space direction="vertical" size={12} style={{ width: '100%' }}>
|
||||||
{grouped.rackGroups.map((rackGroup) => (
|
{grouped.rackGroups.map((rackGroup) => (
|
||||||
|
|
@ -388,3 +388,4 @@ export function HomeObjectDetailPage() {
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue