2023-07-24 22:34:03 +03:00
|
|
|
|
import { useCallback, useLayoutEffect, useState } from 'react';
|
2023-07-15 17:46:19 +03:00
|
|
|
|
import { useRSForm } from '../../context/RSFormContext';
|
2023-07-23 21:38:04 +03:00
|
|
|
|
import { CstType, EditMode, INewCstData } from '../../utils/models';
|
2023-07-16 20:25:55 +03:00
|
|
|
|
import { toast } from 'react-toastify';
|
|
|
|
|
import TextArea from '../../components/Common/TextArea';
|
|
|
|
|
import ExpressionEditor from './ExpressionEditor';
|
|
|
|
|
import SubmitButton from '../../components/Common/SubmitButton';
|
2023-07-23 21:38:04 +03:00
|
|
|
|
import { createAliasFor, getCstTypeLabel } from '../../utils/staticUI';
|
2023-07-20 17:11:03 +03:00
|
|
|
|
import ConstituentsSideList from './ConstituentsSideList';
|
2023-07-23 21:38:04 +03:00
|
|
|
|
import { DumpBinIcon, SaveIcon, SmallPlusIcon } from '../../components/Icons';
|
|
|
|
|
import CreateCstModal from './CreateCstModal';
|
|
|
|
|
import { AxiosResponse } from 'axios';
|
2023-07-15 17:46:19 +03:00
|
|
|
|
|
|
|
|
|
function ConstituentEditor() {
|
2023-07-18 14:55:40 +03:00
|
|
|
|
const {
|
2023-07-24 22:34:03 +03:00
|
|
|
|
activeCst, activeID, schema, setActiveID, processing, isEditable,
|
2023-07-23 21:38:04 +03:00
|
|
|
|
cstDelete, cstUpdate, cstCreate
|
2023-07-18 14:55:40 +03:00
|
|
|
|
} = useRSForm();
|
2023-07-16 20:25:55 +03:00
|
|
|
|
|
2023-07-23 21:38:04 +03:00
|
|
|
|
const [showCstModal, setShowCstModal] = useState(false);
|
2023-07-20 17:11:03 +03:00
|
|
|
|
const [editMode, setEditMode] = useState(EditMode.TEXT);
|
|
|
|
|
|
2023-07-16 20:25:55 +03:00
|
|
|
|
const [alias, setAlias] = useState('');
|
|
|
|
|
const [type, setType] = useState('');
|
|
|
|
|
const [term, setTerm] = useState('');
|
|
|
|
|
const [textDefinition, setTextDefinition] = useState('');
|
|
|
|
|
const [expression, setExpression] = useState('');
|
|
|
|
|
const [convention, setConvention] = useState('');
|
2023-07-20 17:11:03 +03:00
|
|
|
|
const [typification, setTypification] = useState('N/A');
|
2023-07-16 20:25:55 +03:00
|
|
|
|
|
2023-07-24 22:34:03 +03:00
|
|
|
|
useLayoutEffect(() => {
|
2023-07-21 00:09:05 +03:00
|
|
|
|
if (schema?.items && schema?.items.length > 0) {
|
2023-07-24 22:34:03 +03:00
|
|
|
|
setActiveID((prev) => (prev || schema?.items![0].id));
|
2023-07-16 20:25:55 +03:00
|
|
|
|
}
|
2023-07-24 22:34:03 +03:00
|
|
|
|
}, [schema, setActiveID])
|
2023-07-16 20:25:55 +03:00
|
|
|
|
|
2023-07-24 22:34:03 +03:00
|
|
|
|
useLayoutEffect(() => {
|
|
|
|
|
if (activeCst) {
|
|
|
|
|
setAlias(activeCst.alias);
|
|
|
|
|
setType(getCstTypeLabel(activeCst.cstType));
|
|
|
|
|
setConvention(activeCst.convention || '');
|
|
|
|
|
setTerm(activeCst.term?.raw || '');
|
|
|
|
|
setTextDefinition(activeCst.definition?.text?.raw || '');
|
|
|
|
|
setExpression(activeCst.definition?.formal || '');
|
|
|
|
|
setTypification(activeCst?.parse?.typification || 'N/A');
|
2023-07-16 20:25:55 +03:00
|
|
|
|
}
|
2023-07-24 22:34:03 +03:00
|
|
|
|
}, [activeCst]);
|
2023-07-16 20:25:55 +03:00
|
|
|
|
|
2023-07-23 21:38:04 +03:00
|
|
|
|
const handleSubmit =
|
|
|
|
|
async (event: React.FormEvent<HTMLFormElement>) => {
|
2023-07-16 20:25:55 +03:00
|
|
|
|
event.preventDefault();
|
2023-07-18 14:55:40 +03:00
|
|
|
|
if (!processing) {
|
|
|
|
|
const data = {
|
|
|
|
|
'alias': alias,
|
|
|
|
|
'convention': convention,
|
|
|
|
|
'definition_formal': expression,
|
|
|
|
|
'definition_text': {
|
|
|
|
|
'raw': textDefinition,
|
|
|
|
|
'resolved': '',
|
|
|
|
|
},
|
|
|
|
|
'term': {
|
|
|
|
|
'raw': term,
|
|
|
|
|
'resolved': '',
|
2023-07-24 22:34:03 +03:00
|
|
|
|
'forms': activeCst?.term?.forms || [],
|
2023-07-18 14:55:40 +03:00
|
|
|
|
}
|
|
|
|
|
};
|
2023-07-25 00:20:37 +03:00
|
|
|
|
cstUpdate(data, () => toast.success('Изменения сохранены'));
|
2023-07-18 14:55:40 +03:00
|
|
|
|
}
|
2023-07-16 20:25:55 +03:00
|
|
|
|
};
|
|
|
|
|
|
2023-07-23 21:38:04 +03:00
|
|
|
|
const handleDelete = useCallback(
|
|
|
|
|
async () => {
|
2023-07-24 22:34:03 +03:00
|
|
|
|
if (!activeID || !schema?.items || !window.confirm('Вы уверены, что хотите удалить конституенту?')) {
|
2023-07-23 21:38:04 +03:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const data = {
|
2023-07-25 00:20:37 +03:00
|
|
|
|
'items': [{'id': activeID}]
|
2023-07-23 21:38:04 +03:00
|
|
|
|
}
|
2023-07-24 22:34:03 +03:00
|
|
|
|
const index = schema.items.findIndex((cst) => cst.id === activeID);
|
|
|
|
|
if (index !== -1 && index + 1 < schema.items.length) {
|
|
|
|
|
setActiveID(schema.items[index + 1].id);
|
2023-07-23 21:38:04 +03:00
|
|
|
|
}
|
2023-07-25 00:20:37 +03:00
|
|
|
|
cstDelete(data, () => toast.success('Конституента удалена'));
|
2023-07-24 22:34:03 +03:00
|
|
|
|
}, [activeID, schema, setActiveID, cstDelete]);
|
2023-07-23 21:38:04 +03:00
|
|
|
|
|
|
|
|
|
const handleAddNew = useCallback(
|
|
|
|
|
async (csttype?: CstType) => {
|
2023-07-24 22:34:03 +03:00
|
|
|
|
if (!activeID || !schema?.items) {
|
2023-07-23 21:38:04 +03:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!csttype) {
|
|
|
|
|
setShowCstModal(true);
|
|
|
|
|
} else {
|
|
|
|
|
const data: INewCstData = {
|
|
|
|
|
'csttype': csttype,
|
|
|
|
|
'alias': createAliasFor(csttype, schema!),
|
2023-07-24 22:34:03 +03:00
|
|
|
|
'insert_after': activeID
|
2023-07-23 21:38:04 +03:00
|
|
|
|
}
|
2023-07-24 22:34:03 +03:00
|
|
|
|
cstCreate(data,
|
|
|
|
|
async (response: AxiosResponse) => {
|
|
|
|
|
// navigate(`/rsforms/${schema.id}?tab=${RSFormTabsList.CST_EDIT}&active=${response.data['new_cst']['id']}`);
|
|
|
|
|
setActiveID(response.data['new_cst']['id']);
|
|
|
|
|
toast.success(`Конституента добавлена: ${response.data['new_cst']['alias']}`);
|
2023-07-23 21:38:04 +03:00
|
|
|
|
});
|
|
|
|
|
}
|
2023-07-24 22:34:03 +03:00
|
|
|
|
}, [activeID, schema, cstCreate, setActiveID]);
|
2023-07-23 21:38:04 +03:00
|
|
|
|
|
2023-07-16 20:25:55 +03:00
|
|
|
|
const handleRename = useCallback(() => {
|
|
|
|
|
toast.info('Переименование в разработке');
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const handleChangeType = useCallback(() => {
|
|
|
|
|
toast.info('Изменение типа в разработке');
|
|
|
|
|
}, []);
|
|
|
|
|
|
2023-07-15 17:46:19 +03:00
|
|
|
|
|
|
|
|
|
return (
|
2023-07-16 20:25:55 +03:00
|
|
|
|
<div className='flex items-start w-full gap-2'>
|
2023-07-23 21:38:04 +03:00
|
|
|
|
<CreateCstModal
|
|
|
|
|
show={showCstModal}
|
|
|
|
|
toggle={() => setShowCstModal(!showCstModal)}
|
|
|
|
|
onCreate={handleAddNew}
|
2023-07-24 22:34:03 +03:00
|
|
|
|
defaultType={activeCst?.cstType as CstType}
|
2023-07-23 21:38:04 +03:00
|
|
|
|
/>
|
2023-07-20 17:11:03 +03:00
|
|
|
|
<form onSubmit={handleSubmit} className='flex-grow min-w-[50rem] max-w-min px-4 py-2 border'>
|
2023-07-22 03:18:48 +03:00
|
|
|
|
<div className='flex items-start justify-between'>
|
2023-07-23 21:38:04 +03:00
|
|
|
|
<button type='submit'
|
|
|
|
|
title='Сохранить изменения'
|
|
|
|
|
className='px-1 py-1 font-bold rounded whitespace-nowrap disabled:cursor-not-allowed clr-btn-primary'
|
|
|
|
|
disabled={!isEditable}
|
|
|
|
|
>
|
|
|
|
|
<SaveIcon size={5} />
|
|
|
|
|
</button>
|
2023-07-22 03:18:48 +03:00
|
|
|
|
<div className='flex items-start justify-center w-full gap-4'>
|
|
|
|
|
<span className='mr-12'>
|
|
|
|
|
<label
|
|
|
|
|
title='Переименовать конституенту'
|
|
|
|
|
className='font-semibold underline cursor-pointer'
|
|
|
|
|
onClick={handleRename}
|
|
|
|
|
>
|
|
|
|
|
ID
|
|
|
|
|
</label>
|
|
|
|
|
<b className='ml-2'>{alias}</b>
|
|
|
|
|
</span>
|
|
|
|
|
<span>
|
|
|
|
|
<label
|
|
|
|
|
title='Изменить тип конституенты'
|
|
|
|
|
className='font-semibold underline cursor-pointer'
|
|
|
|
|
onClick={handleChangeType}
|
|
|
|
|
>
|
|
|
|
|
Тип
|
|
|
|
|
</label>
|
|
|
|
|
<span className='ml-2'>{type}</span>
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className='flex justify-end'>
|
2023-07-23 21:38:04 +03:00
|
|
|
|
<button type='button'
|
|
|
|
|
title='Создать конституенты после данной'
|
|
|
|
|
className='px-1 py-1 font-bold rounded-full whitespace-nowrap disabled:cursor-not-allowed clr-btn-clear'
|
|
|
|
|
disabled={!isEditable}
|
|
|
|
|
onClick={() => handleAddNew()}
|
|
|
|
|
>
|
|
|
|
|
<SmallPlusIcon size={5} color={isEditable ? 'text-green': ''} />
|
|
|
|
|
</button>
|
|
|
|
|
<button type='button'
|
|
|
|
|
title='Удалить редактируемую конституенту'
|
|
|
|
|
className='px-1 py-1 font-bold rounded-full whitespace-nowrap disabled:cursor-not-allowed clr-btn-clear'
|
|
|
|
|
disabled={!isEditable}
|
|
|
|
|
onClick={handleDelete}
|
|
|
|
|
>
|
|
|
|
|
<DumpBinIcon size={5} color={isEditable ? 'text-red': ''} />
|
|
|
|
|
</button>
|
2023-07-22 03:18:48 +03:00
|
|
|
|
</div>
|
2023-07-16 20:25:55 +03:00
|
|
|
|
</div>
|
|
|
|
|
<TextArea id='term' label='Термин'
|
|
|
|
|
placeholder='Схемный или предметный термин, обозначающий данное понятие или утверждение'
|
|
|
|
|
rows={2}
|
|
|
|
|
value={term}
|
|
|
|
|
disabled={!isEditable}
|
2023-07-20 17:11:03 +03:00
|
|
|
|
spellCheck
|
2023-07-16 20:25:55 +03:00
|
|
|
|
onChange={event => setTerm(event.target.value)}
|
2023-07-20 17:11:03 +03:00
|
|
|
|
onFocus={() => setEditMode(EditMode.TEXT)}
|
|
|
|
|
/>
|
|
|
|
|
<TextArea id='typification' label='Типизация'
|
|
|
|
|
rows={1}
|
|
|
|
|
value={typification}
|
|
|
|
|
disabled
|
2023-07-16 20:25:55 +03:00
|
|
|
|
/>
|
|
|
|
|
<ExpressionEditor id='expression' label='Формальное выражение'
|
|
|
|
|
placeholder='Родоструктурное выражение, задающее формальное определение'
|
|
|
|
|
value={expression}
|
|
|
|
|
disabled={!isEditable}
|
2023-07-20 17:11:03 +03:00
|
|
|
|
isActive={editMode==='rslang'}
|
|
|
|
|
toggleEditMode={() => setEditMode(EditMode.RSLANG)}
|
2023-07-16 20:25:55 +03:00
|
|
|
|
onChange={event => setExpression(event.target.value)}
|
2023-07-21 18:44:14 +03:00
|
|
|
|
setValue={setExpression}
|
2023-07-20 17:11:03 +03:00
|
|
|
|
setTypification={setTypification}
|
2023-07-16 20:25:55 +03:00
|
|
|
|
/>
|
|
|
|
|
<TextArea id='definition' label='Текстовое определение'
|
|
|
|
|
placeholder='Лингвистическая интерпретация формального выражения'
|
|
|
|
|
rows={4}
|
|
|
|
|
value={textDefinition}
|
|
|
|
|
disabled={!isEditable}
|
2023-07-20 17:11:03 +03:00
|
|
|
|
spellCheck
|
2023-07-16 20:25:55 +03:00
|
|
|
|
onChange={event => setTextDefinition(event.target.value)}
|
2023-07-20 17:11:03 +03:00
|
|
|
|
onFocus={() => setEditMode(EditMode.TEXT)}
|
2023-07-16 20:25:55 +03:00
|
|
|
|
/>
|
|
|
|
|
<TextArea id='convention' label='Конвенция / Комментарий'
|
2023-07-22 03:18:48 +03:00
|
|
|
|
placeholder='Договоренность об интерпретации неопределяемого понятия
Комментарий к производному понятию'
|
2023-07-16 20:25:55 +03:00
|
|
|
|
rows={4}
|
|
|
|
|
value={convention}
|
|
|
|
|
disabled={!isEditable}
|
2023-07-20 17:11:03 +03:00
|
|
|
|
spellCheck
|
2023-07-16 20:25:55 +03:00
|
|
|
|
onChange={event => setConvention(event.target.value)}
|
2023-07-20 17:11:03 +03:00
|
|
|
|
onFocus={() => setEditMode(EditMode.TEXT)}
|
2023-07-16 20:25:55 +03:00
|
|
|
|
/>
|
2023-07-22 03:18:48 +03:00
|
|
|
|
<div className='flex justify-center w-full mt-2'>
|
|
|
|
|
<SubmitButton
|
|
|
|
|
text='Сохранить изменения'
|
|
|
|
|
disabled={!isEditable}
|
|
|
|
|
icon={<SaveIcon size={6} />}
|
|
|
|
|
/>
|
2023-07-21 18:44:14 +03:00
|
|
|
|
</div>
|
2023-07-16 20:25:55 +03:00
|
|
|
|
</form>
|
2023-07-20 17:11:03 +03:00
|
|
|
|
<ConstituentsSideList expression={expression}/>
|
|
|
|
|
</div>
|
2023-07-15 17:46:19 +03:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default ConstituentEditor;
|