import { ProblemDetails } from './api-client'; /** * Extract a human-readable message from a rejected API call. A 4xx/5xx with a * ProblemDetails body (RFC 7807) is thrown by the generated client as the parsed * object; anything else falls back to the given message. */ export function problemDetail(e: unknown, fallback: string): string { if (e && typeof e === 'object' && 'detail' in e) { const detail = (e as ProblemDetails).detail; if (typeof detail === 'string' && detail) return detail; } return fallback; } /** * SEAM (G4): map a server validation envelope to field-level errors. * * ASP.NET's ValidationProblemDetails carries `errors: { field: string[] }`. The * backend today returns only `detail` (one banner message), so this returns `{}`. * When the backend starts sending `errors`, a machine's `SubmitFailed` handler can * merge this into its own `errors` map — the field-keyed shape the wizards already * render — so a rejection shows inline per field, not just as a banner. The * consumer hook is the only thing left to wire; the contract boundary lives here. */ export function problemFieldErrors(e: unknown): Record { if (!e || typeof e !== 'object' || !('errors' in e)) return {}; const errors = (e as { errors?: unknown }).errors; if (!errors || typeof errors !== 'object') return {}; const out: Record = {}; for (const [field, msgs] of Object.entries(errors as Record)) { const first = Array.isArray(msgs) ? msgs[0] : msgs; if (typeof first === 'string') out[field] = first; } return out; }