R: Upgrade SubstituteCst dialog
Some checks are pending
Frontend CI / build (22.x) (push) Waiting to run

This commit is contained in:
Ivan 2025-02-12 02:12:08 +03:00
parent 381567491f
commit 2632921dbb
11 changed files with 80 additions and 67 deletions

View File

@ -4,14 +4,8 @@ import { z } from 'zod';
import { axiosGet, axiosPatch, axiosPost } from '@/backend/apiTransport';
import { DELAYS } from '@/backend/configuration';
import { ILibraryItem, ILibraryItemData, LibraryItemID } from '@/features/library/models/library';
import {
IArgument,
ICstSubstituteEx,
IOperation,
OperationID,
OperationType,
schemaCstSubstitute
} from '@/features/oss/models/oss';
import { IArgument, ICstSubstituteEx, IOperation, OperationID, OperationType } from '@/features/oss/models/oss';
import { schemaCstSubstitute } from '@/features/rsform/backend/api';
import { IConstituentaReference, ITargetCst } from '@/features/rsform/models/rsform';
import { infoMsg } from '@/utils/labels';

View File

@ -1,10 +1,8 @@
/**
* Module: Schema of Synthesis Operations.
*/
import { z } from 'zod';
import { ILibraryItem, ILibraryItemData, LibraryItemID } from '@/features/library/models/library';
import { ICstSubstitute } from '@/features/rsform/backend/api';
import { IConstituenta } from '@/features/rsform/models/rsform';
import { Graph } from '../../../models/Graph';
@ -53,26 +51,6 @@ export interface IArgument {
argument: OperationID;
}
/**
* Represents data, used in merging single {@link IConstituenta}.
*/
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}.
*/
export interface ICstSubstitutions {
substitutions: ICstSubstitute[];
}
/**
* Represents substitution for multi synthesis table.
*/

View File

