369 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Autocomplete, Button, FormControl, FormLabel, MenuItem, Select, TextField } from '@mui/material';
import { useEffect, useMemo, useState } from 'react';
import { useNavigate } from 'react-router';
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 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 { exportBulkForms, exportSingleForm } from '../../utils/exportFile';
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 navigate = useNavigate();
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(false);
const [loadingUpdate, setLoadingUpdate] = useState(false);
const [modalOpen, setModalOpen] = useState(false);
const [query, setQuery] = useState('');
// Pagination state
const [page, setPage] = useState(1);
const [limit, setLimit] = useState(20);
const [totalCount, setTotalCount] = useState(0);
const [isLoadAll, setisLoadAll] = useState(false);
const [sspList, setSspList] = useState([]);
const [formData, setFormData] = useState({
year: '',
orgUnitType: '',
org_unit_id: [],
formTypeCode: '',
});
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 [exportMode, setExportMode] = useState(false);
const [exportList, setExportList] = useState([]);
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 = async (currentPage = page, currentLimit = limit) => {
if (isLoadAll) return;
setLoading(true);
try {
const skip = (currentPage - 1) * currentLimit;
const params = {
offset: skip,
limit: currentLimit,
};
const res = await TasksApi.list(params);
const data = res.result;
setItems(data);
setTotalCount(res.count);
} catch (error) {
toast.error('Ошибка загрузки задач');
} finally {
setLoading(false);
}
};
useEffect(() => {
setPage(1);
}, [query]);
const handlePageChange = (event, value) => {
setPage(value);
loadTasks(value, limit);
};
const handleLimitChange = (event) => {
const newLimit = event.target.value;
setLimit(newLimit);
setPage(1);
loadTasks(1, newLimit);
};
useEffect(() => {
if (modalOpen) {
SspApi.getAll({ is_active: true }).then((data) => {
if (data.result) {
setSspList(data.result);
}
});
}
}, [modalOpen]);
useEffect(() => {
loadTasks(page, limit);
}, []);
useEffect(() => {
setExportList([]);
}, [exportMode]);
const handleChangeToExportList = (taskId, checked) => {
if (checked) {
setExportList((prev) => [...prev, taskId]);
} else {
const newExportList = exportList.filter((t) => t != taskId);
setExportList(newExportList);
}
};
const handleBulkExport = async () => {
await exportBulkForms(exportList);
setExportMode(false);
};
const handleSingleExport = (taskId, taskTitle) => {
exportSingleForm(taskId, taskTitle);
};
// Calculate total pages
const totalPages = Math.ceil(totalCount / limit);
if (!user) return;
return (
<>
<PageContainer>
<HeaderSwitch />
<PaginatedList
items={items}
loading={loading}
totalCount={totalCount}
page={page}
limit={limit}
maxHeight='none'
onPageChange={handlePageChange}
onLimitChange={handleLimitChange}
entityName='задач'
emptyMessage='Задачи не найдены'
renderItem={(task, exportProps) => <TaskCardComponent key={task.id} taskForm={task} fluidWidth {...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 && (
<PrimaryButton onClick={openModal} startIcon={<WhitePlus />} sx={{ height: '2.5rem' }}>
Создать задачу
</PrimaryButton>
)
}
/>
</PageContainer>
<Modal
open={modalOpen}
onClose={handleCancel}
title='Создать задачу'
actions={
<>
<Button variant='grey' onClick={handleCancel} style={{ height: '2.5rem' }} disabled={loadingUpdate}>
Отменить
</Button>
<Button variant='contained' onClick={handleSubmit} style={{ height: '2.5rem' }} loading={loadingUpdate}>
Создать
</Button>
</>
}>
<div className='space-y-4'>
<FormControl fullWidth size='small'>
<FormLabel required>Форма</FormLabel>
<Select
size='small'
displayEmpty
fullWidth
value={formData.formTypeCode}
onChange={(e) => handleFormChange('formTypeCode', e.target.value)}
renderValue={(selected) =>
selected ? FORM_TYPE_TRANSLATE[selected] : <span style={{ color: 'rgba(0,0,0,0.6)' }}>Не выбрана</span>
}
MenuProps={modalSelectMenuProps}
sx={modalSelectSx}>
{FORM_TYPE_OPTIONS.map((formType) => (
<MenuItem key={formType} value={formType}>
{FORM_TYPE_TRANSLATE[formType]}
</MenuItem>
))}
</Select>
</FormControl>
<FormControl fullWidth>
<FormLabel required>Год</FormLabel>
<TextField
size='small'
type='number'
placeholder='Укажите год выполнения задачи'
value={formData.year}
onChange={(e) => handleFormChange('year', e.target.value)}
/>
</FormControl>
<FormControl fullWidth size='small'>
<FormLabel required>Тип</FormLabel>
<Select
size='small'
displayEmpty
fullWidth
value={formData.orgUnitType}
onChange={(e) => handleOrgUnitTypeChange(e.target.value)}
renderValue={(selected) => {
const option = ORG_UNIT_TYPE_OPTIONS.find((item) => item.value === selected);
return option ? option.label : <span style={{ color: 'rgba(0,0,0,0.6)' }}>Не выбран</span>;
}}
MenuProps={modalSelectMenuProps}
sx={modalSelectSx}>
{ORG_UNIT_TYPE_OPTIONS.map((option) => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</Select>
</FormControl>
<FormControl fullWidth size='small'>
<FormLabel required>ССП/Региональный филиал</FormLabel>
<Autocomplete
multiple
size='small'
disabled={!formData.orgUnitType}
options={filteredOrgUnits}
disableCloseOnSelect
noOptionsText='Не найдено'
getOptionLabel={(option) => 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) => (
<TextField
{...params}
placeholder={formData.org_unit_id.length === 0 ? 'Не выбрано' : ''}
sx={{
'& .MuiOutlinedInput-root': {
'& fieldset': {
borderColor: 'rgba(0,0,0,0.1)',
},
},
}}
/>
)}
slots={{
paper: OrgUnitsAutocompletePaper,
}}
slotProps={{
paper: {
onSelectAll: handleSelectAllOrgUnits,
isAllSelected: isAllOrgUnitsSelected,
isIndeterminate: isOrgUnitsIndeterminate,
showSelectAll: filteredOrgUnits.length > 0,
},
popper: {
style: { zIndex: 1301 },
},
}}
/>
</FormControl>
</div>
</Modal>
</>
);
}