B: Fix dialog handlers and validators
Some checks failed
Frontend CI / build (22.x) (push) Has been cancelled
Frontend CI / notify-failure (push) Has been cancelled

This commit is contained in:
Ivan 2025-10-03 01:04:47 +03:00
parent c31ba65e1a
commit 803b4d50ef
22 changed files with 76 additions and 58 deletions

View File

@ -41,7 +41,7 @@ export function DlgCreatePromptTemplate() {
mode: 'onChange'
});
const label = useWatch({ control, name: 'label' });
const isValid = !!label && !templates.find(template => template.label === label);
const canSubmit = !!label && !templates.find(template => template.label === label);
function onSubmit(data: ICreatePromptTemplateDTO) {
void createPromptTemplate(data).then(onCreate);
@ -51,7 +51,7 @@ export function DlgCreatePromptTemplate() {
<ModalForm
header='Создание шаблона'
submitText='Создать'
canSubmit={isValid}
canSubmit={canSubmit}
onSubmit={event => void handleSubmit(onSubmit)(event)}
submitInvalidTooltip='Введите уникальное название шаблона'
className='cc-column w-140 max-h-120 py-2 px-6'

View File

@ -26,7 +26,8 @@ export function DlgEditEditors() {
const [selected, setSelected] = useState<number[]>(initial);
const { users } = useUsers();
function handleSubmit() {
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
void setEditors({ itemID: itemID, editors: selected });
}

View File

@ -42,13 +42,12 @@ export const ossApi = {
queryKey: KEYS.composite.ossItem({ itemID }),
staleTime: DELAYS.staleShort,
queryFn: meta =>
!itemID
? undefined
: axiosGet<IOperationSchemaDTO>({
schema: schemaOperationSchema,
endpoint: `/api/oss/${itemID}/details`,
options: { signal: meta.signal }
})
axiosGet<IOperationSchemaDTO>({
schema: schemaOperationSchema,
endpoint: `/api/oss/${itemID}/details`,
options: { signal: meta.signal }
}),
enabled: !!itemID
});
},

View File

@ -18,7 +18,7 @@ export function useOssSuspense({ itemID }: { itemID: number }) {
const { data } = useSuspenseQuery({
...ossApi.getOssQueryOptions({ itemID })
});
const schema = new OssLoader(data!).produceOSS();
const schema = new OssLoader(data).produceOSS();
return { schema };
}

View File

@ -77,7 +77,7 @@ export function DlgCreateBlock() {
const children_blocks = useWatch({ control: methods.control, name: 'children_blocks' });
const children_operations = useWatch({ control: methods.control, name: 'children_operations' });
const [activeTab, setActiveTab] = useState<TabID>(TabID.CARD);
const isValid = !!title && !manager.oss.blocks.some(block => block.title === title);
const canSubmit = methods.formState.isValid && !!title && !manager.oss.blocks.some(block => block.title === title);
function onSubmit(data: ICreateBlockDTO) {
data.position = manager.newBlockPosition(data);
@ -89,7 +89,7 @@ export function DlgCreateBlock() {
<ModalForm
header='Создание блока'
submitText='Создать'
canSubmit={isValid}
canSubmit={canSubmit}
onSubmit={event => void methods.handleSubmit(onSubmit)(event)}
className='w-160 px-6 h-110'
helpTopic={HelpTopic.CC_STRUCTURING}

View File

@ -43,7 +43,7 @@ export function DlgCreateSchema() {
control,
register,
handleSubmit,
formState: { errors }
formState: { errors, isValid }
} = useForm<ICreateSchemaDTO>({
resolver: zodResolver(schemaCreateSchema),
defaultValues: {
@ -64,7 +64,7 @@ export function DlgCreateSchema() {
mode: 'onChange'
});
const alias = useWatch({ control: control, name: 'item_data.alias' });
const isValid = !!alias && !manager.oss.operations.some(operation => operation.alias === alias);
const canSubmit = isValid && !!alias && !manager.oss.operations.some(operation => operation.alias === alias);
function onSubmit(data: ICreateSchemaDTO) {
data.position = manager.newOperationPosition(data);
@ -76,7 +76,7 @@ export function DlgCreateSchema() {
<ModalForm
header='Создание операции: Новая схема'
submitText='Создать'
canSubmit={isValid}
canSubmit={canSubmit}
onSubmit={event => void handleSubmit(onSubmit)(event)}
className='w-180 px-6 pb-3 cc-column'
helpTopic={HelpTopic.CC_OSS}

View File

@ -73,7 +73,8 @@ export function DlgCreateSynthesis() {
});
const alias = useWatch({ control: methods.control, name: 'item_data.alias' });
const [activeTab, setActiveTab] = useState<TabID>(TabID.ARGUMENTS);
const isValid = !!alias && !manager.oss.operations.some(operation => operation.alias === alias);
const canSubmit =
methods.formState.isValid && !!alias && !manager.oss.operations.some(operation => operation.alias === alias);
function onSubmit(data: ICreateSynthesisDTO) {
data.position = manager.newOperationPosition(data);
@ -85,7 +86,7 @@ export function DlgCreateSynthesis() {
<ModalForm
header='Создание операции синтеза'
submitText='Создать'
canSubmit={isValid}
canSubmit={canSubmit}
onSubmit={event => void methods.handleSubmit(onSubmit)(event)}
className='w-180 px-6 h-128'
helpTopic={HelpTopic.CC_OSS}

View File

@ -64,6 +64,7 @@ export function DlgEditOperation() {
mode: 'onChange'
});
const [activeTab, setActiveTab] = useState<TabID>(TabID.CARD);
const canSubmit = methods.formState.isValid;
function onSubmit(data: IUpdateOperationDTO) {
if (data.item_data.parent !== target.parent) {
@ -77,7 +78,7 @@ export function DlgEditOperation() {
<ModalForm
header='Редактирование операции'
submitText='Сохранить'
canSubmit={methods.formState.isValid}
canSubmit={canSubmit}
onSubmit={event => void methods.handleSubmit(onSubmit)(event)}
className='w-160 px-6 h-128'
helpTopic={HelpTopic.UI_SUBSTITUTIONS}

View File

@ -49,7 +49,7 @@ export function DlgImportSchema() {
register,
handleSubmit,
setValue,
formState: { errors }
formState: { errors, isValid }
} = useForm<IImportSchemaDTO>({
resolver: zodResolver(schemaImportSchema),
defaultValues: {
@ -73,7 +73,7 @@ export function DlgImportSchema() {
});
const alias = useWatch({ control: control, name: 'item_data.alias' });
const clone_source = useWatch({ control: control, name: 'clone_source' });
const isValid = !!alias && !manager.oss.operations.some(operation => operation.alias === alias);
const canSubmit = isValid && !!alias && !manager.oss.operations.some(operation => operation.alias === alias);
function onSubmit(data: IImportSchemaDTO) {
data.position = manager.newOperationPosition(data);
@ -100,7 +100,7 @@ export function DlgImportSchema() {
<ModalForm
header='Создание операции: Загрузка'
submitText='Создать'
canSubmit={isValid}
canSubmit={canSubmit}
onSubmit={event => void handleSubmit(onSubmit)(event)}
className='w-180 px-6 pb-3 cc-column'
helpTopic={HelpTopic.CC_OSS}

View File

@ -80,6 +80,7 @@ export function DlgRelocateConstituents() {
.filter(item => !item.is_inherited && selected.includes(item.id))
.map(item => item.id);
const isValid = moveTarget.length > 0;
const canSubmit = isValid && destinationItem !== undefined;
function toggleDirection() {
setDirectionUp(prev => !prev);
@ -118,7 +119,7 @@ export function DlgRelocateConstituents() {
<ModalForm
header='Перенос конституент'
submitText='Переместить'
canSubmit={isValid && destinationItem !== undefined}
canSubmit={canSubmit}
submitInvalidTooltip='Необходимо выбрать хотя бы одну собственную конституенту'
onSubmit={event => void handleSubmit(onSubmit)(event)}
className='w-160 h-132 py-3 px-6'

View File

@ -34,13 +34,12 @@ export const rsformsApi = {
queryKey: KEYS.composite.rsItem({ itemID, version }),
staleTime: DELAYS.staleShort,
queryFn: meta =>
!itemID
? undefined
: axiosGet<IRSFormDTO>({
schema: schemaRSForm,
endpoint: version ? `/api/library/${itemID}/versions/${version}` : `/api/rsforms/${itemID}/details`,
options: { signal: meta.signal }
})
axiosGet<IRSFormDTO>({
schema: schemaRSForm,
endpoint: version ? `/api/library/${itemID}/versions/${version}` : `/api/rsforms/${itemID}/details`,
options: { signal: meta.signal }
}),
enabled: !!itemID
});
},

View File

@ -18,7 +18,7 @@ export function useRSFormSuspense({ itemID, version }: { itemID: number; version
const { data } = useSuspenseQuery({
...rsformsApi.getRSFormQueryOptions({ itemID, version })
});
const schema = new RSFormLoader(data!).produceRSForm();
const schema = new RSFormLoader(data).produceRSForm();
return { schema };
}

View File

@ -31,12 +31,13 @@ export function DlgCreateCst() {
const { schema } = useRSFormSuspense({ itemID: schemaID });
const methods = useForm<ICreateConstituentaDTO>({
mode: 'onChange',
resolver: zodResolver(schemaCreateConstituenta),
defaultValues: { ...initial }
});
const alias = useWatch({ control: methods.control, name: 'alias' });
const cst_type = useWatch({ control: methods.control, name: 'cst_type' });
const isValid = validateNewAlias(alias, cst_type, schema);
const canSubmit = methods.formState.isValid && validateNewAlias(alias, cst_type, schema);
function onSubmit(data: ICreateConstituentaDTO) {
return cstCreate({ itemID: schema.id, data }).then(onCreate);
@ -45,7 +46,7 @@ export function DlgCreateCst() {
return (
<ModalForm
header='Создание конституенты'
canSubmit={isValid}
canSubmit={canSubmit}
onSubmit={event => void methods.handleSubmit(onSubmit)(event)}
submitInvalidTooltip={errorMsg.aliasInvalid}
submitText='Создать'

View File

@ -13,14 +13,13 @@ import { useDialogsStore } from '@/stores/dialogs';
import { type RO } from '@/utils/meta';
import {
CstType,
type IConstituentaBasicsDTO,
type ICreateConstituentaDTO,
schemaCreateConstituenta
} from '../../backend/types';
import { useCreateConstituenta } from '../../backend/use-create-constituenta';
import { useRSFormSuspense } from '../../backend/use-rsform';
import { generateAlias, validateNewAlias } from '../../models/rsform-api';
import { validateNewAlias } from '../../models/rsform-api';
import { FormCreateCst } from '../dlg-create-cst/form-create-cst';
import { TabArguments } from './tab-arguments';
@ -46,11 +45,13 @@ export function DlgCstTemplate() {
const { schema } = useRSFormSuspense({ itemID: schemaID });
const methods = useForm<ICreateConstituentaDTO>({
mode: 'onChange',
resolver: zodResolver(schemaCreateConstituenta),
defaultValues: {
cst_type: CstType.TERM,
insert_after: insertAfter ?? null,
alias: generateAlias(CstType.TERM, schema),
crucial: false,
cst_type: undefined,
alias: '',
convention: '',
definition_formal: '',
definition_raw: '',
@ -60,7 +61,7 @@ export function DlgCstTemplate() {
});
const alias = useWatch({ control: methods.control, name: 'alias' });
const cst_type = useWatch({ control: methods.control, name: 'cst_type' });
const isValid = validateNewAlias(alias, cst_type, schema);
const canSubmit = methods.formState.isValid && validateNewAlias(alias, cst_type, schema);
const [activeTab, setActiveTab] = useState<TabID>(TabID.TEMPLATE);
@ -73,7 +74,7 @@ export function DlgCstTemplate() {
header='Создание конституенты из шаблона'
submitText='Создать'
className='w-172 h-140 px-6'
canSubmit={isValid}
canSubmit={canSubmit}
onSubmit={event => void methods.handleSubmit(onSubmit)(event)}
helpTopic={HelpTopic.RSL_TEMPLATES}
>

View File

@ -27,11 +27,6 @@ export function TabTemplate() {
const { schema: templateSchema } = useRSForm({ itemID: templateID ?? undefined });
const selectedTemplate = templates.find(item => item.id === templateID);
if (!templateID) {
onChangeTemplateID(templates[0].id);
return null;
}
const filteredData = !templateSchema
? []
: !filterCategory

View File

@ -3,6 +3,8 @@
import { useState } from 'react';
import { useFormContext } from 'react-hook-form';
import { useTemplatesSuspense } from '@/features/library/backend/use-templates';
import { useDialogsStore } from '@/stores/dialogs';
import { type ICreateConstituentaDTO } from '../../backend/types';
@ -18,8 +20,10 @@ import { TemplateContext } from './template-context';
export const TemplateState = ({ children }: React.PropsWithChildren) => {
const { schemaID } = useDialogsStore(state => state.props as DlgCstTemplateProps);
const { schema } = useRSFormSuspense({ itemID: schemaID });
const { templates } = useTemplatesSuspense();
const { setValue } = useFormContext<ICreateConstituentaDTO>();
const [templateID, setTemplateID] = useState<number | null>(null);
const [templateID, setTemplateID] = useState<number | null>(templates.length > 0 ? templates[0].id : null);
const [args, setArguments] = useState<IArgumentValue[]>([]);
const [prototype, setPrototype] = useState<IConstituenta | null>(null);
const [filterCategory, setFilterCategory] = useState<IConstituenta | null>(null);
@ -32,8 +36,8 @@ export const TemplateState = ({ children }: React.PropsWithChildren) => {
const newType = inferTemplatedType(prototype.cst_type, newArgs);
setValue('definition_formal', substituteTemplateArgs(prototype.definition_formal, newArgs));
setValue('cst_type', newType);
setValue('alias', generateAlias(newType, schema));
setValue('cst_type', newType, { shouldValidate: true });
setValue('alias', generateAlias(newType, schema), { shouldValidate: true });
}
function onChangePrototype(newPrototype: IConstituenta) {
@ -45,8 +49,8 @@ export const TemplateState = ({ children }: React.PropsWithChildren) => {
value: ''
}))
);
setValue('cst_type', newPrototype.cst_type);
setValue('alias', generateAlias(newPrototype.cst_type, schema));
setValue('cst_type', newPrototype.cst_type, { shouldValidate: true });
setValue('alias', generateAlias(newPrototype.cst_type, schema), { shouldValidate: true });
setValue('definition_formal', newPrototype.definition_formal);
setValue('term_raw', newPrototype.term_raw);
setValue('definition_raw', newPrototype.definition_raw);

View File

@ -31,7 +31,8 @@ export function DlgDeleteCst() {
[selected, schema.inheritance]
);
function handleSubmit() {
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
const deleted = expandOut ? selected.concat(expansion) : selected;
void cstDelete({ itemID: schemaID, data: { items: deleted } }).then(() => afterDelete?.(schema, deleted));
}

View File

@ -35,6 +35,7 @@ export function DlgEditCst() {
const { findPredecessor } = useFindPredecessor();
const methods = useForm<IUpdateConstituentaDTO>({
mode: 'onChange',
resolver: zodResolver(schemaUpdateConstituenta),
defaultValues: {
target: target.id,
@ -53,7 +54,9 @@ export function DlgEditCst() {
const alias = useWatch({ control: methods.control, name: 'item_data.alias' })!;
const cst_type = useWatch({ control: methods.control, name: 'item_data.cst_type' })!;
const isValid = (alias === target.alias && cst_type == target.cst_type) || validateNewAlias(alias, cst_type, schema);
const canSubmit =
(methods.formState.isValid && alias === target.alias && cst_type == target.cst_type) ||
validateNewAlias(alias, cst_type, schema);
function onSubmit(data: IUpdateConstituentaDTO) {
return updateConstituenta({ itemID: schema.id, data });
@ -86,7 +89,7 @@ export function DlgEditCst() {
return (
<ModalForm
header='Редактирование конституенты'
canSubmit={isValid}
canSubmit={canSubmit}
onSubmit={event => void methods.handleSubmit(onSubmit)(event)}
submitInvalidTooltip={errorMsg.aliasInvalid}
submitText='Сохранить'

View File

@ -76,6 +76,8 @@ export function DlgEditReference() {
mode: 'onChange'
});
const canSubmit = methods.formState.isValid;
function onSubmit(data: IEditReferenceState) {
if (data.type === ReferenceType.ENTITY) {
onSave({
@ -113,7 +115,7 @@ export function DlgEditReference() {
<ModalForm
header='Редактирование ссылки'
submitText='Сохранить ссылку'
canSubmit={methods.formState.isValid}
canSubmit={canSubmit}
onCancel={onCancel}
onSubmit={event => void methods.handleSubmit(onSubmit)(event)}
className='w-160 px-6 h-128'

View File

@ -49,7 +49,8 @@ export function DlgEditWordForms() {
}))
);
function handleSubmit() {
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
void cstUpdate({
itemID: itemID,
data: {

View File

@ -46,6 +46,7 @@ export function DlgInlineSynthesis() {
mode: 'onChange'
});
const sourceID = useWatch({ control: methods.control, name: 'source' });
const canSubmit = methods.formState.isValid && sourceID !== null;
function onSubmit(data: IInlineSynthesisDTO) {
return inlineSynthesis(data).then(onSynthesis);
@ -56,7 +57,7 @@ export function DlgInlineSynthesis() {
header='Импорт концептуальной схем'
submitText='Добавить конституенты'
className='w-160 h-132 px-6'
canSubmit={methods.formState.isValid && sourceID !== null}
canSubmit={canSubmit}
onSubmit={event => void methods.handleSubmit(onSubmit)(event)}
>
<Tabs className='grid' selectedIndex={activeTab} onSelect={index => setActiveTab(index as TabID)}>

View File

@ -26,7 +26,14 @@ export function DlgRenameCst() {
const { schema } = useRSFormSuspense({ itemID: schemaID });
const target = schema.cstByID.get(targetID)!;
const { register, setValue, handleSubmit, control } = useForm<IUpdateConstituentaDTO>({
const {
register,
setValue,
handleSubmit,
control,
formState: { isValid }
} = useForm<IUpdateConstituentaDTO>({
mode: 'onChange',
resolver: zodResolver(schemaUpdateConstituenta),
defaultValues: {
target: targetID,
@ -38,7 +45,7 @@ export function DlgRenameCst() {
});
const alias = useWatch({ control, name: 'item_data.alias' })!;
const cst_type = useWatch({ control, name: 'item_data.cst_type' })!;
const isValid = alias !== target.alias && validateNewAlias(alias, cst_type, schema);
const canSubmit = isValid && alias !== target.alias && validateNewAlias(alias, cst_type, schema);
function onSubmit(data: IUpdateConstituentaDTO) {
return cstUpdate({ itemID: schemaID, data: data });
@ -54,7 +61,7 @@ export function DlgRenameCst() {
header='Переименование конституенты'
submitText='Переименовать'
submitInvalidTooltip='Введите незанятое имя, соответствующее типу'
canSubmit={isValid}
canSubmit={canSubmit}
onSubmit={event => void handleSubmit(onSubmit)(event)}
className='w-120 py-6 pr-3 pl-6 flex gap-3 justify-center items-center'
helpTopic={HelpTopic.CC_CONSTITUENTA}