F: Rework CstRename dialog
Some checks are pending
Frontend CI / build (22.x) (push) Waiting to run

This commit is contained in:
Ivan 2025-02-07 20:45:32 +03:00
parent a44c1cd170
commit ef209868bf
5 changed files with 59 additions and 54 deletions

View File

@ -94,11 +94,16 @@ export type ICstUpdateDTO = z.infer<typeof CstUpdateSchema>;
/** /**
* Represents data, used in renaming {@link IConstituenta}. * Represents data, used in renaming {@link IConstituenta}.
*/ */
export interface ICstRenameDTO { export const CstRenameSchema = z.object({
alias: string; target: z.number(),
cst_type: CstType; alias: z.string(),
target: ConstituentaID; cst_type: z.nativeEnum(CstType)
} });
/**
* Represents data, used in renaming {@link IConstituenta}.
*/
export type ICstRenameDTO = z.infer<typeof CstRenameSchema>;
/** /**
* Represents data, used in ordering a list of {@link IConstituenta}. * Represents data, used in ordering a list of {@link IConstituenta}.

View File

@ -4,7 +4,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
import clsx from 'clsx'; import clsx from 'clsx';
import { Controller, useForm, useWatch } from 'react-hook-form'; import { Controller, useForm, useWatch } from 'react-hook-form';
import { VersionCreateSchema, IVersionCreateDTO } from '@/backend/library/api'; import { IVersionCreateDTO, VersionCreateSchema } from '@/backend/library/api';
import { useVersionCreate } from '@/backend/library/useVersionCreate'; import { useVersionCreate } from '@/backend/library/useVersionCreate';
import { Checkbox, TextArea, TextInput } from '@/components/ui/Input'; import { Checkbox, TextArea, TextInput } from '@/components/ui/Input';
import { ModalForm } from '@/components/ui/Modal'; import { ModalForm } from '@/components/ui/Modal';

View File

@ -16,6 +16,7 @@ import { TextArea, TextInput } from '@/components/ui/Input';
import { ModalView } from '@/components/ui/Modal'; import { ModalView } from '@/components/ui/Modal';
import { LibraryItemID, VersionID } from '@/models/library'; import { LibraryItemID, VersionID } from '@/models/library';
import { useDialogsStore } from '@/stores/dialogs'; import { useDialogsStore } from '@/stores/dialogs';
import { errors } from '@/utils/labels';
import TableVersions from './TableVersions'; import TableVersions from './TableVersions';
@ -37,14 +38,15 @@ function DlgEditVersions() {
handleSubmit, handleSubmit,
control, control,
reset, reset,
formState: { isDirty } formState: { isDirty, errors: formErrors }
} = useForm<IVersionUpdateDTO>({ } = useForm<IVersionUpdateDTO>({
resolver: zodResolver(VersionUpdateSchema), resolver: zodResolver(VersionUpdateSchema),
defaultValues: { defaultValues: {
id: schema.versions[0].id, id: schema.versions[0].id,
version: schema.versions[0].version, version: schema.versions[0].version,
description: schema.versions[0].description description: schema.versions[0].description
} },
context: { schema: schema }
}); });
const versionID = useWatch({ control, name: 'id' }); const versionID = useWatch({ control, name: 'id' });
const versionName = useWatch({ control, name: 'version' }); const versionName = useWatch({ control, name: 'version' });
@ -92,11 +94,18 @@ function DlgEditVersions() {
/> />
<form className='flex' onSubmit={event => void handleSubmit(onUpdate)(event)}> <form className='flex' onSubmit={event => void handleSubmit(onUpdate)(event)}>
<TextInput id='dlg_version' {...register('version')} dense label='Версия' className='w-[16rem] mr-3' /> <TextInput
id='dlg_version'
{...register('version')}
dense
label='Версия'
className='w-[16rem] mr-3'
error={formErrors.version}
/>
<div className='cc-icons'> <div className='cc-icons'>
<MiniButton <MiniButton
type='submit' type='submit'
title='Сохранить изменения' title={isValid ? 'Сохранить изменения' : errors.versionTaken}
disabled={!isDirty || !isValid || isProcessing} disabled={!isDirty || !isValid || isProcessing}
icon={<IconSave size='1.25rem' className='icon-primary' />} icon={<IconSave size='1.25rem' className='icon-primary' />}
/> />

View File

@ -1,14 +1,15 @@
'use client'; 'use client';
import { zodResolver } from '@hookform/resolvers/zod';
import clsx from 'clsx'; import clsx from 'clsx';
import { useEffect, useState } from 'react'; import { useForm, useWatch } from 'react-hook-form';
import { ICstRenameDTO } from '@/backend/rsform/api'; import { CstRenameSchema, ICstRenameDTO } from '@/backend/rsform/api';
import { useCstRename } from '@/backend/rsform/useCstRename';
import { SelectSingle, TextInput } from '@/components/ui/Input'; import { SelectSingle, TextInput } from '@/components/ui/Input';
import { ModalForm } from '@/components/ui/Modal'; import { ModalForm } from '@/components/ui/Modal';
import usePartialUpdate from '@/hooks/usePartialUpdate';
import { HelpTopic } from '@/models/miscellaneous'; import { HelpTopic } from '@/models/miscellaneous';
import { CstType, IRSForm } from '@/models/rsform'; import { CstType, IConstituenta, IRSForm } from '@/models/rsform';
import { generateAlias, validateNewAlias } from '@/models/rsformAPI'; import { generateAlias, validateNewAlias } from '@/models/rsformAPI';
import { useDialogsStore } from '@/stores/dialogs'; import { useDialogsStore } from '@/stores/dialogs';
import { labelCstType } from '@/utils/labels'; import { labelCstType } from '@/utils/labels';
@ -16,29 +17,35 @@ import { SelectorCstType } from '@/utils/selectors';
export interface DlgRenameCstProps { export interface DlgRenameCstProps {
schema: IRSForm; schema: IRSForm;
initial: ICstRenameDTO; target: IConstituenta;
allowChangeType: boolean;
onRename: (data: ICstRenameDTO) => void;
} }
function DlgRenameCst() { function DlgRenameCst() {
const { schema, initial, allowChangeType, onRename } = useDialogsStore(state => state.props as DlgRenameCstProps); const { schema, target } = useDialogsStore(state => state.props as DlgRenameCstProps);
const [validated, setValidated] = useState(false); const { cstRename } = useCstRename();
const [cstData, updateData] = usePartialUpdate(initial);
useEffect(() => { const { register, setValue, handleSubmit, control } = useForm<ICstRenameDTO>({
if (initial && cstData.cst_type !== initial.cst_type) { resolver: zodResolver(CstRenameSchema),
updateData({ alias: generateAlias(cstData.cst_type, schema) }); defaultValues: {
target: target.id,
alias: target.alias,
cst_type: target.cst_type
} }
}, [initial, cstData.cst_type, updateData, schema]); });
const alias = useWatch({ control, name: 'alias' });
const cst_type = useWatch({ control, name: 'cst_type' });
useEffect(() => { // TODO: validate in ZOD
setValidated(cstData.alias !== initial.alias && validateNewAlias(cstData.alias, cstData.cst_type, schema)); const validated = alias !== target.alias && validateNewAlias(alias, cst_type, schema);
}, [cstData.cst_type, cstData.alias, initial, schema]);
function handleSubmit() { function onSubmit(data: ICstRenameDTO) {
onRename(cstData); console.log(data);
return true; cstRename({ itemID: schema.id, data: data });
}
function handleChangeType(newType: CstType) {
setValue('alias', generateAlias(newType, schema));
setValue('cst_type', newType);
} }
return ( return (
@ -47,7 +54,7 @@ function DlgRenameCst() {
submitText='Переименовать' submitText='Переименовать'
submitInvalidTooltip='Введите незанятое имя, соответствующее типу' submitInvalidTooltip='Введите незанятое имя, соответствующее типу'
canSubmit={validated} canSubmit={validated}
onSubmit={handleSubmit} onSubmit={event => void handleSubmit(onSubmit)(event)}
className={clsx('w-[30rem]', 'py-6 pr-3 pl-6 flex gap-3 justify-center items-center ')} className={clsx('w-[30rem]', 'py-6 pr-3 pl-6 flex gap-3 justify-center items-center ')}
helpTopic={HelpTopic.CC_CONSTITUENTA} helpTopic={HelpTopic.CC_CONSTITUENTA}
> >
@ -55,22 +62,20 @@ function DlgRenameCst() {
id='dlg_cst_type' id='dlg_cst_type'
placeholder='Выберите тип' placeholder='Выберите тип'
className='min-w-[16rem]' className='min-w-[16rem]'
isDisabled={!allowChangeType} isDisabled={target.is_inherited}
options={SelectorCstType} options={SelectorCstType}
value={{ value={{
value: cstData.cst_type, value: cst_type,
label: labelCstType(cstData.cst_type) label: labelCstType(cst_type)
}} }}
onChange={data => updateData({ cst_type: data?.value ?? CstType.BASE })} onChange={data => handleChangeType(data?.value ?? CstType.BASE)}
/> />
<TextInput <TextInput
id='dlg_cst_alias' id='dlg_cst_alias' //
{...register('alias')}
dense dense
label='Имя' label='Имя'
className='w-[7rem]' className='w-[7rem]'
value={cstData.alias}
onChange={event => updateData({ alias: event.target.value })}
/> />
</ModalForm> </ModalForm>
); );

View File

@ -1,7 +1,5 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { ICstRenameDTO } from '@/backend/rsform/api';
import { useCstRename } from '@/backend/rsform/useCstRename';
import { useMutatingRSForm } from '@/backend/rsform/useMutatingRSForm'; import { useMutatingRSForm } from '@/backend/rsform/useMutatingRSForm';
import { IconEdit } from '@/components/Icons'; import { IconEdit } from '@/components/Icons';
import { Overlay } from '@/components/ui/Container'; import { Overlay } from '@/components/ui/Container';
@ -16,7 +14,6 @@ import { useRSEdit } from '../RSEditContext';
interface EditorControlsProps { interface EditorControlsProps {
constituenta: IConstituenta; constituenta: IConstituenta;
disabled: boolean; disabled: boolean;
onEditTerm: () => void; onEditTerm: () => void;
} }
@ -26,20 +23,9 @@ function EditorControls({ constituenta, disabled, onEditTerm }: EditorControlsPr
const isProcessing = useMutatingRSForm(); const isProcessing = useMutatingRSForm();
const showRenameCst = useDialogsStore(state => state.showRenameCst); const showRenameCst = useDialogsStore(state => state.showRenameCst);
const { cstRename } = useCstRename();
function handleRenameCst() { function handleRenameCst() {
const initialData: ICstRenameDTO = { showRenameCst({ schema: schema, target: constituenta });
target: constituenta.id,
alias: constituenta.alias,
cst_type: constituenta.cst_type
};
showRenameCst({
schema: schema,
initial: initialData,
allowChangeType: !constituenta.is_inherited,
onRename: data => cstRename({ itemID: schema.id, data })
});
} }
return ( return (