F: Rework Edit Operation dialog
This commit is contained in:
parent
c0b22f5ca1
commit
b6f1ff3337
|
@ -6,11 +6,11 @@ import { DELAYS } from '@/backend/configuration';
|
|||
import { ILibraryItem, ILibraryItemData, LibraryItemID } from '@/features/library/models/library';
|
||||
import {
|
||||
IArgument,
|
||||
ICstSubstitute,
|
||||
ICstSubstituteEx,
|
||||
IOperation,
|
||||
OperationID,
|
||||
OperationType
|
||||
OperationType,
|
||||
schemaCstSubstitute
|
||||
} from '@/features/oss/models/oss';
|
||||
import { IConstituentaReference, ITargetCst } from '@/features/rsform/models/rsform';
|
||||
import { information } from '@/utils/labels';
|
||||
|
@ -122,15 +122,22 @@ export type IInputUpdateDTO = z.infer<typeof schemaInputUpdate>;
|
|||
/**
|
||||
* Represents {@link IOperation} data, used in update process.
|
||||
*/
|
||||
export interface IOperationUpdateDTO extends ITargetOperation {
|
||||
item_data: {
|
||||
alias: string;
|
||||
title: string;
|
||||
comment: string;
|
||||
};
|
||||
arguments: OperationID[] | undefined;
|
||||
substitutions: ICstSubstitute[] | undefined;
|
||||
}
|
||||
export const schemaOperationUpdate = z.object({
|
||||
target: z.number(),
|
||||
positions: z.array(schemaOperationPosition),
|
||||
item_data: z.object({
|
||||
alias: z.string().nonempty(),
|
||||
title: z.string(),
|
||||
comment: z.string()
|
||||
}),
|
||||
arguments: z.array(z.number()),
|
||||
substitutions: z.array(schemaCstSubstitute)
|
||||
});
|
||||
|
||||
/**
|
||||
* Represents {@link IOperation} data, used in update process.
|
||||
*/
|
||||
export type IOperationUpdateDTO = z.infer<typeof schemaOperationUpdate>;
|
||||
|
||||
/**
|
||||
* Represents data, used relocating {@link IConstituenta}s between {@link ILibraryItem}s.
|
||||
|
|
|
@ -21,7 +21,7 @@ interface PickMultiOperationProps extends CProps.Styling {
|
|||
|
||||
const columnHelper = createColumnHelper<IOperation>();
|
||||
|
||||
function PickMultiOperation({ rows, items, value, onChange, className, ...restProps }: PickMultiOperationProps) {
|
||||
export function PickMultiOperation({ rows, items, value, onChange, className, ...restProps }: PickMultiOperationProps) {
|
||||
const selectedItems = value.map(itemID => items.find(item => item.id === itemID)!);
|
||||
const nonSelectedItems = items.filter(item => !value.includes(item.id));
|
||||
const [lastSelected, setLastSelected] = useState<IOperation | undefined>(undefined);
|
||||
|
@ -134,5 +134,3 @@ function PickMultiOperation({ rows, items, value, onChange, className, ...restPr
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default PickMultiOperation;
|
||||
|
|
|
@ -115,11 +115,11 @@ function DlgCreateOperation() {
|
|||
</TabList>
|
||||
<FormProvider {...methods}>
|
||||
<TabPanel>
|
||||
<TabInputOperation oss={oss} />
|
||||
<TabInputOperation />
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel>
|
||||
<TabSynthesisOperation oss={oss} />
|
||||
<TabSynthesisOperation />
|
||||
</TabPanel>
|
||||
</FormProvider>
|
||||
</Tabs>
|
||||
|
|
|
@ -7,17 +7,15 @@ import { IconReset } from '@/components/Icons';
|
|||
import { Checkbox, Label, TextArea, TextInput } from '@/components/Input';
|
||||
import { useLibrary } from '@/features/library/backend/useLibrary';
|
||||
import { ILibraryItem, LibraryItemID, LibraryItemType } from '@/features/library/models/library';
|
||||
import { IOperationSchema } from '@/features/oss/models/oss';
|
||||
import { sortItemsForOSS } from '@/features/oss/models/ossAPI';
|
||||
import PickSchema from '@/features/rsform/components/PickSchema';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
|
||||
import { IOperationCreateDTO } from '../../backend/api';
|
||||
import { DlgCreateOperationProps } from './DlgCreateOperation';
|
||||
|
||||
interface TabInputOperationProps {
|
||||
oss: IOperationSchema;
|
||||
}
|
||||
|
||||
function TabInputOperation({ oss }: TabInputOperationProps) {
|
||||
function TabInputOperation() {
|
||||
const { oss } = useDialogsStore(state => state.props as DlgCreateOperationProps);
|
||||
const { items: libraryItems } = useLibrary();
|
||||
const sortedItems = sortItemsForOSS(oss, libraryItems);
|
||||
|
||||
|
|
|
@ -2,16 +2,14 @@ import { Controller, useFormContext, useWatch } from 'react-hook-form';
|
|||
|
||||
import { FlexColumn } from '@/components/Container';
|
||||
import { Label, TextArea, TextInput } from '@/components/Input';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
|
||||
import { IOperationCreateDTO } from '../../backend/api';
|
||||
import PickMultiOperation from '../../components/PickMultiOperation';
|
||||
import { IOperationSchema } from '../../models/oss';
|
||||
import { PickMultiOperation } from '../../components/PickMultiOperation';
|
||||
import { DlgCreateOperationProps } from './DlgCreateOperation';
|
||||
|
||||
interface TabSynthesisOperationProps {
|
||||
oss: IOperationSchema;
|
||||
}
|
||||
|
||||
function TabSynthesisOperation({ oss }: TabSynthesisOperationProps) {
|
||||
function TabSynthesisOperation() {
|
||||
const { oss } = useDialogsStore(state => state.props as DlgCreateOperationProps);
|
||||
const {
|
||||
register,
|
||||
control,
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
'use client';
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { Suspense, useState } from 'react';
|
||||
import { FormProvider, useForm, useWatch } from 'react-hook-form';
|
||||
|
||||
import { Loader } from '@/components/Loader';
|
||||
import { ModalForm } from '@/components/Modal';
|
||||
import { TabLabel, TabList, TabPanel, Tabs } from '@/components/Tabs';
|
||||
import { HelpTopic } from '@/features/help/models/helpTopic';
|
||||
import { LibraryItemID } from '@/features/library/models/library';
|
||||
import { useRSForms } from '@/features/rsform/backend/useRSForms';
|
||||
import { ConstituentaID } from '@/features/rsform/models/rsform';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
|
||||
import { IOperationUpdateDTO } from '../../backend/api';
|
||||
import { ICstSubstitute, IOperation, IOperationSchema, OperationID, OperationType } from '../../models/oss';
|
||||
import { SubstitutionValidator } from '../../models/ossAPI';
|
||||
import { IOperationPosition, IOperationUpdateDTO, schemaOperationUpdate } from '../../backend/api';
|
||||
import { useOperationUpdate } from '../../backend/useOperationUpdate';
|
||||
import { IOperation, IOperationSchema, OperationType } from '../../models/oss';
|
||||
import TabArguments from './TabArguments';
|
||||
import TabOperation from './TabOperation';
|
||||
import TabSynthesis from './TabSynthesis';
|
||||
|
@ -21,7 +21,7 @@ import TabSynthesis from './TabSynthesis';
|
|||
export interface DlgEditOperationProps {
|
||||
oss: IOperationSchema;
|
||||
target: IOperation;
|
||||
onSubmit: (data: IOperationUpdateDTO) => void;
|
||||
positions: IOperationPosition[];
|
||||
}
|
||||
|
||||
export enum TabID {
|
||||
|
@ -31,94 +31,32 @@ export enum TabID {
|
|||
}
|
||||
|
||||
function DlgEditOperation() {
|
||||
const { oss, target, onSubmit } = useDialogsStore(state => state.props as DlgEditOperationProps);
|
||||
const { oss, target, positions } = useDialogsStore(state => state.props as DlgEditOperationProps);
|
||||
const { operationUpdate } = useOperationUpdate();
|
||||
|
||||
const methods = useForm<IOperationUpdateDTO>({
|
||||
resolver: zodResolver(schemaOperationUpdate),
|
||||
defaultValues: {
|
||||
item_data: {
|
||||
alias: target.alias,
|
||||
title: target.alias,
|
||||
comment: target.comment
|
||||
},
|
||||
arguments: target.arguments,
|
||||
substitutions: target.substitutions.map(sub => ({
|
||||
original: sub.original,
|
||||
substitution: sub.substitution
|
||||
})),
|
||||
positions: positions
|
||||
}
|
||||
});
|
||||
const alias = useWatch({ control: methods.control, name: 'item_data.alias' });
|
||||
const canSubmit = alias !== '';
|
||||
|
||||
const [activeTab, setActiveTab] = useState(TabID.CARD);
|
||||
|
||||
const [alias, setAlias] = useState(target.alias);
|
||||
const [title, setTitle] = useState(target.title);
|
||||
const [comment, setComment] = useState(target.comment);
|
||||
|
||||
const [isCorrect, setIsCorrect] = useState(true);
|
||||
const [validationText, setValidationText] = useState('');
|
||||
|
||||
const initialInputs = oss.graph.expandInputs([target.id]);
|
||||
const [inputs, setInputs] = useState<OperationID[]>(initialInputs);
|
||||
const inputOperations = inputs.map(id => oss.operationByID.get(id)!);
|
||||
|
||||
const [substitutions, setSubstitutions] = useState<ICstSubstitute[]>(target.substitutions);
|
||||
const [suggestions, setSuggestions] = useState<ICstSubstitute[]>([]);
|
||||
|
||||
const [schemasIDs, setSchemaIDs] = useState<LibraryItemID[]>([]);
|
||||
const schemas = useRSForms(schemasIDs);
|
||||
|
||||
const isModified =
|
||||
alias !== target.alias ||
|
||||
title !== target.title ||
|
||||
comment !== target.comment ||
|
||||
JSON.stringify(initialInputs) !== JSON.stringify(inputs) ||
|
||||
JSON.stringify(substitutions) !== JSON.stringify(target.substitutions);
|
||||
|
||||
const canSubmit = isModified && alias !== '';
|
||||
|
||||
const getSchemaByCst = useCallback(
|
||||
(id: ConstituentaID) => {
|
||||
for (const schema of schemas) {
|
||||
const cst = schema.items.find(cst => cst.id === id);
|
||||
if (cst) {
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
[schemas]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setSchemaIDs(inputOperations.map(operation => operation.result).filter(id => id !== null));
|
||||
}, [inputOperations]);
|
||||
|
||||
useEffect(() => {
|
||||
if (schemas.length !== schemasIDs.length || schemas.length === 0) {
|
||||
return;
|
||||
}
|
||||
setSubstitutions(prev =>
|
||||
prev.filter(sub => {
|
||||
const original = getSchemaByCst(sub.original);
|
||||
if (!original || !schemasIDs.includes(original.id)) {
|
||||
return false;
|
||||
}
|
||||
const substitution = getSchemaByCst(sub.substitution);
|
||||
if (!substitution || !schemasIDs.includes(substitution.id)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
);
|
||||
}, [schemasIDs, schemas, getSchemaByCst]);
|
||||
|
||||
useEffect(() => {
|
||||
if (schemas.length !== schemasIDs.length || schemas.length === 0) {
|
||||
return;
|
||||
}
|
||||
const validator = new SubstitutionValidator(schemas, substitutions);
|
||||
setIsCorrect(validator.validate());
|
||||
setValidationText(validator.msg);
|
||||
setSuggestions(validator.suggestions);
|
||||
}, [substitutions, schemas, schemasIDs.length]);
|
||||
|
||||
function handleSubmit() {
|
||||
onSubmit({
|
||||
target: target.id,
|
||||
item_data: {
|
||||
alias: alias,
|
||||
title: title,
|
||||
comment: comment
|
||||
},
|
||||
positions: [],
|
||||
arguments: target.operation_type !== OperationType.SYNTHESIS ? undefined : inputs,
|
||||
substitutions: target.operation_type !== OperationType.SYNTHESIS ? undefined : substitutions
|
||||
});
|
||||
return true;
|
||||
function onSubmit(data: IOperationUpdateDTO) {
|
||||
return operationUpdate({ itemID: oss.id, data });
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -126,7 +64,7 @@ function DlgEditOperation() {
|
|||
header='Редактирование операции'
|
||||
submitText='Сохранить'
|
||||
canSubmit={canSubmit}
|
||||
onSubmit={handleSubmit}
|
||||
onSubmit={event => void methods.handleSubmit(onSubmit)(event)}
|
||||
className='w-[40rem] px-6 h-[32rem]'
|
||||
helpTopic={HelpTopic.UI_SUBSTITUTIONS}
|
||||
hideHelpWhen={() => activeTab !== TabID.SUBSTITUTION}
|
||||
|
@ -143,47 +81,28 @@ function DlgEditOperation() {
|
|||
<TabLabel title='Выбор аргументов операции' label='Аргументы' className='w-[8rem]' />
|
||||
) : null}
|
||||
{target.operation_type === OperationType.SYNTHESIS ? (
|
||||
<TabLabel
|
||||
titleHtml={'Таблица отождествлений' + (isCorrect ? '' : '<br/>(не прошла проверку)')}
|
||||
label={isCorrect ? 'Отождествления' : 'Отождествления*'}
|
||||
className='w-[8rem]'
|
||||
/>
|
||||
<TabLabel titleHtml='Таблица отождествлений' label='Отождествления' className='w-[8rem]' />
|
||||
) : null}
|
||||
</TabList>
|
||||
|
||||
<TabPanel>
|
||||
<TabOperation
|
||||
alias={alias}
|
||||
onChangeAlias={setAlias}
|
||||
comment={comment}
|
||||
onChangeComment={setComment}
|
||||
title={title}
|
||||
onChangeTitle={setTitle}
|
||||
/>
|
||||
</TabPanel>
|
||||
<FormProvider {...methods}>
|
||||
<TabPanel>
|
||||
<TabOperation />
|
||||
</TabPanel>
|
||||
|
||||
{target.operation_type === OperationType.SYNTHESIS ? (
|
||||
<TabPanel>
|
||||
<TabArguments
|
||||
target={target.id} // prettier: split-lines
|
||||
oss={oss}
|
||||
inputs={inputs}
|
||||
setInputs={setInputs}
|
||||
/>
|
||||
</TabPanel>
|
||||
) : null}
|
||||
{target.operation_type === OperationType.SYNTHESIS ? (
|
||||
<TabPanel>
|
||||
<TabSynthesis
|
||||
schemas={schemas}
|
||||
validationText={validationText}
|
||||
isCorrect={isCorrect}
|
||||
substitutions={substitutions}
|
||||
setSubstitutions={setSubstitutions}
|
||||
suggestions={suggestions}
|
||||
/>
|
||||
</TabPanel>
|
||||
) : null}
|
||||
{target.operation_type === OperationType.SYNTHESIS ? (
|
||||
<TabPanel>
|
||||
<TabArguments />
|
||||
</TabPanel>
|
||||
) : null}
|
||||
{target.operation_type === OperationType.SYNTHESIS ? (
|
||||
<TabPanel>
|
||||
<Suspense fallback={<Loader />}>
|
||||
<TabSynthesis />
|
||||
</Suspense>
|
||||
</TabPanel>
|
||||
) : null}
|
||||
</FormProvider>
|
||||
</Tabs>
|
||||
</ModalForm>
|
||||
);
|
||||
|
|
|
@ -1,25 +1,47 @@
|
|||
'use client';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
|
||||
import { FlexColumn } from '@/components/Container';
|
||||
import { Label } from '@/components/Input';
|
||||
import { LibraryItemID } from '@/features/library/models/library';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
|
||||
import PickMultiOperation from '../../components/PickMultiOperation';
|
||||
import { IOperationSchema, OperationID } from '../../models/oss';
|
||||
import { IOperationUpdateDTO } from '../../backend/api';
|
||||
import { PickMultiOperation } from '../../components/PickMultiOperation';
|
||||
import { DlgEditOperationProps } from './DlgEditOperation';
|
||||
|
||||
interface TabArgumentsProps {
|
||||
oss: IOperationSchema;
|
||||
target: OperationID;
|
||||
inputs: OperationID[];
|
||||
setInputs: React.Dispatch<React.SetStateAction<OperationID[]>>;
|
||||
}
|
||||
|
||||
function TabArguments({ oss, inputs, target, setInputs }: TabArgumentsProps) {
|
||||
const potentialCycle = [target, ...oss.graph.expandAllOutputs([target])];
|
||||
function TabArguments() {
|
||||
const { control, setValue } = useFormContext<IOperationUpdateDTO>();
|
||||
const { oss, target } = useDialogsStore(state => state.props as DlgEditOperationProps);
|
||||
const potentialCycle = [target.id, ...oss.graph.expandAllOutputs([target.id])];
|
||||
const filtered = oss.items.filter(item => !potentialCycle.includes(item.id));
|
||||
|
||||
function handleChangeArguments(prev: LibraryItemID[], newValue: LibraryItemID[]) {
|
||||
setValue('arguments', newValue);
|
||||
if (prev.some(id => !newValue.includes(id))) {
|
||||
setValue('substitutions', []);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='cc-fade-in cc-column'>
|
||||
<FlexColumn>
|
||||
<Label text={`Выбор аргументов: [ ${inputs.length} ]`} />
|
||||
<PickMultiOperation items={filtered} value={inputs} onChange={setInputs} rows={8} />
|
||||
<Controller
|
||||
name='arguments'
|
||||
control={control}
|
||||
defaultValue={[]}
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<Label text={`Выбор аргументов: [ ${field.value.length} ]`} />
|
||||
<PickMultiOperation
|
||||
items={filtered}
|
||||
value={field.value}
|
||||
onChange={newValue => handleChangeArguments(field.value, newValue)}
|
||||
rows={8}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</FlexColumn>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
import { useFormContext } from 'react-hook-form';
|
||||
|
||||
import { TextArea, TextInput } from '@/components/Input';
|
||||
|
||||
interface TabOperationProps {
|
||||
alias: string;
|
||||
onChangeAlias: (newValue: string) => void;
|
||||
title: string;
|
||||
onChangeTitle: (newValue: string) => void;
|
||||
comment: string;
|
||||
onChangeComment: (newValue: string) => void;
|
||||
}
|
||||
import { IOperationUpdateDTO } from '../../backend/api';
|
||||
|
||||
function TabOperation() {
|
||||
const {
|
||||
register,
|
||||
formState: { errors }
|
||||
} = useFormContext<IOperationUpdateDTO>();
|
||||
|
||||
function TabOperation({ alias, onChangeAlias, title, onChangeTitle, comment, onChangeComment }: TabOperationProps) {
|
||||
return (
|
||||
<div className='cc-fade-in cc-column'>
|
||||
<TextInput
|
||||
id='operation_title'
|
||||
label='Полное название'
|
||||
value={title}
|
||||
onChange={event => onChangeTitle(event.target.value)}
|
||||
{...register('item_data.title')}
|
||||
error={errors.item_data?.title}
|
||||
/>
|
||||
<div className='flex gap-6'>
|
||||
<TextInput
|
||||
id='operation_alias'
|
||||
label='Сокращение'
|
||||
className='w-[16rem]'
|
||||
value={alias}
|
||||
onChange={event => onChangeAlias(event.target.value)}
|
||||
{...register('item_data.alias')}
|
||||
error={errors.item_data?.alias}
|
||||
/>
|
||||
|
||||
<TextArea
|
||||
|
@ -32,8 +32,8 @@ function TabOperation({ alias, onChangeAlias, title, onChangeTitle, comment, onC
|
|||
label='Описание'
|
||||
noResize
|
||||
rows={3}
|
||||
value={comment}
|
||||
onChange={event => onChangeComment(event.target.value)}
|
||||
{...register('item_data.comment')}
|
||||
error={errors.item_data?.comment}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,40 +1,52 @@
|
|||
'use client';
|
||||
|
||||
import { Controller, useFormContext, useWatch } from 'react-hook-form';
|
||||
|
||||
import { TextArea } from '@/components/Input';
|
||||
import { useRSForms } from '@/features/rsform/backend/useRSForms';
|
||||
import PickSubstitutions from '@/features/rsform/components/PickSubstitutions';
|
||||
import { IRSForm } from '@/features/rsform/models/rsform';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
import { APP_COLORS } from '@/styling/colors';
|
||||
|
||||
import { ICstSubstitute } from '../../models/oss';
|
||||
import { IOperationUpdateDTO } from '../../backend/api';
|
||||
import { SubstitutionValidator } from '../../models/ossAPI';
|
||||
import { DlgEditOperationProps } from './DlgEditOperation';
|
||||
|
||||
interface TabSynthesisProps {
|
||||
validationText: string;
|
||||
isCorrect: boolean;
|
||||
function TabSynthesis() {
|
||||
const { oss } = useDialogsStore(state => state.props as DlgEditOperationProps);
|
||||
const { control } = useFormContext<IOperationUpdateDTO>();
|
||||
const inputs = useWatch({ control, name: 'arguments' });
|
||||
const substitutions = useWatch({ control, name: 'substitutions' });
|
||||
|
||||
schemas: IRSForm[];
|
||||
substitutions: ICstSubstitute[];
|
||||
setSubstitutions: React.Dispatch<React.SetStateAction<ICstSubstitute[]>>;
|
||||
suggestions: ICstSubstitute[];
|
||||
}
|
||||
const schemasIDs = inputs
|
||||
.map(id => oss.operationByID.get(id)!)
|
||||
.map(operation => operation.result)
|
||||
.filter(id => id !== null);
|
||||
const schemas = useRSForms(schemasIDs);
|
||||
|
||||
const validator = new SubstitutionValidator(schemas, substitutions);
|
||||
const isCorrect = validator.validate();
|
||||
|
||||
function TabSynthesis({
|
||||
schemas,
|
||||
validationText,
|
||||
isCorrect,
|
||||
substitutions,
|
||||
setSubstitutions,
|
||||
suggestions
|
||||
}: TabSynthesisProps) {
|
||||
return (
|
||||
<div className='cc-fade-in cc-column mt-3'>
|
||||
<PickSubstitutions
|
||||
schemas={schemas}
|
||||
rows={8}
|
||||
value={substitutions}
|
||||
onChange={setSubstitutions}
|
||||
suggestions={suggestions}
|
||||
<Controller
|
||||
name='substitutions'
|
||||
control={control}
|
||||
defaultValue={[]}
|
||||
render={({ field }) => (
|
||||
<PickSubstitutions
|
||||
schemas={schemas}
|
||||
rows={8}
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
suggestions={validator.suggestions}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<TextArea
|
||||
disabled
|
||||
value={validationText}
|
||||
value={validator.msg}
|
||||
rows={4}
|
||||
style={{ borderColor: isCorrect ? undefined : APP_COLORS.fgRed, borderWidth: isCorrect ? undefined : '2px' }}
|
||||
/>
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
* Module: Schema of Synthesis Operations.
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ILibraryItem, ILibraryItemData, LibraryItemID } from '@/features/library/models/library';
|
||||
import { ConstituentaID, IConstituenta } from '@/features/rsform/models/rsform';
|
||||
import { IConstituenta } from '@/features/rsform/models/rsform';
|
||||
|
||||
import { Graph } from '../../../models/Graph';
|
||||
|
||||
|
@ -54,10 +56,15 @@ export interface IArgument {
|
|||
/**
|
||||
* Represents data, used in merging single {@link IConstituenta}.
|
||||
*/
|
||||
export interface ICstSubstitute {
|
||||
original: ConstituentaID;
|
||||
substitution: ConstituentaID;
|
||||
}
|
||||
export const schemaCstSubstitute = z.object({
|
||||
original: z.number(),
|
||||
substitution: z.number()
|
||||
});
|
||||
|
||||
/**
|
||||
* Represents data, used in merging single {@link IConstituenta}.
|
||||
*/
|
||||
export type ICstSubstitute = z.infer<typeof schemaCstSubstitute>;
|
||||
|
||||
/**
|
||||
* Represents data, used in merging multiple {@link IConstituenta}.
|
||||
|
|
|
@ -120,7 +120,6 @@ export class SubstitutionValidator {
|
|||
if (!this.checkSubstitutions()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.setValid();
|
||||
}
|
||||
|
||||
|
|
|
@ -296,7 +296,10 @@ function OssFlow() {
|
|||
|
||||
return (
|
||||
<div tabIndex={-1} onKeyDown={handleKeyDown}>
|
||||
<Overlay position='top-[1.9rem] pt-1 right-1/2 translate-x-1/2' className='rounded-b-2xl cc-blur'>
|
||||
<Overlay
|
||||
position='top-[1.9rem] pt-1 right-1/2 translate-x-1/2'
|
||||
className='rounded-b-2xl cc-blur hover:bg-prim-100 hover:bg-opacity-50'
|
||||
>
|
||||
<ToolbarOssGraph
|
||||
onFitView={() => flow.fitView({ duration: PARAMETER.zoomDuration })}
|
||||
onCreate={() => handleCreateOperation(controller.selected)}
|
||||
|
|
|
@ -15,7 +15,6 @@ import { useRoleStore } from '@/stores/role';
|
|||
import { prompts } from '@/utils/labels';
|
||||
|
||||
import { IOperationPosition } from '../../backend/api';
|
||||
import { useOperationUpdate } from '../../backend/useOperationUpdate';
|
||||
import { useOssSuspense } from '../../backend/useOSS';
|
||||
import { IOperationSchema, OperationID, OperationType } from '../../models/oss';
|
||||
|
||||
|
@ -95,7 +94,6 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEd
|
|||
const showCreateOperation = useDialogsStore(state => state.showCreateOperation);
|
||||
|
||||
const { deleteItem } = useDeleteItem();
|
||||
const { operationUpdate } = useOperationUpdate();
|
||||
|
||||
useEffect(
|
||||
() =>
|
||||
|
@ -166,10 +164,7 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEd
|
|||
showEditOperation({
|
||||
oss: schema,
|
||||
target: operation,
|
||||
onSubmit: data => {
|
||||
data.positions = positions;
|
||||
void operationUpdate({ itemID: schema.id, data });
|
||||
}
|
||||
positions: positions
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useQueries } from '@tanstack/react-query';
|
||||
import { useSuspenseQueries } from '@tanstack/react-query';
|
||||
|
||||
import { LibraryItemID } from '@/features/library/models/library';
|
||||
|
||||
|
@ -7,7 +7,7 @@ import { rsformsApi } from './api';
|
|||
import { RSFormLoader } from './RSFormLoader';
|
||||
|
||||
export function useRSForms(itemIDs: LibraryItemID[]) {
|
||||
const results = useQueries({
|
||||
const results = useSuspenseQueries({
|
||||
queries: itemIDs.map(itemID => ({
|
||||
...rsformsApi.getRSFormQueryOptions({ itemID }),
|
||||
enabled: itemIDs.length > 0,
|
||||
|
|
|
@ -21,7 +21,7 @@ import SelectConstituenta from './SelectConstituenta';
|
|||
|
||||
interface PickSubstitutionsProps extends CProps.Styling {
|
||||
value: ICstSubstitute[];
|
||||
onChange: React.Dispatch<React.SetStateAction<ICstSubstitute[]>>;
|
||||
onChange: (newValue: ICstSubstitute[]) => void;
|
||||
|
||||
suggestions?: ICstSubstitute[];
|
||||
|
||||
|
@ -125,30 +125,24 @@ function PickSubstitutions({
|
|||
return;
|
||||
}
|
||||
}
|
||||
onChange(prev => [...prev, newSubstitution]);
|
||||
onChange([...value, newSubstitution]);
|
||||
setLeftCst(undefined);
|
||||
setRightCst(undefined);
|
||||
}
|
||||
|
||||
function handleDeclineSuggestion(item: IMultiSubstitution) {
|
||||
setIgnores(prev => [...prev, { original: item.original.id, substitution: item.substitution.id }]);
|
||||
setIgnores([...value, { original: item.original.id, substitution: item.substitution.id }]);
|
||||
}
|
||||
|
||||
function handleAcceptSuggestion(item: IMultiSubstitution) {
|
||||
onChange(prev => [...prev, { original: item.original.id, substitution: item.substitution.id }]);
|
||||
onChange([...value, { original: item.original.id, substitution: item.substitution.id }]);
|
||||
}
|
||||
|
||||
function handleDeleteSubstitution(target: IMultiSubstitution) {
|
||||
handleDeclineSuggestion(target);
|
||||
onChange(prev => {
|
||||
const newItems: ICstSubstitute[] = [];
|
||||
prev.forEach(item => {
|
||||
if (item.original !== target.original.id || item.substitution !== target.substitution.id) {
|
||||
newItems.push(item);
|
||||
}
|
||||
});
|
||||
return newItems;
|
||||
});
|
||||
onChange(
|
||||
value.filter(item => item.original !== target.original.id || item.substitution !== target.substitution.id)
|
||||
);
|
||||
}
|
||||
|
||||
const columns = [
|
||||
|
|
Loading…
Reference in New Issue
Block a user