213 lines
6.9 KiB
JavaScript
213 lines
6.9 KiB
JavaScript
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||
import { NameTask, TaskInfoContainer } from '../../components/common/SwitchFormTask/SwitchFormTask.style';
|
||
|
||
import { Box, CircularProgress, Stack } from '@mui/material';
|
||
import { toast } from 'react-toastify';
|
||
import { DictVspApi } from '../../api/dict-vsp';
|
||
import { SspApi } from '../../api/ssp';
|
||
import { useAuth } from '../../app/context/AuthProvider';
|
||
import { BackButton } from '../../components/common/Buttons/BackButton';
|
||
import { HeaderPortal } from '../../components/common/HeaderPortal';
|
||
import { ROLES_NAME_ID } from '../../constants/constants';
|
||
import { PageContainer } from '../../components/common/PageContainer';
|
||
import DictVspFilters from './components/DictVspFilters';
|
||
import DictVspModals from './components/DictVspModals';
|
||
import TableDictVsp from './components/TableDictVsp';
|
||
import { buildVspListParams, vspMatchesStatus } from './utils/vspListParams';
|
||
|
||
const DictVspInfo = () => {
|
||
const portalContent = (
|
||
<TaskInfoContainer>
|
||
<BackButton to='/dicts-navigator' />
|
||
<NameTask>Справочник</NameTask>
|
||
</TaskInfoContainer>
|
||
);
|
||
|
||
return <HeaderPortal>{portalContent}</HeaderPortal>;
|
||
};
|
||
|
||
function DictVspPage() {
|
||
const [isLoading, setIsLoading] = useState(true);
|
||
const [dictVspData, setDictVspData] = useState([]);
|
||
const [ssps, setSsps] = useState([]);
|
||
const [selectedSsp, setSelectedSsp] = useState(null);
|
||
// AURORA-1883: статус ВСП и период закрытия; список ВСП перезагружается с сервера при их смене.
|
||
const [status, setStatus] = useState('active');
|
||
const [closeDates, setCloseDates] = useState({ closeDateStart: '', closeDateEnd: '' });
|
||
const [isListLoading, setIsListLoading] = useState(true);
|
||
const listRequestRef = useRef(0);
|
||
const { user } = useAuth();
|
||
const modalsRef = useRef(null);
|
||
|
||
const listFilter = useMemo(() => ({ status, ...closeDates }), [status, closeDates]);
|
||
|
||
const loadVspList = useCallback(async () => {
|
||
if (!user) return;
|
||
const requestId = ++listRequestRef.current;
|
||
setIsListLoading(true);
|
||
try {
|
||
const vspResponse = await DictVspApi.get(buildVspListParams(listFilter));
|
||
if (requestId !== listRequestRef.current) return;
|
||
if (!vspResponse.success) {
|
||
toast.error('Ошибка загрузки справочника');
|
||
return;
|
||
}
|
||
setDictVspData(vspResponse.result || []);
|
||
} catch (error) {
|
||
if (requestId !== listRequestRef.current) return;
|
||
toast.error(error.response?.data?.message || 'Ошибка загрузки справочника');
|
||
} finally {
|
||
if (requestId === listRequestRef.current) setIsListLoading(false);
|
||
}
|
||
}, [user, listFilter]);
|
||
|
||
useEffect(() => {
|
||
loadVspList();
|
||
}, [loadVspList]);
|
||
|
||
const loadData = useCallback(async () => {
|
||
if (!user) return;
|
||
setIsLoading(true);
|
||
try {
|
||
const isAdmin = user.role_id === ROLES_NAME_ID.admin;
|
||
const sspResponse = isAdmin ? await SspApi.getAll({ is_active: true }) : null;
|
||
|
||
if (isAdmin) {
|
||
if (!sspResponse.success) {
|
||
toast.error('Ошибка загрузки списка ССП');
|
||
return;
|
||
}
|
||
setSsps(sspResponse.result);
|
||
} else {
|
||
setSsps(user.org_units ?? []);
|
||
}
|
||
} catch (error) {
|
||
console.error('Error loading data:', error);
|
||
toast.error(error.response?.data?.message || 'Ошибка загрузки данных');
|
||
} finally {
|
||
setIsLoading(false);
|
||
}
|
||
}, [user]);
|
||
|
||
useEffect(() => {
|
||
loadData();
|
||
}, [loadData]);
|
||
|
||
const handleAdd = useCallback(
|
||
async (formData) => {
|
||
try {
|
||
const response = await DictVspApi.create(formData);
|
||
if (response.success) {
|
||
toast.success('ВСП успешно создан');
|
||
if (vspMatchesStatus(response.result, status)) {
|
||
setDictVspData((prev) => [...prev, response.result]);
|
||
}
|
||
} else {
|
||
toast.error(response.message || 'Ошибка создания ВСП');
|
||
}
|
||
} catch (error) {
|
||
console.error('Error adding VSP:', error);
|
||
toast.error(error.response?.data?.message || 'Ошибка создания ВСП');
|
||
throw error;
|
||
}
|
||
},
|
||
[status],
|
||
);
|
||
|
||
const handleEdit = useCallback(
|
||
async (formData) => {
|
||
try {
|
||
const response = await DictVspApi.update(formData.id, formData);
|
||
if (response.success) {
|
||
toast.success('ВСП успешно обновлен');
|
||
// AURORA-1883: ВСП, переставший подходить под статус (например, закрыт в «Действующих»), уходит из списка.
|
||
setDictVspData((prev) => {
|
||
const rest = prev.filter((v) => v.id !== formData.id);
|
||
return vspMatchesStatus(response.result, status) ? [...rest, response.result] : rest;
|
||
});
|
||
} else {
|
||
toast.error(response.message || 'Ошибка обновления ВСП');
|
||
}
|
||
} catch (error) {
|
||
console.error('Error editing VSP:', error);
|
||
toast.error(error.response?.data?.message || 'Ошибка обновления ВСП');
|
||
throw error;
|
||
}
|
||
},
|
||
[status],
|
||
);
|
||
|
||
const handleDelete = useCallback(async (vspId) => {
|
||
try {
|
||
const response = await DictVspApi.delete(vspId);
|
||
if (response.success) {
|
||
toast.success('ВСП успешно удален');
|
||
setDictVspData((prev) => [...prev.filter((v) => v.id !== vspId)]);
|
||
} else {
|
||
toast.error(response.message || 'Ошибка удаления ВСП');
|
||
}
|
||
} catch (error) {
|
||
console.error('Error deleting VSP:', error);
|
||
toast.error(error.response?.data?.message || 'Ошибка удаления ВСП');
|
||
throw error;
|
||
}
|
||
}, []);
|
||
|
||
const handleAddClick = useCallback(() => modalsRef.current?.openAdd(), []);
|
||
const handleEditClick = useCallback((item) => modalsRef.current?.openEdit(item), []);
|
||
const handleExportClick = useCallback(() => modalsRef.current?.openExport(), []);
|
||
|
||
if (isLoading) {
|
||
return (
|
||
<>
|
||
<DictVspInfo />
|
||
<div className='flex justify-center pt-12'>
|
||
<CircularProgress />
|
||
</div>
|
||
</>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<>
|
||
<DictVspInfo />
|
||
{/* <HeaderSwitch /> */}
|
||
<PageContainer>
|
||
<Stack sx={{ alignItems: 'stretch', width: '100%' }}>
|
||
<Box sx={{ width: '100%' }}>
|
||
<DictVspFilters
|
||
ssps={ssps}
|
||
onSspChange={setSelectedSsp}
|
||
selectedSsp={selectedSsp}
|
||
showSsp={user.role_id !== ROLES_NAME_ID.executor_rf}
|
||
status={status}
|
||
onStatusChange={setStatus}
|
||
closeDateStart={closeDates.closeDateStart}
|
||
closeDateEnd={closeDates.closeDateEnd}
|
||
onCloseDateChange={setCloseDates}
|
||
/>
|
||
<TableDictVsp
|
||
data={dictVspData}
|
||
selectedSsp={selectedSsp}
|
||
onAddClick={handleAddClick}
|
||
onEditClick={handleEditClick}
|
||
onExportClick={handleExportClick}
|
||
loading={isLoading || isListLoading}
|
||
/>
|
||
<DictVspModals
|
||
ref={modalsRef}
|
||
ssps={ssps}
|
||
onAdd={handleAdd}
|
||
onEdit={handleEdit}
|
||
onDelete={handleDelete}
|
||
listFilter={listFilter}
|
||
/>
|
||
</Box>
|
||
</Stack>
|
||
</PageContainer>
|
||
</>
|
||
);
|
||
}
|
||
|
||
export default DictVspPage;
|