фикс фикс

This commit is contained in:
dv 2026-05-27 18:08:28 +03:00
parent 5d1c409739
commit 599fe566b8
2 changed files with 77 additions and 19 deletions

View file

@ -123,3 +123,29 @@ export const useCollectPolicy = (objId: number) => {
}, },
}) })
} }
export const useCreateSnapshotPolicyDynamic = () => {
const qc = useQueryClient()
return useMutation({
mutationFn: ({
objId,
body,
}: {
objId: number
body: {
category: string
action_type: string
params?: Record<string, unknown>
label: string
is_active?: boolean
}
}) =>
api
.post<SnapshotPolicy>(`/api/v1/objects/${objId}/snapshot-policies`, body)
.then((r) => r.data),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['snapshot-policies'] })
qc.invalidateQueries({ queryKey: ['snapshot-dashboard'] })
},
})
}

View file

@ -26,13 +26,14 @@ import 'dayjs/locale/ru'
import { useMemo, useState } from 'react' import { useMemo, useState } from 'react'
import { import {
useSnapshotDashboard, useSnapshotDashboard,
useCreateSnapshotPolicy, useCreateSnapshotPolicyDynamic,
useUpdateSnapshotPolicy, useUpdateSnapshotPolicy,
useDeleteSnapshotPolicy, useDeleteSnapshotPolicy,
useCollectPolicy, useCollectPolicy,
type ObjectSnapshotSummary, type ObjectSnapshotSummary,
type PolicySnapshotStatus, type PolicySnapshotStatus,
} from '../api/snapshotPolicies' } from '../api/snapshotPolicies'
import { useObjects } from '../api/objects'
dayjs.extend(relativeTime) dayjs.extend(relativeTime)
dayjs.locale('ru') dayjs.locale('ru')
@ -57,6 +58,7 @@ const ACTION_OPTIONS = [
] ]
interface PolicyFormValues { interface PolicyFormValues {
object_id?: number
label: string label: string
category: string category: string
action_type: string action_type: string
@ -65,6 +67,7 @@ interface PolicyFormValues {
export function SnapshotDashboardPage() { export function SnapshotDashboardPage() {
const { data: dashboard, isLoading } = useSnapshotDashboard() const { data: dashboard, isLoading } = useSnapshotDashboard()
const { data: allObjects } = useObjects()
const [searchText, setSearchText] = useState('') const [searchText, setSearchText] = useState('')
const [modalOpen, setModalOpen] = useState(false) const [modalOpen, setModalOpen] = useState(false)
const [editTarget, setEditTarget] = useState<{ objId: number; policy: PolicySnapshotStatus } | null>(null) const [editTarget, setEditTarget] = useState<{ objId: number; policy: PolicySnapshotStatus } | null>(null)
@ -98,10 +101,14 @@ export function SnapshotDashboardPage() {
return sorted return sorted
}, [filtered]) }, [filtered])
const openCreate = (objId: number) => { const openCreate = (objId?: number) => {
form.resetFields() form.resetFields()
setEditTarget(null) setEditTarget(null)
setTargetObjId(objId) if (objId) {
setTargetObjId(objId)
} else {
setTargetObjId(null)
}
setModalOpen(true) setModalOpen(true)
} }
@ -120,7 +127,11 @@ export function SnapshotDashboardPage() {
const handleSubmit = async () => { const handleSubmit = async () => {
const values = await form.validateFields() const values = await form.validateFields()
const objId = targetObjId! const objId = targetObjId ?? values.object_id
if (!objId) {
message.error('Выберите объект')
return
}
const params: Record<string, unknown> = {} const params: Record<string, unknown> = {}
if (values.action_type === 'get_file' && values.path) { if (values.action_type === 'get_file' && values.path) {
params.path = values.path params.path = values.path
@ -137,11 +148,14 @@ export function SnapshotDashboardPage() {
}) })
message.success('Политика обновлена') message.success('Политика обновлена')
} else { } else {
await createPolicyMutation.mutateAsync({ await createPolicyFor.mutateAsync({
label: values.label, objId,
category: values.category, body: {
action_type: values.action_type, label: values.label,
params, category: values.category,
action_type: values.action_type,
params,
},
}) })
message.success('Политика создана') message.success('Политика создана')
} }
@ -151,7 +165,7 @@ export function SnapshotDashboardPage() {
} }
} }
const createPolicyMutation = useCreateSnapshotPolicy(targetObjId ?? 0) const createPolicyFor = useCreateSnapshotPolicyDynamic()
const updatePolicyMutation = useUpdateSnapshotPolicy(targetObjId ?? 0) const updatePolicyMutation = useUpdateSnapshotPolicy(targetObjId ?? 0)
return ( return (
@ -160,20 +174,25 @@ export function SnapshotDashboardPage() {
<Typography.Title level={4} style={{ margin: 0 }}> <Typography.Title level={4} style={{ margin: 0 }}>
Снапшоты Снапшоты
</Typography.Title> </Typography.Title>
<Input.Search <Space>
placeholder="Поиск по объекту, городу..." <Input.Search
allowClear placeholder="Поиск по объекту, городу..."
style={{ width: 300 }} allowClear
value={searchText} style={{ width: 300 }}
onChange={(e) => setSearchText(e.target.value)} value={searchText}
/> onChange={(e) => setSearchText(e.target.value)}
/>
<Button type="primary" icon={<PlusOutlined />} onClick={() => openCreate()}>
Добавить политику
</Button>
</Space>
</div> </div>
{isLoading && <Typography.Text type="secondary">Загрузка...</Typography.Text>} {isLoading && <Typography.Text type="secondary">Загрузка...</Typography.Text>}
{!isLoading && filtered.length === 0 && ( {!isLoading && filtered.length === 0 && (
<Typography.Text type="secondary"> <Typography.Text type="secondary">
Нет объектов с политиками снапшотов. Добавьте политику через страницу объекта. Нет объектов с политиками снапшотов. Нажмите «Добавить политику» чтобы начать.
</Typography.Text> </Typography.Text>
)} )}
@ -203,11 +222,24 @@ export function SnapshotDashboardPage() {
open={modalOpen} open={modalOpen}
onCancel={() => setModalOpen(false)} onCancel={() => setModalOpen(false)}
onOk={handleSubmit} onOk={handleSubmit}
confirmLoading={createPolicyMutation.isPending || updatePolicyMutation.isPending} confirmLoading={createPolicyFor.isPending || updatePolicyMutation.isPending}
okText="Сохранить" okText="Сохранить"
cancelText="Отмена" cancelText="Отмена"
> >
<Form form={form} layout="vertical"> <Form form={form} layout="vertical">
{!targetObjId && !editTarget && (
<Form.Item name="object_id" label="Объект" rules={[{ required: true, message: 'Выберите объект' }]}>
<Select
showSearch
placeholder="Выберите объект"
optionFilterProp="label"
options={(allObjects ?? []).map((o) => ({
value: o.id,
label: `${o.name}${o.city ? ` (${o.city})` : ''}`,
}))}
/>
</Form.Item>
)}
<Form.Item name="label" label="Название" rules={[{ required: true, message: 'Введите название' }]}> <Form.Item name="label" label="Название" rules={[{ required: true, message: 'Введите название' }]}>
<Input placeholder="CAPS config" /> <Input placeholder="CAPS config" />
</Form.Item> </Form.Item>