diff --git a/frontend/src/pages/HomeSettings.tsx b/frontend/src/pages/HomeSettings.tsx index 58c4504..50f0918 100644 --- a/frontend/src/pages/HomeSettings.tsx +++ b/frontend/src/pages/HomeSettings.tsx @@ -1,36 +1,21 @@ import { ArrowLeftOutlined } from '@ant-design/icons' -import { Alert, Button, Card, Col, Divider, InputNumber, Row, Select, Space, Switch, Table, Tag, Typography, message } from 'antd' +import { Alert, Button, Card, Checkbox, Collapse, Row, Col, Space, Tag, Typography, message } from 'antd' import dayjs from 'dayjs' -import { useEffect, useMemo, useState } from 'react' +import { useMemo } from 'react' import { useNavigate } from 'react-router-dom' import { useObjects } from '../api/objects' import { - useDashboardChecks, useDashboardMonitorStatus, useDashboardPolicy, useDashboardRunNow, - usePatchDashboardChecks, - usePatchDashboardObjectOverrides, usePatchDashboardObjectPolicy, usePatchDashboardTagPolicy, - type DashboardObjectOverride, } from '../api/dashboard' import { useTags } from '../api/tags' -const CATEGORY_OPTIONS = [ - 'main_server', - 'vm', - 'router', - 'embedded', - 'camera', - 'io_board', - 'bank_terminal', - 'cash_register', - 'other', -] - -type OverrideEnabledMode = 'inherit' | 'enabled' | 'disabled' -type OverrideCategoriesMode = 'inherit' | 'custom' +function cityKey(city: string | null): string { + return city && city.trim() ? city : 'Без города' +} export function HomeSettingsPage() { const navigate = useNavigate() @@ -38,27 +23,13 @@ export function HomeSettingsPage() { const { data: objects } = useObjects() const { data: tags } = useTags() const { data: policy } = useDashboardPolicy() - const { data: checksData } = useDashboardChecks() const { data: monitorStatus } = useDashboardMonitorStatus() const runNow = useDashboardRunNow() const patchTagPolicy = usePatchDashboardTagPolicy() const patchObjectPolicy = usePatchDashboardObjectPolicy() - const patchChecks = usePatchDashboardChecks() - const patchOverrides = usePatchDashboardObjectOverrides() - const [overrideObjectId, setOverrideObjectId] = useState() - const [overrideCheckKey, setOverrideCheckKey] = useState() - const [overrideInterval, setOverrideInterval] = useState() - const [overrideEnabledMode, setOverrideEnabledMode] = useState('inherit') - const [overrideCategoriesMode, setOverrideCategoriesMode] = useState('inherit') - const [overrideCategories, setOverrideCategories] = useState([]) - - const tagPolicyMap = useMemo(() => { - const map = new Map() - for (const row of policy?.tag_policies ?? []) map.set(row.tag_id, row.enabled) - return map - }, [policy]) + const busy = runNow.isPending || patchTagPolicy.isPending || patchObjectPolicy.isPending const objectPolicyMap = useMemo(() => { const map = new Map() @@ -66,46 +37,88 @@ export function HomeSettingsPage() { return map }, [policy]) - const overrideMap = useMemo(() => { - const map = new Map() - for (const row of checksData?.object_overrides ?? []) { - map.set(`${row.object_id}:${row.check_key}`, row) - } + const tagPolicyMap = useMemo(() => { + const map = new Map() + for (const row of policy?.tag_policies ?? []) map.set(row.tag_id, row.enabled) return map - }, [checksData]) + }, [policy]) - useEffect(() => { - if (!overrideObjectId || !overrideCheckKey) { - setOverrideEnabledMode('inherit') - setOverrideInterval(undefined) - setOverrideCategoriesMode('inherit') - setOverrideCategories([]) - return + const objectsByCity = useMemo(() => { + const map = new Map>() + for (const obj of objects ?? []) { + const key = cityKey(obj.city) + if (!map.has(key)) map.set(key, []) + map.get(key)!.push(obj) } - const current = overrideMap.get(`${overrideObjectId}:${overrideCheckKey}`) - if (!current) { - setOverrideEnabledMode('inherit') - setOverrideInterval(undefined) - setOverrideCategoriesMode('inherit') - setOverrideCategories([]) - return + return Array.from(map.entries()) + .sort((a, b) => a[0].localeCompare(b[0])) + .map(([city, rows]) => ({ + city, + rows: rows.sort((x, y) => x.name.localeCompare(y.name)), + })) + }, [objects]) + + const tagsByType = useMemo(() => { + const map = new Map>() + for (const tag of tags ?? []) { + const key = tag.tag_type && tag.tag_type.trim() ? tag.tag_type : 'other' + if (!map.has(key)) map.set(key, []) + map.get(key)!.push(tag) } + return Array.from(map.entries()) + .sort((a, b) => a[0].localeCompare(b[0])) + .map(([tagType, rows]) => ({ + tagType, + rows: rows.sort((x, y) => x.name.localeCompare(y.name)), + })) + }, [tags]) - if (current.enabled_override === true) setOverrideEnabledMode('enabled') - else if (current.enabled_override === false) setOverrideEnabledMode('disabled') - else setOverrideEnabledMode('inherit') + const isObjectEnabled = (objectId: number): boolean => objectPolicyMap.get(objectId) !== 'exclude' + const isTagEnabled = (tagId: number): boolean => tagPolicyMap.get(tagId) ?? true - setOverrideInterval(current.interval_sec_override ?? undefined) - - if (current.categories_override && current.categories_override.length > 0) { - setOverrideCategoriesMode('custom') - setOverrideCategories(current.categories_override) - } else { - setOverrideCategoriesMode('inherit') - setOverrideCategories([]) + const setObjectEnabled = async (objectId: number, enabled: boolean) => { + try { + await patchObjectPolicy.mutateAsync([ + { object_id: objectId, mode: enabled ? 'include' : 'exclude' }, + ]) + } catch { + message.error('Не удалось обновить объект') } - }, [overrideObjectId, overrideCheckKey, overrideMap]) + } + + const setObjectsEnabled = async (objectIds: number[], enabled: boolean) => { + if (objectIds.length === 0) return + try { + await patchObjectPolicy.mutateAsync( + objectIds.map((objectId) => ({ + object_id: objectId, + mode: enabled ? 'include' : 'exclude', + })) + ) + message.success(`Обновлено объектов: ${objectIds.length}`) + } catch { + message.error('Не удалось обновить объекты') + } + } + + const setTagEnabled = async (tagId: number, enabled: boolean) => { + try { + await patchTagPolicy.mutateAsync([{ tag_id: tagId, enabled }]) + } catch { + message.error('Не удалось обновить тег') + } + } + + const setTagsEnabled = async (tagIds: number[], enabled: boolean) => { + if (tagIds.length === 0) return + try { + await patchTagPolicy.mutateAsync(tagIds.map((tagId) => ({ tag_id: tagId, enabled }))) + message.success(`Обновлено тегов: ${tagIds.length}`) + } catch { + message.error('Не удалось обновить теги') + } + } const handleRunNow = async () => { try { @@ -116,6 +129,10 @@ export function HomeSettingsPage() { } } + const allObjectIds = (objects ?? []).map((o) => o.id) + const allObjectsEnabled = allObjectIds.length > 0 && allObjectIds.every((id) => isObjectEnabled(id)) + const someObjectsEnabled = allObjectIds.some((id) => isObjectEnabled(id)) + return (
@@ -130,9 +147,11 @@ export function HomeSettingsPage() { - + + + @@ -147,239 +166,110 @@ export function HomeSettingsPage() { )} - - - {(checksData?.checks ?? []).map((check) => ( - - {check.check_key} - - { - await patchChecks.mutateAsync([{ check_key: check.check_key, enabled }]) - }} - /> - - - { - const val = Number((e.target as HTMLInputElement).value) - if (!Number.isFinite(val) || val <= 0) return - await patchChecks.mutateAsync([{ check_key: check.check_key, interval_sec_default: val }]) - }} - /> - - - { - await patchObjectPolicy.mutateAsync([{ object_id: row.id, mode }]) - }} - /> + return { + key: group.tagType, + label: ( + + { + e.stopPropagation() + void setTagsEnabled(ids, e.target.checked) + }} + onClick={(e) => e.stopPropagation()} + /> + {group.tagType} + ({ids.length}) + ), - }, - ]} - /> - - - - - ({ value: c.check_key, label: c.check_key }))} - /> - setOverrideCategoriesMode(v as OverrideCategoriesMode)} - options={[ - { value: 'inherit', label: 'inherit' }, - { value: 'custom', label: 'custom' }, - ]} - /> -