'use client'; import { toast } from 'sonner'; import { ApiError } from '@/lib/api/client'; /** * Build a multi-line string suitable for an inline form banner — the * primary message followed by `Error code:` / `Reference ID:` lines when * the error is an ApiError. Use from admin forms that want to keep * their inline error UX instead of switching to a toast. * * Example output: * "Couldn't save the role. * Error code: ROLES_DUPLICATE_NAME * Reference ID: 1ab2c3d4-…" */ export function formatErrorBanner(err: unknown, fallback = 'Something went wrong.'): string { if (err instanceof ApiError) { const parts = [err.message || fallback]; if (err.code) parts.push(`Error code: ${err.code}`); if (err.requestId) parts.push(`Reference ID: ${err.requestId}`); return parts.join('\n'); } if (err instanceof Error) return err.message || fallback; return fallback; } /** * Render an API error as a toast in the consistent platform format: * * ┌─────────────────────────────────────────────┐ * │ {plain-text message} │ * │ │ * │ Error code: EXPENSES_RECEIPT_REQUIRED │ * │ Reference ID: ab12-cd34-… [Copy] │ * └─────────────────────────────────────────────┘ * * Use this anywhere a `useMutation({ onError })` would otherwise just * call `toast.error(err.message)`. Falls back gracefully when the error * isn't an ApiError (network errors, programmer errors, etc.). */ export function toastError(err: unknown, fallback = 'Something went wrong.'): void { if (err instanceof ApiError) { const lines: string[] = []; if (err.code) lines.push(`Error code: ${err.code}`); if (err.requestId) lines.push(`Reference ID: ${err.requestId}`); toast.error(err.message, { description: lines.length > 0 ? lines.join('\n') : undefined, // Long enough to read the message + grab the reference id. duration: 8_000, action: err.requestId ? { label: 'Copy ID', onClick: () => { if (typeof navigator !== 'undefined' && navigator.clipboard) { void navigator.clipboard.writeText(err.requestId!); toast.success('Reference ID copied'); } }, } : undefined, }); return; } if (err instanceof Error) { toast.error(err.message || fallback); return; } toast.error(fallback); }