fix(plant-hours): surface real PB field-level error in import response
formatPbError walked err.data but PB's ClientResponseError wraps the field-level error object at err.data.data. The outer err.data has just three keys (data, message, status), all 'invalid' on a failed insert — useless to operators. Read err.data.data (and err.data.data.fieldErrors if present) so the operator sees WHICH field rejected the row.
This commit is contained in:
parent
0290ced51a
commit
0ea0257edb
1 changed files with 31 additions and 11 deletions
|
|
@ -47,22 +47,42 @@ const MAX_BYTES = 10 * 1024 * 1024; // 10 MB — same as fuel-logs import
|
|||
export async function POST(request: Request) {
|
||||
const start = Date.now();
|
||||
|
||||
// PB's ClientResponseError has `.data` with field-level validation
|
||||
// details. Plain `err.message` is just "Failed to create record." -
|
||||
// useless for diagnosis. Flatten field-level detail into a single
|
||||
// human-readable string so the operator can see which field is
|
||||
// rejecting the row.
|
||||
// PB's ClientResponseError has `.data` whose keys are "data",
|
||||
// "message", "status" — only the inner `.data.data` (or sometimes
|
||||
// `.data.data.fieldErrors`) holds the field-level validation object.
|
||||
// Plain `err.message` is just "Failed to create record." - useless
|
||||
// for diagnosis. Flatten the inner field-level detail into a single
|
||||
// human-readable string so the operator can see WHICH field rejected
|
||||
// the row.
|
||||
function formatPbError(err: unknown): string {
|
||||
const e = err as {
|
||||
message?: string;
|
||||
data?: Record<string, { message?: string; code?: string }>;
|
||||
data?: {
|
||||
data?:
|
||||
| Record<string, { message?: string; code?: string }>
|
||||
| {
|
||||
fieldErrors?: Record<string, { message?: string; code?: string }>;
|
||||
};
|
||||
message?: string;
|
||||
};
|
||||
};
|
||||
const head = e?.message ?? "Insert failed";
|
||||
if (e?.data && typeof e.data === "object") {
|
||||
const parts = Object.entries(e.data)
|
||||
.map(([field, info]) => `${field}: ${info?.message ?? "invalid"}`)
|
||||
.join("; ");
|
||||
if (parts) return `${head} (${parts})`;
|
||||
const inner =
|
||||
e?.data?.data && typeof e.data.data === "object" ? e.data.data : null;
|
||||
if (inner) {
|
||||
const fieldErrors =
|
||||
"fieldErrors" in inner && inner.fieldErrors ? inner.fieldErrors : inner;
|
||||
if (typeof fieldErrors === "object" && fieldErrors !== null) {
|
||||
const parts = Object.entries(fieldErrors as Record<string, unknown>)
|
||||
.map(([field, info]) => {
|
||||
if (info && typeof info === "object" && "message" in info) {
|
||||
return `${field}: ${(info as { message?: string }).message ?? "invalid"}`;
|
||||
}
|
||||
return `${field}: ${String(info)}`;
|
||||
})
|
||||
.join("; ");
|
||||
if (parts) return `${head} (${parts})`;
|
||||
}
|
||||
}
|
||||
return head;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue