AURORA-1883: справочник ВСП — просмотр закрытых ВСП (статус, период закрытия), подпись закрытых ВСП в формах (поверх #328) #332
@ -13,7 +13,7 @@ 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';
|
||||
import { buildVspListParams, sspsForStatus, vspMatchesStatus } from './utils/vspListParams';
|
||||
|
||||
const DictVspInfo = () => {
|
||||
const portalContent = (
|
||||
@ -40,6 +40,16 @@ function DictVspPage() {
|
||||
const modalsRef = useRef(null);
|
||||
|
||||
const listFilter = useMemo(() => ({ status, ...closeDates }), [status, closeDates]);
|
||||
const visibleSsps = useMemo(() => sspsForStatus(ssps, status), [ssps, status]);
|
||||
// Создание и правка ВСП — только на активном ССП (бэк отклоняет архивный).
|
||||
const activeSsps = useMemo(() => sspsForStatus(ssps, 'active'), [ssps]);
|
||||
|
||||
// Выбранный архивный ССП исчезает из фильтра при возврате в «Действующие» — сбрасываем выбор.
|
||||
useEffect(() => {
|
||||
if (selectedSsp && !visibleSsps.some((ssp) => ssp.id === selectedSsp.id)) {
|
||||
setSelectedSsp(null);
|
||||
}
|
||||
}, [selectedSsp, visibleSsps]);
|
||||
|
||||
const loadVspList = useCallback(async () => {
|
||||
if (!user) return;
|
||||
@ -70,7 +80,8 @@ function DictVspPage() {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const isAdmin = user.role_id === ROLES_NAME_ID.admin;
|
||||
const sspResponse = isAdmin ? await SspApi.getAll({ is_active: true }) : null;
|
||||
// AURORA-1883: все ССП, включая архивные, — для закрытых ВСП; в «Действующих» архивные скрываются (sspsForStatus).
|
||||
const sspResponse = isAdmin ? await SspApi.getAll() : null;
|
||||
|
||||
if (isAdmin) {
|
||||
if (!sspResponse.success) {
|
||||
@ -176,7 +187,7 @@ function DictVspPage() {
|
||||
<Stack sx={{ alignItems: 'stretch', width: '100%' }}>
|
||||
<Box sx={{ width: '100%' }}>
|
||||
<DictVspFilters
|
||||
ssps={ssps}
|
||||
ssps={visibleSsps}
|
||||
onSspChange={setSelectedSsp}
|
||||
selectedSsp={selectedSsp}
|
||||
showSsp={user.role_id !== ROLES_NAME_ID.executor_rf}
|
||||
@ -196,7 +207,8 @@ function DictVspPage() {
|
||||
/>
|
||||
<DictVspModals
|
||||
ref={modalsRef}
|
||||
ssps={ssps}
|
||||
ssps={activeSsps}
|
||||
exportSsps={visibleSsps}
|
||||
onAdd={handleAdd}
|
||||
onEdit={handleEdit}
|
||||
onDelete={handleDelete}
|
||||
|
||||
@ -3,7 +3,7 @@ import ModalEditAddVsp from './Modals/ModalEditAddVsp';
|
||||
import ModalExport from './Modals/ModalExport';
|
||||
|
||||
// Состояние модалок живёт здесь, а не в таблице: открытие/закрытие не перерисовывает MRT.
|
||||
const DictVspModals = forwardRef(({ onAdd, onEdit, onDelete, ssps, listFilter }, ref) => {
|
||||
const DictVspModals = forwardRef(({ onAdd, onEdit, onDelete, ssps, exportSsps, listFilter }, ref) => {
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [modalExportOpen, setModalExportOpen] = useState(false);
|
||||
const [editingItem, setEditingItem] = useState(null);
|
||||
@ -55,7 +55,7 @@ const DictVspModals = forwardRef(({ onAdd, onEdit, onDelete, ssps, listFilter },
|
||||
loading={saveLoading}
|
||||
ssps={ssps}
|
||||
/>
|
||||
<ModalExport open={modalExportOpen} onClose={closeExport} ssps={ssps} listFilter={listFilter} />
|
||||
<ModalExport open={modalExportOpen} onClose={closeExport} ssps={exportSsps ?? ssps} listFilter={listFilter} />
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
@ -21,3 +21,9 @@ export function vspMatchesStatus(vsp, status) {
|
||||
if (status === 'closed') return vsp.is_active === false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** ССП для фильтра и выгрузки: архивные — только при «Закрытые»/«Все» (закрытые ВСП бывают на архивных ССП). */
|
||||
export function sspsForStatus(ssps, status) {
|
||||
const list = ssps || [];
|
||||
return status === 'active' ? list.filter((ssp) => ssp.is_active !== false) : list;
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { buildVspListParams, vspMatchesStatus } from './vspListParams.js';
|
||||
import { buildVspListParams, sspsForStatus, vspMatchesStatus } from './vspListParams.js';
|
||||
|
||||
// AURORA-1883: параметры запроса справочника ВСП по статусу и периоду закрытия.
|
||||
test('active status sends no close period', () => {
|
||||
@ -31,3 +31,24 @@ test('vspMatchesStatus follows is_active', () => {
|
||||
assert.equal(vspMatchesStatus({ is_active: true }, 'closed'), false);
|
||||
assert.equal(vspMatchesStatus({ is_active: false }, 'all'), true);
|
||||
});
|
||||
|
||||
// AURORA-1883, финальное ревью I-1: закрытые ВСП архивного ССП должны фильтроваться и выгружаться.
|
||||
test('sspsForStatus hides archived SSPs only for active VSPs', () => {
|
||||
const ssps = [
|
||||
{ id: 1, is_active: true },
|
||||
{ id: 2, is_active: false },
|
||||
];
|
||||
assert.deepEqual(
|
||||
sspsForStatus(ssps, 'active').map((s) => s.id),
|
||||
[1],
|
||||
);
|
||||
assert.deepEqual(
|
||||
sspsForStatus(ssps, 'closed').map((s) => s.id),
|
||||
[1, 2],
|
||||
);
|
||||
assert.deepEqual(
|
||||
sspsForStatus(ssps, 'all').map((s) => s.id),
|
||||
[1, 2],
|
||||
);
|
||||
assert.deepEqual(sspsForStatus(undefined, 'active'), []);
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user