From 6eeb6ffd143d774b896aa6f6ef39e6edfd559c57 Mon Sep 17 00:00:00 2001 From: Ivan <8611739+IRBorisov@users.noreply.github.com> Date: Thu, 6 Feb 2025 14:09:20 +0300 Subject: [PATCH] R: Move dialogs to form basis --- rsconcept/frontend/src/backend/library/api.ts | 25 ++-- .../src/backend/library/useCloneItem.tsx | 4 +- .../src/backend/library/useVersionCreate.tsx | 6 +- .../frontend/src/components/ui/Modal.tsx | 16 ++- .../src/dialogs/DlgChangeInputSchema.tsx | 7 +- .../src/dialogs/DlgChangeLocation.tsx | 7 +- .../src/dialogs/DlgCloneLibraryItem.tsx | 5 +- .../src/dialogs/DlgCreateCst/DlgCreateCst.tsx | 6 +- .../DlgCreateOperation/DlgCreateOperation.tsx | 1 + .../frontend/src/dialogs/DlgCreateVersion.tsx | 93 +++++++------ .../dialogs/DlgCstTemplate/DlgCstTemplate.tsx | 1 + .../src/dialogs/DlgDeleteCst/DlgDeleteCst.tsx | 3 +- .../src/dialogs/DlgDeleteOperation.tsx | 1 + .../dialogs/DlgEditEditors/DlgEditEditors.tsx | 1 + .../DlgEditOperation/DlgEditOperation.tsx | 1 + .../DlgEditReference/DlgEditReference.tsx | 7 +- .../DlgEditWordForms/DlgEditWordForms.tsx | 1 + .../frontend/src/dialogs/DlgGraphParams.tsx | 7 +- .../DlgInlineSynthesis/DlgInlineSynthesis.tsx | 3 +- .../src/dialogs/DlgRelocateConstituents.tsx | 12 +- .../frontend/src/dialogs/DlgRenameCst.tsx | 7 +- .../frontend/src/dialogs/DlgSubstituteCst.tsx | 6 +- .../frontend/src/dialogs/DlgUploadRSForm.tsx | 16 +-- rsconcept/frontend/src/models/library.ts | 5 +- .../OssPage/EditorOssCard/EditorOssCard.tsx | 2 +- .../pages/OssPage/EditorOssCard/FormOSS.tsx | 122 +++++++----------- .../EditorRSFormCard/EditorRSFormCard.tsx | 2 +- .../EditorRSFormCard/FormRSForm.tsx | 11 +- .../EditorRSFormCard/ToolbarVersioning.tsx | 12 +- .../src/pages/RestorePasswordPage.tsx | 12 +- rsconcept/frontend/src/utils/constants.ts | 3 +- 31 files changed, 216 insertions(+), 189 deletions(-) diff --git a/rsconcept/frontend/src/backend/library/api.ts b/rsconcept/frontend/src/backend/library/api.ts index fbd522c1..5f0dd5bc 100644 --- a/rsconcept/frontend/src/backend/library/api.ts +++ b/rsconcept/frontend/src/backend/library/api.ts @@ -30,7 +30,7 @@ export interface IRenameLocationDTO { /** * Represents data, used for cloning {@link IRSForm}. */ -export interface IRCloneLibraryItemDTO extends Omit { +export interface ICloneLibraryItemDTO extends Omit { items?: ConstituentaID[]; } @@ -90,11 +90,16 @@ export type IUpdateLibraryItemDTO = z.infer; /** * Create version metadata in persistent storage. */ -export interface IVersionCreateDTO { - version: string; - description: string; - items?: ConstituentaID[]; -} +export const CreateVersionSchema = z.object({ + version: z.string(), + description: z.string(), + items: z.array(z.number()).optional() +}); + +/** + * Create version metadata in persistent storage. + */ +export type IVersionCreateDTO = z.infer; /** * Represents data response when creating {@link IVersionInfo}. @@ -197,8 +202,8 @@ export const libraryApi = { successMessage: information.itemDestroyed } }), - cloneItem: (data: IRCloneLibraryItemDTO) => - axiosPost({ + cloneItem: (data: ICloneLibraryItemDTO) => + axiosPost({ endpoint: `/api/library/${data.id}/clone`, request: { data: data, @@ -214,8 +219,8 @@ export const libraryApi = { } }), - versionCreate: ({ itemID, data }: { itemID: LibraryItemID; data: IVersionData }) => - axiosPost({ + versionCreate: ({ itemID, data }: { itemID: LibraryItemID; data: IVersionCreateDTO }) => + axiosPost({ endpoint: `/api/library/${itemID}/create-version`, request: { data: data, diff --git a/rsconcept/frontend/src/backend/library/useCloneItem.tsx b/rsconcept/frontend/src/backend/library/useCloneItem.tsx index bc90698b..28ce52ba 100644 --- a/rsconcept/frontend/src/backend/library/useCloneItem.tsx +++ b/rsconcept/frontend/src/backend/library/useCloneItem.tsx @@ -3,7 +3,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { DataCallback } from '@/backend/apiTransport'; import { IRSFormDTO } from '../rsform/api'; -import { IRCloneLibraryItemDTO, libraryApi } from './api'; +import { ICloneLibraryItemDTO, libraryApi } from './api'; export const useCloneItem = () => { const client = useQueryClient(); @@ -14,7 +14,7 @@ export const useCloneItem = () => { }); return { cloneItem: ( - data: IRCloneLibraryItemDTO, // + data: ICloneLibraryItemDTO, // onSuccess?: DataCallback ) => mutation.mutate(data, { onSuccess }) }; diff --git a/rsconcept/frontend/src/backend/library/useVersionCreate.tsx b/rsconcept/frontend/src/backend/library/useVersionCreate.tsx index 6aad2c5f..a7800b6c 100644 --- a/rsconcept/frontend/src/backend/library/useVersionCreate.tsx +++ b/rsconcept/frontend/src/backend/library/useVersionCreate.tsx @@ -3,9 +3,9 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { DataCallback } from '@/backend/apiTransport'; import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp'; import { rsformsApi } from '@/backend/rsform/api'; -import { IVersionData, LibraryItemID, VersionID } from '@/models/library'; +import { LibraryItemID, VersionID } from '@/models/library'; -import { libraryApi } from './api'; +import { IVersionCreateDTO, libraryApi } from './api'; export const useVersionCreate = () => { const client = useQueryClient(); @@ -22,7 +22,7 @@ export const useVersionCreate = () => { versionCreate: ( data: { itemID: LibraryItemID; // - data: IVersionData; + data: IVersionCreateDTO; }, onSuccess?: DataCallback ) => mutation.mutate(data, { onSuccess: response => onSuccess?.(response.version) }) diff --git a/rsconcept/frontend/src/components/ui/Modal.tsx b/rsconcept/frontend/src/components/ui/Modal.tsx index d1637f27..74181bb7 100644 --- a/rsconcept/frontend/src/components/ui/Modal.tsx +++ b/rsconcept/frontend/src/components/ui/Modal.tsx @@ -33,11 +33,14 @@ export interface ModalProps extends CProps.Styling { /** Indicates that the modal window should be scrollable. */ overflowVisible?: boolean; + /** ID of the form to be submitted. */ + formID?: string; + /** Callback to be called before submit. */ beforeSubmit?: () => boolean; /** Callback to be called after submit. */ - onSubmit?: () => void; + onSubmit?: () => boolean; /** Callback to be called after cancel. */ onCancel?: () => void; @@ -64,6 +67,7 @@ function Modal({ overflowVisible, beforeSubmit, + formID, onSubmit, onCancel, className, @@ -84,7 +88,15 @@ function Modal({ if (beforeSubmit && !beforeSubmit()) { return; } - onSubmit?.(); + if (onSubmit && !onSubmit()) { + return; + } + if (formID) { + const element = document.getElementById(formID) as HTMLFormElement; + if (element) { + element.requestSubmit(); + } + } hideDialog(); }; diff --git a/rsconcept/frontend/src/dialogs/DlgChangeInputSchema.tsx b/rsconcept/frontend/src/dialogs/DlgChangeInputSchema.tsx index 553d8b3a..f2240c06 100644 --- a/rsconcept/frontend/src/dialogs/DlgChangeInputSchema.tsx +++ b/rsconcept/frontend/src/dialogs/DlgChangeInputSchema.tsx @@ -35,13 +35,18 @@ function DlgChangeInputSchema() { setSelected(newValue); } + function handleSubmit() { + onSubmit(target.id, selected); + return true; + } + return ( onSubmit(target.id, selected)} + onSubmit={handleSubmit} className={clsx('w-[35rem]', 'pb-3 px-6 cc-column')} >
diff --git a/rsconcept/frontend/src/dialogs/DlgChangeLocation.tsx b/rsconcept/frontend/src/dialogs/DlgChangeLocation.tsx index 5393be49..7b5a530b 100644 --- a/rsconcept/frontend/src/dialogs/DlgChangeLocation.tsx +++ b/rsconcept/frontend/src/dialogs/DlgChangeLocation.tsx @@ -33,6 +33,11 @@ function DlgChangeLocation() { setBody(newValue.length > 3 ? newValue.substring(3) : ''); } + function handleSubmit() { + onChangeLocation(location); + return true; + } + return ( onChangeLocation(location)} + onSubmit={handleSubmit} className={clsx('w-[35rem]', 'pb-3 px-6 flex gap-3 h-[9rem]')} >
diff --git a/rsconcept/frontend/src/dialogs/DlgCloneLibraryItem.tsx b/rsconcept/frontend/src/dialogs/DlgCloneLibraryItem.tsx index ae415000..886f8c17 100644 --- a/rsconcept/frontend/src/dialogs/DlgCloneLibraryItem.tsx +++ b/rsconcept/frontend/src/dialogs/DlgCloneLibraryItem.tsx @@ -6,7 +6,7 @@ import { useState } from 'react'; import { useConceptNavigation } from '@/app/Navigation/NavigationContext'; import { urls } from '@/app/urls'; import { useAuthSuspense } from '@/backend/auth/useAuth'; -import { IRCloneLibraryItemDTO } from '@/backend/library/api'; +import { ICloneLibraryItemDTO } from '@/backend/library/api'; import { useCloneItem } from '@/backend/library/useCloneItem'; import { VisibilityIcon } from '@/components/DomainIcons'; import SelectAccessPolicy from '@/components/select/SelectAccessPolicy'; @@ -59,7 +59,7 @@ function DlgCloneLibraryItem() { } function handleSubmit() { - const data: IRCloneLibraryItemDTO = { + const data: ICloneLibraryItemDTO = { id: base.id, item_type: base.item_type, title: title, @@ -74,6 +74,7 @@ function DlgCloneLibraryItem() { data.items = selected; } cloneItem(data, newSchema => router.push(urls.schema(newSchema.id))); + return true; } return ( diff --git a/rsconcept/frontend/src/dialogs/DlgCreateCst/DlgCreateCst.tsx b/rsconcept/frontend/src/dialogs/DlgCreateCst/DlgCreateCst.tsx index 39c311e2..9770975a 100644 --- a/rsconcept/frontend/src/dialogs/DlgCreateCst/DlgCreateCst.tsx +++ b/rsconcept/frontend/src/dialogs/DlgCreateCst/DlgCreateCst.tsx @@ -19,6 +19,7 @@ export interface DlgCreateCstProps { function DlgCreateCst() { const { initial, schema, onCreate } = useDialogsStore(state => state.props as DlgCreateCstProps); + const [validated, setValidated] = useState(false); const [cstData, updateCstData] = usePartialUpdate( initial || { @@ -33,7 +34,10 @@ function DlgCreateCst() { } ); - const handleSubmit = () => onCreate(cstData); + const handleSubmit = () => { + onCreate(cstData); + return true; + }; return ( 0 ? inputs : undefined, create_schema: createSchema }); + return true; }; function handleSelectTab(newTab: TabID, last: TabID) { diff --git a/rsconcept/frontend/src/dialogs/DlgCreateVersion.tsx b/rsconcept/frontend/src/dialogs/DlgCreateVersion.tsx index fc5c9787..021c08a6 100644 --- a/rsconcept/frontend/src/dialogs/DlgCreateVersion.tsx +++ b/rsconcept/frontend/src/dialogs/DlgCreateVersion.tsx @@ -1,71 +1,78 @@ 'use client'; +import { zodResolver } from '@hookform/resolvers/zod'; import clsx from 'clsx'; -import { useState } from 'react'; +import { Controller, useForm, useWatch } from 'react-hook-form'; -import { IVersionCreateDTO } from '@/backend/library/api'; +import { CreateVersionSchema, IVersionCreateDTO } from '@/backend/library/api'; +import { useVersionCreate } from '@/backend/library/useVersionCreate'; import Checkbox from '@/components/ui/Checkbox'; import Modal from '@/components/ui/Modal'; import TextArea from '@/components/ui/TextArea'; import TextInput from '@/components/ui/TextInput'; -import { IVersionInfo } from '@/models/library'; +import { IVersionInfo, LibraryItemID, VersionID } from '@/models/library'; import { nextVersion } from '@/models/libraryAPI'; import { ConstituentaID } from '@/models/rsform'; import { useDialogsStore } from '@/stores/dialogs'; +import { globals } from '@/utils/constants'; export interface DlgCreateVersionProps { + itemID: LibraryItemID; versions: IVersionInfo[]; - onCreate: (data: IVersionCreateDTO) => void; + onCreate: (newVersion: VersionID) => void; selected: ConstituentaID[]; totalCount: number; } function DlgCreateVersion() { - const { versions, selected, totalCount, onCreate } = useDialogsStore(state => state.props as DlgCreateVersionProps); - const [version, setVersion] = useState(versions.length > 0 ? nextVersion(versions[0].version) : '1.0.0'); - const [description, setDescription] = useState(''); - const [onlySelected, setOnlySelected] = useState(false); + const { + itemID, // + versions, + selected, + totalCount, + onCreate + } = useDialogsStore(state => state.props as DlgCreateVersionProps); + const { versionCreate } = useVersionCreate(); + const { register, handleSubmit, control } = useForm({ + resolver: zodResolver(CreateVersionSchema), + defaultValues: { + version: versions.length > 0 ? nextVersion(versions[0].version) : '1.0.0', + description: '', + items: undefined + } + }); + const version = useWatch({ control, name: 'version' }); const canSubmit = !versions.find(ver => ver.version === version); - function handleSubmit() { - onCreate({ - version: version, - description: description, - items: onlySelected ? selected : undefined - }); + function onSubmit(data: IVersionCreateDTO) { + versionCreate({ itemID, data }, onCreate); } return ( - - setVersion(event.target.value)} - /> -