mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-25 20:40:36 +03:00
R: Move dialogs to form basis
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
05422df430
commit
bb33c1f212
|
@ -30,7 +30,7 @@ export interface IRenameLocationDTO {
|
|||
/**
|
||||
* Represents data, used for cloning {@link IRSForm}.
|
||||
*/
|
||||
export interface IRCloneLibraryItemDTO extends Omit<ILibraryItem, 'time_create' | 'time_update' | 'owner'> {
|
||||
export interface ICloneLibraryItemDTO extends Omit<ILibraryItem, 'time_create' | 'time_update' | 'owner'> {
|
||||
items?: ConstituentaID[];
|
||||
}
|
||||
|
||||
|
@ -90,11 +90,16 @@ export type IUpdateLibraryItemDTO = z.infer<typeof UpdateLibraryItemSchema>;
|
|||
/**
|
||||
* Create version metadata in persistent storage.
|
||||
*/
|
||||
export interface IVersionCreateDTO {
|
||||
version: string;
|
||||
description: string;
|
||||
items?: ConstituentaID[];
|
||||
}
|
||||
export const CreateVersionSchema = z.object({
|
||||
version: z.string(),
|
||||
description: z.string(),
|
||||
items: z.array(z.number()).optional()
|
||||
});
|
||||
|
||||
/**
|
||||
* Create version metadata in persistent storage.
|
||||
*/
|
||||
export type IVersionCreateDTO = z.infer<typeof CreateVersionSchema>;
|
||||
|
||||
/**
|
||||
* Represents data response when creating {@link IVersionInfo}.
|
||||
|
@ -197,8 +202,8 @@ export const libraryApi = {
|
|||
successMessage: information.itemDestroyed
|
||||
}
|
||||
}),
|
||||
cloneItem: (data: IRCloneLibraryItemDTO) =>
|
||||
axiosPost<IRCloneLibraryItemDTO, IRSFormDTO>({
|
||||
cloneItem: (data: ICloneLibraryItemDTO) =>
|
||||
axiosPost<ICloneLibraryItemDTO, IRSFormDTO>({
|
||||
endpoint: `/api/library/${data.id}/clone`,
|
||||
request: {
|
||||
data: data,
|
||||
|
@ -214,8 +219,8 @@ export const libraryApi = {
|
|||
}
|
||||
}),
|
||||
|
||||
versionCreate: ({ itemID, data }: { itemID: LibraryItemID; data: IVersionData }) =>
|
||||
axiosPost<IVersionData, IVersionCreatedResponse>({
|
||||
versionCreate: ({ itemID, data }: { itemID: LibraryItemID; data: IVersionCreateDTO }) =>
|
||||
axiosPost<IVersionCreateDTO, IVersionCreatedResponse>({
|
||||
endpoint: `/api/library/${itemID}/create-version`,
|
||||
request: {
|
||||
data: data,
|
||||
|
|
|
@ -3,7 +3,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|||
import { DataCallback } from '@/backend/apiTransport';
|
||||
|
||||
import { IRSFormDTO } from '../rsform/api';
|
||||
import { IRCloneLibraryItemDTO, libraryApi } from './api';
|
||||
import { ICloneLibraryItemDTO, libraryApi } from './api';
|
||||
|
||||
export const useCloneItem = () => {
|
||||
const client = useQueryClient();
|
||||
|
@ -14,7 +14,7 @@ export const useCloneItem = () => {
|
|||
});
|
||||
return {
|
||||
cloneItem: (
|
||||
data: IRCloneLibraryItemDTO, //
|
||||
data: ICloneLibraryItemDTO, //
|
||||
onSuccess?: DataCallback<IRSFormDTO>
|
||||
) => mutation.mutate(data, { onSuccess })
|
||||
};
|
||||
|
|
|
@ -3,9 +3,9 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|||
import { DataCallback } from '@/backend/apiTransport';
|
||||
import { useUpdateTimestamp } from '@/backend/library/useUpdateTimestamp';
|
||||
import { rsformsApi } from '@/backend/rsform/api';
|
||||
import { IVersionData, LibraryItemID, VersionID } from '@/models/library';
|
||||
import { LibraryItemID, VersionID } from '@/models/library';
|
||||
|
||||
import { libraryApi } from './api';
|
||||
import { IVersionCreateDTO, libraryApi } from './api';
|
||||
|
||||
export const useVersionCreate = () => {
|
||||
const client = useQueryClient();
|
||||
|
@ -22,7 +22,7 @@ export const useVersionCreate = () => {
|
|||
versionCreate: (
|
||||
data: {
|
||||
itemID: LibraryItemID; //
|
||||
data: IVersionData;
|
||||
data: IVersionCreateDTO;
|
||||
},
|
||||
onSuccess?: DataCallback<VersionID>
|
||||
) => mutation.mutate(data, { onSuccess: response => onSuccess?.(response.version) })
|
||||
|
|
|
@ -33,11 +33,14 @@ export interface ModalProps extends CProps.Styling {
|
|||
/** Indicates that the modal window should be scrollable. */
|
||||
overflowVisible?: boolean;
|
||||
|
||||
/** ID of the form to be submitted. */
|
||||
formID?: string;
|
||||
|
||||
/** Callback to be called before submit. */
|
||||
beforeSubmit?: () => boolean;
|
||||
|
||||
/** Callback to be called after submit. */
|
||||
onSubmit?: () => void;
|
||||
onSubmit?: () => boolean;
|
||||
|
||||
/** Callback to be called after cancel. */
|
||||
onCancel?: () => void;
|
||||
|
@ -64,6 +67,7 @@ function Modal({
|
|||
overflowVisible,
|
||||
|
||||
beforeSubmit,
|
||||
formID,
|
||||
onSubmit,
|
||||
onCancel,
|
||||
className,
|
||||
|
@ -84,7 +88,15 @@ function Modal({
|
|||
if (beforeSubmit && !beforeSubmit()) {
|
||||
return;
|
||||
}
|
||||
onSubmit?.();
|
||||
if (onSubmit && !onSubmit()) {
|
||||
return;
|
||||
}
|
||||
if (formID) {
|
||||
const element = document.getElementById(formID) as HTMLFormElement;
|
||||
if (element) {
|
||||
element.requestSubmit();
|
||||
}
|
||||
}
|
||||
hideDialog();
|
||||
};
|
||||
|
||||
|
|
|
@ -35,13 +35,18 @@ function DlgChangeInputSchema() {
|
|||
setSelected(newValue);
|
||||
}
|
||||
|
||||
function handleSubmit() {
|
||||
onSubmit(target.id, selected);
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
overflowVisible
|
||||
header='Выбор концептуальной схемы'
|
||||
submitText='Подтвердить выбор'
|
||||
canSubmit={isValid}
|
||||
onSubmit={() => onSubmit(target.id, selected)}
|
||||
onSubmit={handleSubmit}
|
||||
className={clsx('w-[35rem]', 'pb-3 px-6 cc-column')}
|
||||
>
|
||||
<div className='flex justify-between gap-3 items-center'>
|
||||
|
|
|
@ -33,6 +33,11 @@ function DlgChangeLocation() {
|
|||
setBody(newValue.length > 3 ? newValue.substring(3) : '');
|
||||
}
|
||||
|
||||
function handleSubmit() {
|
||||
onChangeLocation(location);
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
overflowVisible
|
||||
|
@ -40,7 +45,7 @@ function DlgChangeLocation() {
|
|||
submitText='Переместить'
|
||||
submitInvalidTooltip={`Допустимы буквы, цифры, подчерк, пробел и "!". Сегмент пути не может начинаться и заканчиваться пробелом. Общая длина (с корнем) не должна превышать ${limits.location_len}`}
|
||||
canSubmit={isValid}
|
||||
onSubmit={() => onChangeLocation(location)}
|
||||
onSubmit={handleSubmit}
|
||||
className={clsx('w-[35rem]', 'pb-3 px-6 flex gap-3 h-[9rem]')}
|
||||
>
|
||||
<div className='flex flex-col gap-2 min-w-[7rem] h-min'>
|
||||
|
|
|
@ -6,7 +6,7 @@ import { useState } from 'react';
|
|||
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
|
||||
import { urls } from '@/app/urls';
|
||||
import { useAuthSuspense } from '@/backend/auth/useAuth';
|
||||
import { IRCloneLibraryItemDTO } from '@/backend/library/api';
|
||||
import { ICloneLibraryItemDTO } from '@/backend/library/api';
|
||||
import { useCloneItem } from '@/backend/library/useCloneItem';
|
||||
import { VisibilityIcon } from '@/components/DomainIcons';
|
||||
import SelectAccessPolicy from '@/components/select/SelectAccessPolicy';
|
||||
|
@ -59,7 +59,7 @@ function DlgCloneLibraryItem() {
|
|||
}
|
||||
|
||||
function handleSubmit() {
|
||||
const data: IRCloneLibraryItemDTO = {
|
||||
const data: ICloneLibraryItemDTO = {
|
||||
id: base.id,
|
||||
item_type: base.item_type,
|
||||
title: title,
|
||||
|
@ -74,6 +74,7 @@ function DlgCloneLibraryItem() {
|
|||
data.items = selected;
|
||||
}
|
||||
cloneItem(data, newSchema => router.push(urls.schema(newSchema.id)));
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -19,6 +19,7 @@ export interface DlgCreateCstProps {
|
|||
|
||||
function DlgCreateCst() {
|
||||
const { initial, schema, onCreate } = useDialogsStore(state => state.props as DlgCreateCstProps);
|
||||
|
||||
const [validated, setValidated] = useState(false);
|
||||
const [cstData, updateCstData] = usePartialUpdate(
|
||||
initial || {
|
||||
|
@ -33,7 +34,10 @@ function DlgCreateCst() {
|
|||
}
|
||||
);
|
||||
|
||||
const handleSubmit = () => onCreate(cstData);
|
||||
const handleSubmit = () => {
|
||||
onCreate(cstData);
|
||||
return true;
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
|
|
|
@ -82,6 +82,7 @@ function DlgCreateOperation() {
|
|||
arguments: activeTab === TabID.INPUT ? undefined : inputs.length > 0 ? inputs : undefined,
|
||||
create_schema: createSchema
|
||||
});
|
||||
return true;
|
||||
};
|
||||
|
||||
function handleSelectTab(newTab: TabID, last: TabID) {
|
||||
|
|
|
@ -1,71 +1,78 @@
|
|||
'use client';
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import clsx from 'clsx';
|
||||
import { useState } from 'react';
|
||||
import { Controller, useForm, useWatch } from 'react-hook-form';
|
||||
|
||||
import { IVersionCreateDTO } from '@/backend/library/api';
|
||||
import { CreateVersionSchema, IVersionCreateDTO } from '@/backend/library/api';
|
||||
import { useVersionCreate } from '@/backend/library/useVersionCreate';
|
||||
import Checkbox from '@/components/ui/Checkbox';
|
||||
import Modal from '@/components/ui/Modal';
|
||||
import TextArea from '@/components/ui/TextArea';
|
||||
import TextInput from '@/components/ui/TextInput';
|
||||
import { IVersionInfo } from '@/models/library';
|
||||
import { IVersionInfo, LibraryItemID, VersionID } from '@/models/library';
|
||||
import { nextVersion } from '@/models/libraryAPI';
|
||||
import { ConstituentaID } from '@/models/rsform';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
import { globals } from '@/utils/constants';
|
||||
|
||||
export interface DlgCreateVersionProps {
|
||||
itemID: LibraryItemID;
|
||||
versions: IVersionInfo[];
|
||||
onCreate: (data: IVersionCreateDTO) => void;
|
||||
onCreate: (newVersion: VersionID) => void;
|
||||
selected: ConstituentaID[];
|
||||
totalCount: number;
|
||||
}
|
||||
|
||||
function DlgCreateVersion() {
|
||||
const { versions, selected, totalCount, onCreate } = useDialogsStore(state => state.props as DlgCreateVersionProps);
|
||||
const [version, setVersion] = useState(versions.length > 0 ? nextVersion(versions[0].version) : '1.0.0');
|
||||
const [description, setDescription] = useState('');
|
||||
const [onlySelected, setOnlySelected] = useState(false);
|
||||
const {
|
||||
itemID, //
|
||||
versions,
|
||||
selected,
|
||||
totalCount,
|
||||
onCreate
|
||||
} = useDialogsStore(state => state.props as DlgCreateVersionProps);
|
||||
const { versionCreate } = useVersionCreate();
|
||||
|
||||
const { register, handleSubmit, control } = useForm<IVersionCreateDTO>({
|
||||
resolver: zodResolver(CreateVersionSchema),
|
||||
defaultValues: {
|
||||
version: versions.length > 0 ? nextVersion(versions[0].version) : '1.0.0',
|
||||
description: '',
|
||||
items: undefined
|
||||
}
|
||||
});
|
||||
const version = useWatch({ control, name: 'version' });
|
||||
const canSubmit = !versions.find(ver => ver.version === version);
|
||||
|
||||
function handleSubmit() {
|
||||
onCreate({
|
||||
version: version,
|
||||
description: description,
|
||||
items: onlySelected ? selected : undefined
|
||||
});
|
||||
function onSubmit(data: IVersionCreateDTO) {
|
||||
versionCreate({ itemID, data }, onCreate);
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
header='Создание версии'
|
||||
canSubmit={canSubmit}
|
||||
onSubmit={handleSubmit}
|
||||
submitText='Создать'
|
||||
className={clsx('cc-column', 'w-[30rem]', 'py-2 px-6')}
|
||||
>
|
||||
<TextInput
|
||||
id='dlg_version'
|
||||
dense
|
||||
label='Версия'
|
||||
className='w-[16rem]'
|
||||
value={version}
|
||||
onChange={event => setVersion(event.target.value)}
|
||||
/>
|
||||
<TextArea
|
||||
id='dlg_description'
|
||||
spellCheck
|
||||
label='Описание'
|
||||
rows={3}
|
||||
value={description}
|
||||
onChange={event => setDescription(event.target.value)}
|
||||
/>
|
||||
<Checkbox
|
||||
id='dlg_only_selected'
|
||||
label={`Только выбранные конституенты [${selected.length} из ${totalCount}]`}
|
||||
value={onlySelected}
|
||||
onChange={value => setOnlySelected(value)}
|
||||
/>
|
||||
<Modal header='Создание версии' canSubmit={canSubmit} submitText='Создать' formID={globals.dlg_create_version}>
|
||||
<form
|
||||
id={globals.dlg_create_version}
|
||||
className={clsx('cc-column', 'w-[30rem]', 'py-2 px-6')}
|
||||
onSubmit={event => void handleSubmit(onSubmit)(event)}
|
||||
>
|
||||
<TextInput id='dlg_version' {...register('version')} dense label='Версия' className='w-[16rem]' />
|
||||
<TextArea id='dlg_description' {...register('description')} spellCheck label='Описание' rows={3} />
|
||||
{selected.length > 0 ? (
|
||||
<Controller
|
||||
control={control}
|
||||
name='items'
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
id='dlg_only_selected'
|
||||
label={`Только выбранные конституенты [${selected.length} из ${totalCount}]`}
|
||||
value={field.value !== undefined}
|
||||
onChange={value => field.onChange(value ? selected : undefined)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
) : null}
|
||||
</form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ function DlgCstTemplate() {
|
|||
|
||||
function handleSubmit() {
|
||||
onCreate(constituenta);
|
||||
return true;
|
||||
}
|
||||
|
||||
function handlePrompt(): boolean {
|
||||
|
|
|
@ -19,7 +19,6 @@ export interface DlgDeleteCstProps {
|
|||
|
||||
function DlgDeleteCst() {
|
||||
const { selected, schema, onDelete } = useDialogsStore(state => state.props as DlgDeleteCstProps);
|
||||
const hideDialog = useDialogsStore(state => state.hideDialog);
|
||||
const [expandOut, setExpandOut] = useState(false);
|
||||
const expansion: ConstituentaID[] = schema.graph.expandAllOutputs(selected);
|
||||
const hasInherited = selected.some(
|
||||
|
@ -28,12 +27,12 @@ function DlgDeleteCst() {
|
|||
);
|
||||
|
||||
function handleSubmit() {
|
||||
hideDialog();
|
||||
if (expandOut) {
|
||||
onDelete(selected.concat(expansion));
|
||||
} else {
|
||||
onDelete(selected);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -22,6 +22,7 @@ function DlgDeleteOperation() {
|
|||
|
||||
function handleSubmit() {
|
||||
onSubmit(target.id, keepConstituents, deleteSchema);
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -26,6 +26,7 @@ function DlgEditEditors() {
|
|||
|
||||
function handleSubmit() {
|
||||
onChangeEditors(selected);
|
||||
return true;
|
||||
}
|
||||
|
||||
function onDeleteEditor(target: UserID) {
|
||||
|
|
|
@ -119,6 +119,7 @@ function DlgEditOperation() {
|
|||
arguments: target.operation_type !== OperationType.SYNTHESIS ? undefined : inputs,
|
||||
substitutions: target.operation_type !== OperationType.SYNTHESIS ? undefined : substitutions
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -40,12 +40,17 @@ function DlgEditReference() {
|
|||
const [reference, setReference] = useState('');
|
||||
const [isValid, setIsValid] = useState(false);
|
||||
|
||||
function handleSubmit() {
|
||||
onSave(reference);
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
header='Редактирование ссылки'
|
||||
submitText='Сохранить ссылку'
|
||||
canSubmit={isValid}
|
||||
onSubmit={() => onSave(reference)}
|
||||
onSubmit={handleSubmit}
|
||||
className='w-[40rem] px-6 h-[32rem]'
|
||||
helpTopic={HelpTopic.TERM_CONTROL}
|
||||
>
|
||||
|
|
|
@ -63,6 +63,7 @@ function DlgEditWordForms() {
|
|||
})
|
||||
);
|
||||
onSave(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
function handleAddForm() {
|
||||
|
|
|
@ -17,11 +17,16 @@ function DlgGraphParams() {
|
|||
const { initial, onConfirm } = useDialogsStore(state => state.props as DlgGraphParamsProps);
|
||||
const [params, updateParams] = usePartialUpdate(initial);
|
||||
|
||||
function handleSubmit() {
|
||||
onConfirm(params);
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
canSubmit
|
||||
header='Настройки графа термов'
|
||||
onSubmit={() => onConfirm(params)}
|
||||
onSubmit={handleSubmit}
|
||||
submitText='Применить'
|
||||
className='flex gap-6 justify-between px-6 pb-3 w-[30rem]'
|
||||
>
|
||||
|
|
|
@ -43,7 +43,7 @@ function DlgInlineSynthesis() {
|
|||
|
||||
function handleSubmit() {
|
||||
if (!sourceID || selected.length === 0) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
onInlineSynthesis({
|
||||
source: sourceID,
|
||||
|
@ -51,6 +51,7 @@ function DlgInlineSynthesis() {
|
|||
items: selected,
|
||||
substitutions: substitutions
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -76,13 +76,13 @@ function DlgRelocateConstituents() {
|
|||
}
|
||||
|
||||
function handleSubmit() {
|
||||
if (!destination) {
|
||||
return;
|
||||
if (destination) {
|
||||
onSubmit({
|
||||
destination: destination.id,
|
||||
items: selected
|
||||
});
|
||||
}
|
||||
onSubmit({
|
||||
destination: destination.id,
|
||||
items: selected
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -37,13 +37,18 @@ function DlgRenameCst() {
|
|||
setValidated(cstData.alias !== initial.alias && validateNewAlias(cstData.alias, cstData.cst_type, schema));
|
||||
}, [cstData.cst_type, cstData.alias, initial, schema]);
|
||||
|
||||
function handleSubmit() {
|
||||
onRename(cstData);
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
header='Переименование конституенты'
|
||||
submitText='Переименовать'
|
||||
submitInvalidTooltip='Введите незанятое имя, соответствующее типу'
|
||||
canSubmit={validated}
|
||||
onSubmit={() => onRename(cstData)}
|
||||
onSubmit={handleSubmit}
|
||||
className={clsx('w-[30rem]', 'py-6 pr-3 pl-6 flex gap-3 justify-center items-center ')}
|
||||
helpTopic={HelpTopic.CC_CONSTITUENTA}
|
||||
>
|
||||
|
|
|
@ -21,10 +21,8 @@ function DlgSubstituteCst() {
|
|||
const canSubmit = substitutions.length > 0;
|
||||
|
||||
function handleSubmit() {
|
||||
const data: ICstSubstitutions = {
|
||||
substitutions: substitutions
|
||||
};
|
||||
onSubstitute(data);
|
||||
onSubstitute({ substitutions: substitutions });
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -21,15 +21,15 @@ function DlgUploadRSForm() {
|
|||
const [file, setFile] = useState<File | undefined>();
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (!file) {
|
||||
return;
|
||||
if (file) {
|
||||
upload({
|
||||
itemID: itemID,
|
||||
load_metadata: loadMetadata,
|
||||
file: file,
|
||||
fileName: file.name
|
||||
});
|
||||
}
|
||||
upload({
|
||||
itemID: itemID,
|
||||
load_metadata: loadMetadata,
|
||||
file: file,
|
||||
fileName: file.name
|
||||
});
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleFile = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
|
|
|
@ -57,7 +57,10 @@ export interface IVersionInfo {
|
|||
/**
|
||||
* Represents version data, intended to update version metadata in persistent storage.
|
||||
*/
|
||||
export interface IVersionData extends Omit<IVersionInfo, 'id' | 'time_create' | 'item'> {}
|
||||
export interface IVersionData {
|
||||
version: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents library item common data typical for all item types.
|
||||
|
|
|
@ -45,7 +45,7 @@ function EditorOssCard() {
|
|||
)}
|
||||
>
|
||||
<FlexColumn className='px-3'>
|
||||
<FormOSS id={globals.library_item_editor} />
|
||||
<FormOSS />
|
||||
<EditorLibraryItem controller={controller} />
|
||||
</FlexColumn>
|
||||
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
'use no memo'; // TODO: remove when react hook forms are compliant with react compiler
|
||||
'use client';
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import clsx from 'clsx';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
|
||||
import { IUpdateLibraryItemDTO } from '@/backend/library/api';
|
||||
import { IUpdateLibraryItemDTO, UpdateLibraryItemSchema } from '@/backend/library/api';
|
||||
import { useUpdateItem } from '@/backend/library/useUpdateItem';
|
||||
import { useMutatingOss } from '@/backend/oss/useMutatingOss';
|
||||
import { IconSave } from '@/components/Icons';
|
||||
|
@ -13,112 +16,85 @@ import TextInput from '@/components/ui/TextInput';
|
|||
import { LibraryItemType } from '@/models/library';
|
||||
import ToolbarItemAccess from '@/pages/RSFormPage/EditorRSFormCard/ToolbarItemAccess';
|
||||
import { useModificationStore } from '@/stores/modification';
|
||||
import { globals } from '@/utils/constants';
|
||||
|
||||
import { useOssEdit } from '../OssEditContext';
|
||||
|
||||
interface FormOSSProps {
|
||||
id?: string;
|
||||
}
|
||||
|
||||
function FormOSS({ id }: FormOSSProps) {
|
||||
const { updateItem: update } = useUpdateItem();
|
||||
function FormOSS() {
|
||||
const { updateItem: updateOss } = useUpdateItem();
|
||||
const controller = useOssEdit();
|
||||
const { isModified, setIsModified } = useModificationStore();
|
||||
const isProcessing = useMutatingOss();
|
||||
const schema = controller.schema;
|
||||
|
||||
const [title, setTitle] = useState(schema.title);
|
||||
const [alias, setAlias] = useState(schema.alias);
|
||||
const [comment, setComment] = useState(schema.comment);
|
||||
const [visible, setVisible] = useState(schema.visible);
|
||||
const [readOnly, setReadOnly] = useState(schema.read_only);
|
||||
|
||||
useEffect(() => {
|
||||
if (schema) {
|
||||
setTitle(schema.title);
|
||||
setAlias(schema.alias);
|
||||
setComment(schema.comment);
|
||||
setVisible(schema.visible);
|
||||
setReadOnly(schema.read_only);
|
||||
}
|
||||
}, [schema]);
|
||||
|
||||
useEffect(() => {
|
||||
setIsModified(
|
||||
schema.title !== title ||
|
||||
schema.alias !== alias ||
|
||||
schema.comment !== comment ||
|
||||
schema.visible !== visible ||
|
||||
schema.read_only !== readOnly
|
||||
);
|
||||
return () => setIsModified(false);
|
||||
}, [
|
||||
schema.title,
|
||||
schema.alias,
|
||||
schema.comment,
|
||||
schema.visible,
|
||||
schema.read_only,
|
||||
title,
|
||||
alias,
|
||||
comment,
|
||||
visible,
|
||||
readOnly,
|
||||
setIsModified
|
||||
]);
|
||||
|
||||
const handleSubmit = (event?: React.FormEvent<HTMLFormElement>) => {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
const data: IUpdateLibraryItemDTO = {
|
||||
id: schema.id,
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
control,
|
||||
setValue,
|
||||
reset,
|
||||
formState: { isDirty, errors }
|
||||
} = useForm<IUpdateLibraryItemDTO>({
|
||||
resolver: zodResolver(UpdateLibraryItemSchema),
|
||||
defaultValues: {
|
||||
id: controller.schema.id,
|
||||
item_type: LibraryItemType.RSFORM,
|
||||
title: title,
|
||||
alias: alias,
|
||||
comment: comment,
|
||||
visible: visible,
|
||||
read_only: readOnly
|
||||
};
|
||||
update(data);
|
||||
};
|
||||
title: controller.schema.title,
|
||||
alias: controller.schema.alias,
|
||||
comment: controller.schema.comment,
|
||||
visible: controller.schema.visible,
|
||||
read_only: controller.schema.read_only
|
||||
}
|
||||
});
|
||||
const visible = useWatch({ control, name: 'visible' });
|
||||
const readOnly = useWatch({ control, name: 'read_only' });
|
||||
|
||||
useEffect(() => {
|
||||
setIsModified(isDirty);
|
||||
}, [isDirty, setIsModified]);
|
||||
|
||||
function onSubmit(data: IUpdateLibraryItemDTO) {
|
||||
updateOss(data, () => reset({ ...data }));
|
||||
}
|
||||
|
||||
return (
|
||||
<form id={id} className={clsx('mt-1 min-w-[22rem] sm:w-[30rem]', 'flex flex-col pt-1')} onSubmit={handleSubmit}>
|
||||
<form
|
||||
id={globals.library_item_editor}
|
||||
className={clsx('mt-1 min-w-[22rem] sm:w-[30rem]', 'flex flex-col pt-1')}
|
||||
onSubmit={event => void handleSubmit(onSubmit)(event)}
|
||||
>
|
||||
<TextInput
|
||||
id='schema_title'
|
||||
required
|
||||
{...register('title')}
|
||||
label='Полное название'
|
||||
className='mb-3'
|
||||
value={title}
|
||||
disabled={!controller.isMutable}
|
||||
onChange={event => setTitle(event.target.value)}
|
||||
error={errors.title}
|
||||
/>
|
||||
<div className='flex justify-between gap-3 mb-3'>
|
||||
<TextInput
|
||||
id='schema_alias'
|
||||
required
|
||||
{...register('alias')}
|
||||
label='Сокращение'
|
||||
className='w-[16rem]'
|
||||
disabled={!controller.isMutable}
|
||||
value={alias}
|
||||
onChange={event => setAlias(event.target.value)}
|
||||
error={errors.alias}
|
||||
/>
|
||||
<ToolbarItemAccess
|
||||
visible={visible}
|
||||
toggleVisible={() => setVisible(prev => !prev)}
|
||||
toggleVisible={() => setValue('visible', !visible, { shouldDirty: true })}
|
||||
readOnly={readOnly}
|
||||
toggleReadOnly={() => setReadOnly(prev => !prev)}
|
||||
toggleReadOnly={() => setValue('read_only', !readOnly, { shouldDirty: true })}
|
||||
controller={controller}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<TextArea
|
||||
id='schema_comment'
|
||||
{...register('comment')}
|
||||
label='Описание'
|
||||
rows={3}
|
||||
value={comment}
|
||||
disabled={!controller.isMutable || isProcessing}
|
||||
onChange={event => setComment(event.target.value)}
|
||||
error={errors.comment}
|
||||
/>
|
||||
{controller.isMutable || isModified ? (
|
||||
<SubmitButton
|
||||
|
|
|
@ -44,7 +44,7 @@ function EditorRSFormCard() {
|
|||
)}
|
||||
>
|
||||
<FlexColumn className='flex-shrink'>
|
||||
<FormRSForm id={globals.library_item_editor} />
|
||||
<FormRSForm />
|
||||
<EditorLibraryItem controller={controller} />
|
||||
</FlexColumn>
|
||||
|
||||
|
|
|
@ -19,16 +19,13 @@ import TextArea from '@/components/ui/TextArea';
|
|||
import TextInput from '@/components/ui/TextInput';
|
||||
import { LibraryItemType, VersionID } from '@/models/library';
|
||||
import { useModificationStore } from '@/stores/modification';
|
||||
import { globals } from '@/utils/constants';
|
||||
|
||||
import { useRSEdit } from '../RSEditContext';
|
||||
import ToolbarItemAccess from './ToolbarItemAccess';
|
||||
import ToolbarVersioning from './ToolbarVersioning';
|
||||
|
||||
interface FormRSFormProps {
|
||||
id?: string;
|
||||
}
|
||||
|
||||
function FormRSForm({ id }: FormRSFormProps) {
|
||||
function FormRSForm() {
|
||||
const controller = useRSEdit();
|
||||
const router = useConceptNavigation();
|
||||
const { updateItem: updateSchema } = useUpdateItem();
|
||||
|
@ -59,7 +56,7 @@ function FormRSForm({ id }: FormRSFormProps) {
|
|||
|
||||
useEffect(() => {
|
||||
setIsModified(isDirty);
|
||||
}, [isDirty, controller.schema, setIsModified]);
|
||||
}, [isDirty, setIsModified]);
|
||||
|
||||
function handleSelectVersion(version?: VersionID) {
|
||||
router.push(urls.schema(controller.schema.id, version));
|
||||
|
@ -71,7 +68,7 @@ function FormRSForm({ id }: FormRSFormProps) {
|
|||
|
||||
return (
|
||||
<form
|
||||
id={id}
|
||||
id={globals.library_item_editor}
|
||||
className={clsx('mt-1 min-w-[22rem] sm:w-[30rem]', 'flex flex-col pt-1')}
|
||||
onSubmit={event => void handleSubmit(onSubmit)(event)}
|
||||
>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
'use client';
|
||||
|
||||
import { useVersionCreate } from '@/backend/library/useVersionCreate';
|
||||
import { useVersionRestore } from '@/backend/library/useVersionRestore';
|
||||
import { IconNewVersion, IconUpload, IconVersions } from '@/components/Icons';
|
||||
import BadgeHelp from '@/components/info/BadgeHelp';
|
||||
|
@ -23,7 +22,6 @@ function ToolbarVersioning({ blockReload }: ToolbarVersioningProps) {
|
|||
const controller = useRSEdit();
|
||||
const { isModified } = useModificationStore();
|
||||
const { versionRestore } = useVersionRestore();
|
||||
const { versionCreate } = useVersionCreate();
|
||||
|
||||
const showCreateVersion = useDialogsStore(state => state.showCreateVersion);
|
||||
const showEditVersions = useDialogsStore(state => state.showEditVersions);
|
||||
|
@ -40,17 +38,11 @@ function ToolbarVersioning({ blockReload }: ToolbarVersioningProps) {
|
|||
return;
|
||||
}
|
||||
showCreateVersion({
|
||||
itemID: controller.schema.id,
|
||||
versions: controller.schema.versions,
|
||||
selected: controller.selected,
|
||||
totalCount: controller.schema.items.length,
|
||||
onCreate: data =>
|
||||
versionCreate(
|
||||
{
|
||||
itemID: controller.schema.id, //
|
||||
data: data
|
||||
},
|
||||
newVersion => controller.navigateVersion(newVersion)
|
||||
)
|
||||
onCreate: newVersion => controller.navigateVersion(newVersion)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import axios from 'axios';
|
||||
import clsx from 'clsx';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { useRequestPasswordReset } from '@/backend/auth/useRequestPasswordReset';
|
||||
import { ErrorData } from '@/components/info/InfoError';
|
||||
|
@ -23,10 +23,6 @@ export function Component() {
|
|||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
clearServerError();
|
||||
}, [email, clearServerError]);
|
||||
|
||||
if (isCompleted) {
|
||||
return (
|
||||
<div className='cc-fade-in flex flex-col items-center gap-1 mt-3'>
|
||||
|
@ -37,7 +33,11 @@ export function Component() {
|
|||
);
|
||||
} else {
|
||||
return (
|
||||
<form className={clsx('cc-fade-in cc-column', 'w-[24rem] mx-auto', 'px-6 mt-3')} onSubmit={handleSubmit}>
|
||||
<form
|
||||
className={clsx('cc-fade-in cc-column', 'w-[24rem] mx-auto', 'px-6 mt-3')}
|
||||
onSubmit={handleSubmit}
|
||||
onChange={clearServerError}
|
||||
>
|
||||
<TextInput
|
||||
id='email'
|
||||
autoComplete='email'
|
||||
|
|
|
@ -114,7 +114,8 @@ export const globals = {
|
|||
main_scroll: 'main_scroll',
|
||||
library_item_editor: 'library_item_editor',
|
||||
constituenta_editor: 'constituenta_editor',
|
||||
graph_schemas: 'graph_schemas_tooltip'
|
||||
graph_schemas: 'graph_schemas_tooltip',
|
||||
dlg_create_version: 'dlg_create_version'
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue
Block a user