From 6deedb3afda5020eea393ec3a06ca8ee5fa0b727 Mon Sep 17 00:00:00 2001 From: Ivan <8611739+IRBorisov@users.noreply.github.com> Date: Mon, 17 Feb 2025 15:12:15 +0300 Subject: [PATCH] M: Fix minor issues --- rsconcept/frontend/src/app/GlobalLoader.tsx | 3 +- .../library/components/SelectLibraryItem.tsx | 7 ++-- .../oss/components/PickMultiOperation.tsx | 12 +++---- .../oss/components/SelectOperation.tsx | 9 +++-- .../DlgCreateOperation/DlgCreateOperation.tsx | 6 ++-- .../frontend/src/features/oss/models/oss.ts | 17 ++++----- .../src/features/oss/models/ossAPI.ts | 8 ++--- .../pages/OssPage/EditorOssGraph/OssFlow.tsx | 15 ++++---- .../oss/pages/OssPage/OssEditContext.tsx | 36 +++++++++---------- .../rsform/components/SelectConstituenta.tsx | 7 ++-- .../src/features/rsform/models/rsformAPI.ts | 7 ++++ .../rsform/pages/RSFormPage/MenuRSTabs.tsx | 11 ++---- .../features/users/components/InfoUsers.tsx | 3 +- .../features/users/components/SelectUser.tsx | 14 ++++---- .../src/features/users/models/user.ts | 7 +--- rsconcept/frontend/src/utils/constants.ts | 2 +- rsconcept/frontend/tests/app.spec.ts | 2 +- 17 files changed, 76 insertions(+), 90 deletions(-) diff --git a/rsconcept/frontend/src/app/GlobalLoader.tsx b/rsconcept/frontend/src/app/GlobalLoader.tsx index 91b3a04d..5e9a4963 100644 --- a/rsconcept/frontend/src/app/GlobalLoader.tsx +++ b/rsconcept/frontend/src/app/GlobalLoader.tsx @@ -5,13 +5,14 @@ import { useDebounce } from 'use-debounce'; import { Loader } from '@/components/Loader'; import { PARAMETER } from '@/utils/constants'; +// TODO: add animation export function GlobalLoader() { const navigation = useNavigation(); const isLoading = navigation.state === 'loading'; const [loadingDebounced] = useDebounce(isLoading, PARAMETER.navigationPopupDelay); - if (!loadingDebounced || !isLoading) { + if (!loadingDebounced) { return null; } diff --git a/rsconcept/frontend/src/features/library/components/SelectLibraryItem.tsx b/rsconcept/frontend/src/features/library/components/SelectLibraryItem.tsx index b7eec2d4..508c0415 100644 --- a/rsconcept/frontend/src/features/library/components/SelectLibraryItem.tsx +++ b/rsconcept/frontend/src/features/library/components/SelectLibraryItem.tsx @@ -31,9 +31,9 @@ export function SelectLibraryItem({ label: `${cst.alias}: ${cst.title}` })) ?? []; - function filter(option: { value: number | undefined; label: string }, inputValue: string) { - const item = items?.find(item => item.id === option.value); - return !item ? false : matchLibraryItem(item, inputValue); + function filter(option: { value: string | undefined; label: string }, query: string) { + const item = items?.find(item => item.id === Number(option.value)); + return !item ? false : matchLibraryItem(item, query); } return ( @@ -42,7 +42,6 @@ export function SelectLibraryItem({ options={options} value={value ? { value: value.id, label: `${value.alias}: ${value.title}` } : null} onChange={data => onChange(items?.find(cst => cst.id === data?.value))} - // @ts-expect-error: TODO: use type definitions from react-select in filter object filterOption={filter} placeholder={placeholder} {...restProps} diff --git a/rsconcept/frontend/src/features/oss/components/PickMultiOperation.tsx b/rsconcept/frontend/src/features/oss/components/PickMultiOperation.tsx index 8918918c..9560eeb5 100644 --- a/rsconcept/frontend/src/features/oss/components/PickMultiOperation.tsx +++ b/rsconcept/frontend/src/features/oss/components/PickMultiOperation.tsx @@ -9,13 +9,13 @@ import { IconMoveDown, IconMoveUp, IconRemove } from '@/components/Icons'; import { CProps } from '@/components/props'; import { NoData } from '@/components/View'; -import { IOperation, OperationID } from '../models/oss'; +import { IOperation } from '../models/oss'; import SelectOperation from './SelectOperation'; interface PickMultiOperationProps extends CProps.Styling { - value: OperationID[]; - onChange: (newValue: OperationID[]) => void; + value: number[]; + onChange: (newValue: number[]) => void; items: IOperation[]; rows?: number; } @@ -27,7 +27,7 @@ export function PickMultiOperation({ rows, items, value, onChange, className, .. const nonSelectedItems = items.filter(item => !value.includes(item.id)); const [lastSelected, setLastSelected] = useState(undefined); - function handleDelete(operation: OperationID) { + function handleDelete(operation: number) { onChange(value.filter(item => item !== operation)); } @@ -39,7 +39,7 @@ export function PickMultiOperation({ rows, items, value, onChange, className, .. } } - function handleMoveUp(operation: OperationID) { + function handleMoveUp(operation: number) { const index = value.indexOf(operation); if (index > 0) { const newSelected = [...value]; @@ -49,7 +49,7 @@ export function PickMultiOperation({ rows, items, value, onChange, className, .. } } - function handleMoveDown(operation: OperationID) { + function handleMoveDown(operation: number) { const index = value.indexOf(operation); if (index < value.length - 1) { const newSelected = [...value]; diff --git a/rsconcept/frontend/src/features/oss/components/SelectOperation.tsx b/rsconcept/frontend/src/features/oss/components/SelectOperation.tsx index dba0c735..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, OperationID } from '../models/oss'; +import { IOperation } from '../models/oss'; import { matchOperation } from '../models/ossAPI'; interface SelectOperationProps extends CProps.Styling { @@ -31,9 +31,9 @@ function SelectOperation({ label: `${cst.alias}: ${cst.title}` })) ?? []; - function filter(option: { value: OperationID | undefined; label: string }, inputValue: string) { - const operation = items?.find(item => item.id === option.value); - return !operation ? false : matchOperation(operation, inputValue); + function filter(option: { value: string | undefined; label: string }, query: string) { + const operation = items?.find(item => item.id === Number(option.value)); + return !operation ? false : matchOperation(operation, query); } return ( @@ -42,7 +42,6 @@ function SelectOperation({ options={options} value={value ? { value: value.id, label: `${value.alias}: ${value.title}` } : null} onChange={data => onChange(items?.find(cst => cst.id === data?.value))} - // @ts-expect-error: TODO: use type definitions from react-select in filter object filterOption={filter} placeholder={placeholder} {...restProps} diff --git a/rsconcept/frontend/src/features/oss/dialogs/DlgCreateOperation/DlgCreateOperation.tsx b/rsconcept/frontend/src/features/oss/dialogs/DlgCreateOperation/DlgCreateOperation.tsx index a3bd2af9..d745245c 100644 --- a/rsconcept/frontend/src/features/oss/dialogs/DlgCreateOperation/DlgCreateOperation.tsx +++ b/rsconcept/frontend/src/features/oss/dialogs/DlgCreateOperation/DlgCreateOperation.tsx @@ -14,7 +14,7 @@ import { useDialogsStore } from '@/stores/dialogs'; import { IOperationCreateDTO, IOperationPosition, schemaOperationCreate } from '../../backend/types'; import { useOperationCreate } from '../../backend/useOperationCreate'; import { describeOperationType, labelOperationType } from '../../labels'; -import { IOperationSchema, OperationID, OperationType } from '../../models/oss'; +import { IOperationSchema, OperationType } from '../../models/oss'; import { calculateInsertPosition } from '../../models/ossAPI'; import TabInputOperation from './TabInputOperation'; @@ -23,10 +23,10 @@ import TabSynthesisOperation from './TabSynthesisOperation'; export interface DlgCreateOperationProps { oss: IOperationSchema; positions: IOperationPosition[]; - initialInputs: OperationID[]; + initialInputs: number[]; defaultX: number; defaultY: number; - onCreate?: (newID: OperationID) => void; + onCreate?: (newID: number) => void; } export enum TabID { diff --git a/rsconcept/frontend/src/features/oss/models/oss.ts b/rsconcept/frontend/src/features/oss/models/oss.ts index e4fa22c3..a7cd6ef6 100644 --- a/rsconcept/frontend/src/features/oss/models/oss.ts +++ b/rsconcept/frontend/src/features/oss/models/oss.ts @@ -6,11 +6,6 @@ import { ICstSubstitute } from '@/features/rsform'; import { Graph } from '@/models/Graph'; -/** - * Represents {@link IOperation} identifier type. - */ -export type OperationID = number; - /** * Represents {@link IOperation} type. */ @@ -23,7 +18,7 @@ export enum OperationType { * Represents Operation. */ export interface IOperation { - id: OperationID; + id: number; operation_type: OperationType; oss: number; @@ -39,22 +34,22 @@ export interface IOperation { is_owned: boolean; is_consolidation: boolean; // aka 'diamond synthesis' substitutions: ICstSubstituteEx[]; - arguments: OperationID[]; + arguments: number[]; } /** * Represents {@link IOperation} Argument. */ export interface IArgument { - operation: OperationID; - argument: OperationID; + operation: number; + argument: number; } /** * Represents {@link ICstSubstitute} extended data. */ export interface ICstSubstituteEx extends ICstSubstitute { - operation: OperationID; + operation: number; original_alias: string; original_term: string; substitution_alias: string; @@ -83,7 +78,7 @@ export interface IOperationSchema extends ILibraryItemData { graph: Graph; schemas: number[]; stats: IOperationSchemaStats; - operationByID: Map; + operationByID: Map; } /** diff --git a/rsconcept/frontend/src/features/oss/models/ossAPI.ts b/rsconcept/frontend/src/features/oss/models/ossAPI.ts index 372972ac..4c06a03f 100644 --- a/rsconcept/frontend/src/features/oss/models/ossAPI.ts +++ b/rsconcept/frontend/src/features/oss/models/ossAPI.ts @@ -21,7 +21,7 @@ import { Graph } from '../../../models/Graph'; import { IOperationPosition } from '../backend/types'; import { describeSubstitutionError } from '../labels'; -import { IOperation, IOperationSchema, OperationID, OperationType, SubstitutionErrorType } from './oss'; +import { IOperation, IOperationSchema, OperationType, SubstitutionErrorType } from './oss'; import { Position2D } from './ossLayout'; /** @@ -439,8 +439,8 @@ export class SubstitutionValidator { * Filter relocate candidates from gives schema. */ export function getRelocateCandidates( - source: OperationID, - destination: OperationID, + source: number, + destination: number, schema: IRSForm, oss: IOperationSchema ): IConstituenta[] { @@ -482,7 +482,7 @@ export function getRelocateCandidates( export function calculateInsertPosition( oss: IOperationSchema, operationType: OperationType, - argumentsOps: OperationID[], + argumentsOps: number[], positions: IOperationPosition[], defaultPosition: Position2D ): Position2D { diff --git a/rsconcept/frontend/src/features/oss/pages/OssPage/EditorOssGraph/OssFlow.tsx b/rsconcept/frontend/src/features/oss/pages/OssPage/EditorOssGraph/OssFlow.tsx index 899f197b..6e13dbd9 100644 --- a/rsconcept/frontend/src/features/oss/pages/OssPage/EditorOssGraph/OssFlow.tsx +++ b/rsconcept/frontend/src/features/oss/pages/OssPage/EditorOssGraph/OssFlow.tsx @@ -31,7 +31,6 @@ import { useInputCreate } from '../../../backend/useInputCreate'; import { useMutatingOss } from '../../../backend/useMutatingOss'; import { useOperationExecute } from '../../../backend/useOperationExecute'; import { useUpdatePositions } from '../../../backend/useUpdatePositions'; -import { OperationID } from '../../../models/oss'; import { OssNode } from '../../../models/ossLayout'; import { useOSSGraphStore } from '../../../stores/ossGraph'; import { useOssEdit } from '../OssEditContext'; @@ -137,7 +136,7 @@ function OssFlow() { }); } - function handleCreateOperation(inputs: OperationID[]) { + function handleCreateOperation(inputs: number[]) { const positions = getPositions(); const target = flow.project({ x: window.innerWidth / 2, y: window.innerHeight / 2 }); controller.promptCreateOperation({ @@ -149,7 +148,7 @@ function OssFlow() { }); } - function handleDeleteOperation(target: OperationID) { + function handleDeleteOperation(target: number) { if (!controller.canDelete(target)) { return; } @@ -163,7 +162,7 @@ function OssFlow() { handleDeleteOperation(controller.selected[0]); } - function handleInputCreate(target: OperationID) { + function handleInputCreate(target: number) { const operation = controller.schema.operationByID.get(target); if (!operation) { return; @@ -178,15 +177,15 @@ function OssFlow() { }).then(new_schema => router.push(urls.schema(new_schema.id))); } - function handleEditSchema(target: OperationID) { + function handleEditSchema(target: number) { controller.promptEditInput(target, getPositions()); } - function handleEditOperation(target: OperationID) { + function handleEditOperation(target: number) { controller.promptEditOperation(target, getPositions()); } - function handleOperationExecute(target: OperationID) { + function handleOperationExecute(target: number) { void operationExecute({ itemID: controller.schema.id, // data: { target: target, positions: getPositions() } @@ -200,7 +199,7 @@ function OssFlow() { handleOperationExecute(controller.selected[0]); } - function handleRelocateConstituents(target: OperationID) { + function handleRelocateConstituents(target: number) { controller.promptRelocateConstituents(target, getPositions()); } diff --git a/rsconcept/frontend/src/features/oss/pages/OssPage/OssEditContext.tsx b/rsconcept/frontend/src/features/oss/pages/OssPage/OssEditContext.tsx index 957b5e94..65292c88 100644 --- a/rsconcept/frontend/src/features/oss/pages/OssPage/OssEditContext.tsx +++ b/rsconcept/frontend/src/features/oss/pages/OssPage/OssEditContext.tsx @@ -14,7 +14,7 @@ import { promptText } from '@/utils/labels'; import { IOperationPosition } from '../../backend/types'; import { useOssSuspense } from '../../backend/useOSS'; -import { IOperationSchema, OperationID, OperationType } from '../../models/oss'; +import { IOperationSchema, OperationType } from '../../models/oss'; export enum OssTabID { CARD = 0, @@ -24,14 +24,14 @@ export enum OssTabID { export interface ICreateOperationPrompt { defaultX: number; defaultY: number; - inputs: OperationID[]; + inputs: number[]; positions: IOperationPosition[]; - callback: (newID: OperationID) => void; + callback: (newID: number) => void; } export interface IOssEditContext extends ILibraryItemEditor { schema: IOperationSchema; - selected: OperationID[]; + selected: number[]; isOwned: boolean; isMutable: boolean; @@ -41,17 +41,17 @@ export interface IOssEditContext extends ILibraryItemEditor { setShowTooltip: (newValue: boolean) => void; navigateTab: (tab: OssTabID) => void; - navigateOperationSchema: (target: OperationID) => void; + navigateOperationSchema: (target: number) => void; deleteSchema: () => void; - setSelected: React.Dispatch>; + setSelected: React.Dispatch>; - canDelete: (target: OperationID) => boolean; + canDelete: (target: number) => boolean; promptCreateOperation: (props: ICreateOperationPrompt) => void; - promptDeleteOperation: (target: OperationID, positions: IOperationPosition[]) => void; - promptEditInput: (target: OperationID, positions: IOperationPosition[]) => void; - promptEditOperation: (target: OperationID, positions: IOperationPosition[]) => void; - promptRelocateConstituents: (target: OperationID | undefined, positions: IOperationPosition[]) => void; + promptDeleteOperation: (target: number, positions: IOperationPosition[]) => void; + promptEditInput: (target: number, positions: IOperationPosition[]) => void; + promptEditOperation: (target: number, positions: IOperationPosition[]) => void; + promptRelocateConstituents: (target: number | undefined, positions: IOperationPosition[]) => void; } const OssEditContext = createContext(null); @@ -83,7 +83,7 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren UserRole.READER && !schema.read_only; const [showTooltip, setShowTooltip] = useState(true); - const [selected, setSelected] = useState([]); + const [selected, setSelected] = useState([]); const showEditInput = useDialogsStore(state => state.showChangeInputSchema); const showEditOperation = useDialogsStore(state => state.showEditOperation); @@ -112,7 +112,7 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren item.id === option.value); - return !cst ? false : matchConstituenta(cst, inputValue, CstMatchMode.ALL); + function filter(option: { value: string | undefined; label: string }, query: string) { + const cst = items?.find(item => item.id === Number(option.value)); + return !cst ? false : matchConstituenta(cst, query, CstMatchMode.ALL); } return ( @@ -44,7 +44,6 @@ function SelectConstituenta({ options={options} value={value ? { value: value.id, label: `${value.alias}: ${describeConstituentaTerm(value)}` } : null} onChange={data => onChange(items?.find(cst => cst.id === data?.value))} - // @ts-expect-error: TODO: use type definitions from react-select in filter object filterOption={filter} placeholder={placeholder} {...restProps} diff --git a/rsconcept/frontend/src/features/rsform/models/rsformAPI.ts b/rsconcept/frontend/src/features/rsform/models/rsformAPI.ts index e2e114ee..c208fde6 100644 --- a/rsconcept/frontend/src/features/rsform/models/rsformAPI.ts +++ b/rsconcept/frontend/src/features/rsform/models/rsformAPI.ts @@ -212,6 +212,13 @@ export function isFunctional(type: CstType): boolean { } } +/** + * Evaluate if {@link IConstituenta} can be used produce structure. + */ +export function canProduceStructure(cst: IConstituenta): boolean { + return !!cst.parse.typification && cst.cst_type !== CstType.BASE && cst.cst_type !== CstType.CONSTANT; +} + /** * Validate new alias against {@link CstType} and {@link IRSForm}. */ diff --git a/rsconcept/frontend/src/features/rsform/pages/RSFormPage/MenuRSTabs.tsx b/rsconcept/frontend/src/features/rsform/pages/RSFormPage/MenuRSTabs.tsx index ae6fab35..9949498a 100644 --- a/rsconcept/frontend/src/features/rsform/pages/RSFormPage/MenuRSTabs.tsx +++ b/rsconcept/frontend/src/features/rsform/pages/RSFormPage/MenuRSTabs.tsx @@ -46,7 +46,7 @@ import { useMutatingRSForm } from '../../backend/useMutatingRSForm'; import { useProduceStructure } from '../../backend/useProduceStructure'; import { useResetAliases } from '../../backend/useResetAliases'; import { useRestoreOrder } from '../../backend/useRestoreOrder'; -import { CstType } from '../../models/rsform'; +import { canProduceStructure } from '../../models/rsformAPI'; import { useRSEdit } from './RSEditContext'; @@ -75,12 +75,7 @@ function MenuRSTabs() { const editMenu = useDropdown(); const accessMenu = useDropdown(); - // TODO: move into separate function - const canProduceStructure = - !!controller.activeCst && - !!controller.activeCst.parse.typification && - controller.activeCst.cst_type !== CstType.BASE && - controller.activeCst.cst_type !== CstType.CONSTANT; + const structureEnabled = !!controller.activeCst && canProduceStructure(controller.activeCst); function calculateCloneLocation() { const location = controller.schema.location; @@ -344,7 +339,7 @@ function MenuRSTabs() { text='Порождение структуры' titleHtml='Раскрыть структуру типизации
выделенной конституенты' icon={} - disabled={!controller.isContentEditable || !canProduceStructure || isProcessing} + disabled={!controller.isContentEditable || !structureEnabled || isProcessing} onClick={handleProduceStructure} /> void; - filter?: (userID: UserID) => boolean; + value?: number; + onChange: (newValue: number) => void; + filter?: (userID: number) => boolean; placeholder?: string; noBorder?: boolean; @@ -36,9 +35,9 @@ export function SelectUser({ label: getUserLabel(user.id) })); - function filterLabel(option: { value: UserID | undefined; label: string }, inputValue: string) { - const user = items.find(item => item.id === option.value); - return !user ? false : matchUser(user, inputValue); + function filterLabel(option: { value: string | undefined; label: string }, query: string) { + const user = items.find(item => item.id === Number(option.value)); + return !user ? false : matchUser(user, query); } return ( @@ -49,7 +48,6 @@ export function SelectUser({ onChange={data => { if (data?.value !== undefined) onChange(data.value); }} - // @ts-expect-error: TODO: use type definitions from react-select in filter object filterOption={filterLabel} placeholder={placeholder} {...restProps} diff --git a/rsconcept/frontend/src/features/users/models/user.ts b/rsconcept/frontend/src/features/users/models/user.ts index 2c571d48..5b3bae34 100644 --- a/rsconcept/frontend/src/features/users/models/user.ts +++ b/rsconcept/frontend/src/features/users/models/user.ts @@ -2,17 +2,12 @@ * Module: Models for Users. */ -/** - * Represents {@link User} identifier type. - */ -export type UserID = number; - /** * Represents user detailed information. * Some information should only be accessible to authorized users */ export interface IUser { - id: UserID; + id: number; username: string; is_staff: boolean; email: string; diff --git a/rsconcept/frontend/src/utils/constants.ts b/rsconcept/frontend/src/utils/constants.ts index b04dcf1e..3762202c 100644 --- a/rsconcept/frontend/src/utils/constants.ts +++ b/rsconcept/frontend/src/utils/constants.ts @@ -12,7 +12,7 @@ export const PARAMETER = { minimalTimeout: 10, // milliseconds delay for fast updates zoomDuration: 500, // milliseconds animation duration navigationDuration: 300, // milliseconds navigation duration - navigationPopupDelay: 200, // milliseconds delay for navigation popup + navigationPopupDelay: 300, // milliseconds delay for navigation popup ossImageWidth: 1280, // pixels - size of OSS image ossImageHeight: 960, // pixels - size of OSS image diff --git a/rsconcept/frontend/tests/app.spec.ts b/rsconcept/frontend/tests/app.spec.ts index e6a1a906..a29b9162 100644 --- a/rsconcept/frontend/tests/app.spec.ts +++ b/rsconcept/frontend/tests/app.spec.ts @@ -6,8 +6,8 @@ test('should load the homepage and display login button', async ({ page }) => { await authAnonymous(page); await page.goto('/'); + await page.waitForSelector('.h-full > .mr-1'); await expect(page).toHaveTitle('Концепт Портал'); - await expect(page.getByRole('heading', { name: 'Портал' })).toBeVisible(); await page.click('.h-full > .mr-1'); await expect(page.getByText('Логин или email')).toBeVisible();