- Replace manual ApiError interface with Zod-inferred type from apiSchemas - Update all imports (15+ files) to use ApiError from @/schemas/apiSchemas - Remove ApiError interface from types/api.ts - Update ApiResponse to import ApiError from schemas - All TypeScript checks pass for ApiError-related code
124 lines
3.4 KiB
TypeScript
124 lines
3.4 KiB
TypeScript
/**
|
|
* Form Validation Utilities
|
|
* Utilities for applying API validation errors to form fields
|
|
*/
|
|
|
|
import type { ApiError } from '@/schemas/apiSchemas';
|
|
import { getValidationErrors, getErrorCategory } from './apiErrorHandler';
|
|
import type { UseFormSetError, Path } from 'react-hook-form';
|
|
|
|
/**
|
|
* Applies API validation errors to react-hook-form fields
|
|
* @param error - The API error containing validation details
|
|
* @param setError - react-hook-form's setError function
|
|
* @param fieldNameMap - Optional mapping from API field names to form field names
|
|
* @returns true if validation errors were applied, false otherwise
|
|
*/
|
|
export function applyApiValidationErrors<T extends Record<string, any>>(
|
|
error: ApiError | Error | unknown,
|
|
setError: UseFormSetError<T>,
|
|
fieldNameMap?: Record<string, keyof T>,
|
|
): boolean {
|
|
// Check if this is a validation error
|
|
const apiError =
|
|
error && typeof error === 'object' && 'message' in error
|
|
? (error as ApiError)
|
|
: null;
|
|
|
|
if (!apiError) {
|
|
return false;
|
|
}
|
|
|
|
const category = getErrorCategory(apiError);
|
|
if (category !== 'validation') {
|
|
return false;
|
|
}
|
|
|
|
// Extract validation errors from error.details
|
|
const validationErrors = getValidationErrors(apiError);
|
|
|
|
if (Object.keys(validationErrors).length === 0) {
|
|
return false;
|
|
}
|
|
|
|
// Apply errors to form fields
|
|
let appliedCount = 0;
|
|
for (const [apiFieldName, message] of Object.entries(validationErrors)) {
|
|
// Map API field name to form field name if mapping provided
|
|
const formFieldName = (fieldNameMap?.[apiFieldName] ||
|
|
apiFieldName) as Path<T>;
|
|
|
|
// Set error on the form field
|
|
setError(formFieldName, {
|
|
type: 'server',
|
|
message: message,
|
|
});
|
|
|
|
appliedCount++;
|
|
}
|
|
|
|
return appliedCount > 0;
|
|
}
|
|
|
|
/**
|
|
* Gets validation error for a specific field from an API error
|
|
* @param error - The API error
|
|
* @param fieldName - The field name to get error for
|
|
* @param fieldNameMap - Optional mapping from API field names to form field names
|
|
* @returns Error message for the field, or undefined if no error
|
|
*/
|
|
export function getFieldValidationError(
|
|
error: ApiError | Error | unknown,
|
|
fieldName: string,
|
|
fieldNameMap?: Record<string, string>,
|
|
): string | undefined {
|
|
const apiError =
|
|
error && typeof error === 'object' && 'message' in error
|
|
? (error as ApiError)
|
|
: null;
|
|
|
|
if (!apiError) {
|
|
return undefined;
|
|
}
|
|
|
|
const category = getErrorCategory(apiError);
|
|
if (category !== 'validation') {
|
|
return undefined;
|
|
}
|
|
|
|
const validationErrors = getValidationErrors(apiError);
|
|
|
|
// Check direct field name
|
|
if (validationErrors[fieldName]) {
|
|
return validationErrors[fieldName];
|
|
}
|
|
|
|
// Check mapped field name
|
|
if (fieldNameMap) {
|
|
for (const [apiFieldName, formFieldName] of Object.entries(fieldNameMap)) {
|
|
if (formFieldName === fieldName && validationErrors[apiFieldName]) {
|
|
return validationErrors[apiFieldName];
|
|
}
|
|
}
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
/**
|
|
* Checks if an error is a validation error
|
|
* @param error - The error to check
|
|
* @returns true if the error is a validation error
|
|
*/
|
|
export function isValidationError(error: ApiError | Error | unknown): boolean {
|
|
const apiError =
|
|
error && typeof error === 'object' && 'message' in error
|
|
? (error as ApiError)
|
|
: null;
|
|
|
|
if (!apiError) {
|
|
return false;
|
|
}
|
|
|
|
return getErrorCategory(apiError) === 'validation';
|
|
}
|