Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EAKRDNzXtRrwmWz9z6PZu3
199 lines
6.4 KiB
TypeScript
199 lines
6.4 KiB
TypeScript
import { ArrowRightOutlined, DownOutlined, MoreOutlined, RightOutlined } from '@ant-design/icons';
|
||
import { Button, Dropdown, Space, message } from 'antd';
|
||
import type { ColumnsType, TableProps } from 'antd/es/table';
|
||
import { memo, useCallback, useMemo } from 'react';
|
||
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
|
||
import { ColumnSettings, DataTable, type TableConfig, isGroup, useFilteredRows, useVisibleColumns } from '../../components/GenericTable';
|
||
import { useTableFilters } from '../../components/GenericTable/useTableFilters';
|
||
import { useAppSelector } from '../../store/store';
|
||
import type { ConsRow } from '../../types/consolidations/consolidations';
|
||
import { consConfigBase } from './config';
|
||
import { flattenCons } from './flatten';
|
||
import { groupAdjacent } from './groupAdjacent';
|
||
import { useGrouping } from './useGrouping';
|
||
|
||
type ActionKey = 'copy' | 'disable_confirm' | 'history';
|
||
const ITEMS = [
|
||
{ key: 'copy', label: 'Добавить версию' },
|
||
{ key: 'disable_confirm', label: 'Отключить подвтерждение КФО' },
|
||
{ key: 'history', label: 'История изменений' },
|
||
{},
|
||
] as const;
|
||
|
||
const RowActions = memo(function RowActions({
|
||
row,
|
||
onAction,
|
||
}: {
|
||
row: ConsRow;
|
||
onAction: (key: ActionKey, _row: ConsRow) => void;
|
||
}) {
|
||
return (
|
||
<Dropdown
|
||
trigger={['click']}
|
||
menu={{
|
||
items: ITEMS as any,
|
||
onClick: ({ key, domEvent }) => {
|
||
domEvent.stopPropagation();
|
||
onAction(key as ActionKey, row);
|
||
},
|
||
}}>
|
||
<Button icon={<MoreOutlined />} onClick={(e) => e.stopPropagation()} />
|
||
</Dropdown>
|
||
);
|
||
});
|
||
export function ConsoleTableWidget() {
|
||
const navigate = useNavigate();
|
||
const { group_id } = useParams();
|
||
const [searchParams, setSearchParams] = useSearchParams();
|
||
const consolidations = useAppSelector((s) => s.consolidations.consolidations);
|
||
const loading = useAppSelector((s) => s.consolidations.loading);
|
||
|
||
const { columnFilters, setColumnFilters, sort, setSort, resetAll, hasActiveFilters } = useTableFilters<ConsRow>();
|
||
const rawRows = useMemo(() => flattenCons(consolidations), [consolidations]);
|
||
|
||
// const [sort, setSort] = useState<SortState<ConsRow>>(null);
|
||
|
||
// no globalQuery state at all
|
||
const { rows: filtered } = useFilteredRows(rawRows, consConfigBase, '', columnFilters, sort);
|
||
const grouped = useMemo(() => groupAdjacent(filtered), [filtered]);
|
||
const { visibleRows, expanded, toggle } = useGrouping(grouped);
|
||
|
||
const config: TableConfig<ConsRow> = useMemo(
|
||
() => ({
|
||
...consConfigBase,
|
||
columns: consConfigBase.columns.map((col) =>
|
||
isGroup(col) || col.key !== 'year'
|
||
? col
|
||
: {
|
||
...col,
|
||
render: (value, row: ConsRow) => {
|
||
if (row.isPrimary && row.groupSize > 1) {
|
||
const open = expanded.has(row.group_id);
|
||
return (
|
||
<Space size={4}>
|
||
<button
|
||
type='button'
|
||
aria-label={open ? 'Свернуть версии' : 'Развернуть версии'}
|
||
onClick={(e) => {
|
||
e.stopPropagation();
|
||
toggle(row.group_id);
|
||
}}
|
||
style={{ cursor: 'pointer', display: 'inline-flex', background: 'none', border: 0, padding: 0 }}>
|
||
{open ? <DownOutlined /> : <RightOutlined />}
|
||
</button>
|
||
<span>{String(value)}</span>
|
||
<span style={{ color: '#999', fontSize: 12 }}>({row.groupSize})</span>
|
||
</Space>
|
||
);
|
||
}
|
||
if (!row.isPrimary) {
|
||
return (
|
||
<span style={{ paddingLeft: 24, color: '#666' }}>
|
||
<span style={{ marginRight: 6, color: '#bbb' }}>↳</span>
|
||
{String(value)}
|
||
</span>
|
||
);
|
||
}
|
||
return <span style={{ paddingLeft: 22 }}>{String(value)}</span>;
|
||
},
|
||
},
|
||
),
|
||
}),
|
||
[expanded, toggle],
|
||
);
|
||
|
||
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(
|
||
() => [
|
||
{
|
||
key: 'actions',
|
||
title: '',
|
||
width: 120,
|
||
render: (_: unknown, row: ConsRow) => (
|
||
<div style={{ display: 'flex' }}>
|
||
<Button
|
||
type='default'
|
||
onClick={(e) => {
|
||
// иначе клик всплывает до onRow строки, и его setSearchParams(replace) отменяет переход
|
||
e.stopPropagation();
|
||
navigate(`/groups/${group_id}/versions/${row.id}/osv`);
|
||
}}>
|
||
К отчётности <ArrowRightOutlined />
|
||
</Button>
|
||
<RowActions row={row} onAction={handleDropdownAction} />
|
||
</div>
|
||
),
|
||
},
|
||
],
|
||
[navigate, group_id, handleDropdownAction],
|
||
);
|
||
|
||
const onRow: TableProps<ConsRow>['onRow'] = (row) => ({
|
||
// выбранная версия живёт в URL (?version=): переживает уход на «К отчётности» и «Назад»
|
||
onClick: () => setSearchParams({ version: row.id }, { replace: true }),
|
||
// выбранная версия подсвечена, как в макете [34]
|
||
style: row.id === searchParams.get('version') ? { background: '#f0f7ee' } : undefined,
|
||
});
|
||
return (
|
||
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
|
||
<Space
|
||
style={{
|
||
width: '100%',
|
||
justifyContent: 'space-between',
|
||
padding: '12px 16px',
|
||
flex: 0,
|
||
}}>
|
||
<div style={{ display: 'flex', alignItems: 'baseline', gap: 8, margin: 0 }}>
|
||
<h2
|
||
style={{
|
||
display: 'block',
|
||
boxSizing: 'border-box',
|
||
fontSize: 17,
|
||
fontWeight: 600,
|
||
margin: 0,
|
||
}}>
|
||
Консолидации
|
||
</h2>
|
||
<span style={{ fontSize: 13, color: 'rgb(102, 112, 133)' }}>{rawRows.length} шт.</span>
|
||
</div>
|
||
<div>
|
||
{hasActiveFilters && (
|
||
<Button
|
||
type='default'
|
||
onClick={() => {
|
||
resetAll();
|
||
}}>
|
||
Сбросить фильтры
|
||
</Button>
|
||
)}
|
||
<ColumnSettings defs={config.columns} visibleKeys={visibleKeys} onChange={setVisibleKeys} />
|
||
</div>
|
||
</Space>
|
||
<DataTable
|
||
config={config}
|
||
rows={visibleRows}
|
||
allRows={rawRows}
|
||
loading={loading}
|
||
visibleKeys={visibleKeys}
|
||
columnFilters={columnFilters}
|
||
onColumnFiltersChange={setColumnFilters}
|
||
sortState={sort}
|
||
onSortChange={setSort}
|
||
extraColumns={extraColumns}
|
||
onRow={onRow}
|
||
/>
|
||
</div>
|
||
);
|
||
}
|