mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-25 20:40:36 +03:00
F: Rework Constituenta editor form
Some checks are pending
Frontend CI / build (22.x) (push) Waiting to run
Some checks are pending
Frontend CI / build (22.x) (push) Waiting to run
This commit is contained in:
parent
cc6e592149
commit
a1f27bd822
|
@ -1,4 +1,5 @@
|
|||
import { queryOptions } from '@tanstack/react-query';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { axiosGet, axiosPatch, axiosPost } from '@/backend/apiTransport';
|
||||
import { DELAYS } from '@/backend/configuration';
|
||||
|
@ -74,16 +75,21 @@ export interface ICstCreatedResponse {
|
|||
/**
|
||||
* Represents data, used in updating persistent attributes in {@link IConstituenta}.
|
||||
*/
|
||||
export interface ICstUpdateDTO {
|
||||
target: ConstituentaID;
|
||||
item_data: {
|
||||
convention?: string;
|
||||
definition_formal?: string;
|
||||
definition_raw?: string;
|
||||
term_raw?: string;
|
||||
term_forms?: TermForm[];
|
||||
};
|
||||
}
|
||||
export const CstUpdateSchema = z.object({
|
||||
target: z.number(),
|
||||
item_data: z.object({
|
||||
convention: z.string().optional(),
|
||||
definition_formal: z.string().optional(),
|
||||
definition_raw: z.string().optional(),
|
||||
term_raw: z.string().optional(),
|
||||
term_forms: z.array(z.object({ text: z.string(), tags: z.string() })).optional()
|
||||
})
|
||||
});
|
||||
|
||||
/**
|
||||
* Represents data, used in updating persistent attributes in {@link IConstituenta}.
|
||||
*/
|
||||
export type ICstUpdateDTO = z.infer<typeof CstUpdateSchema>;
|
||||
|
||||
/**
|
||||
* Represents data, used in renaming {@link IConstituenta}.
|
||||
|
|
|
@ -8,7 +8,7 @@ import { ICheckConstituentaDTO, rsformsApi } from './api';
|
|||
|
||||
export const useCheckConstituenta = () => {
|
||||
const mutation = useMutation({
|
||||
mutationKey: [rsformsApi.baseKey, 'check-constituenta'],
|
||||
mutationKey: ['actions', 'check-constituenta'],
|
||||
mutationFn: rsformsApi.checkConstituenta
|
||||
});
|
||||
return {
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
'use client';
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import clsx from 'clsx';
|
||||
import { useEffect, useLayoutEffect, useState } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import { ICstUpdateDTO } from '@/backend/rsform/api';
|
||||
import { CstUpdateSchema, ICstUpdateDTO } from '@/backend/rsform/api';
|
||||
import { useCstUpdate } from '@/backend/rsform/useCstUpdate';
|
||||
import { useMutatingRSForm } from '@/backend/rsform/useMutatingRSForm';
|
||||
import { IconChild, IconPredecessor, IconSave } from '@/components/Icons';
|
||||
|
@ -19,7 +21,7 @@ import { isBaseSet, isBasicConcept, isFunctional } from '@/models/rsformAPI';
|
|||
import { IExpressionParse, ParsingStatus } from '@/models/rslang';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
import { useModificationStore } from '@/stores/modification';
|
||||
import { errors, labelCstTypification } from '@/utils/labels';
|
||||
import { errors, labelCstTypification, labelTypification } from '@/utils/labels';
|
||||
|
||||
import EditorRSExpression from '../EditorRSExpression';
|
||||
import { useRSEdit } from '../RSEditContext';
|
||||
|
@ -42,15 +44,29 @@ function FormConstituenta({
|
|||
}: FormConstituentaProps) {
|
||||
const { cstUpdate } = useCstUpdate();
|
||||
const { schema, activeCst, navigateCst } = useRSEdit();
|
||||
const showTypification = useDialogsStore(activeCst => activeCst.showShowTypeGraph);
|
||||
const { isModified, setIsModified } = useModificationStore();
|
||||
const isProcessing = useMutatingRSForm();
|
||||
|
||||
const [term, setTerm] = useState(activeCst?.term_raw ?? '');
|
||||
const [textDefinition, setTextDefinition] = useState(activeCst?.definition_raw ?? '');
|
||||
const [expression, setExpression] = useState(activeCst?.definition_formal ?? '');
|
||||
const [convention, setConvention] = useState(activeCst?.convention ?? '');
|
||||
const [typification, setTypification] = useState('N/A');
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
control,
|
||||
reset,
|
||||
formState: { isDirty }
|
||||
} = useForm<ICstUpdateDTO>({ resolver: zodResolver(CstUpdateSchema) });
|
||||
|
||||
const [localParse, setLocalParse] = useState<IExpressionParse | undefined>(undefined);
|
||||
const typification = localParse
|
||||
? labelTypification({
|
||||
isValid: localParse.parseResult,
|
||||
resultType: localParse.typification,
|
||||
args: localParse.args
|
||||
})
|
||||
: activeCst
|
||||
? labelCstTypification(activeCst)
|
||||
: 'N/A';
|
||||
|
||||
const typeInfo = activeCst
|
||||
? {
|
||||
alias: activeCst.alias,
|
||||
|
@ -60,67 +76,36 @@ function FormConstituenta({
|
|||
: undefined;
|
||||
|
||||
const [forceComment, setForceComment] = useState(false);
|
||||
|
||||
const isBasic = !!activeCst && isBasicConcept(activeCst.cst_type);
|
||||
const isElementary = !!activeCst && isBaseSet(activeCst.cst_type);
|
||||
const showConvention = !activeCst || !!activeCst.convention || forceComment || isBasic;
|
||||
|
||||
const showTypification = useDialogsStore(activeCst => activeCst.showShowTypeGraph);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeCst) {
|
||||
setConvention(activeCst.convention);
|
||||
setTerm(activeCst.term_raw);
|
||||
setTextDefinition(activeCst.definition_raw);
|
||||
setExpression(activeCst.definition_formal);
|
||||
setTypification(activeCst ? labelCstTypification(activeCst) : 'N/A');
|
||||
setForceComment(false);
|
||||
setLocalParse(undefined);
|
||||
}
|
||||
}, [activeCst, schema, toggleReset, setIsModified]);
|
||||
reset({
|
||||
target: activeCst?.id ?? 0,
|
||||
item_data: {
|
||||
convention: activeCst?.convention ?? '',
|
||||
term_raw: activeCst?.term_raw ?? '',
|
||||
definition_raw: activeCst?.definition_raw ?? '',
|
||||
definition_formal: activeCst?.definition_formal ?? ''
|
||||
}
|
||||
});
|
||||
setForceComment(false);
|
||||
setLocalParse(undefined);
|
||||
}, [activeCst, schema, toggleReset, reset]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!activeCst) {
|
||||
setIsModified(false);
|
||||
return;
|
||||
}
|
||||
setIsModified(
|
||||
activeCst.term_raw !== term ||
|
||||
activeCst.definition_raw !== textDefinition ||
|
||||
activeCst.convention !== convention ||
|
||||
activeCst.definition_formal !== expression
|
||||
);
|
||||
setIsModified(isDirty);
|
||||
return () => setIsModified(false);
|
||||
}, [
|
||||
activeCst,
|
||||
activeCst?.term_raw,
|
||||
activeCst?.definition_formal,
|
||||
activeCst?.definition_raw,
|
||||
activeCst?.convention,
|
||||
term,
|
||||
textDefinition,
|
||||
expression,
|
||||
convention,
|
||||
setIsModified
|
||||
]);
|
||||
}, [isDirty, activeCst, setIsModified]);
|
||||
|
||||
function handleSubmit(event?: React.FormEvent<HTMLFormElement>) {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
if (!activeCst || isProcessing || !schema) {
|
||||
return;
|
||||
}
|
||||
const data: ICstUpdateDTO = {
|
||||
target: activeCst.id,
|
||||
item_data: {
|
||||
term_raw: activeCst.term_raw !== term ? term : undefined,
|
||||
definition_formal: activeCst.definition_formal !== expression ? expression : undefined,
|
||||
definition_raw: activeCst.definition_raw !== textDefinition ? textDefinition : undefined,
|
||||
convention: activeCst.convention !== convention ? convention : undefined
|
||||
}
|
||||
};
|
||||
function onSubmit(data: ICstUpdateDTO) {
|
||||
cstUpdate({ itemID: schema.id, data });
|
||||
reset({ ...data });
|
||||
}
|
||||
|
||||
function handleTypeGraph(event: CProps.EventMouse) {
|
||||
|
@ -136,20 +121,30 @@ function FormConstituenta({
|
|||
return (
|
||||
<div className='mx-0 md:mx-auto pt-[2rem] xs:pt-0'>
|
||||
{activeCst ? <EditorControls disabled={disabled} constituenta={activeCst} onEditTerm={onEditTerm} /> : null}
|
||||
<form id={id} className={clsx('cc-column', 'mt-1 md:w-[48.8rem] shrink-0', 'px-6 py-1')} onSubmit={handleSubmit}>
|
||||
<RefsInput
|
||||
key='cst_term'
|
||||
id='cst_term'
|
||||
label='Термин'
|
||||
maxHeight='8rem'
|
||||
placeholder='Обозначение для текстовых определений'
|
||||
schema={schema}
|
||||
onOpenEdit={navigateCst}
|
||||
value={term}
|
||||
initialValue={activeCst?.term_raw ?? ''}
|
||||
resolved={activeCst?.term_resolved ?? 'Конституента не выбрана'}
|
||||
disabled={disabled}
|
||||
onChange={newValue => setTerm(newValue)}
|
||||
<form
|
||||
id={id}
|
||||
className={clsx('cc-column', 'mt-1 md:w-[48.8rem] shrink-0', 'px-6 py-1')}
|
||||
onSubmit={event => void handleSubmit(onSubmit)(event)}
|
||||
>
|
||||
<Controller
|
||||
control={control}
|
||||
name='item_data.term_raw'
|
||||
render={({ field }) => (
|
||||
<RefsInput
|
||||
key='cst_term'
|
||||
id='cst_term'
|
||||
label='Термин'
|
||||
maxHeight='8rem'
|
||||
placeholder='Обозначение для текстовых определений'
|
||||
schema={schema}
|
||||
onOpenEdit={navigateCst}
|
||||
value={field.value}
|
||||
initialValue={activeCst?.term_raw ?? ''}
|
||||
resolved={activeCst?.term_resolved ?? 'Конституента не выбрана'}
|
||||
disabled={disabled}
|
||||
onChange={newValue => field.onChange(newValue)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{activeCst ? (
|
||||
<TextArea
|
||||
|
@ -168,59 +163,69 @@ function FormConstituenta({
|
|||
{activeCst ? (
|
||||
<>
|
||||
{!!activeCst.definition_formal || !isElementary ? (
|
||||
<EditorRSExpression
|
||||
id='cst_expression'
|
||||
label={
|
||||
activeCst.cst_type === CstType.STRUCTURED
|
||||
? 'Область определения'
|
||||
: isFunctional(activeCst.cst_type)
|
||||
? 'Определение функции'
|
||||
: 'Формальное определение'
|
||||
}
|
||||
placeholder={
|
||||
activeCst.cst_type !== CstType.STRUCTURED
|
||||
? 'Родоструктурное выражение'
|
||||
: 'Типизация родовой структуры'
|
||||
}
|
||||
value={expression}
|
||||
activeCst={activeCst}
|
||||
disabled={disabled || activeCst.is_inherited}
|
||||
toggleReset={toggleReset}
|
||||
onChangeExpression={newValue => setExpression(newValue)}
|
||||
onChangeTypification={setTypification}
|
||||
onChangeLocalParse={setLocalParse}
|
||||
onOpenEdit={navigateCst}
|
||||
onShowTypeGraph={handleTypeGraph}
|
||||
<Controller
|
||||
control={control}
|
||||
name='item_data.definition_formal'
|
||||
render={({ field }) => (
|
||||
<EditorRSExpression
|
||||
id='cst_expression'
|
||||
label={
|
||||
activeCst.cst_type === CstType.STRUCTURED
|
||||
? 'Область определения'
|
||||
: isFunctional(activeCst.cst_type)
|
||||
? 'Определение функции'
|
||||
: 'Формальное определение'
|
||||
}
|
||||
placeholder={
|
||||
activeCst.cst_type !== CstType.STRUCTURED
|
||||
? 'Родоструктурное выражение'
|
||||
: 'Типизация родовой структуры'
|
||||
}
|
||||
value={field.value ?? ''}
|
||||
activeCst={activeCst}
|
||||
disabled={disabled || activeCst.is_inherited}
|
||||
toggleReset={toggleReset}
|
||||
onChange={newValue => field.onChange(newValue)}
|
||||
onChangeLocalParse={setLocalParse}
|
||||
onOpenEdit={navigateCst}
|
||||
onShowTypeGraph={handleTypeGraph}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
) : null}
|
||||
{!!activeCst.definition_raw || !isElementary ? (
|
||||
<RefsInput
|
||||
id='cst_definition'
|
||||
label='Текстовое определение'
|
||||
placeholder='Текстовая интерпретация формального выражения'
|
||||
minHeight='3.75rem'
|
||||
maxHeight='8rem'
|
||||
schema={schema}
|
||||
onOpenEdit={navigateCst}
|
||||
value={textDefinition}
|
||||
initialValue={activeCst.definition_raw}
|
||||
resolved={activeCst.definition_resolved}
|
||||
disabled={disabled}
|
||||
onChange={newValue => setTextDefinition(newValue)}
|
||||
<Controller
|
||||
control={control}
|
||||
name='item_data.definition_raw'
|
||||
render={({ field }) => (
|
||||
<RefsInput
|
||||
id='cst_definition'
|
||||
label='Текстовое определение'
|
||||
placeholder='Текстовая интерпретация формального выражения'
|
||||
minHeight='3.75rem'
|
||||
maxHeight='8rem'
|
||||
schema={schema}
|
||||
onOpenEdit={navigateCst}
|
||||
value={field.value}
|
||||
initialValue={activeCst.definition_raw}
|
||||
resolved={activeCst.definition_resolved}
|
||||
disabled={disabled}
|
||||
onChange={newValue => field.onChange(newValue)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{showConvention ? (
|
||||
<TextArea
|
||||
id='cst_convention'
|
||||
{...register('item_data.convention')}
|
||||
fitContent
|
||||
className='max-h-[8rem]'
|
||||
spellCheck
|
||||
label={isBasic ? 'Конвенция' : 'Комментарий'}
|
||||
placeholder={isBasic ? 'Договоренность об интерпретации' : 'Пояснение разработчика'}
|
||||
value={convention}
|
||||
disabled={disabled || (isBasic && activeCst.is_inherited)}
|
||||
onChange={event => setConvention(event.target.value)}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ import { TokenID } from '@/models/rslang';
|
|||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
import { usePreferencesStore } from '@/stores/preferences';
|
||||
import { transformAST } from '@/utils/codemirror';
|
||||
import { errors, labelTypification } from '@/utils/labels';
|
||||
import { errors } from '@/utils/labels';
|
||||
|
||||
import { useRSEdit } from '../RSEditContext';
|
||||
import ParsingResult from './ParsingResult';
|
||||
|
@ -32,17 +32,17 @@ import ToolbarRSExpression from './ToolbarRSExpression';
|
|||
|
||||
interface EditorRSExpressionProps {
|
||||
id?: string;
|
||||
activeCst: IConstituenta;
|
||||
value: string;
|
||||
onChange: (newValue: string) => void;
|
||||
|
||||
activeCst: IConstituenta;
|
||||
|
||||
label: string;
|
||||
placeholder?: string;
|
||||
|
||||
disabled?: boolean;
|
||||
toggleReset?: boolean;
|
||||
|
||||
onChangeTypification: (typification: string) => void;
|
||||
onChangeLocalParse: (typification: IExpressionParse | undefined) => void;
|
||||
onChangeExpression: (newValue: string) => void;
|
||||
onOpenEdit?: (cstID: ConstituentaID) => void;
|
||||
onShowTypeGraph: (event: CProps.EventMouse) => void;
|
||||
}
|
||||
|
@ -52,9 +52,8 @@ function EditorRSExpression({
|
|||
disabled,
|
||||
value,
|
||||
toggleReset,
|
||||
onChangeTypification,
|
||||
onChange,
|
||||
onChangeLocalParse,
|
||||
onChangeExpression,
|
||||
onOpenEdit,
|
||||
onShowTypeGraph,
|
||||
...restProps
|
||||
|
@ -89,7 +88,7 @@ function EditorRSExpression({
|
|||
}, [activeCst, toggleReset]);
|
||||
|
||||
function handleChange(newValue: string) {
|
||||
onChangeExpression(newValue);
|
||||
onChange(newValue);
|
||||
setIsModified(newValue !== activeCst.definition_formal);
|
||||
}
|
||||
|
||||
|
@ -102,13 +101,6 @@ function EditorRSExpression({
|
|||
rsInput.current?.view?.focus();
|
||||
}
|
||||
setIsModified(false);
|
||||
onChangeTypification(
|
||||
labelTypification({
|
||||
isValid: parse.parseResult,
|
||||
resultType: parse.typification,
|
||||
args: parse.args
|
||||
})
|
||||
);
|
||||
callback?.(parse);
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user