Display validation errors returned by the server
This commit is contained in:
@@ -13,6 +13,7 @@ All notable changes to this project will be documented in this file. This projec
|
|||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
- Display validation errors returned by the server.
|
||||||
|
|
||||||
## [1.0.1] - 2026-04-25
|
## [1.0.1] - 2026-04-25
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import { toast } from '@/hooks/use-toast';
|
|||||||
import { resolveObject, resolveSchema, resolveForm, buildCreateDefaults, deepMerge } from '@/lib/schemaResolver';
|
import { resolveObject, resolveSchema, resolveForm, buildCreateDefaults, deepMerge } from '@/lib/schemaResolver';
|
||||||
import { calculateJmapPatch } from '@/lib/jmapPatch';
|
import { calculateJmapPatch } from '@/lib/jmapPatch';
|
||||||
import { jmapGet, jmapSet, getAccountId } from '@/services/jmap/client';
|
import { jmapGet, jmapSet, getAccountId } from '@/services/jmap/client';
|
||||||
import { friendlySetError } from '@/lib/jmapErrors';
|
import { friendlySetError, validationErrorMessage } from '@/lib/jmapErrors';
|
||||||
|
|
||||||
import type { Field, Fields, Form, FormField } from '@/types/schema';
|
import type { Field, Fields, Form, FormField } from '@/types/schema';
|
||||||
import type { JmapSetError, JmapSetResponse } from '@/types/jmap';
|
import type { JmapSetError, JmapSetResponse } from '@/types/jmap';
|
||||||
@@ -181,19 +181,7 @@ export function BootstrapWizard() {
|
|||||||
for (const ve of error.validationErrors) {
|
for (const ve of error.validationErrors) {
|
||||||
const top = ve.property?.split('/')[0] ?? '';
|
const top = ve.property?.split('/')[0] ?? '';
|
||||||
if (!top) continue;
|
if (!top) continue;
|
||||||
const msg =
|
record(top, validationErrorMessage(ve));
|
||||||
ve.type === 'Required'
|
|
||||||
? t('form.required', 'This field is required.')
|
|
||||||
: ve.type === 'MaxLength'
|
|
||||||
? t('form.maxLengthIs', 'Maximum length is {{max}}.', { max: ve.required })
|
|
||||||
: ve.type === 'MinLength'
|
|
||||||
? t('form.minLengthIs', 'Minimum length is {{min}}.', { min: ve.required })
|
|
||||||
: ve.type === 'MaxValue'
|
|
||||||
? t('form.maxValueIs', 'Maximum value is {{max}}.', { max: ve.required })
|
|
||||||
: ve.type === 'MinValue'
|
|
||||||
? t('form.minValueIs', 'Minimum value is {{min}}.', { min: ve.required })
|
|
||||||
: t('form.invalidValue', 'Invalid value.');
|
|
||||||
record(top, msg);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ import {
|
|||||||
} from '@/lib/schemaResolver';
|
} from '@/lib/schemaResolver';
|
||||||
import { jmapGet, jmapSet, jmapRequest, getAccountId } from '@/services/jmap/client';
|
import { jmapGet, jmapSet, jmapRequest, getAccountId } from '@/services/jmap/client';
|
||||||
import { calculateJmapPatch } from '@/lib/jmapPatch';
|
import { calculateJmapPatch } from '@/lib/jmapPatch';
|
||||||
import { friendlySetError } from '@/lib/jmapErrors';
|
import { friendlySetError, validationErrorMessage } from '@/lib/jmapErrors';
|
||||||
import { coerceLabel } from '@/lib/objectOptions';
|
import { coerceLabel } from '@/lib/objectOptions';
|
||||||
import { SECRET_MASK } from '@/lib/jmapUtils';
|
import { SECRET_MASK } from '@/lib/jmapUtils';
|
||||||
import { toast } from '@/hooks/use-toast';
|
import { toast } from '@/hooks/use-toast';
|
||||||
@@ -347,21 +347,12 @@ export function DynamicForm({ viewName, objectId }: DynamicFormProps) {
|
|||||||
if (error.validationErrors && error.validationErrors.length > 0) {
|
if (error.validationErrors && error.validationErrors.length > 0) {
|
||||||
for (const ve of error.validationErrors) {
|
for (const ve of error.validationErrors) {
|
||||||
if (ve.property && currentFields?.properties[ve.property]) {
|
if (ve.property && currentFields?.properties[ve.property]) {
|
||||||
const msg =
|
newFieldErrors[ve.property] = validationErrorMessage(ve);
|
||||||
ve.type === 'Required'
|
|
||||||
? t('form.required', 'This field is required.')
|
|
||||||
: ve.type === 'MaxLength'
|
|
||||||
? t('form.maxLengthIs', 'Maximum length is {{max}}.', { max: ve.required })
|
|
||||||
: ve.type === 'MinLength'
|
|
||||||
? t('form.minLengthIs', 'Minimum length is {{min}}.', { min: ve.required })
|
|
||||||
: ve.type === 'MaxValue'
|
|
||||||
? t('form.maxValueIs', 'Maximum value is {{max}}.', { max: ve.required })
|
|
||||||
: ve.type === 'MinValue'
|
|
||||||
? t('form.minValueIs', 'Minimum value is {{min}}.', { min: ve.required })
|
|
||||||
: t('form.invalidValue', 'Invalid value.');
|
|
||||||
newFieldErrors[ve.property] = msg;
|
|
||||||
} else if (ve.property) {
|
} else if (ve.property) {
|
||||||
setGeneralError((prev) => (prev ? `${prev}\n${ve.property}: ${ve.type}` : `${ve.property}: ${ve.type}`));
|
const detail = ve.value && ve.value.length > 0 ? ve.value : ve.type;
|
||||||
|
setGeneralError((prev) =>
|
||||||
|
prev ? `${prev}\n${ve.property}: ${detail}` : `${ve.property}: ${detail}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-1
@@ -4,9 +4,27 @@
|
|||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { JmapSetError } from '@/types/jmap';
|
import type { JmapSetError, ValidationError } from '@/types/jmap';
|
||||||
import i18n from '@/i18n';
|
import i18n from '@/i18n';
|
||||||
|
|
||||||
|
export function validationErrorMessage(ve: ValidationError): string {
|
||||||
|
switch (ve.type) {
|
||||||
|
case 'Required':
|
||||||
|
return i18n.t('form.required', 'This field is required.');
|
||||||
|
case 'MaxLength':
|
||||||
|
return i18n.t('form.maxLengthIs', 'Maximum length is {{max}}.', { max: ve.required });
|
||||||
|
case 'MinLength':
|
||||||
|
return i18n.t('form.minLengthIs', 'Minimum length is {{min}}.', { min: ve.required });
|
||||||
|
case 'MaxValue':
|
||||||
|
return i18n.t('form.maxValueIs', 'Maximum value is {{max}}.', { max: ve.required });
|
||||||
|
case 'MinValue':
|
||||||
|
return i18n.t('form.minValueIs', 'Minimum value is {{min}}.', { min: ve.required });
|
||||||
|
default:
|
||||||
|
if (ve.value && ve.value.length > 0) return ve.value;
|
||||||
|
return i18n.t('form.invalidValue', 'Invalid value.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function friendlySetError(err: JmapSetError): string {
|
export function friendlySetError(err: JmapSetError): string {
|
||||||
if (err.description) return err.description;
|
if (err.description) return err.description;
|
||||||
switch (err.type) {
|
switch (err.type) {
|
||||||
|
|||||||
+1
-1
@@ -60,6 +60,6 @@ export interface JmapSetError {
|
|||||||
export interface ValidationError {
|
export interface ValidationError {
|
||||||
type: 'Invalid' | 'Required' | 'MaxLength' | 'MinLength' | 'MaxValue' | 'MinValue';
|
type: 'Invalid' | 'Required' | 'MaxLength' | 'MinLength' | 'MaxValue' | 'MinValue';
|
||||||
property: string;
|
property: string;
|
||||||
value?: unknown;
|
value?: string;
|
||||||
required?: number;
|
required?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user