chore(admin-ui): диагностическая инструментация сохранения записи [TEMP]
ВРЕМЕННО — для поиска причины «Save ничего не делает». Откатить после диагностики (все логи помечены [ordinis-diag]). SchemaDrivenForm: console.warn в submit() — событие формы, RHF onInvalid, результат AJV, ветки return, вызов onSubmit. dictionaries.$name: логи в route.handleSubmit + createMut/updateMut mutate + onSuccess/onError со статусом ответа. Также добавлен постоянный регресс-тест: повторный submit после исправления валидации вызывает onSubmit.
This commit is contained in:
@@ -219,4 +219,39 @@ describe('SchemaDrivenForm', () => {
|
||||
)
|
||||
expect(screen.getByText('Дубликат business_key')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Regression: после первой неудачной AJV-валидации manual-ошибки
|
||||
// (setError на data.*) застревали в formState.errors. На повторном Save
|
||||
// RHF handleSubmit видел непустой errors и молча НЕ вызывал submit —
|
||||
// форма «не сохранялась» после исправления полей, ошибок не видно.
|
||||
it('повторный submit после исправления валидации вызывает onSubmit', async () => {
|
||||
const user = userEvent.setup()
|
||||
const onSubmit = vi.fn()
|
||||
const { container } = renderWithI18n(
|
||||
<SchemaDrivenForm
|
||||
schema={minimalSchema}
|
||||
supportedLocales={['ru-RU']}
|
||||
defaultLocale="ru-RU"
|
||||
mode="create"
|
||||
isPending={false}
|
||||
onSubmit={onSubmit}
|
||||
onCancel={() => {}}
|
||||
/>,
|
||||
)
|
||||
const save = () => screen.getByRole('button', { name: /^Сохранить$/i })
|
||||
const field = (name: string): HTMLElement => {
|
||||
const el = container.querySelector(`input[name="${name}"]`)
|
||||
if (!el) throw new Error(`field input not found: ${name}`)
|
||||
return el as HTMLElement
|
||||
}
|
||||
// Попытка 1 — required code/name пустые → AJV fail, submit не проходит.
|
||||
await user.click(save())
|
||||
expect(onSubmit).not.toHaveBeenCalled()
|
||||
// Исправляем — заполняем оба required-поля.
|
||||
await user.type(field('data.code'), 'X1')
|
||||
await user.type(field('data.name'), 'Тест')
|
||||
// Попытка 2 — данные валидны, должно вызвать onSubmit.
|
||||
await user.click(save())
|
||||
expect(onSubmit).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -268,10 +268,18 @@ export const SchemaDrivenForm = ({
|
||||
}, [visibleFieldCount, totalFieldCount])
|
||||
|
||||
const submit: SubmitHandler<FormValues> = (values) => {
|
||||
// [ordinis-diag] DIAGNOSTIC BUILD — временная инструментация для поиска
|
||||
// причины «Save ничего не делает». Удалить после диагностики.
|
||||
console.warn('[ordinis-diag] submit(): RHF-валидация пройдена, handler запущен', {
|
||||
mode,
|
||||
businessKey: values.businessKey,
|
||||
dataKeys: Object.keys(values.data ?? {}),
|
||||
})
|
||||
if (values.validFrom && values.validTo) {
|
||||
const from = new Date(values.validFrom)
|
||||
const to = new Date(values.validTo)
|
||||
if (!isNaN(from.getTime()) && !isNaN(to.getTime()) && from >= to) {
|
||||
console.warn('[ordinis-diag] submit(): ЗАБЛОКИРОВАНО — validTo <= validFrom')
|
||||
setError('validTo', { type: 'manual', message: t('form.error.validToBeforeFrom') })
|
||||
setActiveTab('identity')
|
||||
return
|
||||
@@ -279,7 +287,13 @@ export const SchemaDrivenForm = ({
|
||||
}
|
||||
if (validator) {
|
||||
const ok = validator(values.data)
|
||||
console.warn('[ordinis-diag] submit(): AJV-валидация →', {
|
||||
ok,
|
||||
errorCount: validator.errors?.length ?? 0,
|
||||
errors: validator.errors,
|
||||
})
|
||||
if (!ok && validator.errors && validator.errors.length > 0) {
|
||||
console.warn('[ordinis-diag] submit(): ЗАБЛОКИРОВАНО — AJV errors, onSubmit НЕ вызван')
|
||||
applyAjvErrors(validator.errors, setError)
|
||||
const firstField = firstFieldFromErrors(validator.errors)
|
||||
if (firstField) {
|
||||
@@ -288,10 +302,15 @@ export const SchemaDrivenForm = ({
|
||||
}
|
||||
return
|
||||
}
|
||||
} else {
|
||||
console.warn('[ordinis-diag] submit(): validator == null (схема не скомпилировалась)')
|
||||
}
|
||||
const derivedKey = idSource && mode === 'create'
|
||||
? String(values.data?.[idSource] ?? '').trim()
|
||||
: values.businessKey.trim()
|
||||
console.warn('[ordinis-diag] submit(): вызываю onSubmit() → мутация', {
|
||||
businessKey: derivedKey,
|
||||
})
|
||||
onSubmit({
|
||||
businessKey: derivedKey,
|
||||
data: values.data,
|
||||
@@ -300,6 +319,13 @@ export const SchemaDrivenForm = ({
|
||||
})
|
||||
}
|
||||
|
||||
// [ordinis-diag] RHF onInvalid — вызывается когда handleSubmit РЕШИЛ что
|
||||
// форма невалидна и НЕ вызвал submit(). Показывает что именно RHF считает
|
||||
// ошибкой (включая застрявшие manual-ошибки). Удалить после диагностики.
|
||||
const diagOnInvalid = (errs: unknown) => {
|
||||
console.warn('[ordinis-diag] handleSubmit ЗАБЛОКИРОВАН (onInvalid) — formState.errors:', errs)
|
||||
}
|
||||
|
||||
const tabErrorCount = countErrorsPerSection(formState.errors, buckets, sectionOrder)
|
||||
const visibleTabs: TabItem[] = sectionOrder
|
||||
.filter((id) => id === 'identity' || (buckets[id]?.length ?? 0) > 0)
|
||||
@@ -474,7 +500,12 @@ export const SchemaDrivenForm = ({
|
||||
return (
|
||||
<form
|
||||
id={formId}
|
||||
onSubmit={handleSubmit(submit)}
|
||||
onSubmit={(e) => {
|
||||
// [ordinis-diag] DIAGNOSTIC — событие submit формы вообще дошло?
|
||||
// Удалить после диагностики.
|
||||
console.warn('[ordinis-diag] <form> submit event сработал (formId=' + String(formId) + ')')
|
||||
void handleSubmit(submit, diagOnInvalid)(e)
|
||||
}}
|
||||
ref={formContainerRef}
|
||||
>
|
||||
<div className="space-y-4 min-w-0">
|
||||
|
||||
Reference in New Issue
Block a user