diff --git a/backend/app/services/task.py b/backend/app/services/task.py index 4fe02f1..92c99a8 100644 --- a/backend/app/services/task.py +++ b/backend/app/services/task.py @@ -64,11 +64,15 @@ async def _resolve_scope(scope: dict) -> list[tuple[Device, Object]]: query = query.where(Device.rack_id.in_(rack_ids)) elif scope_type == "category": query = query.where(Device.category == scope["value"]) - if "object_id" in scope: + if "object_ids" in scope: + query = query.where(Device.object_id.in_(scope["object_ids"])) + elif "object_id" in scope: query = query.where(Device.object_id == scope["object_id"]) elif scope_type == "role": query = query.where(Device.role == scope["value"]) - if "object_id" in scope: + if "object_ids" in scope: + query = query.where(Device.object_id.in_(scope["object_ids"])) + elif "object_id" in scope: query = query.where(Device.object_id == scope["object_id"]) else: return [] diff --git a/frontend/src/pages/Tasks.tsx b/frontend/src/pages/Tasks.tsx index ae905f4..f1899f2 100644 --- a/frontend/src/pages/Tasks.tsx +++ b/frontend/src/pages/Tasks.tsx @@ -1,16 +1,69 @@ -import { Table, Typography } from 'antd' +import { PlusOutlined } from '@ant-design/icons' +import { Button, Form, Input, Modal, Select, Space, Table, Typography, message } from 'antd' import dayjs from 'dayjs' import { useState } from 'react' import { useNavigate } from 'react-router-dom' -import { useTasks, type Task } from '../api/tasks' +import { useObjects } from '../api/objects' +import { useActions, useCreateTask, useTasks, type Task } from '../api/tasks' import { TaskStatusBadge } from '../components/StatusBadge' const PAGE_SIZE = 50 +const CATEGORY_OPTIONS = [ + { value: 'server', label: 'Сервер' }, + { value: 'raspberry', label: 'Raspberry Pi' }, + { value: 'mikrotik', label: 'Mikrotik' }, + { value: 'camera', label: 'Камера' }, + { value: 'embedded', label: 'Embedded' }, + { value: 'other', label: 'Другое' }, +] + export function TasksPage() { const navigate = useNavigate() const [page, setPage] = useState(1) const { data: tasks, isLoading } = useTasks(page, PAGE_SIZE) + const { data: actions } = useActions() + const { data: objects } = useObjects() + const createTask = useCreateTask() + + const [modalOpen, setModalOpen] = useState(false) + const [form] = Form.useForm() + + // watched form values for conditional rendering + const scopeType: string = Form.useWatch('scope_type', form) + const selectedAction: string = Form.useWatch('action', form) + const currentAction = actions?.find((a) => a.name === selectedAction) + + const openModal = () => { + form.resetFields() + setModalOpen(true) + } + + const handleSubmit = async () => { + const values = await form.validateFields() + const { action, scope_type, scope_category, scope_object_ids, scope_object_id, ...params } = values + + let target_scope: object + if (scope_type === 'category') { + if (scope_object_ids?.length > 0) { + target_scope = { type: 'category', value: scope_category, object_ids: scope_object_ids } + } else { + target_scope = { type: 'category', value: scope_category } + } + } else { + // scope_type === 'object' + target_scope = { type: 'object', id: scope_object_id } + } + + try { + const task = await createTask.mutateAsync({ type: action, target_scope, params }) + message.success('Задача создана') + setModalOpen(false) + navigate(`/tasks/${task.id}`) + } catch { + message.error('Ошибка создания задачи') + } + } const columns = [ { @@ -46,11 +99,19 @@ export function TasksPage() { }, ] + const objectOptions = (objects ?? []).map((o) => ({ value: o.id, label: o.name })) + return (