From a1f27bd822e0889ec263d11db50ff56863af1f38 Mon Sep 17 00:00:00 2001 From: Ivan <8611739+IRBorisov@users.noreply.github.com> Date: Wed, 5 Feb 2025 15:55:56 +0300 Subject: [PATCH] F: Rework Constituenta editor form --- rsconcept/frontend/src/backend/rsform/api.ts | 26 ++- .../backend/rsform/useCheckConstituenta.tsx | 2 +- .../EditorConstituenta/FormConstituenta.tsx | 217 +++++++++--------- .../EditorRSExpression/EditorRSExpression.tsx | 22 +- 4 files changed, 135 insertions(+), 132 deletions(-) diff --git a/rsconcept/frontend/src/backend/rsform/api.ts b/rsconcept/frontend/src/backend/rsform/api.ts index 171c466c..001a9929 100644 --- a/rsconcept/frontend/src/backend/rsform/api.ts +++ b/rsconcept/frontend/src/backend/rsform/api.ts @@ -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; /** * Represents data, used in renaming {@link IConstituenta}. diff --git a/rsconcept/frontend/src/backend/rsform/useCheckConstituenta.tsx b/rsconcept/frontend/src/backend/rsform/useCheckConstituenta.tsx index 2deed079..c8dd1b5d 100644 --- a/rsconcept/frontend/src/backend/rsform/useCheckConstituenta.tsx +++ b/rsconcept/frontend/src/backend/rsform/useCheckConstituenta.tsx @@ -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 { diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/FormConstituenta.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/FormConstituenta.tsx index ea166958..f971df7c 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/FormConstituenta.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/FormConstituenta.tsx @@ -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({ resolver: zodResolver(CstUpdateSchema) }); + const [localParse, setLocalParse] = useState(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) { - 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 (
{activeCst ? : null} -
- setTerm(newValue)} + void handleSubmit(onSubmit)(event)} + > + ( + field.onChange(newValue)} + /> + )} /> {activeCst ? (