import { Autocomplete, Box, Button, FormControl, FormLabel, MenuItem, Select, TextField } from '@mui/material'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { findItemPage, positiveInteger, readListVisit, rememberListVisit } from '../../utils/listNavigation'; import { useSearchParams } from 'react-router-dom'; import { toast } from 'react-toastify'; import { SspApi } from '../../api/ssp'; import { TasksApi } from '../../api/tasks'; import { useAuth } from '../../app/context/AuthProvider'; import { HeaderSwitch } from '../../components/HeaderMenu/HeaderSwitch'; import { PrimaryButton } from '../../components/common/Buttons/Buttons'; import { ResetFiltersButton } from '../../components/common/Buttons/ButtonsActions'; import Modal from '../../components/common/Modal/Modal'; import { OrgUnitsAutocompletePaper } from '../../components/common/OrgUnitsAutocompletePaper/OrgUnitsAutocompletePaper'; import { renderOrgUnitValue } from '../../components/common/OrgUnitsAutocompletePaper/renderOrgUnitValue'; import { PaginatedList } from '../../components/common/PaginatedList'; import { TaskCardComponent } from '../../components/common/TaskCardComponent'; import { WhitePlus } from '../../components/common/icons/icons'; import { FORM_TYPE_OPTIONS, FORM_TYPE_TRANSLATE, ORG_UNIT_TYPE_OPTIONS, ROLES_NAME_ID } from '../../constants/constants'; import { PageContainer } from '../StyledForPage'; const modalSelectMenuProps = { sx: { zIndex: 1301 }, style: { zIndex: 1301 }, }; const modalSelectSx = { '& .MuiOutlinedInput-notchedOutline': { borderColor: 'rgba(0,0,0,0.1)', }, }; export default function TasksPage() { const [searchParams, setSearchParams] = useSearchParams(); const [items, setItems] = useState([]); const [loading, setLoading] = useState(false); const [loadingUpdate, setLoadingUpdate] = useState(false); const [modalOpen, setModalOpen] = useState(false); const yearFilter = searchParams.get('year') || ''; const formTypeFilter = searchParams.get('form_type') || ''; const orgUnitsFilter = searchParams.get('org_units') || ''; const deleteStatusFilter = searchParams.get('delete_status') || 'all'; const orgUnitIdsFilter = useMemo( () => orgUnitsFilter .split(',') .filter(Boolean) .map(Number) .filter((value) => Number.isInteger(value) && value > 0), [orgUnitsFilter], ); const page = positiveInteger(searchParams.get('page'), 1); const limit = [15, 25, 50, 100, 200].includes(Number(searchParams.get('limit'))) ? Number(searchParams.get('limit')) : 25; const [lastVisit] = useState(() => readListVisit('tasks')); const listUrl = `/tasks${searchParams.size ? `?${searchParams}` : ''}`; const restoreVisit = useRef(lastVisit?.url === listUrl ? lastVisit : null); const requestId = useRef(0); const setPage = (value) => setSearchParams((params) => { const next = new URLSearchParams(params); next.set('page', String(value)); return next; }); const [totalCount, setTotalCount] = useState(0); const [sspList, setSspList] = useState([]); const [formData, setFormData] = useState({ year: '', orgUnitType: '', org_unit_id: [], formTypeCode: '', }); const yearOptions = useMemo(() => { const currentYear = new Date().getFullYear(); return Array.from({ length: 8 }, (_, index) => currentYear - 5 + index); }, []); const selectedOrgUnits = useMemo(() => sspList.filter((unit) => orgUnitIdsFilter.includes(unit.id)), [sspList, orgUnitIdsFilter]); const filteredOrgUnits = useMemo(() => { if (!formData.orgUnitType) return []; const isSsp = formData.orgUnitType === 'ssp'; return sspList.filter((unit) => unit.is_ssp === isSsp); }, [sspList, formData.orgUnitType]); const isAllOrgUnitsSelected = useMemo(() => { return filteredOrgUnits.length > 0 && filteredOrgUnits.every((unit) => formData.org_unit_id.includes(unit.id)); }, [filteredOrgUnits, formData.org_unit_id]); const isOrgUnitsIndeterminate = useMemo(() => { return formData.org_unit_id.length > 0 && !isAllOrgUnitsSelected; }, [formData.org_unit_id, isAllOrgUnitsSelected]); const { user } = useAuth(); const updateFilter = (name, value) => { restoreVisit.current = null; setSearchParams((currentParams) => { const nextParams = new URLSearchParams(currentParams); nextParams.set('page', '1'); if (value) { nextParams.set(name, value); } else { nextParams.delete(name); } return nextParams; }); }; const openModal = () => { setModalOpen(true); }; const handleSubmit = async () => { if (!formData.year || !formData.orgUnitType || formData.org_unit_id.length === 0 || !formData.formTypeCode) { alert('Пожалуйста, заполните все поля'); return; } try { setLoadingUpdate(true); await TasksApi.create({ year: Number(formData.year), form_type_code: formData.formTypeCode, org_unit_ids: formData.org_unit_id, }); setPage(1); loadTasks(1, limit); setModalOpen(false); setFormData({ year: '', orgUnitType: '', org_unit_id: [], formTypeCode: '', }); } catch (error) { toast.error(error.response?.data?.detail || error.response?.data?.message || 'Ошибка при создании задачи'); } finally { setLoadingUpdate(false); } }; const handleCancel = () => { setModalOpen(false); setFormData({ year: '', orgUnitType: '', org_unit_id: [], formTypeCode: '', }); }; const handleFormChange = (field, value) => { setFormData((prev) => ({ ...prev, [field]: value, })); }; const handleOrgUnitTypeChange = (value) => { setFormData((prev) => ({ ...prev, orgUnitType: value, org_unit_id: [], })); }; const handleSelectAllOrgUnits = (checked) => { handleFormChange('org_unit_id', checked ? filteredOrgUnits.map((unit) => unit.id) : []); }; const loadTasks = useCallback( async (currentPage, currentLimit) => { const currentRequest = ++requestId.current; setLoading(true); try { const skip = (currentPage - 1) * currentLimit; const params = { offset: skip, limit: currentLimit, ...(yearFilter && { year: Number(yearFilter) }), ...(formTypeFilter && { form_type: formTypeFilter }), ...(orgUnitIdsFilter.length > 0 && { org_units: orgUnitIdsFilter.join(',') }), ...(user?.role_id === ROLES_NAME_ID.admin && deleteStatusFilter && { delete_status: deleteStatusFilter }), }; const res = await TasksApi.list(params); const data = res.result; if (currentRequest !== requestId.current) return; const visit = restoreVisit.current; restoreVisit.current = null; if (visit && !data.some((item) => String(item.id) === visit.id)) { const foundPage = await findItemPage({ id: visit.id, currentPage, pageSize: currentLimit, count: res.count, fetchPage: async (nextPage) => (await TasksApi.list({ ...params, offset: (nextPage - 1) * currentLimit })).result, isCancelled: () => currentRequest !== requestId.current, }); if (currentRequest !== requestId.current) return; if (foundPage) { const savedUrl = new URL(visit.url, window.location.origin); savedUrl.searchParams.set('page', String(foundPage)); rememberListVisit('tasks', visit.id, `${savedUrl.pathname}${savedUrl.search}`); setSearchParams(savedUrl.searchParams, { replace: true }); return; } } setItems(data); setTotalCount(res.count); } catch (_error) { if (currentRequest === requestId.current) toast.error('Ошибка загрузки задач'); } finally { if (currentRequest === requestId.current) setLoading(false); } }, [yearFilter, formTypeFilter, orgUnitIdsFilter, deleteStatusFilter, user?.role_id, setSearchParams], ); const handlePageChange = (_event, value) => { restoreVisit.current = null; setPage(value); }; const handleLimitChange = (event) => { const newLimit = event.target.value; updateFilter('limit', newLimit); }; useEffect(() => { if (!user) return; if (user.role_id !== ROLES_NAME_ID.admin) { setSspList(user.org_units || []); return; } SspApi.getAll({ is_active: true }) .then((data) => { if (data.result) { setSspList(data.result); } }) .catch(() => toast.error('Ошибка загрузки подразделений')); }, [user]); useEffect(() => { loadTasks(page, limit); return () => { requestId.current++; }; }, [page, limit, loadTasks]); const handleFilterChange = (name, value) => { updateFilter(name, value); }; const handleClearFilters = () => { restoreVisit.current = null; setSearchParams((currentParams) => { const nextParams = new URLSearchParams(currentParams); nextParams.set('page', '1'); nextParams.delete('year'); nextParams.delete('form_type'); nextParams.delete('org_units'); nextParams.delete('delete_status'); return nextParams; }); }; if (!user) return; return ( <> ( loadTasks(page, limit)} {...exportProps} /> )} getItemId={(task) => task.id} getItemTitle={(task) => task.title || `${FORM_TYPE_TRANSLATE[task.form_type_code]}_${task.org_unit ? task.org_unit.title : task_org_unit_id}_${task.year}` || 'Задача' } extraActions={ user.role_id === ROLES_NAME_ID.admin && ( } sx={{ height: '2.5rem' }}> Создать задачу ) } filterActions={ option.title || ''} isOptionEqualToValue={(option, value) => option.id === value.id} value={selectedOrgUnits} onChange={(_event, newValue) => handleFilterChange('org_units', newValue.map((unit) => unit.id).join(','))} renderValue={renderOrgUnitValue} renderInput={(params) => } sx={{ width: 480, maxWidth: '100%' }} /> {user.role_id === ROLES_NAME_ID.admin && ( )} } /> }>
Форма Год handleFormChange('year', e.target.value)} /> Тип ССП/Региональный филиал option.title || ''} isOptionEqualToValue={(option, value) => option.id === value.id} renderValue={renderOrgUnitValue} value={filteredOrgUnits.filter((ssp) => formData.org_unit_id.includes(ssp.id))} onChange={(_event, newValue) => { handleFormChange( 'org_unit_id', newValue.map((ssp) => ssp.id), ); }} renderInput={(params) => ( )} slots={{ paper: OrgUnitsAutocompletePaper, }} slotProps={{ paper: { onSelectAll: handleSelectAllOrgUnits, isAllSelected: isAllOrgUnitsSelected, isIndeterminate: isOrgUnitsIndeterminate, showSelectAll: filteredOrgUnits.length > 0, }, popper: { style: { zIndex: 1301 }, }, }} />
); }