2024-06-07 20:17:03 +03:00
|
|
|
|
'use client';
|
|
|
|
|
|
2025-02-05 21:55:26 +03:00
|
|
|
|
import { useState } from 'react';
|
2024-06-07 20:17:03 +03:00
|
|
|
|
|
2025-03-12 11:54:32 +03:00
|
|
|
|
import { createColumnHelper, DataTable, type RowSelectionState } from '@/components/data-table';
|
2025-03-12 12:04:23 +03:00
|
|
|
|
import { SearchBar } from '@/components/input';
|
2025-02-20 20:22:05 +03:00
|
|
|
|
import { type Styling } from '@/components/props';
|
2025-04-12 21:47:46 +03:00
|
|
|
|
import { cn } from '@/components/utils';
|
2025-03-12 12:04:23 +03:00
|
|
|
|
import { NoData } from '@/components/view';
|
|
|
|
|
import { Graph } from '@/models/graph';
|
2024-06-07 20:17:03 +03:00
|
|
|
|
|
2025-02-11 20:56:11 +03:00
|
|
|
|
import { describeConstituenta } from '../labels';
|
2025-02-20 20:22:05 +03:00
|
|
|
|
import { type IConstituenta, type IRSForm } from '../models/rsform';
|
2025-03-12 11:54:32 +03:00
|
|
|
|
import { isBasicConcept, matchConstituenta } from '../models/rsform-api';
|
|
|
|
|
import { CstMatchMode } from '../stores/cst-search';
|
2025-02-12 21:36:03 +03:00
|
|
|
|
|
2025-03-12 11:54:32 +03:00
|
|
|
|
import { BadgeConstituenta } from './badge-constituenta';
|
|
|
|
|
import { ToolbarGraphSelection } from './toolbar-graph-selection';
|
2024-06-07 20:17:03 +03:00
|
|
|
|
|
2025-02-20 20:22:05 +03:00
|
|
|
|
interface PickMultiConstituentaProps extends Styling {
|
2024-06-07 20:17:03 +03:00
|
|
|
|
id?: string;
|
2025-02-12 20:53:01 +03:00
|
|
|
|
value: number[];
|
|
|
|
|
onChange: (newValue: number[]) => void;
|
2025-02-04 20:35:18 +03:00
|
|
|
|
|
2024-10-23 15:18:46 +03:00
|
|
|
|
schema: IRSForm;
|
2025-02-05 12:42:45 +03:00
|
|
|
|
items: IConstituenta[];
|
2024-10-23 15:18:46 +03:00
|
|
|
|
|
2024-06-07 20:17:03 +03:00
|
|
|
|
rows?: number;
|
2024-10-28 23:55:12 +03:00
|
|
|
|
noBorder?: boolean;
|
2024-06-07 20:17:03 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const columnHelper = createColumnHelper<IConstituenta>();
|
|
|
|
|
|
2025-02-12 20:53:01 +03:00
|
|
|
|
export function PickMultiConstituenta({
|
2024-10-23 15:18:46 +03:00
|
|
|
|
id,
|
|
|
|
|
schema,
|
2025-02-05 12:42:45 +03:00
|
|
|
|
items,
|
2024-10-23 15:18:46 +03:00
|
|
|
|
rows,
|
2024-10-28 23:55:12 +03:00
|
|
|
|
noBorder,
|
2025-02-04 20:35:18 +03:00
|
|
|
|
value,
|
|
|
|
|
onChange,
|
2024-10-29 12:44:51 +03:00
|
|
|
|
className,
|
|
|
|
|
...restProps
|
2024-10-23 15:18:46 +03:00
|
|
|
|
}: PickMultiConstituentaProps) {
|
2024-08-25 13:45:32 +03:00
|
|
|
|
const [filterText, setFilterText] = useState('');
|
2024-06-07 20:17:03 +03:00
|
|
|
|
|
2025-02-05 21:55:26 +03:00
|
|
|
|
const filtered = filterText ? items.filter(cst => matchConstituenta(cst, filterText, CstMatchMode.ALL)) : items;
|
|
|
|
|
const rowSelection: RowSelectionState = Object.fromEntries(
|
|
|
|
|
filtered.map((cst, index) => [String(index), value.includes(cst.id)])
|
|
|
|
|
);
|
2025-02-05 12:42:45 +03:00
|
|
|
|
|
2024-12-13 21:30:49 +03:00
|
|
|
|
const foldedGraph = (() => {
|
2025-02-05 12:42:45 +03:00
|
|
|
|
if (items.length === schema.items.length) {
|
2024-10-23 15:18:46 +03:00
|
|
|
|
return schema.graph;
|
|
|
|
|
}
|
|
|
|
|
const newGraph = new Graph();
|
|
|
|
|
schema.graph.nodes.forEach(node => {
|
|
|
|
|
newGraph.addNode(node.id);
|
|
|
|
|
node.outputs.forEach(output => {
|
|
|
|
|
newGraph.addEdge(node.id, output);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
schema.items
|
2025-02-05 12:42:45 +03:00
|
|
|
|
.filter(item => items.find(cst => cst.id === item.id) === undefined)
|
2024-10-23 15:18:46 +03:00
|
|
|
|
.forEach(item => {
|
|
|
|
|
newGraph.foldNode(item.id);
|
|
|
|
|
});
|
|
|
|
|
return newGraph;
|
2024-12-13 21:30:49 +03:00
|
|
|
|
})();
|
2024-10-23 15:18:46 +03:00
|
|
|
|
|
2024-06-07 20:17:03 +03:00
|
|
|
|
function handleRowSelection(updater: React.SetStateAction<RowSelectionState>) {
|
2025-02-05 12:42:45 +03:00
|
|
|
|
if (!items) {
|
2025-02-04 20:35:18 +03:00
|
|
|
|
onChange([]);
|
2024-06-07 20:17:03 +03:00
|
|
|
|
} else {
|
|
|
|
|
const newRowSelection = typeof updater === 'function' ? updater(rowSelection) : updater;
|
2025-02-12 20:53:01 +03:00
|
|
|
|
const newSelection: number[] = [];
|
2024-08-25 13:45:32 +03:00
|
|
|
|
filtered.forEach((cst, index) => {
|
2024-06-07 20:17:03 +03:00
|
|
|
|
if (newRowSelection[String(index)] === true) {
|
|
|
|
|
newSelection.push(cst.id);
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-02-10 15:48:58 +03:00
|
|
|
|
onChange([...value.filter(cst_id => !filtered.find(cst => cst.id === cst_id)), ...newSelection]);
|
2024-06-07 20:17:03 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-13 21:30:49 +03:00
|
|
|
|
const columns = [
|
|
|
|
|
columnHelper.accessor('alias', {
|
|
|
|
|
id: 'alias',
|
|
|
|
|
header: () => <span className='pl-3'>Имя</span>,
|
|
|
|
|
size: 65,
|
2025-02-05 12:42:45 +03:00
|
|
|
|
cell: props => <BadgeConstituenta value={props.row.original} />
|
2024-12-13 21:30:49 +03:00
|
|
|
|
}),
|
|
|
|
|
columnHelper.accessor(cst => describeConstituenta(cst), {
|
|
|
|
|
id: 'description',
|
|
|
|
|
size: 1000,
|
|
|
|
|
header: 'Описание'
|
|
|
|
|
})
|
|
|
|
|
];
|
2024-06-07 20:17:03 +03:00
|
|
|
|
|
|
|
|
|
return (
|
2025-04-12 21:47:46 +03:00
|
|
|
|
<div className={cn(!noBorder && 'border', className)} {...restProps}>
|
|
|
|
|
<div className='px-3 flex justify-between items-center bg-input border-b rounded-t-md'>
|
2024-08-25 13:45:32 +03:00
|
|
|
|
<div className='w-[24ch] select-none whitespace-nowrap'>
|
2025-02-05 12:42:45 +03:00
|
|
|
|
{items.length > 0 ? `Выбраны ${value.length} из ${items.length}` : 'Конституенты'}
|
2024-08-25 13:45:32 +03:00
|
|
|
|
</div>
|
|
|
|
|
<SearchBar
|
|
|
|
|
id='dlg_constituents_search'
|
|
|
|
|
noBorder
|
2025-03-10 16:01:40 +03:00
|
|
|
|
className='min-w-24 pr-2 grow'
|
2024-11-21 15:09:31 +03:00
|
|
|
|
query={filterText}
|
|
|
|
|
onChangeQuery={setFilterText}
|
2024-08-25 13:45:32 +03:00
|
|
|
|
/>
|
2024-10-23 15:18:46 +03:00
|
|
|
|
<ToolbarGraphSelection
|
|
|
|
|
graph={foldedGraph}
|
2025-02-19 22:32:50 +03:00
|
|
|
|
isCore={cstID => {
|
|
|
|
|
const cst = schema.cstByID.get(cstID);
|
|
|
|
|
return !!cst && isBasicConcept(cst.cst_type);
|
|
|
|
|
}}
|
2024-10-23 15:18:46 +03:00
|
|
|
|
isOwned={cstID => !schema.cstByID.get(cstID)?.is_inherited}
|
2025-02-04 20:35:18 +03:00
|
|
|
|
value={value}
|
|
|
|
|
onChange={onChange}
|
2024-10-23 15:18:46 +03:00
|
|
|
|
className='w-fit'
|
|
|
|
|
/>
|
2024-06-07 20:17:03 +03:00
|
|
|
|
</div>
|
2025-03-10 16:01:40 +03:00
|
|
|
|
|
2024-06-07 20:17:03 +03:00
|
|
|
|
<DataTable
|
|
|
|
|
id={id}
|
|
|
|
|
dense
|
|
|
|
|
noFooter
|
|
|
|
|
rows={rows}
|
|
|
|
|
contentHeight='1.3rem'
|
2024-10-29 12:44:51 +03:00
|
|
|
|
className='cc-scroll-y text-sm select-none rounded-b-md'
|
2024-08-25 13:45:32 +03:00
|
|
|
|
data={filtered}
|
2024-06-07 20:17:03 +03:00
|
|
|
|
columns={columns}
|
|
|
|
|
headPosition='0rem'
|
|
|
|
|
enableRowSelection
|
|
|
|
|
rowSelection={rowSelection}
|
|
|
|
|
onRowSelectionChange={handleRowSelection}
|
|
|
|
|
noDataComponent={
|
2024-06-21 19:16:41 +03:00
|
|
|
|
<NoData>
|
2024-06-07 20:17:03 +03:00
|
|
|
|
<p>Список пуст</p>
|
2024-06-21 19:16:41 +03:00
|
|
|
|
</NoData>
|
2024-06-07 20:17:03 +03:00
|
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|