@ -3,6 +3,7 @@
*/
import { ILibraryItem, LibraryItemID } from '@/features/library/models/library';
import { ICstSubstitute } from '@/features/rsform/backend/api';
import { ConstituentaID, CstClass, CstType, IConstituenta, IRSForm } from '@/features/rsform/models/rsform';
import { AliasMapping, ParsingStatus } from '@/features/rsform/models/rslang';
import {
@ -18,7 +19,7 @@ import { TextMatcher } from '@/utils/utils';
import { Graph } from '../../../models/Graph';
import { IOperationPosition } from '../backend/api';
import { describeSubstitutionError } from '../labels';
import { ICstSubstitute, IOperation, IOperationSchema, OperationID, OperationType, SubstitutionErrorType } from './oss';
import { IOperation, IOperationSchema, OperationID, OperationType, SubstitutionErrorType } from './oss';
import { Position2D } from './ossLayout';
/**

View File

@ -9,8 +9,7 @@ import {
LibraryItemID,
VersionID
} from '@/features/library/models/library';
import { ICstSubstitute, ICstSubstitutions } from '@/features/oss/models/oss';
import { infoMsg } from '@/utils/labels';
import { errorMsg, infoMsg } from '@/utils/labels';
import {
ConstituentaID,
@ -127,6 +126,19 @@ export interface IProduceStructureResponse {
schema: IRSFormDTO;
}
/**
* Represents data, used in merging single {@link IConstituenta}.
*/
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 input data for inline synthesis.
*/
@ -146,6 +158,18 @@ export interface ICheckConstituentaDTO {
definition_formal: string;
}
/**
* Represents data, used in renaming {@link IConstituenta}.
*/
export const schemaCstSubstitutions = z.object({
substitutions: z.array(schemaCstSubstitute).min(1, { message: errorMsg.emptySubstitutions })
});
/**
* Represents data, used in merging multiple {@link IConstituenta}.
*/
export type ICstSubstitutionsDTO = z.infer<typeof schemaCstSubstitutions>;
export const rsformsApi = {
baseKey: 'rsform',
@ -214,8 +238,8 @@ export const rsformsApi = {
successMessage: infoMsg.changesSaved
}
}),
cstSubstitute: ({ itemID, data }: { itemID: LibraryItemID; data: ICstSubstitutions }) =>
axiosPatch<ICstSubstitutions, IRSFormDTO>({
cstSubstitute: ({ itemID, data }: { itemID: LibraryItemID; data: ICstSubstitutionsDTO }) =>
axiosPatch<ICstSubstitutionsDTO, IRSFormDTO>({
endpoint: `/api/rsforms/${itemID}/substitute`,
request: {
data: data,
@ -229,7 +253,7 @@ export const rsformsApi = {
}),
produceStructure: ({ itemID, data }: { itemID: LibraryItemID; data: ITargetCst }) =>
axiosPost<ITargetCst, IProduceStructureResponse>({
axiosPatch<ITargetCst, IProduceStructureResponse>({
endpoint: `/api/rsforms/${itemID}/produce-structure`,
request: {
data: data,

View File

@ -3,9 +3,8 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useUpdateTimestamp } from '@/features/library/backend/useUpdateTimestamp';
import { LibraryItemID } from '@/features/library/models/library';
import { ossApi } from '@/features/oss/backend/api';
import { ICstSubstitutions } from '@/features/oss/models/oss';
import { rsformsApi } from './api';
import { ICstSubstitutionsDTO, rsformsApi } from './api';
export const useCstSubstitute = () => {
const client = useQueryClient();
@ -27,6 +26,6 @@ export const useCstSubstitute = () => {
}
});
return {
cstSubstitute: (data: { itemID: LibraryItemID; data: ICstSubstitutions }) => mutation.mutateAsync(data)
cstSubstitute: (data: { itemID: LibraryItemID; data: ICstSubstitutionsDTO }) => mutation.mutateAsync(data)
};
};

View File

@ -11,10 +11,11 @@ import { CProps } from '@/components/props';
import { NoData } from '@/components/View';
import SelectLibraryItem from '@/features/library/components/SelectLibraryItem';
import { ILibraryItem } from '@/features/library/models/library';
import { ICstSubstitute, IMultiSubstitution } from '@/features/oss/models/oss';
import { IMultiSubstitution } from '@/features/oss/models/oss';
import { APP_COLORS } from '@/styling/colors';
import { errorMsg } from '@/utils/labels';
import { ICstSubstitute } from '../backend/api';
import { ConstituentaID, IConstituenta, IRSForm } from '../models/rsform';
import BadgeConstituenta from './BadgeConstituenta';
import SelectConstituenta from './SelectConstituenta';

View File

@ -7,11 +7,10 @@ import { Loader } from '@/components/Loader';
import { ModalForm } from '@/components/Modal';
import { TabLabel, TabList, TabPanel, Tabs } from '@/components/Tabs';
import { LibraryItemID } from '@/features/library/models/library';
import { ICstSubstitute } from '@/features/oss/models/oss';
import { useRSForm } from '@/features/rsform/backend/useRSForm';
import { useDialogsStore } from '@/stores/dialogs';
import { IInlineSynthesisDTO } from '../../backend/api';
import { ICstSubstitute, IInlineSynthesisDTO } from '../../backend/api';
import { ConstituentaID, IRSForm } from '../../models/rsform';
import TabConstituents from './TabConstituents';
import TabSource from './TabSource';

View File

@ -1,8 +1,8 @@
'use client';
import { LibraryItemID } from '@/features/library/models/library';
import { ICstSubstitute } from '@/features/oss/models/oss';
import { ICstSubstitute } from '../../backend/api';
import { useRSFormSuspense } from '../../backend/useRSForm';
import PickSubstitutions from '../../components/PickSubstitutions';
import { ConstituentaID, IRSForm } from '../../models/rsform';

View File

@ -1,29 +1,42 @@
'use client';
import { zodResolver } from '@hookform/resolvers/zod';
import clsx from 'clsx';
import { useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { ErrorField } from '@/components/Input';
import { ModalForm } from '@/components/Modal';
import { HelpTopic } from '@/features/help/models/helpTopic';
import { ICstSubstitute, ICstSubstitutions } from '@/features/oss/models/oss';
import PickSubstitutions from '@/features/rsform/components/PickSubstitutions';
import { useDialogsStore } from '@/stores/dialogs';
import { ICstSubstitutionsDTO, schemaCstSubstitutions } from '../backend/api';
import { useCstSubstitute } from '../backend/useCstSubstitute';
import PickSubstitutions from '../components/PickSubstitutions';
import { IRSForm } from '../models/rsform';
export interface DlgSubstituteCstProps {
schema: IRSForm;
onSubstitute: (data: ICstSubstitutions) => void;
onSubstitute: (data: ICstSubstitutionsDTO) => void;
}
function DlgSubstituteCst() {
const { onSubstitute, schema } = useDialogsStore(state => state.props as DlgSubstituteCstProps);
const [substitutions, setSubstitutions] = useState<ICstSubstitute[]>([]);
const canSubmit = substitutions.length > 0;
const { cstSubstitute } = useCstSubstitute();
function handleSubmit() {
onSubstitute({ substitutions: substitutions });
return true;
const {
handleSubmit,
control,
formState: { errors, isValid }
} = useForm<ICstSubstitutionsDTO>({
resolver: zodResolver(schemaCstSubstitutions),
defaultValues: {
substitutions: []
},
mode: 'onChange'
});
function onSubmit(data: ICstSubstitutionsDTO) {
return cstSubstitute({ itemID: schema.id, data: data }).then(() => onSubstitute(data));
}
return (
@ -31,18 +44,25 @@ function DlgSubstituteCst() {
header='Отождествление'
submitText='Отождествить'
submitInvalidTooltip='Выберите две различные конституенты'
canSubmit={canSubmit}
onSubmit={handleSubmit}
canSubmit={isValid}
onSubmit={event => void handleSubmit(onSubmit)(event)}
className={clsx('w-[40rem]', 'px-6 pb-3')}
helpTopic={HelpTopic.UI_SUBSTITUTIONS}
>
<PickSubstitutions
allowSelfSubstitution
value={substitutions}
onChange={setSubstitutions}
rows={6}
schemas={[schema]}
<Controller
control={control}
name='substitutions'
render={({ field }) => (
<PickSubstitutions
allowSelfSubstitution
value={field.value}
onChange={field.onChange}
rows={6}
schemas={[schema]}
/>
)}
/>
<ErrorField className='mt-2' error={errors.substitutions} />
</ModalForm>
);
}

View File

@ -42,7 +42,6 @@ import { EXTEOR_TRS_FILE } from '@/utils/constants';
import { describeAccessMode, labelAccessMode, tooltipText } from '@/utils/labels';
import { generatePageQR, promptUnsaved, sharePage } from '@/utils/utils';
import { useCstSubstitute } from '../../backend/useCstSubstitute';
import { useDownloadRSForm } from '../../backend/useDownloadRSForm';
import { useInlineSynthesis } from '../../backend/useInlineSynthesis';
import { useMutatingRSForm } from '../../backend/useMutatingRSForm';
@ -66,7 +65,6 @@ function MenuRSTabs() {
const { restoreOrder } = useRestoreOrder();
const { produceStructure } = useProduceStructure();
const { inlineSynthesis } = useInlineSynthesis();
const { cstSubstitute } = useCstSubstitute();
const { download } = useDownloadRSForm();
const showInlineSynthesis = useDialogsStore(state => state.showInlineSynthesis);
@ -167,9 +165,7 @@ function MenuRSTabs() {
showSubstituteCst({
schema: controller.schema,
onSubstitute: data =>
void cstSubstitute({ itemID: controller.schema.id, data }).then(() =>
controller.setSelected(prev => prev.filter(id => !data.substitutions.find(sub => sub.original === id)))
)
controller.setSelected(prev => prev.filter(id => !data.substitutions.find(sub => sub.original === id)))
});
}

View File

@ -139,7 +139,8 @@ export const errorMsg = {
privacyNotAccepted: 'Примите политику обработки персональных данных',
loginFormat: 'Имя пользователя должно содержать только буквы и цифры',
invalidLocation: 'Некорректный формат пути',
versionTaken: 'Версия с таким шифром уже существует'
versionTaken: 'Версия с таким шифром уже существует',
emptySubstitutions: 'Выберите хотя бы одно отождествление'
};
/**