2023-11-04 01:29:21 +03:00
|
|
|
|
import { Dispatch, useEffect, useMemo, useState } from 'react';
|
|
|
|
|
|
2023-11-06 02:20:16 +03:00
|
|
|
|
import ConceptSearch from '../../components/Common/ConceptSearch';
|
2023-11-04 01:29:21 +03:00
|
|
|
|
import SelectSingle from '../../components/Common/SelectSingle';
|
|
|
|
|
import TextArea from '../../components/Common/TextArea';
|
|
|
|
|
import DataTable, { createColumnHelper,IConditionalStyle } from '../../components/DataTable';
|
|
|
|
|
import ConstituentaTooltip from '../../components/Help/ConstituentaTooltip';
|
|
|
|
|
import RSInput from '../../components/RSInput';
|
|
|
|
|
import { useLibrary } from '../../context/LibraryContext';
|
|
|
|
|
import { useConceptTheme } from '../../context/ThemeContext';
|
|
|
|
|
import { CstMatchMode } from '../../models/miscelanious';
|
|
|
|
|
import { applyFilterCategory, CATEGORY_CST_TYPE, IConstituenta, IRSForm, matchConstituenta } from '../../models/rsform';
|
|
|
|
|
import { colorfgCstStatus } from '../../utils/color';
|
|
|
|
|
import { prefixes } from '../../utils/constants';
|
|
|
|
|
|
|
|
|
|
export interface ITemplateState {
|
|
|
|
|
templateID?: number
|
|
|
|
|
prototype?: IConstituenta
|
|
|
|
|
filterCategory?: IConstituenta
|
|
|
|
|
filterText: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface TemplateTabProps {
|
|
|
|
|
state: ITemplateState
|
|
|
|
|
partialUpdate: Dispatch<Partial<ITemplateState>>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const constituentaHelper = createColumnHelper<IConstituenta>();
|
|
|
|
|
|
|
|
|
|
function TemplateTab({ state, partialUpdate }: TemplateTabProps) {
|
|
|
|
|
const { colors } = useConceptTheme();
|
|
|
|
|
|
|
|
|
|
const { templates, retrieveTemplate } = useLibrary();
|
|
|
|
|
const [ selectedSchema, setSelectedSchema ] = useState<IRSForm | undefined>(undefined);
|
|
|
|
|
|
|
|
|
|
const [filteredData, setFilteredData] = useState<IConstituenta[]>([]);
|
|
|
|
|
|
|
|
|
|
const prototypeInfo = useMemo(
|
|
|
|
|
() => {
|
|
|
|
|
if (!state.prototype) {
|
|
|
|
|
return '';
|
|
|
|
|
} else {
|
|
|
|
|
return `${state.prototype?.term_raw}${state.prototype?.definition_raw ? ` — ${state.prototype?.definition_raw}` : ''}`;
|
|
|
|
|
}
|
|
|
|
|
}, [state.prototype]);
|
|
|
|
|
|
|
|
|
|
const templateSelector = useMemo(
|
|
|
|
|
() => templates.map(
|
|
|
|
|
(template) => ({
|
|
|
|
|
value: template.id,
|
|
|
|
|
label: template.title
|
|
|
|
|
})
|
|
|
|
|
), [templates]);
|
|
|
|
|
|
|
|
|
|
const categorySelector = useMemo(
|
|
|
|
|
(): {value: number, label: string}[] => {
|
|
|
|
|
if (!selectedSchema) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
return selectedSchema.items
|
|
|
|
|
.filter(cst => cst.cst_type === CATEGORY_CST_TYPE)
|
|
|
|
|
.map(cst => ({
|
|
|
|
|
value: cst.id,
|
|
|
|
|
label: cst.term_raw
|
|
|
|
|
}));
|
|
|
|
|
}, [selectedSchema]);
|
|
|
|
|
|
|
|
|
|
useEffect(
|
|
|
|
|
() => {
|
|
|
|
|
if (templates.length > 0 && !state.templateID) {
|
|
|
|
|
partialUpdate({ templateID: templates[0].id });
|
|
|
|
|
}
|
|
|
|
|
}, [templates, state.templateID, partialUpdate]);
|
|
|
|
|
|
|
|
|
|
useEffect(
|
|
|
|
|
() => {
|
|
|
|
|
if (!state.templateID) {
|
|
|
|
|
setSelectedSchema(undefined);
|
|
|
|
|
} else {
|
|
|
|
|
retrieveTemplate(state.templateID, setSelectedSchema);
|
|
|
|
|
}
|
|
|
|
|
}, [state.templateID, retrieveTemplate]);
|
|
|
|
|
|
|
|
|
|
// Filter constituents
|
|
|
|
|
useEffect(
|
|
|
|
|
() => {
|
|
|
|
|
if (!selectedSchema) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
let data = selectedSchema.items;
|
|
|
|
|
if (state.filterCategory) {
|
|
|
|
|
data = applyFilterCategory(state.filterCategory, selectedSchema);
|
|
|
|
|
}
|
|
|
|
|
if (state.filterText) {
|
|
|
|
|
data = data.filter(cst => matchConstituenta(state.filterText, cst, CstMatchMode.TERM));
|
|
|
|
|
}
|
|
|
|
|
setFilteredData(data);
|
|
|
|
|
}, [state.filterText, state.filterCategory, selectedSchema]);
|
|
|
|
|
|
|
|
|
|
function handleSelectTemplate(cst: IConstituenta) {
|
|
|
|
|
partialUpdate( { prototype: cst } );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const columns = useMemo(
|
|
|
|
|
() => [
|
|
|
|
|
constituentaHelper.accessor('alias', {
|
|
|
|
|
id: 'alias',
|
|
|
|
|
size: 65,
|
|
|
|
|
minSize: 65,
|
|
|
|
|
cell: props => {
|
|
|
|
|
const cst = props.row.original;
|
|
|
|
|
return (<>
|
|
|
|
|
<div
|
|
|
|
|
id={`${prefixes.cst_template_ist}${cst.alias}`}
|
|
|
|
|
className='min-w-[3.1rem] max-w-[3.1rem] px-1 text-center rounded-md whitespace-nowrap'
|
|
|
|
|
style={{
|
|
|
|
|
borderWidth: '1px',
|
|
|
|
|
borderColor: colorfgCstStatus(cst.status, colors),
|
|
|
|
|
color: colorfgCstStatus(cst.status, colors),
|
|
|
|
|
fontWeight: 600
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{cst.alias}
|
|
|
|
|
</div>
|
|
|
|
|
<ConstituentaTooltip data={cst} anchor={`#${prefixes.cst_template_ist}${cst.alias}`} />
|
|
|
|
|
</>);
|
|
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
constituentaHelper.accessor('term_resolved', {
|
|
|
|
|
id: 'term',
|
|
|
|
|
size: 600,
|
|
|
|
|
minSize: 350,
|
|
|
|
|
maxSize: 600
|
|
|
|
|
})
|
|
|
|
|
], [colors]);
|
|
|
|
|
|
|
|
|
|
const conditionalRowStyles = useMemo(
|
|
|
|
|
(): IConditionalStyle<IConstituenta>[] => [
|
|
|
|
|
{
|
|
|
|
|
when: (cst: IConstituenta) => cst.id === state.prototype?.id,
|
|
|
|
|
style: {
|
|
|
|
|
backgroundColor: colors.bgSelected
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
], [state.prototype, colors]);
|
|
|
|
|
|
|
|
|
|
return (
|
2023-11-06 02:20:16 +03:00
|
|
|
|
<div className='flex flex-col gap-3'>
|
|
|
|
|
<div className='flex justify-between gap-3'>
|
2023-11-04 01:29:21 +03:00
|
|
|
|
<SelectSingle
|
|
|
|
|
className='w-full'
|
|
|
|
|
options={categorySelector}
|
|
|
|
|
placeholder='Выберите категорию'
|
|
|
|
|
value={state.filterCategory && selectedSchema ? {
|
|
|
|
|
value: state.filterCategory.id,
|
|
|
|
|
label: state.filterCategory.term_raw
|
|
|
|
|
} : null}
|
|
|
|
|
onChange={data => partialUpdate({filterCategory: selectedSchema?.items.find(cst => cst.id === data?.value) })}
|
|
|
|
|
isClearable
|
|
|
|
|
/>
|
|
|
|
|
<SelectSingle
|
|
|
|
|
className='min-w-[15rem]'
|
|
|
|
|
options={templateSelector}
|
|
|
|
|
placeholder='Выберите источник'
|
|
|
|
|
value={state.templateID ? { value: state.templateID, label: templates.find(item => item.id == state.templateID)!.title }: null}
|
|
|
|
|
onChange={data => partialUpdate({templateID: (data ? data.value : undefined)})}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
2023-11-06 02:20:16 +03:00
|
|
|
|
<ConceptSearch
|
|
|
|
|
value={state.filterText}
|
|
|
|
|
onChange={newValue => partialUpdate({ filterText: newValue} )}
|
|
|
|
|
dense
|
|
|
|
|
/>
|
|
|
|
|
<div className='border min-h-[17.5rem] max-h-[17.5rem] text-sm overflow-y-auto select-none'>
|
2023-11-04 01:29:21 +03:00
|
|
|
|
<DataTable
|
|
|
|
|
data={filteredData}
|
|
|
|
|
columns={columns}
|
|
|
|
|
conditionalRowStyles={conditionalRowStyles}
|
|
|
|
|
|
|
|
|
|
dense
|
2023-11-06 02:20:16 +03:00
|
|
|
|
noHeader
|
2023-11-04 01:29:21 +03:00
|
|
|
|
|
|
|
|
|
noDataComponent={
|
|
|
|
|
<span className='flex flex-col justify-center p-2 text-center min-h-[5rem]'>
|
|
|
|
|
<p>Список конституент пуст</p>
|
|
|
|
|
<p>Измените параметры фильтра</p>
|
|
|
|
|
</span>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onRowClicked={handleSelectTemplate}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<TextArea id='term'
|
2023-11-06 02:20:16 +03:00
|
|
|
|
rows={1}
|
2023-11-04 01:29:21 +03:00
|
|
|
|
disabled
|
|
|
|
|
placeholder='Шаблон конституенты не выбран'
|
|
|
|
|
value={prototypeInfo}
|
|
|
|
|
spellCheck
|
|
|
|
|
/>
|
|
|
|
|
<RSInput id='expression'
|
|
|
|
|
height='4.8rem'
|
|
|
|
|
placeholder='Выберите шаблон из списка'
|
2023-11-06 02:20:16 +03:00
|
|
|
|
disabled
|
2023-11-04 01:29:21 +03:00
|
|
|
|
value={state.prototype?.definition_formal}
|
|
|
|
|
/>
|
|
|
|
|
</div>);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default TemplateTab;
|