diff --git a/rsconcept/frontend/src/features/rsform/backend/types.ts b/rsconcept/frontend/src/features/rsform/backend/types.ts index d259ffda..0d281b91 100644 --- a/rsconcept/frontend/src/features/rsform/backend/types.ts +++ b/rsconcept/frontend/src/features/rsform/backend/types.ts @@ -4,7 +4,7 @@ import { ILibraryItemReference, ILibraryItemVersioned } from '@/features/library import { errorMsg } from '@/utils/labels'; -import { CstType, IConstituentaMeta, IInheritanceInfo, TermForm } from '../models/rsform'; +import { CstType, IConstituentaMeta, IInheritanceInfo } from '../models/rsform'; import { IArgumentInfo, ParsingStatus, ValueClass } from '../models/rslang'; /** @@ -42,17 +42,21 @@ export interface IRSFormUploadDTO { /** * Represents {@link IConstituenta} data, used in creation process. */ -export interface ICstCreateDTO { - alias: string; - cst_type: CstType; - definition_raw: string; - term_raw: string; - convention: string; - definition_formal: string; - term_forms: TermForm[]; +export const schemaCstCreate = z.object({ + cst_type: z.nativeEnum(CstType), + alias: z.string().nonempty(errorMsg.requiredField), + convention: z.string(), + definition_formal: z.string(), + definition_raw: z.string(), + term_raw: z.string(), + term_forms: z.array(z.object({ text: z.string(), tags: z.string() })), + insert_after: z.number().nullable() +}); - insert_after: number | null; -} +/** + * Represents {@link IConstituenta} data, used in creation process. + */ +export type ICstCreateDTO = z.infer; /** * Represents data response when creating {@link IConstituenta}. diff --git a/rsconcept/frontend/src/features/rsform/dialogs/DlgCreateCst/DlgCreateCst.tsx b/rsconcept/frontend/src/features/rsform/dialogs/DlgCreateCst/DlgCreateCst.tsx index 81a6b77c..63fecc0b 100644 --- a/rsconcept/frontend/src/features/rsform/dialogs/DlgCreateCst/DlgCreateCst.tsx +++ b/rsconcept/frontend/src/features/rsform/dialogs/DlgCreateCst/DlgCreateCst.tsx @@ -1,54 +1,53 @@ 'use client'; -import { useState } from 'react'; +import { FormProvider, useForm, useWatch } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; import { ModalForm } from '@/components/Modal'; -import usePartialUpdate from '@/hooks/usePartialUpdate'; import { useDialogsStore } from '@/stores/dialogs'; +import { errorMsg } from '@/utils/labels'; -import { ICstCreateDTO } from '../../backend/types'; -import { CstType, IRSForm } from '../../models/rsform'; -import { generateAlias } from '../../models/rsformAPI'; +import { ICstCreateDTO, schemaCstCreate } from '../../backend/types'; +import { useCstCreate } from '../../backend/useCstCreate'; +import { IConstituentaMeta, IRSForm } from '../../models/rsform'; +import { validateNewAlias } from '../../models/rsformAPI'; import FormCreateCst from './FormCreateCst'; export interface DlgCreateCstProps { - initial?: ICstCreateDTO; + initial: ICstCreateDTO; schema: IRSForm; - onCreate: (data: ICstCreateDTO) => void; + onCreate: (data: IConstituentaMeta) => void; } function DlgCreateCst() { const { initial, schema, onCreate } = useDialogsStore(state => state.props as DlgCreateCstProps); + const { cstCreate } = useCstCreate(); - const [validated, setValidated] = useState(false); - const [cstData, updateCstData] = usePartialUpdate( - initial || { - cst_type: CstType.BASE, - insert_after: null, - alias: generateAlias(CstType.BASE, schema), - convention: '', - definition_formal: '', - definition_raw: '', - term_raw: '', - term_forms: [] - } - ); + const methods = useForm({ + resolver: zodResolver(schemaCstCreate), + defaultValues: { ...initial } + }); + const alias = useWatch({ control: methods.control, name: 'alias' }); + const cst_type = useWatch({ control: methods.control, name: 'cst_type' }); + const isValid = alias !== initial.alias && validateNewAlias(alias, cst_type, schema); - const handleSubmit = () => { - onCreate(cstData); - return true; - }; + function onSubmit(data: ICstCreateDTO) { + return cstCreate({ itemID: schema.id, data }).then(onCreate); + } return ( void methods.handleSubmit(onSubmit)(event)} + submitInvalidTooltip={errorMsg.aliasInvalid} submitText='Создать' className='cc-column w-[35rem] max-h-[30rem] py-2 px-6' > - + + + ); } diff --git a/rsconcept/frontend/src/features/rsform/dialogs/DlgCreateCst/FormCreateCst.tsx b/rsconcept/frontend/src/features/rsform/dialogs/DlgCreateCst/FormCreateCst.tsx index ccaa5f0a..1a80e2c7 100644 --- a/rsconcept/frontend/src/features/rsform/dialogs/DlgCreateCst/FormCreateCst.tsx +++ b/rsconcept/frontend/src/features/rsform/dialogs/DlgCreateCst/FormCreateCst.tsx @@ -1,6 +1,7 @@ 'use client'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; +import { Controller, useFormContext, useWatch } from 'react-hook-form'; import clsx from 'clsx'; import { BadgeHelp, HelpTopic } from '@/features/help'; @@ -12,48 +13,45 @@ import { ICstCreateDTO } from '../../backend/types'; import RSInput from '../../components/RSInput'; import { SelectCstType } from '../../components/SelectCstType'; import { CstType, IRSForm } from '../../models/rsform'; -import { generateAlias, isBaseSet, isBasicConcept, isFunctional, validateNewAlias } from '../../models/rsformAPI'; +import { generateAlias, isBaseSet, isBasicConcept, isFunctional } from '../../models/rsformAPI'; interface FormCreateCstProps { schema: IRSForm; - state: ICstCreateDTO; - - partialUpdate: React.Dispatch>; - setValidated?: React.Dispatch>; } -function FormCreateCst({ schema, state, partialUpdate, setValidated }: FormCreateCstProps) { +function FormCreateCst({ schema }: FormCreateCstProps) { + const { + setValue, + register, + control, + formState: { errors } + } = useFormContext(); const [forceComment, setForceComment] = useState(false); - const isBasic = isBasicConcept(state.cst_type); - const isElementary = isBaseSet(state.cst_type); - const showConvention = !!state.convention || forceComment || isBasic; - - useEffect(() => { - setForceComment(false); - }, [state.cst_type, partialUpdate, schema]); - - useEffect(() => { - if (setValidated) { - setValidated(validateNewAlias(state.alias, state.cst_type, schema)); - } - }, [state.alias, state.cst_type, schema, setValidated]); + const cst_type = useWatch({ control, name: 'cst_type' }); + const convention = useWatch({ control, name: 'convention' }); + const isBasic = isBasicConcept(cst_type); + const isElementary = isBaseSet(cst_type); + const isFunction = isFunctional(cst_type); + const showConvention = !!convention || forceComment || isBasic; function handleTypeChange(target: CstType) { - return partialUpdate({ cst_type: target, alias: generateAlias(target, schema) }); + setValue('cst_type', target); + setValue('alias', generateAlias(target, schema)); + setForceComment(false); } return ( <>
- + partialUpdate({ alias: event.target.value })} + {...register('alias')} + error={errors.alias} /> partialUpdate({ term_raw: event.target.value })} + {...register('term_raw')} + error={errors.term_raw} /> - {!!state.definition_formal || !isElementary ? ( - partialUpdate({ definition_formal: value })} - schema={schema} - /> - ) : null} + + !!field.value || !isElementary ? ( + + ) : ( + <> + ) + } + /> - {!!state.definition_raw || !isElementary ? ( -