feat(vsp): справочник ВСП — статус «Действующие/Закрытые/Все», период закрытия, бейдж и выгрузка по фильтру (AURORA-1883, #330)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
a561939a66
commit
c39bbbfd06
@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { NameTask, TaskInfoContainer } from '../../components/common/SwitchFormTask/SwitchFormTask.style';
|
import { NameTask, TaskInfoContainer } from '../../components/common/SwitchFormTask/SwitchFormTask.style';
|
||||||
|
|
||||||
import { Box, CircularProgress, Stack } from '@mui/material';
|
import { Box, CircularProgress, Stack } from '@mui/material';
|
||||||
@ -13,6 +13,7 @@ import { PageContainer } from '../../components/common/PageContainer';
|
|||||||
import DictVspFilters from './components/DictVspFilters';
|
import DictVspFilters from './components/DictVspFilters';
|
||||||
import DictVspModals from './components/DictVspModals';
|
import DictVspModals from './components/DictVspModals';
|
||||||
import TableDictVsp from './components/TableDictVsp';
|
import TableDictVsp from './components/TableDictVsp';
|
||||||
|
import { buildVspListParams, vspMatchesStatus } from './utils/vspListParams';
|
||||||
|
|
||||||
const DictVspInfo = () => {
|
const DictVspInfo = () => {
|
||||||
const portalContent = (
|
const portalContent = (
|
||||||
@ -30,24 +31,46 @@ function DictVspPage() {
|
|||||||
const [dictVspData, setDictVspData] = useState([]);
|
const [dictVspData, setDictVspData] = useState([]);
|
||||||
const [ssps, setSsps] = useState([]);
|
const [ssps, setSsps] = useState([]);
|
||||||
const [selectedSsp, setSelectedSsp] = useState(null);
|
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 { user } = useAuth();
|
||||||
const modalsRef = useRef(null);
|
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 () => {
|
const loadData = useCallback(async () => {
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
const isAdmin = user.role_id === ROLES_NAME_ID.admin;
|
const isAdmin = user.role_id === ROLES_NAME_ID.admin;
|
||||||
const [vspResponse, sspResponse] = await Promise.all([
|
const sspResponse = isAdmin ? await SspApi.getAll({ is_active: true }) : null;
|
||||||
DictVspApi.get(),
|
|
||||||
isAdmin ? SspApi.getAll({ is_active: true }) : Promise.resolve(null),
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (!vspResponse.success) {
|
|
||||||
toast.error('Ошибка загрузки справочника');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setDictVspData(vspResponse.result || []);
|
|
||||||
|
|
||||||
if (isAdmin) {
|
if (isAdmin) {
|
||||||
if (!sspResponse.success) {
|
if (!sspResponse.success) {
|
||||||
@ -70,37 +93,49 @@ function DictVspPage() {
|
|||||||
loadData();
|
loadData();
|
||||||
}, [loadData]);
|
}, [loadData]);
|
||||||
|
|
||||||
const handleAdd = useCallback(async (formData) => {
|
const handleAdd = useCallback(
|
||||||
try {
|
async (formData) => {
|
||||||
const response = await DictVspApi.create(formData);
|
try {
|
||||||
if (response.success) {
|
const response = await DictVspApi.create(formData);
|
||||||
toast.success('ВСП успешно создан');
|
if (response.success) {
|
||||||
setDictVspData((prev) => [...prev, response.result]);
|
toast.success('ВСП успешно создан');
|
||||||
} else {
|
if (vspMatchesStatus(response.result, status)) {
|
||||||
toast.error(response.message || 'Ошибка создания ВСП');
|
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;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
},
|
||||||
console.error('Error adding VSP:', error);
|
[status],
|
||||||
toast.error(error.response?.data?.message || 'Ошибка создания ВСП');
|
);
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleEdit = useCallback(async (formData) => {
|
const handleEdit = useCallback(
|
||||||
try {
|
async (formData) => {
|
||||||
const response = await DictVspApi.update(formData.id, formData);
|
try {
|
||||||
if (response.success) {
|
const response = await DictVspApi.update(formData.id, formData);
|
||||||
toast.success('ВСП успешно обновлен');
|
if (response.success) {
|
||||||
setDictVspData((prev) => [...prev.filter((v) => v.id !== formData.id), response.result]);
|
toast.success('ВСП успешно обновлен');
|
||||||
} else {
|
// AURORA-1883: ВСП, переставший подходить под статус (например, закрыт в «Действующих»), уходит из списка.
|
||||||
toast.error(response.message || 'Ошибка обновления ВСП');
|
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;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
},
|
||||||
console.error('Error editing VSP:', error);
|
[status],
|
||||||
toast.error(error.response?.data?.message || 'Ошибка обновления ВСП');
|
);
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleDelete = useCallback(async (vspId) => {
|
const handleDelete = useCallback(async (vspId) => {
|
||||||
try {
|
try {
|
||||||
@ -140,16 +175,24 @@ function DictVspPage() {
|
|||||||
<PageContainer>
|
<PageContainer>
|
||||||
<Stack sx={{ alignItems: 'stretch', width: '100%' }}>
|
<Stack sx={{ alignItems: 'stretch', width: '100%' }}>
|
||||||
<Box sx={{ width: '100%' }}>
|
<Box sx={{ width: '100%' }}>
|
||||||
{user.role_id !== ROLES_NAME_ID.executor_rf && (
|
<DictVspFilters
|
||||||
<DictVspFilters ssps={ssps} onSspChange={setSelectedSsp} selectedSsp={selectedSsp} />
|
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
|
<TableDictVsp
|
||||||
data={dictVspData}
|
data={dictVspData}
|
||||||
selectedSsp={selectedSsp}
|
selectedSsp={selectedSsp}
|
||||||
onAddClick={handleAddClick}
|
onAddClick={handleAddClick}
|
||||||
onEditClick={handleEditClick}
|
onEditClick={handleEditClick}
|
||||||
onExportClick={handleExportClick}
|
onExportClick={handleExportClick}
|
||||||
loading={isLoading}
|
loading={isLoading || isListLoading}
|
||||||
/>
|
/>
|
||||||
<DictVspModals
|
<DictVspModals
|
||||||
ref={modalsRef}
|
ref={modalsRef}
|
||||||
@ -157,6 +200,7 @@ function DictVspPage() {
|
|||||||
onAdd={handleAdd}
|
onAdd={handleAdd}
|
||||||
onEdit={handleEdit}
|
onEdit={handleEdit}
|
||||||
onDelete={handleDelete}
|
onDelete={handleDelete}
|
||||||
|
listFilter={listFilter}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { Autocomplete, Box, Paper, Stack, TextField } from '@mui/material';
|
import { Autocomplete, Box, Paper, Stack, TextField, ToggleButton, ToggleButtonGroup } from '@mui/material';
|
||||||
import { styled } from '@mui/material';
|
import { styled } from '@mui/material';
|
||||||
|
import { VSP_STATUS_OPTIONS } from '../utils/vspListParams';
|
||||||
|
|
||||||
const AutocompleteStyled = styled(Autocomplete)`
|
const AutocompleteStyled = styled(Autocomplete)`
|
||||||
& .MuiOutlinedInput-root {
|
& .MuiOutlinedInput-root {
|
||||||
@ -23,7 +24,22 @@ const AutocompleteStyled = styled(Autocomplete)`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const DictVspFilters = ({ ssps, selectedSsp = null, onSspChange }) => {
|
const DATE_INPUT_SX = {
|
||||||
|
width: '11rem',
|
||||||
|
'& .MuiOutlinedInput-root': { backgroundColor: 'white', height: '2.56rem', borderRadius: '0.63rem' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const DictVspFilters = ({
|
||||||
|
ssps,
|
||||||
|
selectedSsp = null,
|
||||||
|
onSspChange,
|
||||||
|
showSsp = true,
|
||||||
|
status = 'active',
|
||||||
|
onStatusChange,
|
||||||
|
closeDateStart = '',
|
||||||
|
closeDateEnd = '',
|
||||||
|
onCloseDateChange,
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Paper
|
<Paper
|
||||||
sx={{
|
sx={{
|
||||||
@ -37,19 +53,56 @@ const DictVspFilters = ({ ssps, selectedSsp = null, onSspChange }) => {
|
|||||||
gap: '1rem',
|
gap: '1rem',
|
||||||
mb: '1.5rem',
|
mb: '1.5rem',
|
||||||
}}>
|
}}>
|
||||||
<Stack direction={'row'} spacing={'1rem'}>
|
<Stack direction={'row'} spacing={'1rem'} sx={{ alignItems: 'center', flexWrap: 'wrap', rowGap: '1rem' }}>
|
||||||
<Box sx={{ width: '30rem' }}>
|
{showSsp && (
|
||||||
<AutocompleteStyled
|
<Box sx={{ width: '30rem' }}>
|
||||||
size='small'
|
<AutocompleteStyled
|
||||||
options={ssps || []}
|
size='small'
|
||||||
noOptionsText='Не найдено'
|
options={ssps || []}
|
||||||
getOptionLabel={(option) => option.title || option.name || ''}
|
noOptionsText='Не найдено'
|
||||||
value={selectedSsp}
|
getOptionLabel={(option) => option.title || option.name || ''}
|
||||||
getOptionKey={(option) => option.id || option.name || option.title}
|
value={selectedSsp}
|
||||||
onChange={(event, newValue) => onSspChange(newValue)}
|
getOptionKey={(option) => option.id || option.name || option.title}
|
||||||
renderInput={(params) => <TextField {...params} placeholder='Все ССП/РФ' />}
|
onChange={(event, newValue) => onSspChange(newValue)}
|
||||||
/>
|
renderInput={(params) => <TextField {...params} placeholder='Все ССП/РФ' />}
|
||||||
</Box>
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{/* AURORA-1883: статус ВСП и период закрытия */}
|
||||||
|
<ToggleButtonGroup
|
||||||
|
exclusive
|
||||||
|
size='small'
|
||||||
|
value={status}
|
||||||
|
onChange={(_event, value) => value && onStatusChange?.(value)}
|
||||||
|
sx={{ backgroundColor: 'white', height: '2.56rem' }}>
|
||||||
|
{VSP_STATUS_OPTIONS.map((option) => (
|
||||||
|
<ToggleButton key={option.value} value={option.value} sx={{ textTransform: 'none', px: '1rem' }}>
|
||||||
|
{option.label}
|
||||||
|
</ToggleButton>
|
||||||
|
))}
|
||||||
|
</ToggleButtonGroup>
|
||||||
|
{status !== 'active' && (
|
||||||
|
<>
|
||||||
|
<TextField
|
||||||
|
type='date'
|
||||||
|
size='small'
|
||||||
|
label='Закрыт с'
|
||||||
|
value={closeDateStart}
|
||||||
|
onChange={(event) => onCloseDateChange?.({ closeDateStart: event.target.value, closeDateEnd })}
|
||||||
|
InputLabelProps={{ shrink: true }}
|
||||||
|
sx={DATE_INPUT_SX}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
type='date'
|
||||||
|
size='small'
|
||||||
|
label='по'
|
||||||
|
value={closeDateEnd}
|
||||||
|
onChange={(event) => onCloseDateChange?.({ closeDateStart, closeDateEnd: event.target.value })}
|
||||||
|
InputLabelProps={{ shrink: true }}
|
||||||
|
sx={DATE_INPUT_SX}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import ModalEditAddVsp from './Modals/ModalEditAddVsp';
|
|||||||
import ModalExport from './Modals/ModalExport';
|
import ModalExport from './Modals/ModalExport';
|
||||||
|
|
||||||
// Состояние модалок живёт здесь, а не в таблице: открытие/закрытие не перерисовывает MRT.
|
// Состояние модалок живёт здесь, а не в таблице: открытие/закрытие не перерисовывает MRT.
|
||||||
const DictVspModals = forwardRef(({ onAdd, onEdit, onDelete, ssps }, ref) => {
|
const DictVspModals = forwardRef(({ onAdd, onEdit, onDelete, ssps, listFilter }, ref) => {
|
||||||
const [modalOpen, setModalOpen] = useState(false);
|
const [modalOpen, setModalOpen] = useState(false);
|
||||||
const [modalExportOpen, setModalExportOpen] = useState(false);
|
const [modalExportOpen, setModalExportOpen] = useState(false);
|
||||||
const [editingItem, setEditingItem] = useState(null);
|
const [editingItem, setEditingItem] = useState(null);
|
||||||
@ -55,7 +55,7 @@ const DictVspModals = forwardRef(({ onAdd, onEdit, onDelete, ssps }, ref) => {
|
|||||||
loading={saveLoading}
|
loading={saveLoading}
|
||||||
ssps={ssps}
|
ssps={ssps}
|
||||||
/>
|
/>
|
||||||
<ModalExport open={modalExportOpen} onClose={closeExport} ssps={ssps} />
|
<ModalExport open={modalExportOpen} onClose={closeExport} ssps={ssps} listFilter={listFilter} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -7,9 +7,10 @@ import Checkbox from '../../../../components/common/Checkbox';
|
|||||||
import Modal from '../../../../components/common/Modal/Modal';
|
import Modal from '../../../../components/common/Modal/Modal';
|
||||||
import SearchComponent from '../../../../components/common/SearchComponent';
|
import SearchComponent from '../../../../components/common/SearchComponent';
|
||||||
import { downloadFile } from '../../../../utils/downloadFile';
|
import { downloadFile } from '../../../../utils/downloadFile';
|
||||||
|
import { buildVspListParams } from '../../utils/vspListParams';
|
||||||
import { ModalContainer } from './Modal.style';
|
import { ModalContainer } from './Modal.style';
|
||||||
|
|
||||||
const ModalExport = ({ open, onClose, ssps = [], loading = false }) => {
|
const ModalExport = ({ open, onClose, ssps = [], loading = false, listFilter = { status: 'active' } }) => {
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [selectedSsps, setSelectedSsps] = useState([]);
|
const [selectedSsps, setSelectedSsps] = useState([]);
|
||||||
|
|
||||||
@ -45,8 +46,10 @@ const ModalExport = ({ open, onClose, ssps = [], loading = false }) => {
|
|||||||
const handleExport = async () => {
|
const handleExport = async () => {
|
||||||
if (selectedSsps.length > 0) {
|
if (selectedSsps.length > 0) {
|
||||||
try {
|
try {
|
||||||
|
// AURORA-1883: выгрузка по текущему статусу и периоду закрытия справочника.
|
||||||
const response = await DictVspApi.export({
|
const response = await DictVspApi.export({
|
||||||
ssp_ids: selectedSsps,
|
ssp_ids: selectedSsps,
|
||||||
|
...buildVspListParams(listFilter),
|
||||||
});
|
});
|
||||||
await downloadFile(response, 'Справочник ВСП', 'csv');
|
await downloadFile(response, 'Справочник ВСП', 'csv');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -125,6 +125,36 @@ const TableDictVsp = ({ data, onEditClick, onAddClick, onExportClick, loading =
|
|||||||
enableColumnActions: false,
|
enableColumnActions: false,
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// AURORA-1883: статус ВСП — как бейдж «Активен»/«Архив» в SspTable.
|
||||||
|
accessorKey: 'is_active',
|
||||||
|
header: 'Статус',
|
||||||
|
size: 110,
|
||||||
|
minSize: 100,
|
||||||
|
maxSize: 130,
|
||||||
|
Cell: ({ row }) => {
|
||||||
|
const isActive = row.original.is_active !== false;
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
component='span'
|
||||||
|
sx={{
|
||||||
|
border: isActive ? '.0625rem solid #bedbff' : '.0625rem solid rgba(0, 0, 0, 0.1)',
|
||||||
|
borderRadius: '0.5rem',
|
||||||
|
padding: '0.12rem 0.5rem',
|
||||||
|
background: isActive ? '#e8f2ff' : '#f3f4f6',
|
||||||
|
color: isActive ? '#155dfc' : '#99a1af',
|
||||||
|
fontSize: '0.75rem',
|
||||||
|
display: 'inline-flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
width: 'max-content',
|
||||||
|
}}>
|
||||||
|
{isActive ? 'Действует' : 'Закрыт'}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
enableColumnActions: false,
|
||||||
|
enableSorting: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'placement_type',
|
accessorKey: 'placement_type',
|
||||||
header: 'Форма расположения ВСП',
|
header: 'Форма расположения ВСП',
|
||||||
@ -412,15 +442,16 @@ const TableDictVsp = ({ data, onEditClick, onAddClick, onExportClick, loading =
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
muiTableBodyCellProps: {
|
muiTableBodyCellProps: ({ row }) => ({
|
||||||
sx: {
|
sx: {
|
||||||
padding: '0.75rem',
|
padding: '0.75rem',
|
||||||
fontSize: '0.75rem',
|
fontSize: '0.75rem',
|
||||||
lineHeight: '150%',
|
lineHeight: '150%',
|
||||||
color: '#364153',
|
// AURORA-1883: текст строк закрытых ВСП приглушён.
|
||||||
|
color: row.original.is_active === false ? '#99a1af' : '#364153',
|
||||||
whiteSpace: 'nowrap', // Предотвращаем перенос текста
|
whiteSpace: 'nowrap', // Предотвращаем перенос текста
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
|
|
||||||
muiTableHeadCellProps: {
|
muiTableHeadCellProps: {
|
||||||
sx: {
|
sx: {
|
||||||
|
|||||||
23
web/src/pages/DictVspPage/utils/vspListParams.js
Normal file
23
web/src/pages/DictVspPage/utils/vspListParams.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// AURORA-1883: статус справочника ВСП — 'active' (по умолчанию) | 'closed' | 'all'.
|
||||||
|
|
||||||
|
export const VSP_STATUS_OPTIONS = [
|
||||||
|
{ value: 'active', label: 'Действующие' },
|
||||||
|
{ value: 'closed', label: 'Закрытые' },
|
||||||
|
{ value: 'all', label: 'Все' },
|
||||||
|
];
|
||||||
|
|
||||||
|
/** Параметры GET /dict/info и тела выгрузки: период закрытия — только для «Закрытые»/«Все». */
|
||||||
|
export function buildVspListParams({ status, closeDateStart, closeDateEnd }) {
|
||||||
|
const params = { status };
|
||||||
|
if (status === 'active') return params;
|
||||||
|
if (closeDateStart) params.close_date_start = closeDateStart;
|
||||||
|
if (closeDateEnd) params.close_date_end = closeDateEnd;
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Подходит ли ВСП под выбранный статус (после правки строка может выпасть из списка). */
|
||||||
|
export function vspMatchesStatus(vsp, status) {
|
||||||
|
if (status === 'active') return vsp.is_active !== false;
|
||||||
|
if (status === 'closed') return vsp.is_active === false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
33
web/src/pages/DictVspPage/utils/vspListParams.test.js
Normal file
33
web/src/pages/DictVspPage/utils/vspListParams.test.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import test from 'node:test';
|
||||||
|
import { buildVspListParams, vspMatchesStatus } from './vspListParams.js';
|
||||||
|
|
||||||
|
// AURORA-1883: параметры запроса справочника ВСП по статусу и периоду закрытия.
|
||||||
|
test('active status sends no close period', () => {
|
||||||
|
assert.deepEqual(buildVspListParams({ status: 'active', closeDateStart: '2021-01-01', closeDateEnd: '' }), {
|
||||||
|
status: 'active',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('closed status sends both close dates', () => {
|
||||||
|
assert.deepEqual(buildVspListParams({ status: 'closed', closeDateStart: '2021-01-01', closeDateEnd: '2021-12-31' }), {
|
||||||
|
status: 'closed',
|
||||||
|
close_date_start: '2021-01-01',
|
||||||
|
close_date_end: '2021-12-31',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('all status sends only the filled close date', () => {
|
||||||
|
assert.deepEqual(buildVspListParams({ status: 'all', closeDateStart: '', closeDateEnd: '2021-12-31' }), {
|
||||||
|
status: 'all',
|
||||||
|
close_date_end: '2021-12-31',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('vspMatchesStatus follows is_active', () => {
|
||||||
|
assert.equal(vspMatchesStatus({ is_active: true }, 'active'), true);
|
||||||
|
assert.equal(vspMatchesStatus({ is_active: false }, 'active'), false);
|
||||||
|
assert.equal(vspMatchesStatus({ is_active: false }, 'closed'), true);
|
||||||
|
assert.equal(vspMatchesStatus({ is_active: true }, 'closed'), false);
|
||||||
|
assert.equal(vspMatchesStatus({ is_active: false }, 'all'), true);
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user