mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
F: Rework create operation dialog
Some checks are pending
Frontend CI / build (22.x) (push) Waiting to run
Some checks are pending
Frontend CI / build (22.x) (push) Waiting to run
This commit is contained in:
parent
c5b73612f3
commit
f31acfc817
|
@ -15,7 +15,7 @@ import { Label, TextArea, TextInput } from '@/components/Input';
|
||||||
import { useAuthSuspense } from '@/features/auth/backend/useAuth';
|
import { useAuthSuspense } from '@/features/auth/backend/useAuth';
|
||||||
import { EXTEOR_TRS_FILE } from '@/utils/constants';
|
import { EXTEOR_TRS_FILE } from '@/utils/constants';
|
||||||
|
|
||||||
import { schemaCreateLibraryItem, ICreateLibraryItemDTO } from '../../backend/api';
|
import { ICreateLibraryItemDTO, schemaCreateLibraryItem } from '../../backend/api';
|
||||||
import { useCreateItem } from '../../backend/useCreateItem';
|
import { useCreateItem } from '../../backend/useCreateItem';
|
||||||
import SelectAccessPolicy from '../../components/SelectAccessPolicy';
|
import SelectAccessPolicy from '../../components/SelectAccessPolicy';
|
||||||
import SelectItemType from '../../components/SelectItemType';
|
import SelectItemType from '../../components/SelectItemType';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { queryOptions } from '@tanstack/react-query';
|
import { queryOptions } from '@tanstack/react-query';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { axiosDelete, axiosGet, axiosPatch, axiosPost } from '@/backend/apiTransport';
|
import { axiosGet, axiosPatch, axiosPost } from '@/backend/apiTransport';
|
||||||
import { DELAYS } from '@/backend/configuration';
|
import { DELAYS } from '@/backend/configuration';
|
||||||
import { ILibraryItem, ILibraryItemData, LibraryItemID } from '@/features/library/models/library';
|
import { ILibraryItem, ILibraryItemData, LibraryItemID } from '@/features/library/models/library';
|
||||||
import {
|
import {
|
||||||
|
@ -46,20 +46,25 @@ export type IOperationPosition = z.infer<typeof schemaOperationPosition>;
|
||||||
/**
|
/**
|
||||||
* Represents {@link IOperation} data, used in creation process.
|
* Represents {@link IOperation} data, used in creation process.
|
||||||
*/
|
*/
|
||||||
export interface IOperationCreateDTO {
|
export const schemaOperationCreate = z.object({
|
||||||
positions: IOperationPosition[];
|
positions: z.array(schemaOperationPosition),
|
||||||
item_data: {
|
item_data: z.object({
|
||||||
alias: string;
|
alias: z.string().nonempty(),
|
||||||
operation_type: OperationType;
|
operation_type: z.nativeEnum(OperationType),
|
||||||
title: string;
|
title: z.string(),
|
||||||
comment: string;
|
comment: z.string(),
|
||||||
position_x: number;
|
position_x: z.number(),
|
||||||
position_y: number;
|
position_y: z.number(),
|
||||||
result: LibraryItemID | null;
|
result: z.number().nullable()
|
||||||
};
|
}),
|
||||||
arguments: OperationID[] | undefined;
|
arguments: z.array(z.number()),
|
||||||
create_schema: boolean;
|
create_schema: z.boolean()
|
||||||
}
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents {@link IOperation} data, used in creation process.
|
||||||
|
*/
|
||||||
|
export type IOperationCreateDTO = z.infer<typeof schemaOperationCreate>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents data response when creating {@link IOperation}.
|
* Represents data response when creating {@link IOperation}.
|
||||||
|
@ -183,7 +188,7 @@ export const ossApi = {
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
operationDelete: ({ itemID, data }: { itemID: LibraryItemID; data: IOperationDeleteDTO }) =>
|
operationDelete: ({ itemID, data }: { itemID: LibraryItemID; data: IOperationDeleteDTO }) =>
|
||||||
axiosDelete<IOperationDeleteDTO, IOperationSchemaDTO>({
|
axiosPatch<IOperationDeleteDTO, IOperationSchemaDTO>({
|
||||||
endpoint: `/api/oss/${itemID}/delete-operation`,
|
endpoint: `/api/oss/${itemID}/delete-operation`,
|
||||||
request: {
|
request: {
|
||||||
data: data,
|
data: data,
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { DataCallback } from '@/backend/apiTransport';
|
|
||||||
import { useUpdateTimestamp } from '@/features/library/backend/useUpdateTimestamp';
|
import { useUpdateTimestamp } from '@/features/library/backend/useUpdateTimestamp';
|
||||||
import { LibraryItemID } from '@/features/library/models/library';
|
import { LibraryItemID } from '@/features/library/models/library';
|
||||||
|
|
||||||
import { IOperationCreateDTO, IOperationDTO, ossApi } from './api';
|
import { IOperationCreateDTO, ossApi } from './api';
|
||||||
|
|
||||||
export const useOperationCreate = () => {
|
export const useOperationCreate = () => {
|
||||||
const client = useQueryClient();
|
const client = useQueryClient();
|
||||||
|
@ -12,18 +11,12 @@ export const useOperationCreate = () => {
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [ossApi.baseKey, 'operation-create'],
|
mutationKey: [ossApi.baseKey, 'operation-create'],
|
||||||
mutationFn: ossApi.operationCreate,
|
mutationFn: ossApi.operationCreate,
|
||||||
onSuccess: data => {
|
onSuccess: response => {
|
||||||
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.oss.id }).queryKey, data.oss);
|
client.setQueryData(ossApi.getOssQueryOptions({ itemID: response.oss.id }).queryKey, response.oss);
|
||||||
updateTimestamp(data.oss.id);
|
updateTimestamp(response.oss.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
operationCreate: (
|
operationCreate: (data: { itemID: LibraryItemID; data: IOperationCreateDTO }) => mutation.mutateAsync(data)
|
||||||
data: {
|
|
||||||
itemID: LibraryItemID; //
|
|
||||||
data: IOperationCreateDTO;
|
|
||||||
},
|
|
||||||
onSuccess?: DataCallback<IOperationDTO>
|
|
||||||
) => mutation.mutate(data, { onSuccess: response => onSuccess?.(response.new_operation) })
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,8 +14,7 @@ import SelectOperation from './SelectOperation';
|
||||||
|
|
||||||
interface PickMultiOperationProps extends CProps.Styling {
|
interface PickMultiOperationProps extends CProps.Styling {
|
||||||
value: OperationID[];
|
value: OperationID[];
|
||||||
onChange: React.Dispatch<React.SetStateAction<OperationID[]>>;
|
onChange: (newValue: OperationID[]) => void;
|
||||||
|
|
||||||
items: IOperation[];
|
items: IOperation[];
|
||||||
rows?: number;
|
rows?: number;
|
||||||
}
|
}
|
||||||
|
@ -28,13 +27,13 @@ function PickMultiOperation({ rows, items, value, onChange, className, ...restPr
|
||||||
const [lastSelected, setLastSelected] = useState<IOperation | undefined>(undefined);
|
const [lastSelected, setLastSelected] = useState<IOperation | undefined>(undefined);
|
||||||
|
|
||||||
function handleDelete(operation: OperationID) {
|
function handleDelete(operation: OperationID) {
|
||||||
onChange(prev => prev.filter(item => item !== operation));
|
onChange(value.filter(item => item !== operation));
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSelect(operation?: IOperation) {
|
function handleSelect(operation?: IOperation) {
|
||||||
if (operation) {
|
if (operation) {
|
||||||
setLastSelected(operation);
|
setLastSelected(operation);
|
||||||
onChange(prev => [...prev, operation.id]);
|
onChange([...value, operation.id]);
|
||||||
setTimeout(() => setLastSelected(undefined), 1000);
|
setTimeout(() => setLastSelected(undefined), 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,24 +41,20 @@ function PickMultiOperation({ rows, items, value, onChange, className, ...restPr
|
||||||
function handleMoveUp(operation: OperationID) {
|
function handleMoveUp(operation: OperationID) {
|
||||||
const index = value.indexOf(operation);
|
const index = value.indexOf(operation);
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
onChange(prev => {
|
const newSelected = [...value];
|
||||||
const newSelected = [...prev];
|
|
||||||
newSelected[index] = newSelected[index - 1];
|
newSelected[index] = newSelected[index - 1];
|
||||||
newSelected[index - 1] = operation;
|
newSelected[index - 1] = operation;
|
||||||
return newSelected;
|
onChange(newSelected);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMoveDown(operation: OperationID) {
|
function handleMoveDown(operation: OperationID) {
|
||||||
const index = value.indexOf(operation);
|
const index = value.indexOf(operation);
|
||||||
if (index < value.length - 1) {
|
if (index < value.length - 1) {
|
||||||
onChange(prev => {
|
const newSelected = [...value];
|
||||||
const newSelected = [...prev];
|
|
||||||
newSelected[index] = newSelected[index + 1];
|
newSelected[index] = newSelected[index + 1];
|
||||||
newSelected[index + 1] = operation;
|
newSelected[index + 1] = operation;
|
||||||
return newSelected;
|
onChange(newSelected);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,30 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useEffect, useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import { FormProvider, useForm, useWatch } from 'react-hook-form';
|
||||||
|
|
||||||
import { ModalForm } from '@/components/Modal';
|
import { ModalForm } from '@/components/Modal';
|
||||||
import { TabLabel, TabList, TabPanel, Tabs } from '@/components/Tabs';
|
import { TabLabel, TabList, TabPanel, Tabs } from '@/components/Tabs';
|
||||||
import { HelpTopic } from '@/features/help/models/helpTopic';
|
import { HelpTopic } from '@/features/help/models/helpTopic';
|
||||||
import { useLibrary } from '@/features/library/backend/useLibrary';
|
|
||||||
import { LibraryItemID } from '@/features/library/models/library';
|
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { describeOperationType, labelOperationType } from '@/utils/labels';
|
import { describeOperationType, labelOperationType } from '@/utils/labels';
|
||||||
|
|
||||||
import { IOperationCreateDTO } from '../../backend/api';
|
import { IOperationCreateDTO, IOperationPosition, schemaOperationCreate } from '../../backend/api';
|
||||||
|
import { useOperationCreate } from '../../backend/useOperationCreate';
|
||||||
import { IOperationSchema, OperationID, OperationType } from '../../models/oss';
|
import { IOperationSchema, OperationID, OperationType } from '../../models/oss';
|
||||||
|
import { calculateInsertPosition } from '../../models/ossAPI';
|
||||||
import TabInputOperation from './TabInputOperation';
|
import TabInputOperation from './TabInputOperation';
|
||||||
import TabSynthesisOperation from './TabSynthesisOperation';
|
import TabSynthesisOperation from './TabSynthesisOperation';
|
||||||
|
|
||||||
export interface DlgCreateOperationProps {
|
export interface DlgCreateOperationProps {
|
||||||
oss: IOperationSchema;
|
oss: IOperationSchema;
|
||||||
onCreate: (data: IOperationCreateDTO) => void;
|
positions: IOperationPosition[];
|
||||||
initialInputs: OperationID[];
|
initialInputs: OperationID[];
|
||||||
|
defaultX: number;
|
||||||
|
defaultY: number;
|
||||||
|
onCreate?: (newID: OperationID) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum TabID {
|
export enum TabID {
|
||||||
|
@ -28,70 +33,57 @@ export enum TabID {
|
||||||
}
|
}
|
||||||
|
|
||||||
function DlgCreateOperation() {
|
function DlgCreateOperation() {
|
||||||
const { items: libraryItems } = useLibrary();
|
const { operationCreate } = useOperationCreate();
|
||||||
|
|
||||||
const { oss, onCreate, initialInputs } = useDialogsStore(state => state.props as DlgCreateOperationProps);
|
const { oss, positions, initialInputs, onCreate, defaultX, defaultY } = useDialogsStore(
|
||||||
const [activeTab, setActiveTab] = useState(initialInputs.length > 0 ? TabID.SYNTHESIS : TabID.INPUT);
|
state => state.props as DlgCreateOperationProps
|
||||||
|
);
|
||||||
|
|
||||||
const [alias, setAlias] = useState('');
|
const methods = useForm<IOperationCreateDTO>({
|
||||||
const [title, setTitle] = useState('');
|
resolver: zodResolver(schemaOperationCreate),
|
||||||
const [comment, setComment] = useState('');
|
defaultValues: {
|
||||||
const [inputs, setInputs] = useState<OperationID[]>(initialInputs);
|
|
||||||
const [attachedID, setAttachedID] = useState<LibraryItemID | undefined>(undefined);
|
|
||||||
const [createSchema, setCreateSchema] = useState(false);
|
|
||||||
|
|
||||||
const isValid = (() => {
|
|
||||||
if (alias === '') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (activeTab === TabID.SYNTHESIS && inputs.length === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (activeTab === TabID.INPUT && !attachedID) {
|
|
||||||
if (oss.items.some(operation => operation.alias === alias)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
})();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (attachedID) {
|
|
||||||
const schema = libraryItems.find(value => value.id === attachedID);
|
|
||||||
if (schema) {
|
|
||||||
setAlias(schema.alias);
|
|
||||||
setTitle(schema.title);
|
|
||||||
setComment(schema.comment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [attachedID, libraryItems]);
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
onCreate({
|
|
||||||
item_data: {
|
item_data: {
|
||||||
position_x: 0,
|
operation_type: initialInputs.length === 0 ? OperationType.INPUT : OperationType.SYNTHESIS,
|
||||||
position_y: 0,
|
result: null,
|
||||||
alias: alias,
|
position_x: defaultX,
|
||||||
title: title,
|
position_y: defaultY,
|
||||||
comment: comment,
|
alias: '',
|
||||||
operation_type: activeTab === TabID.INPUT ? OperationType.INPUT : OperationType.SYNTHESIS,
|
title: '',
|
||||||
result: activeTab === TabID.INPUT ? attachedID ?? null : null
|
comment: ''
|
||||||
},
|
},
|
||||||
positions: [],
|
arguments: initialInputs,
|
||||||
arguments: activeTab === TabID.INPUT ? undefined : inputs.length > 0 ? inputs : undefined,
|
create_schema: false,
|
||||||
create_schema: createSchema
|
positions: positions
|
||||||
|
},
|
||||||
|
mode: 'onChange'
|
||||||
});
|
});
|
||||||
return true;
|
const alias = useWatch({ control: methods.control, name: 'item_data.alias' });
|
||||||
};
|
const [activeTab, setActiveTab] = useState(initialInputs.length === 0 ? TabID.INPUT : TabID.SYNTHESIS);
|
||||||
|
const isValid = !!alias && !oss.items.some(operation => operation.alias === alias);
|
||||||
|
|
||||||
|
function onSubmit(data: IOperationCreateDTO) {
|
||||||
|
const target = calculateInsertPosition(oss, data.item_data.operation_type, data.arguments, positions, {
|
||||||
|
x: defaultX,
|
||||||
|
y: defaultY
|
||||||
|
});
|
||||||
|
data.item_data.position_x = target.x;
|
||||||
|
data.item_data.position_y = target.y;
|
||||||
|
operationCreate({ itemID: oss.id, data: data })
|
||||||
|
.then(response => onCreate?.(response.new_operation.id))
|
||||||
|
.catch(console.error);
|
||||||
|
}
|
||||||
|
|
||||||
function handleSelectTab(newTab: TabID, last: TabID) {
|
function handleSelectTab(newTab: TabID, last: TabID) {
|
||||||
if (last === newTab) {
|
if (last === newTab) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (newTab === TabID.INPUT) {
|
if (newTab === TabID.INPUT) {
|
||||||
setAttachedID(undefined);
|
methods.setValue('item_data.operation_type', OperationType.INPUT);
|
||||||
|
methods.setValue('item_data.result', null);
|
||||||
|
methods.setValue('arguments', []);
|
||||||
} else {
|
} else {
|
||||||
setInputs(initialInputs);
|
methods.setValue('item_data.operation_type', OperationType.SYNTHESIS);
|
||||||
|
methods.setValue('arguments', initialInputs);
|
||||||
}
|
}
|
||||||
setActiveTab(newTab);
|
setActiveTab(newTab);
|
||||||
}
|
}
|
||||||
|
@ -101,7 +93,7 @@ function DlgCreateOperation() {
|
||||||
header='Создание операции'
|
header='Создание операции'
|
||||||
submitText='Создать'
|
submitText='Создать'
|
||||||
canSubmit={isValid}
|
canSubmit={isValid}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={event => void methods.handleSubmit(onSubmit)(event)}
|
||||||
className='w-[40rem] px-6 h-[32rem]'
|
className='w-[40rem] px-6 h-[32rem]'
|
||||||
helpTopic={HelpTopic.CC_OSS}
|
helpTopic={HelpTopic.CC_OSS}
|
||||||
>
|
>
|
||||||
|
@ -123,36 +115,15 @@ function DlgCreateOperation() {
|
||||||
label={labelOperationType(OperationType.SYNTHESIS)}
|
label={labelOperationType(OperationType.SYNTHESIS)}
|
||||||
/>
|
/>
|
||||||
</TabList>
|
</TabList>
|
||||||
|
<FormProvider {...methods}>
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<TabInputOperation
|
<TabInputOperation oss={oss} />
|
||||||
oss={oss}
|
|
||||||
alias={alias}
|
|
||||||
onChangeAlias={setAlias}
|
|
||||||
comment={comment}
|
|
||||||
onChangeComment={setComment}
|
|
||||||
title={title}
|
|
||||||
onChangeTitle={setTitle}
|
|
||||||
attachedID={attachedID}
|
|
||||||
onChangeAttachedID={setAttachedID}
|
|
||||||
createSchema={createSchema}
|
|
||||||
onChangeCreateSchema={setCreateSchema}
|
|
||||||
/>
|
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<TabSynthesisOperation
|
<TabSynthesisOperation oss={oss} />
|
||||||
oss={oss}
|
|
||||||
alias={alias}
|
|
||||||
onChangeAlias={setAlias}
|
|
||||||
comment={comment}
|
|
||||||
onChangeComment={setComment}
|
|
||||||
title={title}
|
|
||||||
onChangeTitle={setTitle}
|
|
||||||
inputs={inputs}
|
|
||||||
setInputs={setInputs}
|
|
||||||
/>
|
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
</FormProvider>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</ModalForm>
|
</ModalForm>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useEffect } from 'react';
|
import { Controller, useFormContext, useWatch } from 'react-hook-form';
|
||||||
|
|
||||||
import { MiniButton } from '@/components/Control';
|
import { MiniButton } from '@/components/Control';
|
||||||
import { IconReset } from '@/components/Icons';
|
import { IconReset } from '@/components/Icons';
|
||||||
|
@ -11,73 +11,70 @@ import { IOperationSchema } from '@/features/oss/models/oss';
|
||||||
import { sortItemsForOSS } from '@/features/oss/models/ossAPI';
|
import { sortItemsForOSS } from '@/features/oss/models/ossAPI';
|
||||||
import PickSchema from '@/features/rsform/components/PickSchema';
|
import PickSchema from '@/features/rsform/components/PickSchema';
|
||||||
|
|
||||||
|
import { IOperationCreateDTO } from '../../backend/api';
|
||||||
|
|
||||||
interface TabInputOperationProps {
|
interface TabInputOperationProps {
|
||||||
oss: IOperationSchema;
|
oss: IOperationSchema;
|
||||||
alias: string;
|
|
||||||
onChangeAlias: (newValue: string) => void;
|
|
||||||
title: string;
|
|
||||||
onChangeTitle: (newValue: string) => void;
|
|
||||||
comment: string;
|
|
||||||
onChangeComment: (newValue: string) => void;
|
|
||||||
attachedID: LibraryItemID | undefined;
|
|
||||||
onChangeAttachedID: (newValue: LibraryItemID | undefined) => void;
|
|
||||||
createSchema: boolean;
|
|
||||||
onChangeCreateSchema: (newValue: boolean) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function TabInputOperation({
|
function TabInputOperation({ oss }: TabInputOperationProps) {
|
||||||
oss,
|
|
||||||
alias,
|
|
||||||
onChangeAlias,
|
|
||||||
title,
|
|
||||||
onChangeTitle,
|
|
||||||
comment,
|
|
||||||
onChangeComment,
|
|
||||||
attachedID,
|
|
||||||
onChangeAttachedID,
|
|
||||||
createSchema,
|
|
||||||
onChangeCreateSchema
|
|
||||||
}: TabInputOperationProps) {
|
|
||||||
const { items: libraryItems } = useLibrary();
|
const { items: libraryItems } = useLibrary();
|
||||||
const sortedItems = sortItemsForOSS(oss, libraryItems);
|
const sortedItems = sortItemsForOSS(oss, libraryItems);
|
||||||
|
|
||||||
|
const {
|
||||||
|
register,
|
||||||
|
control,
|
||||||
|
setValue,
|
||||||
|
formState: { errors }
|
||||||
|
} = useFormContext<IOperationCreateDTO>();
|
||||||
|
const createSchema = useWatch({ control, name: 'create_schema' });
|
||||||
|
|
||||||
function baseFilter(item: ILibraryItem) {
|
function baseFilter(item: ILibraryItem) {
|
||||||
return !oss.schemas.includes(item.id);
|
return !oss.schemas.includes(item.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
function handleChangeCreateSchema(value: boolean) {
|
||||||
if (createSchema) {
|
if (value) {
|
||||||
onChangeAttachedID(undefined);
|
setValue('item_data.result', null);
|
||||||
|
}
|
||||||
|
setValue('create_schema', value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSetInput(value: LibraryItemID) {
|
||||||
|
const schema = libraryItems.find(item => item.id === value);
|
||||||
|
if (!schema) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setValue('item_data.result', value);
|
||||||
|
setValue('create_schema', false);
|
||||||
|
setValue('item_data.alias', schema.alias);
|
||||||
|
setValue('item_data.title', schema.title);
|
||||||
|
setValue('item_data.comment', schema.comment);
|
||||||
}
|
}
|
||||||
}, [createSchema, onChangeAttachedID]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='cc-fade-in cc-column'>
|
<div className='cc-fade-in cc-column'>
|
||||||
<TextInput
|
<TextInput
|
||||||
id='operation_title'
|
id='operation_title' //
|
||||||
label='Полное название'
|
label='Полное название'
|
||||||
value={title}
|
{...register('item_data.title')}
|
||||||
onChange={event => onChangeTitle(event.target.value)}
|
error={errors.item_data?.title}
|
||||||
disabled={attachedID !== undefined}
|
|
||||||
/>
|
/>
|
||||||
<div className='flex gap-6'>
|
<div className='flex gap-6'>
|
||||||
<TextInput
|
<TextInput
|
||||||
id='operation_alias'
|
id='operation_alias' //
|
||||||
label='Сокращение'
|
label='Сокращение'
|
||||||
className='w-[16rem]'
|
className='w-[16rem]'
|
||||||
value={alias}
|
{...register('item_data.alias')}
|
||||||
onChange={event => onChangeAlias(event.target.value)}
|
error={errors.item_data?.alias}
|
||||||
disabled={attachedID !== undefined}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextArea
|
<TextArea
|
||||||
id='operation_comment'
|
id='operation_comment' //
|
||||||
label='Описание'
|
label='Описание'
|
||||||
noResize
|
noResize
|
||||||
rows={3}
|
rows={3}
|
||||||
value={comment}
|
{...register('item_data.comment')}
|
||||||
onChange={event => onChangeComment(event.target.value)}
|
|
||||||
disabled={attachedID !== undefined}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -89,26 +86,30 @@ function TabInputOperation({
|
||||||
noHover
|
noHover
|
||||||
noPadding
|
noPadding
|
||||||
icon={<IconReset size='1.25rem' className='icon-primary' />}
|
icon={<IconReset size='1.25rem' className='icon-primary' />}
|
||||||
onClick={() => onChangeAttachedID(undefined)}
|
onClick={() => setValue('item_data.result', null)}
|
||||||
disabled={attachedID == undefined}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
value={createSchema}
|
value={createSchema} //
|
||||||
onChange={onChangeCreateSchema}
|
onChange={handleChangeCreateSchema}
|
||||||
label='Создать новую схему'
|
label='Создать новую схему'
|
||||||
titleHtml='Создать пустую схему для загрузки'
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{!createSchema ? (
|
{!createSchema ? (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name='item_data.result'
|
||||||
|
render={({ field }) => (
|
||||||
<PickSchema
|
<PickSchema
|
||||||
items={sortedItems}
|
items={sortedItems}
|
||||||
value={attachedID ?? null}
|
value={field.value}
|
||||||
itemType={LibraryItemType.RSFORM}
|
itemType={LibraryItemType.RSFORM}
|
||||||
onChange={onChangeAttachedID}
|
onChange={handleSetInput}
|
||||||
rows={8}
|
rows={8}
|
||||||
baseFilter={baseFilter}
|
baseFilter={baseFilter}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,47 +1,39 @@
|
||||||
|
import { Controller, useFormContext, useWatch } from 'react-hook-form';
|
||||||
|
|
||||||
import { FlexColumn } from '@/components/Container';
|
import { FlexColumn } from '@/components/Container';
|
||||||
import { Label, TextArea, TextInput } from '@/components/Input';
|
import { Label, TextArea, TextInput } from '@/components/Input';
|
||||||
|
|
||||||
|
import { IOperationCreateDTO } from '../../backend/api';
|
||||||
import PickMultiOperation from '../../components/PickMultiOperation';
|
import PickMultiOperation from '../../components/PickMultiOperation';
|
||||||
import { IOperationSchema, OperationID } from '../../models/oss';
|
import { IOperationSchema } from '../../models/oss';
|
||||||
|
|
||||||
interface TabSynthesisOperationProps {
|
interface TabSynthesisOperationProps {
|
||||||
oss: IOperationSchema;
|
oss: IOperationSchema;
|
||||||
alias: string;
|
|
||||||
onChangeAlias: (newValue: string) => void;
|
|
||||||
title: string;
|
|
||||||
onChangeTitle: (newValue: string) => void;
|
|
||||||
comment: string;
|
|
||||||
onChangeComment: (newValue: string) => void;
|
|
||||||
inputs: OperationID[];
|
|
||||||
setInputs: React.Dispatch<React.SetStateAction<OperationID[]>>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function TabSynthesisOperation({
|
function TabSynthesisOperation({ oss }: TabSynthesisOperationProps) {
|
||||||
oss,
|
const {
|
||||||
alias,
|
register,
|
||||||
onChangeAlias,
|
control,
|
||||||
title,
|
formState: { errors }
|
||||||
onChangeTitle,
|
} = useFormContext<IOperationCreateDTO>();
|
||||||
comment,
|
const inputs = useWatch({ control, name: 'arguments' });
|
||||||
onChangeComment,
|
|
||||||
inputs,
|
|
||||||
setInputs
|
|
||||||
}: TabSynthesisOperationProps) {
|
|
||||||
return (
|
return (
|
||||||
<div className='cc-fade-in cc-column'>
|
<div className='cc-fade-in cc-column'>
|
||||||
<TextInput
|
<TextInput
|
||||||
id='operation_title'
|
id='operation_title'
|
||||||
label='Полное название'
|
label='Полное название'
|
||||||
value={title}
|
{...register('item_data.title')}
|
||||||
onChange={event => onChangeTitle(event.target.value)}
|
error={errors.item_data?.title}
|
||||||
/>
|
/>
|
||||||
<div className='flex gap-6'>
|
<div className='flex gap-6'>
|
||||||
<TextInput
|
<TextInput
|
||||||
id='operation_alias'
|
id='operation_alias'
|
||||||
label='Сокращение'
|
label='Сокращение'
|
||||||
className='w-[16rem]'
|
className='w-[16rem]'
|
||||||
value={alias}
|
{...register('item_data.alias')}
|
||||||
onChange={event => onChangeAlias(event.target.value)}
|
error={errors.item_data?.alias}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextArea
|
<TextArea
|
||||||
|
@ -49,14 +41,21 @@ function TabSynthesisOperation({
|
||||||
label='Описание'
|
label='Описание'
|
||||||
noResize
|
noResize
|
||||||
rows={3}
|
rows={3}
|
||||||
value={comment}
|
{...register('item_data.comment')}
|
||||||
onChange={event => onChangeComment(event.target.value)}
|
error={errors.item_data?.comment}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FlexColumn>
|
<FlexColumn>
|
||||||
<Label text={`Выбор аргументов: [ ${inputs.length} ]`} />
|
<Label text={`Выбор аргументов: [ ${inputs.length} ]`} />
|
||||||
<PickMultiOperation items={oss.items} value={inputs} onChange={setInputs} rows={6} />
|
<Controller
|
||||||
|
name='arguments'
|
||||||
|
control={control}
|
||||||
|
defaultValue={[]}
|
||||||
|
render={({ field }) => (
|
||||||
|
<PickMultiOperation items={oss.items} value={field.value} onChange={field.onChange} rows={6} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</FlexColumn>
|
</FlexColumn>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -56,8 +56,6 @@ function DlgRelocateConstituents() {
|
||||||
libraryItems.find(item => item.id === initialTarget?.result)
|
libraryItems.find(item => item.id === initialTarget?.result)
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(isValid);
|
|
||||||
|
|
||||||
const operation = oss.items.find(item => item.result === source?.id);
|
const operation = oss.items.find(item => item.result === source?.id);
|
||||||
const sourceSchemas = libraryItems.filter(item => oss.schemas.includes(item.id));
|
const sourceSchemas = libraryItems.filter(item => oss.schemas.includes(item.id));
|
||||||
const destinationSchemas = (() => {
|
const destinationSchemas = (() => {
|
||||||
|
|
|
@ -488,7 +488,7 @@ export function calculateInsertPosition(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (operationType === OperationType.INPUT) {
|
if (operationType === OperationType.INPUT || argumentsOps.length === 0) {
|
||||||
let inputsNodes = positions.filter(pos =>
|
let inputsNodes = positions.filter(pos =>
|
||||||
oss.items.find(operation => operation.operation_type === OperationType.INPUT && operation.id === pos.id)
|
oss.items.find(operation => operation.operation_type === OperationType.INPUT && operation.id === pos.id)
|
||||||
);
|
);
|
||||||
|
|
|
@ -143,7 +143,7 @@ function OssFlow() {
|
||||||
defaultY: target.y,
|
defaultY: target.y,
|
||||||
inputs: inputs,
|
inputs: inputs,
|
||||||
positions: positions,
|
positions: positions,
|
||||||
callback: () => flow.fitView({ duration: PARAMETER.zoomDuration })
|
callback: () => setTimeout(() => flow.fitView({ duration: PARAMETER.zoomDuration }), PARAMETER.refreshTimeout)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,15 +12,12 @@ import { UserRole } from '@/features/users/models/user';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { usePreferencesStore } from '@/stores/preferences';
|
import { usePreferencesStore } from '@/stores/preferences';
|
||||||
import { useRoleStore } from '@/stores/role';
|
import { useRoleStore } from '@/stores/role';
|
||||||
import { PARAMETER } from '@/utils/constants';
|
|
||||||
import { prompts } from '@/utils/labels';
|
import { prompts } from '@/utils/labels';
|
||||||
|
|
||||||
import { IOperationPosition } from '../../backend/api';
|
import { IOperationPosition } from '../../backend/api';
|
||||||
import { useOperationCreate } from '../../backend/useOperationCreate';
|
|
||||||
import { useOperationUpdate } from '../../backend/useOperationUpdate';
|
import { useOperationUpdate } from '../../backend/useOperationUpdate';
|
||||||
import { useOssSuspense } from '../../backend/useOSS';
|
import { useOssSuspense } from '../../backend/useOSS';
|
||||||
import { IOperationSchema, OperationID, OperationType } from '../../models/oss';
|
import { IOperationSchema, OperationID, OperationType } from '../../models/oss';
|
||||||
import { calculateInsertPosition } from '../../models/ossAPI';
|
|
||||||
|
|
||||||
export enum OssTabID {
|
export enum OssTabID {
|
||||||
CARD = 0,
|
CARD = 0,
|
||||||
|
@ -98,7 +95,6 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEd
|
||||||
const showCreateOperation = useDialogsStore(state => state.showCreateOperation);
|
const showCreateOperation = useDialogsStore(state => state.showCreateOperation);
|
||||||
|
|
||||||
const { deleteItem } = useDeleteItem();
|
const { deleteItem } = useDeleteItem();
|
||||||
const { operationCreate } = useOperationCreate();
|
|
||||||
const { operationUpdate } = useOperationUpdate();
|
const { operationUpdate } = useOperationUpdate();
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
|
@ -143,21 +139,11 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEd
|
||||||
function promptCreateOperation({ defaultX, defaultY, inputs, positions, callback }: ICreateOperationPrompt) {
|
function promptCreateOperation({ defaultX, defaultY, inputs, positions, callback }: ICreateOperationPrompt) {
|
||||||
showCreateOperation({
|
showCreateOperation({
|
||||||
oss: schema,
|
oss: schema,
|
||||||
onCreate: data => {
|
defaultX: defaultX,
|
||||||
const target = calculateInsertPosition(schema, data.item_data.operation_type, data.arguments ?? [], positions, {
|
defaultY: defaultY,
|
||||||
x: defaultX,
|
positions: positions,
|
||||||
y: defaultY
|
initialInputs: inputs,
|
||||||
});
|
onCreate: callback
|
||||||
data.positions = positions;
|
|
||||||
data.item_data.position_x = target.x;
|
|
||||||
data.item_data.position_y = target.y;
|
|
||||||
operationCreate({ itemID: schema.id, data }, operation => {
|
|
||||||
if (callback) {
|
|
||||||
setTimeout(() => callback(operation.id), PARAMETER.refreshTimeout);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
initialInputs: inputs
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { VisibilityIcon } from '@/components/DomainIcons';
|
||||||
import { Checkbox, Label, TextArea, TextInput } from '@/components/Input';
|
import { Checkbox, Label, TextArea, TextInput } from '@/components/Input';
|
||||||
import { ModalForm } from '@/components/Modal';
|
import { ModalForm } from '@/components/Modal';
|
||||||
import { useAuthSuspense } from '@/features/auth/backend/useAuth';
|
import { useAuthSuspense } from '@/features/auth/backend/useAuth';
|
||||||
import { schemaCloneLibraryItem, ICloneLibraryItemDTO } from '@/features/library/backend/api';
|
import { ICloneLibraryItemDTO, schemaCloneLibraryItem } from '@/features/library/backend/api';
|
||||||
import { useCloneItem } from '@/features/library/backend/useCloneItem';
|
import { useCloneItem } from '@/features/library/backend/useCloneItem';
|
||||||
import SelectAccessPolicy from '@/features/library/components/SelectAccessPolicy';
|
import SelectAccessPolicy from '@/features/library/components/SelectAccessPolicy';
|
||||||
import SelectLocationContext from '@/features/library/components/SelectLocationContext';
|
import SelectLocationContext from '@/features/library/components/SelectLocationContext';
|
||||||
|
|
Loading…
Reference in New Issue
Block a user