From ab9f058b0a47ea3d7f7ce88552880c0653c4d85b Mon Sep 17 00:00:00 2001 From: Ivan <8611739+IRBorisov@users.noreply.github.com> Date: Tue, 18 Feb 2025 23:38:33 +0300 Subject: [PATCH] F: Implement schema checks for backend + small fixes --- .../src/features/auth/pages/LoginPage.tsx | 2 - .../src/features/library/backend/api.ts | 21 ++- .../src/features/library/backend/types.ts | 121 ++++++------- .../library/components/SelectAccessPolicy.tsx | 2 +- .../library/components/SelectItemType.tsx | 2 +- .../DlgEditVersions/DlgEditVersions.tsx | 4 +- .../frontend/src/features/library/labels.ts | 50 ++++++ .../src/features/library/models/library.ts | 10 +- .../src/features/oss/backend/OssLoader.ts | 12 +- .../frontend/src/features/oss/backend/api.ts | 13 +- .../src/features/oss/backend/types.ts | 162 ++++++++---------- .../oss/components/PickMultiOperation.tsx | 2 +- .../oss/components/SelectOperation.tsx | 2 +- .../oss/components/TooltipOperation.tsx | 4 +- .../oss/dialogs/DlgChangeInputSchema.tsx | 4 +- .../oss/dialogs/DlgDeleteOperation.tsx | 4 +- .../DlgEditOperation/DlgEditOperation.tsx | 15 +- .../oss/dialogs/DlgRelocateConstituents.tsx | 17 +- .../frontend/src/features/oss/models/oss.ts | 18 +- .../src/features/oss/models/ossAPI.ts | 4 +- .../src/features/oss/models/ossLayout.ts | 3 +- .../EditorOssGraph/NodeContextMenu.tsx | 3 +- .../src/features/rsform/backend/cctext/api.ts | 25 +-- .../features/rsform/backend/cctext/types.ts | 25 +++ .../rsform/backend/cctext/useInflectText.tsx | 3 +- .../src/features/rsform/backend/schemas.ts | 0 .../src/features/rsform/backend/types.ts | 8 + .../rsform/components/ToolbarRSFormCard.tsx | 2 +- .../src/features/rsform/models/rsform.ts | 8 +- .../rsform/pages/RSFormPage/MenuRSTabs.tsx | 2 +- rsconcept/frontend/src/utils/constants.ts | 10 +- rsconcept/frontend/src/utils/labels.ts | 53 +----- 32 files changed, 309 insertions(+), 302 deletions(-) create mode 100644 rsconcept/frontend/src/features/rsform/backend/cctext/types.ts delete mode 100644 rsconcept/frontend/src/features/rsform/backend/schemas.ts diff --git a/rsconcept/frontend/src/features/auth/pages/LoginPage.tsx b/rsconcept/frontend/src/features/auth/pages/LoginPage.tsx index 4b38f292..2bb31a3f 100644 --- a/rsconcept/frontend/src/features/auth/pages/LoginPage.tsx +++ b/rsconcept/frontend/src/features/auth/pages/LoginPage.tsx @@ -27,7 +27,6 @@ function LoginPage() { register, handleSubmit, clearErrors, - resetField, formState: { errors } } = useForm({ resolver: zodResolver(schemaUserLogin), @@ -39,7 +38,6 @@ function LoginPage() { function onSubmit(data: IUserLoginDTO) { return login(data).then(() => { - resetField('password'); if (router.canBack()) { router.back(); } else { diff --git a/rsconcept/frontend/src/features/library/backend/api.ts b/rsconcept/frontend/src/features/library/backend/api.ts index fb9f9709..a55b214e 100644 --- a/rsconcept/frontend/src/features/library/backend/api.ts +++ b/rsconcept/frontend/src/features/library/backend/api.ts @@ -1,6 +1,11 @@ import { queryOptions } from '@tanstack/react-query'; -import { IRSFormDTO } from '@/features/rsform/backend/types'; +import { + IRSFormDTO, + IVersionCreatedResponse, + schemaRSForm, + schemaVersionCreatedResponse +} from '@/features/rsform/backend/types'; import { axiosDelete, axiosGet, axiosPatch, axiosPost } from '@/backend/apiTransport'; import { DELAYS, KEYS } from '@/backend/configuration'; @@ -13,10 +18,12 @@ import { ILibraryItem, IRenameLocationDTO, IUpdateLibraryItemDTO, - IVersionCreatedResponse, IVersionCreateDTO, IVersionInfo, - IVersionUpdateDTO + IVersionUpdateDTO, + schemaLibraryItem, + schemaLibraryItemArray, + schemaVersionInfo } from './types'; export const libraryApi = { @@ -29,6 +36,7 @@ export const libraryApi = { staleTime: DELAYS.staleMedium, queryFn: meta => axiosGet({ + schema: schemaLibraryItemArray, endpoint: isAdmin ? '/api/library/all' : '/api/library/active', options: { signal: meta.signal } }) @@ -39,6 +47,7 @@ export const libraryApi = { staleTime: DELAYS.staleMedium, queryFn: meta => axiosGet({ + schema: schemaLibraryItemArray, endpoint: '/api/library/templates', options: { signal: meta.signal } }) @@ -46,6 +55,7 @@ export const libraryApi = { createItem: (data: ICreateLibraryItemDTO) => axiosPost({ + schema: schemaLibraryItem, endpoint: !data.file ? '/api/library' : '/api/rsforms/create-detailed', request: { data: data, @@ -61,6 +71,7 @@ export const libraryApi = { }), updateItem: (data: IUpdateLibraryItemDTO) => axiosPatch({ + schema: schemaLibraryItem, endpoint: `/api/library/${data.id}`, request: { data: data, @@ -109,6 +120,7 @@ export const libraryApi = { }), cloneItem: (data: ICloneLibraryItemDTO) => axiosPost({ + schema: schemaRSForm, endpoint: `/api/library/${data.id}/clone`, request: { data: data, @@ -126,6 +138,7 @@ export const libraryApi = { versionCreate: ({ itemID, data }: { itemID: number; data: IVersionCreateDTO }) => axiosPost({ + schema: schemaVersionCreatedResponse, endpoint: `/api/library/${itemID}/create-version`, request: { data: data, @@ -134,6 +147,7 @@ export const libraryApi = { }), versionRestore: ({ versionID }: { versionID: number }) => axiosPatch({ + schema: schemaRSForm, endpoint: `/api/versions/${versionID}/restore`, request: { successMessage: infoMsg.versionRestored @@ -141,6 +155,7 @@ export const libraryApi = { }), versionUpdate: (data: { itemID: number; version: IVersionUpdateDTO }) => axiosPatch({ + schema: schemaVersionInfo, endpoint: `/api/versions/${data.version.id}`, request: { data: data.version, diff --git a/rsconcept/frontend/src/features/library/backend/types.ts b/rsconcept/frontend/src/features/library/backend/types.ts index 4bdd89f8..58358a49 100644 --- a/rsconcept/frontend/src/features/library/backend/types.ts +++ b/rsconcept/frontend/src/features/library/backend/types.ts @@ -1,7 +1,5 @@ import { z } from 'zod'; -import { IRSFormDTO } from '@/features/rsform/backend/types'; - import { errorMsg } from '@/utils/labels'; import { validateLocation } from '../models/libraryAPI'; @@ -19,34 +17,15 @@ export enum AccessPolicy { PRIVATE = 'private' } -/** - * Represents library item common data typical for all item types. - */ -export interface ILibraryItem { - id: number; - item_type: LibraryItemType; - title: string; - alias: string; - comment: string; - visible: boolean; - read_only: boolean; - location: string; - access_policy: AccessPolicy; - time_create: string; - time_update: string; - owner: number | null; -} +/** Represents library item common data typical for all item types. */ +export type ILibraryItem = z.infer; -/** - * Represents {@link ILibraryItem} constant data loaded for both OSS and RSForm. - */ +/** Represents {@link ILibraryItem} data loaded for both OSS and RSForm. */ export interface ILibraryItemData extends ILibraryItem { editors: number[]; } -/** - * Represents update data for renaming Location. - */ +/** Represents update data for renaming Location. */ export interface IRenameLocationDTO { target: string; new_location: string; @@ -61,65 +40,55 @@ export type ICloneLibraryItemDTO = z.infer; /** Represents data, used for creating {@link IRSForm}. */ export type ICreateLibraryItemDTO = z.infer; -/** - * Represents update data for editing {@link ILibraryItem}. - */ -export const schemaUpdateLibraryItem = z.object({ - id: z.number(), - item_type: z.nativeEnum(LibraryItemType), - title: z.string().nonempty(errorMsg.requiredField), - alias: z.string().nonempty(errorMsg.requiredField), - comment: z.string(), - visible: z.boolean(), - read_only: z.boolean() -}); - -/** - * Represents update data for editing {@link ILibraryItem}. - */ +/** Represents update data for editing {@link ILibraryItem}. */ export type IUpdateLibraryItemDTO = z.infer; -/** - * Create version metadata in persistent storage. - */ -export const schemaVersionCreate = z.object({ - version: z.string(), - description: z.string(), - items: z.array(z.number()).optional() -}); - -/** - * Create version metadata in persistent storage. - */ +/** Create version metadata in persistent storage. */ export type IVersionCreateDTO = z.infer; -/** - * Represents data response when creating {@link IVersionInfo}. - */ -export interface IVersionCreatedResponse { - version: number; - schema: IRSFormDTO; -} - /** Represents version data, intended to update version metadata in persistent storage. */ export type IVersionUpdateDTO = z.infer; // ======= SCHEMAS ========= -/** Represents data, used for cloning {@link IRSForm}. */ -export const schemaCloneLibraryItem = z.object({ - id: z.number(), + +export const schemaLibraryItem = z.object({ + id: z.coerce.number(), item_type: z.nativeEnum(LibraryItemType), - title: z.string().nonempty(errorMsg.requiredField), - alias: z.string().nonempty(errorMsg.requiredField), + title: z.string(), + alias: z.string().nonempty(), comment: z.string(), visible: z.boolean(), read_only: z.boolean(), - location: z.string().refine(data => validateLocation(data), { message: errorMsg.invalidLocation }), + location: z.string(), access_policy: z.nativeEnum(AccessPolicy), - items: z.array(z.number()).optional() + time_create: z.string(), + time_update: z.string(), + owner: z.coerce.number().nullable() }); +export const schemaLibraryItemArray = z.array(schemaLibraryItem); + +export const schemaCloneLibraryItem = schemaLibraryItem + .pick({ + id: true, + item_type: true, + title: true, + alias: true, + comment: true, + visible: true, + read_only: true, + location: true, + access_policy: true + }) + .extend({ + title: z.string().nonempty(errorMsg.requiredField), + alias: z.string().nonempty(errorMsg.requiredField), + location: z.string().refine(data => validateLocation(data), { message: errorMsg.invalidLocation }), + + items: z.array(z.number()).optional() + }); + export const schemaCreateLibraryItem = z .object({ item_type: z.nativeEnum(LibraryItemType), @@ -143,6 +112,16 @@ export const schemaCreateLibraryItem = z message: errorMsg.requiredField }); +export const schemaUpdateLibraryItem = z.object({ + id: z.number(), + item_type: z.nativeEnum(LibraryItemType), + title: z.string().nonempty(errorMsg.requiredField), + alias: z.string().nonempty(errorMsg.requiredField), + comment: z.string(), + visible: z.boolean(), + read_only: z.boolean() +}); + export const schemaVersionInfo = z.object({ id: z.coerce.number(), version: z.string(), @@ -155,3 +134,9 @@ export const schemaVersionUpdate = z.object({ version: z.string().nonempty(errorMsg.requiredField), description: z.string() }); + +export const schemaVersionCreate = z.object({ + version: z.string(), + description: z.string(), + items: z.array(z.number()).optional() +}); diff --git a/rsconcept/frontend/src/features/library/components/SelectAccessPolicy.tsx b/rsconcept/frontend/src/features/library/components/SelectAccessPolicy.tsx index 04efe285..82b150a1 100644 --- a/rsconcept/frontend/src/features/library/components/SelectAccessPolicy.tsx +++ b/rsconcept/frontend/src/features/library/components/SelectAccessPolicy.tsx @@ -6,9 +6,9 @@ import { Dropdown, DropdownButton, useDropdown } from '@/components/Dropdown'; import { IconPrivate, IconProtected, IconPublic } from '@/components/Icons'; import { CProps } from '@/components/props'; import { prefixes } from '@/utils/constants'; -import { describeAccessPolicy, labelAccessPolicy } from '@/utils/labels'; import { AccessPolicy } from '../backend/types'; +import { describeAccessPolicy, labelAccessPolicy } from '../labels'; interface SelectAccessPolicyProps extends CProps.Styling { value: AccessPolicy; diff --git a/rsconcept/frontend/src/features/library/components/SelectItemType.tsx b/rsconcept/frontend/src/features/library/components/SelectItemType.tsx index c917f66d..770dbe1e 100644 --- a/rsconcept/frontend/src/features/library/components/SelectItemType.tsx +++ b/rsconcept/frontend/src/features/library/components/SelectItemType.tsx @@ -6,9 +6,9 @@ import { Dropdown, DropdownButton, useDropdown } from '@/components/Dropdown'; import { IconOSS, IconRSForm } from '@/components/Icons'; import { CProps } from '@/components/props'; import { prefixes } from '@/utils/constants'; -import { describeLibraryItemType, labelLibraryItemType } from '@/utils/labels'; import { LibraryItemType } from '../backend/types'; +import { describeLibraryItemType, labelLibraryItemType } from '../labels'; interface SelectItemTypeProps extends CProps.Styling { value: LibraryItemType; diff --git a/rsconcept/frontend/src/features/library/dialogs/DlgEditVersions/DlgEditVersions.tsx b/rsconcept/frontend/src/features/library/dialogs/DlgEditVersions/DlgEditVersions.tsx index 716b978c..920db4c4 100644 --- a/rsconcept/frontend/src/features/library/dialogs/DlgEditVersions/DlgEditVersions.tsx +++ b/rsconcept/frontend/src/features/library/dialogs/DlgEditVersions/DlgEditVersions.tsx @@ -94,7 +94,7 @@ function DlgEditVersions() { selected={versionID} /> -
void handleSubmit(onUpdate)(event)}> + void handleSubmit(onUpdate)(event)}> -
+
(); private schemaIDs: number[] = []; private items: ILibraryItem[]; constructor(input: IOperationSchemaDTO, items: ILibraryItem[]) { - this.oss = input; + this.oss = input as unknown as IOperationSchema; this.items = items; } produceOSS(): IOperationSchema { - const result = this.oss as IOperationSchema; + const result = this.oss; this.prepareLookups(); this.createGraph(); this.extractSchemas(); @@ -42,7 +42,7 @@ export class OssLoader { private prepareLookups() { this.oss.items.forEach(operation => { - this.operationByID.set(operation.id, operation as IOperation); + this.operationByID.set(operation.id, operation); this.graph.addNode(operation.id); }); } diff --git a/rsconcept/frontend/src/features/oss/backend/api.ts b/rsconcept/frontend/src/features/oss/backend/api.ts index a045ab63..61044634 100644 --- a/rsconcept/frontend/src/features/oss/backend/api.ts +++ b/rsconcept/frontend/src/features/oss/backend/api.ts @@ -17,7 +17,10 @@ import { IOperationPosition, IOperationSchemaDTO, IOperationUpdateDTO, - ITargetOperation + ITargetOperation, + schemaConstituentaReference, + schemaOperationCreatedResponse, + schemaOperationSchema } from './types'; export const ossApi = { @@ -31,6 +34,7 @@ export const ossApi = { !itemID ? undefined : axiosGet({ + schema: schemaOperationSchema, endpoint: `/api/oss/${itemID}/details`, options: { signal: meta.signal } }) @@ -56,6 +60,7 @@ export const ossApi = { operationCreate: ({ itemID, data }: { itemID: number; data: IOperationCreateDTO }) => axiosPost({ + schema: schemaOperationCreatedResponse, endpoint: `/api/oss/${itemID}/create-operation`, request: { data: data, @@ -64,6 +69,7 @@ export const ossApi = { }), operationDelete: ({ itemID, data }: { itemID: number; data: IOperationDeleteDTO }) => axiosPatch({ + schema: schemaOperationSchema, endpoint: `/api/oss/${itemID}/delete-operation`, request: { data: data, @@ -80,6 +86,7 @@ export const ossApi = { }), inputUpdate: ({ itemID, data }: { itemID: number; data: IInputUpdateDTO }) => axiosPatch({ + schema: schemaOperationSchema, endpoint: `/api/oss/${itemID}/set-input`, request: { data: data, @@ -88,6 +95,7 @@ export const ossApi = { }), operationUpdate: ({ itemID, data }: { itemID: number; data: IOperationUpdateDTO }) => axiosPatch({ + schema: schemaOperationSchema, endpoint: `/api/oss/${itemID}/update-operation`, request: { data: data, @@ -96,6 +104,7 @@ export const ossApi = { }), operationExecute: ({ itemID, data }: { itemID: number; data: ITargetOperation }) => axiosPost({ + schema: schemaOperationSchema, endpoint: `/api/oss/${itemID}/execute-operation`, request: { data: data, @@ -105,6 +114,7 @@ export const ossApi = { relocateConstituents: (data: ICstRelocateDTO) => axiosPost({ + schema: schemaOperationSchema, endpoint: `/api/oss/relocate-constituents`, request: { data: data, @@ -113,6 +123,7 @@ export const ossApi = { }), getPredecessor: (data: ITargetCst) => axiosPost({ + schema: schemaConstituentaReference, endpoint: '/api/oss/get-predecessor', request: { data: data } }) diff --git a/rsconcept/frontend/src/features/oss/backend/types.ts b/rsconcept/frontend/src/features/oss/backend/types.ts index 8e9dcd6d..138964d5 100644 --- a/rsconcept/frontend/src/features/oss/backend/types.ts +++ b/rsconcept/frontend/src/features/oss/backend/types.ts @@ -1,7 +1,9 @@ import { z } from 'zod'; -import { ILibraryItem, ILibraryItemData } from '@/features/library/backend/types'; -import { ICstSubstitute, schemaCstSubstitute } from '@/features/rsform/backend/types'; +import { schemaLibraryItem } from '@/features/library/backend/types'; +import { schemaCstSubstitute } from '@/features/rsform/backend/types'; + +import { errorMsg } from '@/utils/labels'; /** * Represents {@link IOperation} type. @@ -11,61 +13,14 @@ export enum OperationType { SYNTHESIS = 'synthesis' } -/** - * Represents {@link ICstSubstitute} extended data. - */ -export interface ICstSubstituteEx extends ICstSubstitute { - operation: number; - original_alias: string; - original_term: string; - substitution_alias: string; - substitution_term: string; -} +/** Represents {@link ICstSubstitute} extended data. */ +export type ICstSubstituteInfo = z.infer; -/** - * Represents Operation. - */ -export interface IOperation { - id: number; - operation_type: OperationType; - oss: number; +/** Represents {@link IOperation} data from server. */ +export type IOperationDTO = z.infer; - alias: string; - title: string; - comment: string; - - position_x: number; - position_y: number; - - result: number | null; - - is_owned: boolean; - is_consolidation: boolean; // aka 'diamond synthesis' - substitutions: ICstSubstituteEx[]; - arguments: number[]; -} - -/** - * Represents {@link IOperation} Argument. - */ -export interface IArgument { - operation: number; - argument: number; -} - -/** - * Represents {@link IOperation} data from server. - */ -export interface IOperationDTO extends Omit {} - -/** - * Represents backend data for {@link IOperationSchema}. - */ -export interface IOperationSchemaDTO extends ILibraryItemData { - items: IOperationDTO[]; - arguments: IArgument[]; - substitutions: ICstSubstituteEx[]; -} +/** Represents backend data for {@link IOperationSchema}. */ +export type IOperationSchemaDTO = z.infer; /** Represents {@link IOperation} position. */ export type IOperationPosition = z.infer; @@ -73,14 +28,8 @@ export type IOperationPosition = z.infer; /** Represents {@link IOperation} data, used in creation process. */ export type IOperationCreateDTO = z.infer; -/** - * Represents data response when creating {@link IOperation}. - */ -export interface IOperationCreatedResponse { - new_operation: IOperationDTO; - oss: IOperationSchemaDTO; -} - +/** Represents data response when creating {@link IOperation}. */ +export type IOperationCreatedResponse = z.infer; /** * Represents target {@link IOperation}. */ @@ -92,13 +41,8 @@ export interface ITargetOperation { /** Represents {@link IOperation} data, used in destruction process. */ export type IOperationDeleteDTO = z.infer; -/** - * Represents data response when creating {@link IRSForm} for Input {@link IOperation}. - */ -export interface IInputCreatedResponse { - new_schema: ILibraryItem; - oss: IOperationSchemaDTO; -} +/** Represents data response when creating {@link IRSForm} for Input {@link IOperation}. */ +export type IInputCreatedResponse = z.infer; /** Represents {@link IOperation} data, used in setInput process. */ export type IInputUpdateDTO = z.infer; @@ -106,29 +50,49 @@ export type IInputUpdateDTO = z.infer; /** Represents {@link IOperation} data, used in update process. */ export type IOperationUpdateDTO = z.infer; -/** - * Represents data, used relocating {@link IConstituenta}s between {@link ILibraryItem}s. - */ -export const schemaCstRelocate = z.object({ - destination: z.number(), - items: z.array(z.number()).refine(data => data.length > 0) -}); - -/** - * Represents data, used relocating {@link IConstituenta}s between {@link ILibraryItem}s. - */ +/** Represents data, used relocating {@link IConstituenta}s between {@link ILibraryItem}s. */ export type ICstRelocateDTO = z.infer; -/** - * Represents {@link IConstituenta} reference. - */ -export interface IConstituentaReference { - id: number; - schema: number; -} +/** Represents {@link IConstituenta} reference. */ +export type IConstituentaReference = z.infer; // ====== Schemas ====== +export const schemaOperation = z.object({ + id: z.number(), + operation_type: z.nativeEnum(OperationType), + oss: z.number(), + + alias: z.string(), + title: z.string(), + comment: z.string(), + + position_x: z.number(), + position_y: z.number(), + + result: z.number().nullable() +}); + +export const schemaCstSubstituteInfo = schemaCstSubstitute.extend({ + operation: z.number(), + original_alias: z.string(), + original_term: z.string(), + substitution_alias: z.string(), + substitution_term: z.string() +}); + +export const schemaOperationSchema = schemaLibraryItem.extend({ + editors: z.number().array(), + items: z.array(schemaOperation), + arguments: z + .object({ + operation: z.number(), + argument: z.number() + }) + .array(), + substitutions: z.array(schemaCstSubstituteInfo) +}); + export const schemaOperationPosition = z.object({ id: z.number(), position_x: z.number(), @@ -150,6 +114,11 @@ export const schemaOperationCreate = z.object({ create_schema: z.boolean() }); +export const schemaOperationCreatedResponse = z.object({ + new_operation: schemaOperation, + oss: schemaOperationSchema +}); + export const schemaOperationDelete = z.object({ target: z.number(), positions: z.array(schemaOperationPosition), @@ -163,14 +132,29 @@ export const schemaInputUpdate = z.object({ input: z.number().nullable() }); +export const schemaInputCreatedResponse = z.object({ + new_schema: schemaLibraryItem, + oss: schemaOperationSchema +}); + export const schemaOperationUpdate = z.object({ target: z.number(), positions: z.array(schemaOperationPosition), item_data: z.object({ - alias: z.string().nonempty(), + alias: z.string().nonempty(errorMsg.requiredField), title: z.string(), comment: z.string() }), arguments: z.array(z.number()), substitutions: z.array(schemaCstSubstitute) }); + +export const schemaCstRelocate = z.object({ + destination: z.number().nullable(), + items: z.array(z.number()).refine(data => data.length > 0) +}); + +export const schemaConstituentaReference = z.object({ + id: z.number(), + schema: z.number() +}); diff --git a/rsconcept/frontend/src/features/oss/components/PickMultiOperation.tsx b/rsconcept/frontend/src/features/oss/components/PickMultiOperation.tsx index a6218c36..9560eeb5 100644 --- a/rsconcept/frontend/src/features/oss/components/PickMultiOperation.tsx +++ b/rsconcept/frontend/src/features/oss/components/PickMultiOperation.tsx @@ -9,7 +9,7 @@ import { IconMoveDown, IconMoveUp, IconRemove } from '@/components/Icons'; import { CProps } from '@/components/props'; import { NoData } from '@/components/View'; -import { IOperation } from '../backend/types'; +import { IOperation } from '../models/oss'; import SelectOperation from './SelectOperation'; diff --git a/rsconcept/frontend/src/features/oss/components/SelectOperation.tsx b/rsconcept/frontend/src/features/oss/components/SelectOperation.tsx index abf69742..33869c5e 100644 --- a/rsconcept/frontend/src/features/oss/components/SelectOperation.tsx +++ b/rsconcept/frontend/src/features/oss/components/SelectOperation.tsx @@ -5,7 +5,7 @@ import clsx from 'clsx'; import { SelectSingle } from '@/components/Input'; import { CProps } from '@/components/props'; -import { IOperation } from '../backend/types'; +import { IOperation } from '../models/oss'; import { matchOperation } from '../models/ossAPI'; interface SelectOperationProps extends CProps.Styling { diff --git a/rsconcept/frontend/src/features/oss/components/TooltipOperation.tsx b/rsconcept/frontend/src/features/oss/components/TooltipOperation.tsx index 8413f01f..186dc4ab 100644 --- a/rsconcept/frontend/src/features/oss/components/TooltipOperation.tsx +++ b/rsconcept/frontend/src/features/oss/components/TooltipOperation.tsx @@ -6,7 +6,7 @@ import { Tooltip } from '@/components/Container'; import DataTable from '@/components/DataTable'; import { IconPageRight } from '@/components/Icons'; -import { ICstSubstituteEx, OperationType } from '../backend/types'; +import { ICstSubstituteInfo, OperationType } from '../backend/types'; import { labelOperationType } from '../labels'; import { OssNodeInternal } from '../models/ossLayout'; @@ -15,7 +15,7 @@ interface TooltipOperationProps { anchor: string; } -const columnHelper = createColumnHelper(); +const columnHelper = createColumnHelper(); function TooltipOperation({ node, anchor }: TooltipOperationProps) { const columns = [ diff --git a/rsconcept/frontend/src/features/oss/dialogs/DlgChangeInputSchema.tsx b/rsconcept/frontend/src/features/oss/dialogs/DlgChangeInputSchema.tsx index 94d88ae7..7bab46fb 100644 --- a/rsconcept/frontend/src/features/oss/dialogs/DlgChangeInputSchema.tsx +++ b/rsconcept/frontend/src/features/oss/dialogs/DlgChangeInputSchema.tsx @@ -12,9 +12,9 @@ import { Label } from '@/components/Input'; import { ModalForm } from '@/components/Modal'; import { useDialogsStore } from '@/stores/dialogs'; -import { IInputUpdateDTO, IOperation, IOperationPosition, schemaInputUpdate } from '../backend/types'; +import { IInputUpdateDTO, IOperationPosition, schemaInputUpdate } from '../backend/types'; import { useInputUpdate } from '../backend/useInputUpdate'; -import { IOperationSchema } from '../models/oss'; +import { IOperation, IOperationSchema } from '../models/oss'; import { sortItemsForOSS } from '../models/ossAPI'; export interface DlgChangeInputSchemaProps { diff --git a/rsconcept/frontend/src/features/oss/dialogs/DlgDeleteOperation.tsx b/rsconcept/frontend/src/features/oss/dialogs/DlgDeleteOperation.tsx index 1f8e5526..6233d601 100644 --- a/rsconcept/frontend/src/features/oss/dialogs/DlgDeleteOperation.tsx +++ b/rsconcept/frontend/src/features/oss/dialogs/DlgDeleteOperation.tsx @@ -10,9 +10,9 @@ import { Checkbox, TextInput } from '@/components/Input'; import { ModalForm } from '@/components/Modal'; import { useDialogsStore } from '@/stores/dialogs'; -import { IOperation, IOperationDeleteDTO, IOperationPosition, schemaOperationDelete } from '../backend/types'; +import { IOperationDeleteDTO, IOperationPosition, schemaOperationDelete } from '../backend/types'; import { useOperationDelete } from '../backend/useOperationDelete'; -import { IOperationSchema } from '../models/oss'; +import { IOperation, IOperationSchema } from '../models/oss'; export interface DlgDeleteOperationProps { oss: IOperationSchema; diff --git a/rsconcept/frontend/src/features/oss/dialogs/DlgEditOperation/DlgEditOperation.tsx b/rsconcept/frontend/src/features/oss/dialogs/DlgEditOperation/DlgEditOperation.tsx index 31c50a00..7db59565 100644 --- a/rsconcept/frontend/src/features/oss/dialogs/DlgEditOperation/DlgEditOperation.tsx +++ b/rsconcept/frontend/src/features/oss/dialogs/DlgEditOperation/DlgEditOperation.tsx @@ -12,15 +12,9 @@ import { ModalForm } from '@/components/Modal'; import { TabLabel, TabList, TabPanel, Tabs } from '@/components/Tabs'; import { useDialogsStore } from '@/stores/dialogs'; -import { - IOperation, - IOperationPosition, - IOperationUpdateDTO, - OperationType, - schemaOperationUpdate -} from '../../backend/types'; +import { IOperationPosition, IOperationUpdateDTO, OperationType, schemaOperationUpdate } from '../../backend/types'; import { useOperationUpdate } from '../../backend/useOperationUpdate'; -import { IOperationSchema } from '../../models/oss'; +import { IOperation, IOperationSchema } from '../../models/oss'; import TabArguments from './TabArguments'; import TabOperation from './TabOperation'; @@ -45,6 +39,7 @@ function DlgEditOperation() { const methods = useForm({ resolver: zodResolver(schemaOperationUpdate), defaultValues: { + target: target.id, item_data: { alias: target.alias, title: target.alias, @@ -59,7 +54,7 @@ function DlgEditOperation() { } }); const alias = useWatch({ control: methods.control, name: 'item_data.alias' }); - const canSubmit = alias !== ''; + const isValid = alias !== ''; const [activeTab, setActiveTab] = useState(TabID.CARD); @@ -71,7 +66,7 @@ function DlgEditOperation() { void methods.handleSubmit(onSubmit)(event)} className='w-[40rem] px-6 h-[32rem]' helpTopic={HelpTopic.UI_SUBSTITUTIONS} diff --git a/rsconcept/frontend/src/features/oss/dialogs/DlgRelocateConstituents.tsx b/rsconcept/frontend/src/features/oss/dialogs/DlgRelocateConstituents.tsx index 938e78c6..edc2e635 100644 --- a/rsconcept/frontend/src/features/oss/dialogs/DlgRelocateConstituents.tsx +++ b/rsconcept/frontend/src/features/oss/dialogs/DlgRelocateConstituents.tsx @@ -15,10 +15,10 @@ import { Loader } from '@/components/Loader'; import { ModalForm } from '@/components/Modal'; import { useDialogsStore } from '@/stores/dialogs'; -import { ICstRelocateDTO, IOperation, IOperationPosition, schemaCstRelocate } from '../backend/types'; +import { ICstRelocateDTO, IOperationPosition, schemaCstRelocate } from '../backend/types'; import { useRelocateConstituents } from '../backend/useRelocateConstituents'; import { useUpdatePositions } from '../backend/useUpdatePositions'; -import { IOperationSchema } from '../models/oss'; +import { IOperation, IOperationSchema } from '../models/oss'; import { getRelocateCandidates } from '../models/ossAPI'; export interface DlgRelocateConstituentsProps { @@ -37,7 +37,6 @@ function DlgRelocateConstituents() { handleSubmit, control, setValue, - resetField, formState: { isValid } } = useForm({ resolver: zodResolver(schemaCstRelocate), @@ -78,22 +77,22 @@ function DlgRelocateConstituents() { function toggleDirection() { setDirectionUp(prev => !prev); - resetField('destination'); + setValue('destination', null); } function handleSelectSource(newValue: ILibraryItem | undefined) { setSource(newValue); - resetField('destination'); - resetField('items'); + setValue('destination', null); + setValue('items', []); } function handleSelectDestination(newValue: ILibraryItem | undefined) { if (newValue) { setValue('destination', newValue.id); } else { - resetField('destination'); + setValue('destination', null); } - resetField('items'); + setValue('items', []); } function onSubmit(data: ICstRelocateDTO) { @@ -116,7 +115,7 @@ function DlgRelocateConstituents() { void handleSubmit(onSubmit)(event)} className={clsx('w-[40rem] h-[33rem]', 'py-3 px-6')} helpTopic={HelpTopic.UI_RELOCATE_CST} diff --git a/rsconcept/frontend/src/features/oss/models/oss.ts b/rsconcept/frontend/src/features/oss/models/oss.ts index aaca4b80..704ac8f4 100644 --- a/rsconcept/frontend/src/features/oss/models/oss.ts +++ b/rsconcept/frontend/src/features/oss/models/oss.ts @@ -2,11 +2,19 @@ * Module: Schema of Synthesis Operations. */ -import { ILibraryItemData } from '@/features/library/backend/types'; - import { Graph } from '@/models/Graph'; -import { IArgument, ICstSubstituteEx, IOperation } from '../backend/types'; +import { ICstSubstituteInfo, IOperationDTO, IOperationSchemaDTO } from '../backend/types'; + +/** + * Represents Operation. + */ +export interface IOperation extends IOperationDTO { + is_owned: boolean; + is_consolidation: boolean; // aka 'diamond synthesis' + substitutions: ICstSubstituteInfo[]; + arguments: number[]; +} /** * Represents {@link IOperationSchema} statistics. @@ -22,10 +30,8 @@ export interface IOperationSchemaStats { /** * Represents OperationSchema. */ -export interface IOperationSchema extends ILibraryItemData { +export interface IOperationSchema extends IOperationSchemaDTO { items: IOperation[]; - arguments: IArgument[]; - substitutions: ICstSubstituteEx[]; graph: Graph; schemas: number[]; diff --git a/rsconcept/frontend/src/features/oss/models/ossAPI.ts b/rsconcept/frontend/src/features/oss/models/ossAPI.ts index 3ba0f00e..de3780f3 100644 --- a/rsconcept/frontend/src/features/oss/models/ossAPI.ts +++ b/rsconcept/frontend/src/features/oss/models/ossAPI.ts @@ -18,10 +18,10 @@ import { infoMsg } from '@/utils/labels'; import { TextMatcher } from '@/utils/utils'; import { Graph } from '../../../models/Graph'; -import { IOperation, IOperationPosition, OperationType } from '../backend/types'; +import { IOperationPosition, OperationType } from '../backend/types'; import { describeSubstitutionError } from '../labels'; -import { IOperationSchema, SubstitutionErrorType } from './oss'; +import { IOperation, IOperationSchema, SubstitutionErrorType } from './oss'; import { Position2D } from './ossLayout'; /** diff --git a/rsconcept/frontend/src/features/oss/models/ossLayout.ts b/rsconcept/frontend/src/features/oss/models/ossLayout.ts index b58b5447..731244bb 100644 --- a/rsconcept/frontend/src/features/oss/models/ossLayout.ts +++ b/rsconcept/frontend/src/features/oss/models/ossLayout.ts @@ -3,7 +3,8 @@ */ import { Node } from 'reactflow'; -import { IOperation } from '../backend/types'; +import { IOperation } from './oss'; + /** * Represents XY Position. */ diff --git a/rsconcept/frontend/src/features/oss/pages/OssPage/EditorOssGraph/NodeContextMenu.tsx b/rsconcept/frontend/src/features/oss/pages/OssPage/EditorOssGraph/NodeContextMenu.tsx index 58dea338..55ab1b57 100644 --- a/rsconcept/frontend/src/features/oss/pages/OssPage/EditorOssGraph/NodeContextMenu.tsx +++ b/rsconcept/frontend/src/features/oss/pages/OssPage/EditorOssGraph/NodeContextMenu.tsx @@ -16,8 +16,9 @@ import useClickedOutside from '@/hooks/useClickedOutside'; import { PARAMETER } from '@/utils/constants'; import { prepareTooltip } from '@/utils/utils'; -import { IOperation, OperationType } from '../../../backend/types'; +import { OperationType } from '../../../backend/types'; import { useMutatingOss } from '../../../backend/useMutatingOss'; +import { IOperation } from '../../../models/oss'; import { useOssEdit } from '../OssEditContext'; export interface ContextMenuData { diff --git a/rsconcept/frontend/src/features/rsform/backend/cctext/api.ts b/rsconcept/frontend/src/features/rsform/backend/cctext/api.ts index 2439d644..5f34e598 100644 --- a/rsconcept/frontend/src/features/rsform/backend/cctext/api.ts +++ b/rsconcept/frontend/src/features/rsform/backend/cctext/api.ts @@ -1,43 +1,26 @@ import { axiosPost } from '@/backend/apiTransport'; import { KEYS } from '@/backend/configuration'; -/** - * Represents API result for text output. - */ -export interface ITextResult { - result: string; -} - -/** - * Represents wordform data used for backend communication. - */ -export interface IWordFormDTO { - text: string; - grams: string; -} - -/** - * Represents lexeme response containing multiple {@link Wordform}s. - */ -export interface ILexemeResponse { - items: IWordFormDTO[]; -} +import { ILexemeResponse, ITextResult, IWordFormDTO, schemaLexemeResponse, schemaTextResult } from './types'; export const cctextApi = { baseKey: KEYS.cctext, inflectText: (data: IWordFormDTO) => axiosPost({ + schema: schemaTextResult, endpoint: '/api/cctext/inflect', request: { data: data } }), parseText: (data: { text: string }) => axiosPost<{ text: string }, ITextResult>({ + schema: schemaTextResult, endpoint: '/api/cctext/parse', request: { data: data } }), generateLexeme: (data: { text: string }) => axiosPost<{ text: string }, ILexemeResponse>({ + schema: schemaLexemeResponse, endpoint: '/api/cctext/generate-lexeme', request: { data: data } }) diff --git a/rsconcept/frontend/src/features/rsform/backend/cctext/types.ts b/rsconcept/frontend/src/features/rsform/backend/cctext/types.ts new file mode 100644 index 00000000..5c6f1bf6 --- /dev/null +++ b/rsconcept/frontend/src/features/rsform/backend/cctext/types.ts @@ -0,0 +1,25 @@ +import { z } from 'zod'; + +/** Represents API result for text output. */ +export type ITextResult = z.infer; + +/** Represents wordform data used for backend communication. */ +export type IWordFormDTO = z.infer; + +/** Represents lexeme response containing multiple {@link Wordform}s. */ +export type ILexemeResponse = z.infer; + +// ====== Schemas ========= + +export const schemaTextResult = z.object({ + result: z.string() +}); + +export const schemaWordForm = z.object({ + text: z.string(), + grams: z.string() +}); + +export const schemaLexemeResponse = z.object({ + items: z.array(schemaWordForm) +}); diff --git a/rsconcept/frontend/src/features/rsform/backend/cctext/useInflectText.tsx b/rsconcept/frontend/src/features/rsform/backend/cctext/useInflectText.tsx index 94fed04d..77d72a17 100644 --- a/rsconcept/frontend/src/features/rsform/backend/cctext/useInflectText.tsx +++ b/rsconcept/frontend/src/features/rsform/backend/cctext/useInflectText.tsx @@ -1,6 +1,7 @@ import { useMutation } from '@tanstack/react-query'; -import { cctextApi, IWordFormDTO } from './api'; +import { cctextApi } from './api'; +import { IWordFormDTO } from './types'; export const useInflectText = () => { const mutation = useMutation({ diff --git a/rsconcept/frontend/src/features/rsform/backend/schemas.ts b/rsconcept/frontend/src/features/rsform/backend/schemas.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/rsconcept/frontend/src/features/rsform/backend/types.ts b/rsconcept/frontend/src/features/rsform/backend/types.ts index 4a4b0267..fb1e7242 100644 --- a/rsconcept/frontend/src/features/rsform/backend/types.ts +++ b/rsconcept/frontend/src/features/rsform/backend/types.ts @@ -109,6 +109,9 @@ export type IRSErrorDescription = z.infer; /** Represents results of expression parse in RSLang. */ export type IExpressionParseDTO = z.infer; +/** Represents data response when creating {@link IVersionInfo}. */ +export type IVersionCreatedResponse = z.infer; + /** Represents RSLang token types. */ export enum TokenID { // Global, local IDs and literals @@ -322,6 +325,11 @@ export const schemaRSForm = z.object({ oss: z.array(z.object({ id: z.coerce.number(), alias: z.string() })) }); +export const schemaVersionCreatedResponse = z.object({ + version: z.number(), + schema: schemaRSForm +}); + export const schemaCstCreate = schemaConstituentaBasics .pick({ cst_type: true, diff --git a/rsconcept/frontend/src/features/rsform/components/ToolbarRSFormCard.tsx b/rsconcept/frontend/src/features/rsform/components/ToolbarRSFormCard.tsx index 2f71a9d8..bb93ee14 100644 --- a/rsconcept/frontend/src/features/rsform/components/ToolbarRSFormCard.tsx +++ b/rsconcept/frontend/src/features/rsform/components/ToolbarRSFormCard.tsx @@ -62,7 +62,7 @@ export function ToolbarRSFormCard({ controller, onSubmit }: ToolbarRSFormCardPro /> ) : null} } onClick={sharePage} disabled={controller.schema.access_policy !== AccessPolicy.PUBLIC} diff --git a/rsconcept/frontend/src/features/rsform/models/rsform.ts b/rsconcept/frontend/src/features/rsform/models/rsform.ts index 675acd17..bde243c3 100644 --- a/rsconcept/frontend/src/features/rsform/models/rsform.ts +++ b/rsconcept/frontend/src/features/rsform/models/rsform.ts @@ -2,7 +2,8 @@ * Module: Models for formal representation for systems of concepts. */ -import { ILibraryItemReference, ILibraryItemVersioned } from '@/features/library/models/library'; +import { ILibraryItemData, IVersionInfo } from '@/features/library/backend/types'; +import { ILibraryItemReference } from '@/features/library/models/library'; import { Graph } from '@/models/Graph'; @@ -137,7 +138,10 @@ export interface IInheritanceInfo { /** * Represents formal explication for set of concepts. */ -export interface IRSForm extends ILibraryItemVersioned { +export interface IRSForm extends ILibraryItemData { + version?: number; + versions: IVersionInfo[]; + items: IConstituenta[]; inheritance: IInheritanceInfo[]; oss: ILibraryItemReference[]; diff --git a/rsconcept/frontend/src/features/rsform/pages/RSFormPage/MenuRSTabs.tsx b/rsconcept/frontend/src/features/rsform/pages/RSFormPage/MenuRSTabs.tsx index c6e39f46..cae65441 100644 --- a/rsconcept/frontend/src/features/rsform/pages/RSFormPage/MenuRSTabs.tsx +++ b/rsconcept/frontend/src/features/rsform/pages/RSFormPage/MenuRSTabs.tsx @@ -227,7 +227,7 @@ function MenuRSTabs() { } onClick={handleShare} disabled={controller.schema.access_policy !== AccessPolicy.PUBLIC} diff --git a/rsconcept/frontend/src/utils/constants.ts b/rsconcept/frontend/src/utils/constants.ts index 3762202c..5506d012 100644 --- a/rsconcept/frontend/src/utils/constants.ts +++ b/rsconcept/frontend/src/utils/constants.ts @@ -14,6 +14,11 @@ export const PARAMETER = { navigationDuration: 300, // milliseconds navigation duration navigationPopupDelay: 300, // milliseconds delay for navigation popup + fastAnimation: 200, // milliseconds - duration of fast animation + fadeDuration: 300, // milliseconds - duration of fade animation + dropdownDuration: 200, // milliseconds - duration of dropdown animation + moveDuration: 500, // milliseconds - duration of move animation + ossImageWidth: 1280, // pixels - size of OSS image ossImageHeight: 960, // pixels - size of OSS image ossContextMenuWidth: 200, // pixels - width of OSS context menu @@ -23,11 +28,6 @@ export const PARAMETER = { ossDistanceX: 180, // pixels - insert x-distance between node centers ossDistanceY: 100, // pixels - insert y-distance between node centers - fastAnimation: 200, // milliseconds - duration of fast animation - fadeDuration: 300, // milliseconds - duration of fade animation - dropdownDuration: 300, // milliseconds - duration of dropdown animation - moveDuration: 500, // milliseconds - duration of move animation - graphHandleSize: 3, // pixels - size of graph connection handle graphNodeRadius: 20, // pixels - radius of graph node graphNodePadding: 5, // pixels - padding of graph node diff --git a/rsconcept/frontend/src/utils/labels.ts b/rsconcept/frontend/src/utils/labels.ts index ca907363..0dd02f1e 100644 --- a/rsconcept/frontend/src/utils/labels.ts +++ b/rsconcept/frontend/src/utils/labels.ts @@ -4,7 +4,6 @@ * Label is a short text used to represent an entity. * Description is a long description used in tooltips. */ -import { AccessPolicy, LibraryItemType } from '@/features/library/backend/types'; import { UserRole } from '@/features/users/stores/role'; /** @@ -37,55 +36,6 @@ export function describeAccessMode(mode: UserRole): string { } } -/** - * Retrieves label for {@link AccessPolicy}. - */ -export function labelAccessPolicy(policy: AccessPolicy): string { - // prettier-ignore - switch (policy) { - case AccessPolicy.PRIVATE: return 'Личный'; - case AccessPolicy.PROTECTED: return 'Защищенный'; - case AccessPolicy.PUBLIC: return 'Открытый'; - } -} - -/** - * Retrieves description for {@link AccessPolicy}. - */ -export function describeAccessPolicy(policy: AccessPolicy): string { - // prettier-ignore - switch (policy) { - case AccessPolicy.PRIVATE: - return 'Доступ только для владельца'; - case AccessPolicy.PROTECTED: - return 'Доступ для владельца и редакторов'; - case AccessPolicy.PUBLIC: - return 'Открытый доступ'; - } -} - -/** - * Retrieves label for {@link LibraryItemType}. - */ -export function labelLibraryItemType(itemType: LibraryItemType): string { - // prettier-ignore - switch (itemType) { - case LibraryItemType.RSFORM: return 'КС'; - case LibraryItemType.OSS: return 'ОСС'; - } -} - -/** - * Retrieves description for {@link LibraryItemType}. - */ -export function describeLibraryItemType(itemType: LibraryItemType): string { - // prettier-ignore - switch (itemType) { - case LibraryItemType.RSFORM: return 'Концептуальная схема'; - case LibraryItemType.OSS: return 'Операционная схема синтеза'; - } -} - /** * UI info descriptors. */ @@ -150,8 +100,7 @@ export const errorMsg = { */ export const tooltipText = { unsaved: 'Сохраните или отмените изменения', - shareItem: (policy?: AccessPolicy) => - policy === AccessPolicy.PUBLIC ? 'Поделиться схемой' : 'Поделиться можно только
открытой схемой' + shareItem: (isPublic: boolean) => (isPublic ? 'Поделиться схемой' : 'Поделиться можно только
открытой схемой') }; /**