fix(web): список консолидаций и «Взять в работу» по спеке (AURORA-1589, PR #166)

«Только в работе» выключен по умолчанию, параметр — boolean; ошибка загрузки списка
показывается; пункты «⋯» disabled с подсказкой «Будет позже», клик не выбирает строку;
«Взять в работу» неактивна по коду CANCELLED. Модалка [35]: опечатки, плейсхолдер,
≤ 1000 символов, «К3», ошибки (403 и прочие) — текст сервера, ФИО без второй точки;
после взятия в работу перечитываются список и панель «Организации» («Ответственный»).
Боковое меню: thunk списка с объектом, отступ секций. Порядок импортов (biome).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andrey Epifancev 2026-10-23 01:15:55 +04:00
parent ccca6dfa13
commit d71c46e846
7 changed files with 104 additions and 127 deletions

View File

@ -1,6 +1,6 @@
import { Settings2 } from 'lucide-react';
import { Button, Checkbox, Dropdown } from 'antd'; import { Button, Checkbox, Dropdown } from 'antd';
import type { MenuProps } from 'antd'; import type { MenuProps } from 'antd';
import { Settings2 } from 'lucide-react';
import { type ColumnDef, isGroup } from './tableTypes'; import { type ColumnDef, isGroup } from './tableTypes';
interface Props<T> { interface Props<T> {

View File

@ -1,7 +1,7 @@
import { Filter } from 'lucide-react';
import { Table } from 'antd'; import { Table } from 'antd';
import type { TableProps } from 'antd'; import type { TableProps } from 'antd';
import type { ColumnGroupType, ColumnType, ColumnsType } from 'antd/es/table'; import type { ColumnGroupType, ColumnType, ColumnsType } from 'antd/es/table';
import { Filter } from 'lucide-react';
import { useLayoutEffect, useMemo, useRef, useState } from 'react'; import { useLayoutEffect, useMemo, useRef, useState } from 'react';
import { CustomFilterDropdown } from './CustomFilterDropdown'; import { CustomFilterDropdown } from './CustomFilterDropdown';
import { Highlight } from './Highlight'; import { Highlight } from './Highlight';

View File

@ -5,16 +5,14 @@ import { ApiError } from '../api/client';
import PageTitle from '../components/PageTitle'; import PageTitle from '../components/PageTitle';
import { fetchOneCompanyGroup } from '../services/api/directories/company_groups'; import { fetchOneCompanyGroup } from '../services/api/directories/company_groups';
import { setSelectedVersion } from '../store/slices/consolidations/consolidationsSlice'; import { setSelectedVersion } from '../store/slices/consolidations/consolidationsSlice';
import { getConsolidationsListThunk } from '../store/slices/consolidations/thunks';
import { useAppDispatch } from '../store/store'; import { useAppDispatch } from '../store/store';
import { ConsoleLegalEntitiesTable } from '../widgets/ConsoleLegalEntities/ConsoleLegalEntities'; import { ConsoleLegalEntitiesTable } from '../widgets/ConsoleLegalEntities/ConsoleLegalEntities';
import { ConsoleTableWidget } from '../widgets/ConsoleTable/ConsoleTable'; import { ConsoleTableWidget } from '../widgets/ConsoleTable/ConsoleTable';
import { DocumentsDrawer } from '../widgets/FilesDrawer/FilesDrawer'; import { DocumentsDrawer } from '../widgets/FilesDrawer/FilesDrawer';
// unwrap() бросает rejectValue (строку), fetch группы — ApiError // список консолидаций грузит ConsoleTableWidget (с фильтром «Только в работе»), здесь — только группа
function errorText(e: unknown): string { function errorText(e: unknown): string {
if (e instanceof ApiError) return e.message; if (e instanceof ApiError) return e.message;
if (typeof e === 'string') return e;
return 'Не удалось загрузить группу компаний'; return 'Не удалось загрузить группу компаний';
} }
@ -43,7 +41,7 @@ export function ConsolidationPage() {
} }
} }
getAllInfo(group_id); getAllInfo(group_id);
}, [dispatch, group_id]); }, [group_id]);
// выбор версии следует за ?version= и не перезагружает список // выбор версии следует за ?version= и не перезагружает список
useEffect(() => { useEffect(() => {
@ -85,7 +83,8 @@ export function ConsolidationPage() {
flexDirection: 'column', flexDirection: 'column',
minHeight: 0, minHeight: 0,
}}> }}>
<ConsoleTableWidget /> {/* после «Взять в работу» перечитываем и панель «Организации»: в ней «Ответственный» */}
<ConsoleTableWidget onVersionLocked={() => setReloadKey((k) => k + 1)} />
</div> </div>
<div <div
style={{ style={{

View File

@ -1,85 +1,63 @@
import { DownOutlined, RightOutlined } from '@ant-design/icons'; import { DownOutlined, RightOutlined } from '@ant-design/icons';
import { Button, Checkbox, Dropdown, Space, Tooltip, message } from 'antd'; import { Button, Checkbox, Dropdown, type MenuProps, Space, Tooltip, message } from 'antd';
import type { ColumnsType, TableProps } from 'antd/es/table'; import type { ColumnsType, TableProps } from 'antd/es/table';
import { memo, useCallback, useEffect, useMemo, useState } from 'react'; import { ArrowRight, Briefcase, GitBranchPlus, History, MoreHorizontal, ToggleLeft } from 'lucide-react';
import { type ReactNode, memo, useCallback, useEffect, useMemo, useState } from 'react';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
import { useCurrentUser } from '../../auth/MeContext';
import { ColumnSettings, DataTable, type TableConfig, isGroup, useFilteredRows, useVisibleColumns } from '../../components/GenericTable'; import { ColumnSettings, DataTable, type TableConfig, isGroup, useFilteredRows, useVisibleColumns } from '../../components/GenericTable';
import { useTableFilters } from '../../components/GenericTable/useTableFilters'; import { useTableFilters } from '../../components/GenericTable/useTableFilters';
import { getConsolidationsListThunk } from '../../store/slices/consolidations/thunks';
import { useAppDispatch, useAppSelector } from '../../store/store'; import { useAppDispatch, useAppSelector } from '../../store/store';
import type { ConsRow } from '../../types/consolidations/consolidations'; import type { ConsRow } from '../../types/consolidations/consolidations';
import { canTakeInWork, isLockedBy, parseInWorkOnly } from '../../utils/consolidations';
import LockModal from './LockModal';
import { consConfigBase } from './config'; import { consConfigBase } from './config';
import { flattenCons } from './flatten'; import { flattenCons } from './flatten';
import { groupAdjacent } from './groupAdjacent'; import { groupAdjacent } from './groupAdjacent';
import { useGrouping } from './useGrouping'; import { useGrouping } from './useGrouping';
import { ArrowRight, Briefcase, GitBranchPlus, History, MoreHorizontal, ToggleLeft } from 'lucide-react';
import { useCurrentUser } from '../../auth/MeContext';
import LockModal from './LockModal';
import { LockOut } from '../../types/consolidations/common_types';
import { Me } from '../../api/types';
import { getConsolidationsListThunk } from '../../store/slices/consolidations/thunks';
type ActionKey = 'copy' | 'disable_confirm' | 'history'; const SOON = 'Будет позже';
const ITEMS = [
{
key: 'copy',
label: (
<Tooltip title='Будет позже'>
<span style={{ display: 'block', width: '100%', cursor: 'not-allowed' }}>Добавить версию</span>
</Tooltip>
),
className: 'ant-dropdown-menu-item-disabled',
icon: <GitBranchPlus size={14} />,
},
{
key: 'disable_confirm',
label: (
<Tooltip title='Будет позже'>
<span style={{ display: 'block', width: '100%', cursor: 'not-allowed' }}>Отключить подтверждение КФО</span>
</Tooltip>
),
className: 'ant-dropdown-menu-item-disabled',
icon: <ToggleLeft size={14} />,
},
{
key: 'history',
label: (
<Tooltip title='Будет позже'>
<span style={{ display: 'block', width: '100%', cursor: 'not-allowed' }}>История изменений</span>
</Tooltip>
),
className: 'ant-dropdown-menu-item-disabled',
icon: <History size={14} />,
},
{},
] as const;
const RowActions = memo(function RowActions({ row, onAction }: { row: ConsRow; onAction: (key: ActionKey, _row: ConsRow) => void }) { // пункт неактивен (disabled — клик не обрабатывается), подсказка — на подписи
const soonItem = (key: string, label: string, icon: ReactNode) => ({
key,
disabled: true,
icon,
label: (
<Tooltip title={SOON}>
<span style={{ display: 'block', width: '100%' }}>{label}</span>
</Tooltip>
),
});
// функций ещё нет: «История изменений» — после журнала с фильтрами [43-02], остальное — зонтичная задача экранов консолидаций
const ITEMS: MenuProps['items'] = [
soonItem('copy', 'Добавить версию', <GitBranchPlus size={14} />),
soonItem('disable_confirm', 'Отключить подтверждение КФО', <ToggleLeft size={14} />),
soonItem('history', 'История изменений', <History size={14} />),
];
const RowActions = memo(function RowActions() {
return ( return (
<Dropdown // меню рендерится в портал, но React-событие всплывает по дереву до onRow строки — гасим здесь
trigger={['click']} <span onClick={(e) => e.stopPropagation()} onKeyDown={(e) => e.stopPropagation()}>
menu={{ <Dropdown trigger={['click']} menu={{ items: ITEMS }}>
items: ITEMS as any, <Button icon={<MoreHorizontal size={14} />} aria-label='Действия' />
onClick: ({ key, domEvent }) => { </Dropdown>
domEvent.stopPropagation(); </span>
onAction(key as ActionKey, row);
},
}}>
<Button icon={<MoreHorizontal size={14} />} onClick={(e) => e.stopPropagation()} />
</Dropdown>
); );
}); });
function isLockedByCurrentUser(currentUser: Me, lockData: LockOut | null) {
if (lockData === null) return false; /** `onVersionLocked` — версию взяли в работу: страница перечитывает панель «Организации» («Ответственный»). */
if (lockData.locked_by !== currentUser.id) return false; export function ConsoleTableWidget({ onVersionLocked }: { onVersionLocked?: () => void }) {
return true;
}
export function ConsoleTableWidget() {
const navigate = useNavigate(); const navigate = useNavigate();
const { group_id } = useParams(); const { group_id } = useParams();
const [searchParams, setSearchParams] = useSearchParams(); const [searchParams, setSearchParams] = useSearchParams();
const consolidations = useAppSelector((s) => s.consolidations.consolidations); const consolidations = useAppSelector((s) => s.consolidations.consolidations);
const loading = useAppSelector((s) => s.consolidations.loading); const loading = useAppSelector((s) => s.consolidations.loading);
const inWorkOnly = searchParams.get('in_work_only') || true; // «Только в работе» по умолчанию выключен (макет [34], умолчание API — false)
const inWorkOnly = parseInWorkOnly(searchParams.get('in_work_only'));
const currentUser = useCurrentUser(); const currentUser = useCurrentUser();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
@ -96,11 +74,12 @@ export function ConsoleTableWidget() {
const { visibleRows, expanded, toggle } = useGrouping(grouped); const { visibleRows, expanded, toggle } = useGrouping(grouped);
const loadConsolidations = useCallback(async () => { const loadConsolidations = useCallback(async () => {
if (!group_id) return; if (!group_id) return;
let in_work_only = true; try {
if (inWorkOnly === 'false') { await dispatch(getConsolidationsListThunk({ company_group_id: group_id, in_work_only: inWorkOnly })).unwrap();
in_work_only = false; } catch (e: unknown) {
// unwrap() бросает rejectValue — текст ошибки сервера
message.error(typeof e === 'string' ? e : 'Не удалось загрузить консолидации');
} }
await dispatch(getConsolidationsListThunk({ company_group_id: group_id, in_work_only })).unwrap();
}, [dispatch, group_id, inWorkOnly]); }, [dispatch, group_id, inWorkOnly]);
useEffect(() => { useEffect(() => {
loadConsolidations(); loadConsolidations();
@ -152,16 +131,6 @@ export function ConsoleTableWidget() {
const { visibleKeys, setVisibleKeys } = useVisibleColumns(config); const { visibleKeys, setVisibleKeys } = useVisibleColumns(config);
const handleDropdownAction = useCallback(
(key: ActionKey, _row: ConsRow) => {
if (key === 'history') {
navigate('/logging');
} else {
message.info('Будет позже');
}
},
[navigate],
);
const extraColumns: ColumnsType<ConsRow> = useMemo( const extraColumns: ColumnsType<ConsRow> = useMemo(
() => [ () => [
{ {
@ -170,7 +139,7 @@ export function ConsoleTableWidget() {
width: 120, width: 120,
render: (_: unknown, row: ConsRow) => ( render: (_: unknown, row: ConsRow) => (
<div style={{ display: 'flex', gap: 4 }}> <div style={{ display: 'flex', gap: 4 }}>
{isLockedByCurrentUser(currentUser, row.lock) ? ( {isLockedBy(row.lock, currentUser.id) ? (
<Button disabled icon={<Briefcase size={16} />} type='default'> <Button disabled icon={<Briefcase size={16} />} type='default'>
В работе В работе
</Button> </Button>
@ -179,7 +148,7 @@ export function ConsoleTableWidget() {
iconPlacement='start' iconPlacement='start'
icon={<Briefcase size={16} />} icon={<Briefcase size={16} />}
type='default' type='default'
disabled={row.status === 'Отменён'} disabled={!canTakeInWork(row.status_code)}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
setLockVersionId(row.id); setLockVersionId(row.id);
@ -198,13 +167,17 @@ export function ConsoleTableWidget() {
}}> }}>
К отчётности К отчётности
</Button> </Button>
<RowActions row={row} onAction={handleDropdownAction} /> <RowActions />
</div> </div>
), ),
}, },
], ],
[navigate, group_id, handleDropdownAction, currentUser], [navigate, group_id, currentUser],
); );
const afterLock = useCallback(() => {
loadConsolidations();
onVersionLocked?.();
}, [loadConsolidations, onVersionLocked]);
const onRow: TableProps<ConsRow>['onRow'] = (row) => ({ const onRow: TableProps<ConsRow>['onRow'] = (row) => ({
// выбранная версия живёт в URL (?version=): переживает уход на «К отчётности» и «Назад» // выбранная версия живёт в URL (?version=): переживает уход на «К отчётности» и «Назад»
@ -252,7 +225,7 @@ export function ConsoleTableWidget() {
</Button> </Button>
)} )}
<Checkbox <Checkbox
checked={searchParams.get('in_work_only') === 'false' ? false : true} checked={inWorkOnly}
onChange={(e) => { onChange={(e) => {
setSearchParams( setSearchParams(
(searchParams) => { (searchParams) => {
@ -284,7 +257,7 @@ export function ConsoleTableWidget() {
open={lockModalOpen} open={lockModalOpen}
versionId={lockVersionId} versionId={lockVersionId}
onOpenChange={setLockModalOpen} onOpenChange={setLockModalOpen}
handleReload={loadConsolidations} handleReload={afterLock}
currentUserId={currentUser.id} currentUserId={currentUser.id}
/> />
</div> </div>

View File

@ -1,8 +1,11 @@
import { Alert, Button, Input, Modal, Skeleton, message } from 'antd';
import { useCallback, useEffect, useRef, useState } from 'react'; import { useCallback, useEffect, useRef, useState } from 'react';
import { LockOut } from '../../types/consolidations/common_types';
import { Alert, Button, Input, message, Modal, Skeleton } from 'antd';
import { ApiError } from '../../api/client'; import { ApiError } from '../../api/client';
import { useBeforeUnload } from '../../hooks/useBeforeUnload';
import { getVersionCard, lockVersion } from '../../services/api/consolidations'; import { getVersionCard, lockVersion } from '../../services/api/consolidations';
import type { LockOut } from '../../types/consolidations/common_types';
import { MAX_REASON_LENGTH, endSentence, isValidReason } from '../../utils/consolidations';
import { quarterShortLabel } from '../../utils/labels';
type LockModalProps = { type LockModalProps = {
versionId: string | null; versionId: string | null;
@ -11,12 +14,8 @@ type LockModalProps = {
currentUserId: string; currentUserId: string;
handleReload: () => void; handleReload: () => void;
}; };
function isValidReason(reason: string) {
const trimmed = reason.trim();
if (trimmed.length > 0 && trimmed.length < 1000) return true;
return false;
}
/** «Взять консолидацию в работу?» [35]: свободная версия — подтверждение, чужая — перехват с причиной. */
export default function LockModal({ versionId, open, onOpenChange, currentUserId, handleReload }: LockModalProps) { export default function LockModal({ versionId, open, onOpenChange, currentUserId, handleReload }: LockModalProps) {
const [data, setData] = useState<LockOut | null>(null); const [data, setData] = useState<LockOut | null>(null);
const [consolidationName, setConsolidationName] = useState<string | null>(null); const [consolidationName, setConsolidationName] = useState<string | null>(null);
@ -27,11 +26,14 @@ export default function LockModal({ versionId, open, onOpenChange, currentUserId
const [conflict, setConflict] = useState(false); const [conflict, setConflict] = useState(false);
const [submitting, setSubmitting] = useState(false); const [submitting, setSubmitting] = useState(false);
// Stale-response guard for fast open/switch/close sequences. // Защита от устаревшего ответа при быстром открытии/смене версии/закрытии.
const reqId = useRef(0); const reqId = useRef(0);
const close = useCallback(() => onOpenChange(false), [onOpenChange]); const close = useCallback(() => onOpenChange(false), [onOpenChange]);
// введённая причина перехвата — несохранённая правка: браузер предупредит при закрытии вкладки
useBeforeUnload(open && reason.trim() !== '');
const load = useCallback(async () => { const load = useCallback(async () => {
if (!versionId) return; if (!versionId) return;
const my = ++reqId.current; const my = ++reqId.current;
@ -39,16 +41,14 @@ export default function LockModal({ versionId, open, onOpenChange, currentUserId
setLoadError(false); setLoadError(false);
try { try {
const currentCard = await getVersionCard(versionId); const currentCard = await getVersionCard(versionId);
const lockData = currentCard.lock;
const { company_group, year, quarter } = currentCard; const { company_group, year, quarter } = currentCard;
const consName = `${year} ${quarter} · ${company_group.name}`; if (my !== reqId.current) return; // успел прийти ответ на более новый запрос
if (my !== reqId.current) return; // a newer request won setData(currentCard.lock);
setData(lockData); setConsolidationName(`${year} ${quarterShortLabel(quarter)} · ${company_group.name}`);
setConsolidationName(consName);
} catch (e) { } catch (e) {
if (my !== reqId.current) return; if (my !== reqId.current) return;
if (e instanceof ApiError && e.status === 404) { if (e instanceof ApiError && e.status === 404) {
message.warning('Версия консолидации не обнаржуена'); message.warning('Версия консолидации не обнаружена');
close(); close();
return; return;
} }
@ -58,46 +58,47 @@ export default function LockModal({ versionId, open, onOpenChange, currentUserId
} }
}, [versionId, close]); }, [versionId, close]);
// Reset + fetch on every open, and on target change while open. // Сброс и загрузка при каждом открытии и при смене версии в открытой модалке (load меняется вместе с versionId).
useEffect(() => { useEffect(() => {
if (!open) return; if (!open) return;
const requests = reqId; // счётчик запросов, а не DOM-узел: в cleanup нужен его актуальный .current
setReason(''); setReason('');
setConflict(false); setConflict(false);
setData(null); setData(null);
setLoadError(false); setLoadError(false);
load(); load();
return () => { return () => {
reqId.current++; // invalidate in-flight response on close/switch requests.current++; // ответ в полёте больше не нужен
}; };
}, [open, versionId, load]); }, [open, load]);
const hasToReason = !loading && data !== null && data.locked_by !== currentUserId; const hasToReason = !loading && data !== null && data.locked_by !== currentUserId;
const handleOk = async () => { const handleOk = async () => {
if (!versionId || loading || submitting) return; if (!versionId || loading || submitting) return;
if (hasToReason && !reason.trim()) return; if (hasToReason && !isValidReason(reason)) return;
setSubmitting(true); setSubmitting(true);
try { try {
const result = await lockVersion(versionId, hasToReason ? reason.trim() : null); await lockVersion(versionId, hasToReason ? reason.trim() : null);
close(); close();
message.success('Консолидация взята в работу.'); message.success('Консолидация взята в работу.');
handleReload();
} catch (e) { } catch (e) {
if (e instanceof ApiError && e.status === 409) { if (e instanceof ApiError && e.code === 'VERSION_NOT_EDITABLE') {
if (e.code === 'VERSION_NOT_EDITABLE') { message.error('Нельзя взять в работу отменённую версию.');
message.error('Нельзя взять в работу отменённую версию.'); close();
close(); handleReload();
} else if (e.code === 'LOCKED_BY_OTHER' && !conflict) { } else if (e instanceof ApiError && e.code === 'LOCKED_BY_OTHER' && !conflict) {
await load(); // версию взяли, пока модалка была открыта, — режим перехвата
setConflict(true); await load();
setReason(''); setConflict(true);
} setReason('');
} else { } else {
message.error('Что-то пошло не так. Попробуйте снова.'); // 403 без права, прочие 409/422 — текст сервера
message.error(e instanceof ApiError ? e.message : 'Не удалось взять консолидацию в работу');
} }
} finally { } finally {
setSubmitting(false); setSubmitting(false);
handleReload();
} }
}; };
@ -110,6 +111,7 @@ export default function LockModal({ versionId, open, onOpenChange, currentUserId
closable={!submitting} closable={!submitting}
okText='Взять в работу' okText='Взять в работу'
okButtonProps={{ disabled: loading || loadError || (hasToReason && !isValidReason(reason)) }} okButtonProps={{ disabled: loading || loadError || (hasToReason && !isValidReason(reason)) }}
confirmLoading={submitting}
cancelText='Отмена' cancelText='Отмена'
onOk={handleOk} onOk={handleOk}
cancelButtonProps={{ disabled: submitting }}> cancelButtonProps={{ disabled: submitting }}>
@ -119,10 +121,10 @@ export default function LockModal({ versionId, open, onOpenChange, currentUserId
<Alert <Alert
type='error' type='error'
showIcon showIcon
title='Не получилось загрузить нформацию о текущей блокировке' title='Не удалось загрузить информацию о текущей блокировке'
action={ action={
<Button size='small' onClick={load}> <Button size='small' onClick={load}>
Попробовать перезагрузить Повторить
</Button> </Button>
} }
/> />
@ -141,12 +143,13 @@ export default function LockModal({ versionId, open, onOpenChange, currentUserId
{hasToReason && data ? ( {hasToReason && data ? (
<> <>
<p style={{ marginTop: 0 }}>Консолидация взята в работу {data.locked_by_name}. Укажите причину замены.</p> <p style={{ marginTop: 0 }}>Консолидация взята в работу {endSentence(data.locked_by_name)} Укажите причину замены.</p>
<Input.TextArea <Input.TextArea
autoFocus autoFocus
placeholder='Type message' placeholder='Укажите причину замены'
value={reason} value={reason}
maxLength={1000} maxLength={MAX_REASON_LENGTH}
showCount
rows={3} rows={3}
onChange={(e) => setReason(e.target.value)} onChange={(e) => setReason(e.target.value)}
/> />

View File

@ -1,5 +1,6 @@
import { LeftOutlined, PlusOutlined, RightOutlined } from '@ant-design/icons'; import { LeftOutlined, PlusOutlined, RightOutlined } from '@ant-design/icons';
import { Button, Divider, Layout, Tooltip, message } from 'antd'; import { Button, Divider, Layout, Tooltip, message } from 'antd';
import { Archive, Building2, ScrollText, UserCog2, Users2 } from 'lucide-react';
import { type ReactNode, memo, useState } from 'react'; import { type ReactNode, memo, useState } from 'react';
import { NavLink, useNavigate } from 'react-router-dom'; import { NavLink, useNavigate } from 'react-router-dom';
import { USERS_ADMIN } from '../../auth/permissions'; import { USERS_ADMIN } from '../../auth/permissions';
@ -10,7 +11,6 @@ import { useAppDispatch } from '../../store/store';
import { quarterLabel } from '../../utils/labels'; import { quarterLabel } from '../../utils/labels';
import { NewConsolidationModal } from '../NewConsoleModal/NewConsoleModal'; import { NewConsolidationModal } from '../NewConsoleModal/NewConsoleModal';
import styles from './styles.module.css'; import styles from './styles.module.css';
import { Archive, Building2, ScrollText, UserCog2, Users2 } from 'lucide-react';
type CollapsedIconProps = { type CollapsedIconProps = {
icon: ReactNode; icon: ReactNode;
@ -205,7 +205,8 @@ export function Sidebar() {
onOk={async (values) => { onOk={async (values) => {
const card = await createConsolidation(values); const card = await createConsolidation(values);
// перечитываем список: на странице группы он сам не обновится, и новая консолидация не станет текущей // перечитываем список: на странице группы он сам не обновится, и новая консолидация не станет текущей
await dispatch(getConsolidationsListThunk(values.company_group_id)); // in_work_only: false — переход ниже открывает список без ?in_work_only (флажок выключен)
await dispatch(getConsolidationsListThunk({ company_group_id: values.company_group_id, in_work_only: false }));
setModalOpen(false); setModalOpen(false);
message.success(`Создана консолидация ${values.year}, ${quarterLabel(values.quarter)}`); message.success(`Создана консолидация ${values.year}, ${quarterLabel(values.quarter)}`);
navigate(`/groups/${values.company_group_id}?version=${card.id}`); navigate(`/groups/${values.company_group_id}?version=${card.id}`);

View File

@ -7,7 +7,8 @@
.section { .section {
margin-bottom: 0px; margin-bottom: 0px;
/* padding: 12px; */ /* отступ сверху и снизу, как в макете [34]; по горизонтали пункты выровнены по кнопке «Новая консолидация» */
padding: 12px 0;
border-radius: 10px; border-radius: 10px;
} }