фикс фикс фикс фикс фикс фикс
This commit is contained in:
parent
057a798f3c
commit
2cc1c71cfa
3 changed files with 114 additions and 66 deletions
|
|
@ -269,7 +269,7 @@ async def dashboard_devices(
|
|||
|
||||
# All eligible devices for this object+category
|
||||
devices_q = (
|
||||
select(Device.id, Device.ip, Device.hostname)
|
||||
select(Device.id, Device.ip, Device.hostname, Device.status)
|
||||
.where(
|
||||
Device.object_id == obj_id,
|
||||
Device.category == policy.category,
|
||||
|
|
@ -324,6 +324,7 @@ async def dashboard_devices(
|
|||
"device_id": d.id,
|
||||
"ip": d.ip,
|
||||
"hostname": d.hostname,
|
||||
"status": d.status,
|
||||
"has_snapshot": has_snapshot,
|
||||
"last_collected_at": latest_map.get(d.id),
|
||||
"snapshot_id": snap_info[0] if snap_info else None,
|
||||
|
|
|
|||
|
|
@ -135,6 +135,7 @@ export interface DeviceSnapshotStatus {
|
|||
device_id: number
|
||||
ip: string
|
||||
hostname: string | null
|
||||
status: string | null
|
||||
has_snapshot: boolean
|
||||
last_collected_at: string | null
|
||||
snapshot_id: number | null
|
||||
|
|
|
|||
|
|
@ -291,13 +291,12 @@ export function SnapshotDashboardPage() {
|
|||
<Tag>{objects.length}</Tag>
|
||||
</Space>
|
||||
),
|
||||
children: objects.map((obj) => (
|
||||
<ObjectPoliciesTable
|
||||
key={obj.object_id}
|
||||
obj={obj}
|
||||
onCollect={(policyId) => handleCollectObj(policyId, obj.object_id)}
|
||||
children: (
|
||||
<ObjectsTable
|
||||
objects={objects}
|
||||
onCollect={handleCollectObj}
|
||||
/>
|
||||
)),
|
||||
),
|
||||
}))}
|
||||
/>
|
||||
|
||||
|
|
@ -331,51 +330,58 @@ export function SnapshotDashboardPage() {
|
|||
)
|
||||
}
|
||||
|
||||
function ObjectPoliciesTable({
|
||||
obj,
|
||||
function ObjectsTable({
|
||||
objects,
|
||||
onCollect,
|
||||
}: {
|
||||
obj: ObjectSnapshotSummary
|
||||
onCollect: (policyId: number) => void
|
||||
objects: ObjectSnapshotSummary[]
|
||||
onCollect: (policyId: number, objId: number) => void
|
||||
}) {
|
||||
const [expandedKey, setExpandedKey] = useState<number | null>(null)
|
||||
const [expandedKeys, setExpandedKeys] = useState<number[]>([])
|
||||
|
||||
const columns: ColumnsType<PolicySnapshotStatus> = [
|
||||
const columns: ColumnsType<ObjectSnapshotSummary> = [
|
||||
{
|
||||
title: 'Политика',
|
||||
dataIndex: 'policy_label',
|
||||
key: 'label',
|
||||
},
|
||||
{
|
||||
title: 'Категория',
|
||||
dataIndex: 'category',
|
||||
key: 'category',
|
||||
render: (cat: string) => <Tag>{cat}</Tag>,
|
||||
title: 'Объект',
|
||||
key: 'name',
|
||||
render: (_, obj) => (
|
||||
<span>
|
||||
<Typography.Text strong>{obj.object_name}</Typography.Text>
|
||||
{obj.client && <Typography.Text type="secondary"> — {obj.client}</Typography.Text>}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Последний сбор',
|
||||
dataIndex: 'last_collected_at',
|
||||
key: 'last_collected',
|
||||
render: (val: string | null) =>
|
||||
val ? (
|
||||
<Tooltip title={dayjs(val).format('DD.MM.YYYY HH:mm:ss')}>
|
||||
{dayjs(val).fromNow()}
|
||||
width: 160,
|
||||
render: (_, obj) => {
|
||||
const latest = obj.policies.reduce<string | null>((acc, p) => {
|
||||
if (!p.last_collected_at) return acc
|
||||
if (!acc) return p.last_collected_at
|
||||
return p.last_collected_at > acc ? p.last_collected_at : acc
|
||||
}, null)
|
||||
return latest ? (
|
||||
<Tooltip title={dayjs(latest).format('DD.MM.YYYY HH:mm:ss')}>
|
||||
{dayjs(latest).fromNow()}
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Typography.Text type="secondary">—</Typography.Text>
|
||||
),
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Покрытие',
|
||||
key: 'coverage',
|
||||
render: (_, row) => {
|
||||
const { success_count, total_devices } = row
|
||||
if (total_devices === 0) return <Tag>нет устройств</Tag>
|
||||
const ratio = success_count / total_devices
|
||||
width: 120,
|
||||
render: (_, obj) => {
|
||||
const total = obj.policies.reduce((s, p) => s + p.total_devices, 0)
|
||||
const success = obj.policies.reduce((s, p) => s + p.success_count, 0)
|
||||
if (total === 0) return <Tag>нет устройств</Tag>
|
||||
const ratio = success / total
|
||||
const color = ratio >= 1 ? 'green' : ratio > 0 ? 'orange' : 'red'
|
||||
return (
|
||||
<Tag color={color}>
|
||||
{success_count} / {total_devices}
|
||||
{success} / {total}
|
||||
</Tag>
|
||||
)
|
||||
},
|
||||
|
|
@ -383,16 +389,16 @@ function ObjectPoliciesTable({
|
|||
{
|
||||
title: '',
|
||||
key: 'actions',
|
||||
width: 60,
|
||||
render: (_, row) => (
|
||||
<Tooltip title="Собрать для этого объекта">
|
||||
width: 50,
|
||||
render: (_, obj) => (
|
||||
<Tooltip title="Собрать все политики для объекта">
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<PlayCircleOutlined />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
onCollect(row.policy_id)
|
||||
obj.policies.forEach((p) => onCollect(p.policy_id, obj.object_id))
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
|
@ -401,30 +407,71 @@ function ObjectPoliciesTable({
|
|||
]
|
||||
|
||||
return (
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<Typography.Text strong>
|
||||
{obj.object_name}
|
||||
{obj.client && <Typography.Text type="secondary"> — {obj.client}</Typography.Text>}
|
||||
</Typography.Text>
|
||||
<Table
|
||||
dataSource={obj.policies}
|
||||
columns={columns}
|
||||
rowKey="policy_id"
|
||||
size="small"
|
||||
pagination={false}
|
||||
style={{ marginTop: 4 }}
|
||||
expandable={{
|
||||
expandedRowKeys: expandedKey !== null ? [expandedKey] : [],
|
||||
onExpand: (expanded, record) => setExpandedKey(expanded ? record.policy_id : null),
|
||||
expandedRowRender: (record) => (
|
||||
<DevicesList
|
||||
objId={obj.object_id}
|
||||
objName={obj.object_name}
|
||||
policyId={record.policy_id}
|
||||
/>
|
||||
<Table
|
||||
dataSource={objects}
|
||||
columns={columns}
|
||||
rowKey="object_id"
|
||||
size="small"
|
||||
pagination={false}
|
||||
expandable={{
|
||||
expandedRowKeys: expandedKeys,
|
||||
onExpand: (expanded, record) =>
|
||||
setExpandedKeys(
|
||||
expanded
|
||||
? [...expandedKeys, record.object_id]
|
||||
: expandedKeys.filter((k) => k !== record.object_id)
|
||||
),
|
||||
}}
|
||||
expandedRowRender: (record) => (
|
||||
<ObjectDevicesExpanded
|
||||
obj={record}
|
||||
onCollect={onCollect}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function ObjectDevicesExpanded({
|
||||
obj,
|
||||
onCollect,
|
||||
}: {
|
||||
obj: ObjectSnapshotSummary
|
||||
onCollect: (policyId: number, objId: number) => void
|
||||
}) {
|
||||
if (obj.policies.length === 1) {
|
||||
return (
|
||||
<DevicesList
|
||||
objId={obj.object_id}
|
||||
objName={obj.object_name}
|
||||
policyId={obj.policies[0].policy_id}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{obj.policies.map((p) => (
|
||||
<div key={p.policy_id} style={{ marginBottom: 12 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
|
||||
<Typography.Text strong>{p.policy_label}</Typography.Text>
|
||||
<Tag>{p.category}</Tag>
|
||||
<Tooltip title="Собрать">
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<PlayCircleOutlined />}
|
||||
onClick={() => onCollect(p.policy_id, obj.object_id)}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<DevicesList
|
||||
objId={obj.object_id}
|
||||
objName={obj.object_name}
|
||||
policyId={p.policy_id}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -483,14 +530,13 @@ function DevicesList({
|
|||
render: (val: string | null) => val || <Typography.Text type="secondary">—</Typography.Text>,
|
||||
},
|
||||
{
|
||||
title: 'Статус',
|
||||
title: 'Снапшот',
|
||||
key: 'status',
|
||||
render: (_, row) =>
|
||||
row.has_snapshot ? (
|
||||
<Tag color="green">Есть</Tag>
|
||||
) : (
|
||||
<Tag color="red">Нет</Tag>
|
||||
),
|
||||
render: (_, row) => {
|
||||
if (row.has_snapshot) return <Tag color="green">Есть</Tag>
|
||||
if (row.status === 'offline') return <Tag color="red">Нет пинга</Tag>
|
||||
return <Tag color="orange">Нет снапшота</Tag>
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Собран',
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue