fix(api): treat empty strings as null in optional fields; show issues on web

- zod-utils.ts: optionalRegex/optionalEmail/optionalText with preprocess('' -> null)
- clients/organizations/services schemas now accept empty strings for optional fields
  (controlled inputs naturally produce '' instead of null)
- ApiError.prettyMessage() unfolds zod fieldErrors so users see which field failed

Reproduce: POST /api/clients with email="" or empty inn returned 400 validation_error.
Now it succeeds (empty stays null), and any real validation error names the field.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
admin
2026-05-01 08:42:03 +03:00
parent 9807d47c8d
commit 747246197a
10 changed files with 71 additions and 24 deletions
+15
View File
@@ -4,6 +4,21 @@ export class ApiError extends Error {
constructor(public status: number, public code: string, public details?: unknown) {
super(`${status} ${code}`);
}
/** Удобочитаемое сообщение для UI: разворачивает zod issues если есть. */
prettyMessage(): string {
if (this.code === 'validation_error' && this.details && typeof this.details === 'object') {
const d = this.details as { issues?: { fieldErrors?: Record<string, string[]> } };
const fields = d.issues?.fieldErrors;
if (fields) {
const parts = Object.entries(fields)
.filter(([, msgs]) => msgs && msgs.length > 0)
.map(([k, msgs]) => `${k}: ${msgs!.join(', ')}`);
if (parts.length) return `Ошибка в полях: ${parts.join('; ')}`;
}
}
return `${this.code} (HTTP ${this.status})`;
}
}
async function request<T>(method: string, path: string, body?: unknown): Promise<T> {