R: Move dialogs to form basis

This commit is contained in:
Ivan 2025-02-06 14:09:20 +03:00
parent 4fbd30897b
commit 6eeb6ffd14
31 changed files with 216 additions and 189 deletions

View File

@ -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,

View File

@ -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 })
};

View File

@ -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) })

View File

@ -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();
};

View File

@ -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'>

View File

@ -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'>

View File

@ -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 (

View File

@ -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

View File

@ -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) {

View File

@ -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='Создать'
<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'
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)}
/>
<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={onlySelected}
onChange={value => setOnlySelected(value)}
value={field.value !== undefined}
onChange={value => field.onChange(value ? selected : undefined)}
/>
)}
/>
) : null}
</form>
</Modal>
);
}

View File

@ -58,6 +58,7 @@ function DlgCstTemplate() {
function handleSubmit() {
onCreate(constituenta);
return true;
}
function handlePrompt(): boolean {

View File

@ -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 (

View File

@ -22,6 +22,7 @@ function DlgDeleteOperation() {
function handleSubmit() {
onSubmit(target.id, keepConstituents, deleteSchema);
return true;
}
return (

View File

@ -26,6 +26,7 @@ function DlgEditEditors() {
function handleSubmit() {
onChangeEditors(selected);
return true;
}
function onDeleteEditor(target: UserID) {

View File

@ -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 (

View File

@ -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}
>

View File

@ -63,6 +63,7 @@ function DlgEditWordForms() {
})
);
onSave(result);
return true;
}
function handleAddForm() {

View File

@ -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]'
>

View File

@ -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(() => {

View File

@ -76,14 +76,14 @@ function DlgRelocateConstituents() {
}
function handleSubmit() {
if (!destination) {
return;
}
if (destination) {
onSubmit({
destination: destination.id,
items: selected
});
}
return true;
}
return (
<Modal

View File

@ -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}
>

View File

@ -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 (

View File

@ -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
});
}
return true;
};
const handleFile = (event: React.ChangeEvent<HTMLInputElement>) => {

View File

@ -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.

View File

@ -45,7 +45,7 @@ function EditorOssCard() {
)}
>
<FlexColumn className='px-3'>
<FormOSS id={globals.library_item_editor} />
<FormOSS />
<EditorLibraryItem controller={controller} />
</FlexColumn>

View File

@ -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

View File

@ -44,7 +44,7 @@ function EditorRSFormCard() {
)}
>
<FlexColumn className='flex-shrink'>
<FormRSForm id={globals.library_item_editor} />
<FormRSForm />
<EditorLibraryItem controller={controller} />
</FlexColumn>

View File

@ -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)}
>

View File

@ -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)
});
}

View File

@ -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'

View File

@ -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'
};
/**