fix(frontend): parse department api validation errors

This commit is contained in:
lizerk 2026-04-14 02:10:20 +03:00
parent 5adbf27097
commit 858448512e
3 changed files with 66 additions and 25 deletions

View File

@ -0,0 +1,29 @@
export function extractDepartmentApiErrorMessage(error, fallback) {
const raw = String(error?.message || fallback || "").trim();
const prefix = "API Error:";
if (!raw.startsWith(prefix)) return raw || fallback;
const payload = raw.slice(prefix.length).trim();
const jsonStart = payload.indexOf("{");
if (jsonStart >= 0) {
try {
const parsed = JSON.parse(payload.slice(jsonStart));
const detail = parsed.detail;
if (typeof detail === "string" && detail.trim()) return detail;
const firstEntry = Object.entries(parsed)[0];
if (firstEntry) {
const value = firstEntry[1];
if (Array.isArray(value) && value[0]) return String(value[0]);
if (typeof value === "string" && value.trim()) return value;
}
} catch {
return fallback;
}
}
const plainMessage = payload.replace(/^\d+\s+/, "").trim();
if (/^\d+$/.test(plainMessage || payload)) {
return fallback;
}
return plainMessage || fallback;
}

View File

@ -0,0 +1,35 @@
import test from "node:test";
import assert from "node:assert/strict";
import { extractDepartmentApiErrorMessage } from "./api-error.js";
test("extracts plain API error text after status code", () => {
const error = new Error(
"API Error: 400 Employee already belongs to another active department: Бухгалтерия.",
);
assert.equal(
extractDepartmentApiErrorMessage(error, "fallback"),
"Employee already belongs to another active department: Бухгалтерия.",
);
});
test("extracts first field error from JSON payload", () => {
const error = new Error(
'API Error: 400 {"employee_id":["Employee already belongs to another active department: Бухгалтерия."]}',
);
assert.equal(
extractDepartmentApiErrorMessage(error, "fallback"),
"Employee already belongs to another active department: Бухгалтерия.",
);
});
test("falls back for unparsable payload", () => {
const error = new Error("API Error: 500");
assert.equal(
extractDepartmentApiErrorMessage(error, "fallback"),
"fallback",
);
});

View File

@ -3,6 +3,7 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { toast } from "sonner";
import { extractDepartmentApiErrorMessage } from "@/components/departments/api-error";
import { getDepartmentMemberModalError } from "@/components/departments/member-modal-state";
import { useUser } from "@/contexts/UserContext";
import { apiClient } from "@/lib/api";
@ -96,31 +97,7 @@ const EMPTY_USER_PERMS: DepartmentUserPermissions = {
can_assign_roles: false,
};
function getErrorMessage(error: unknown, fallback: string) {
const raw = String((error as Error)?.message || fallback).trim();
const prefix = "API Error:";
if (!raw.startsWith(prefix)) return raw || fallback;
const payload = raw.slice(prefix.length).trim();
const jsonStart = payload.indexOf("{");
if (jsonStart >= 0) {
try {
const parsed = JSON.parse(payload.slice(jsonStart)) as Record<string, unknown>;
const detail = parsed.detail;
if (typeof detail === "string" && detail.trim()) return detail;
const firstEntry = Object.entries(parsed)[0];
if (firstEntry) {
const value = firstEntry[1];
if (Array.isArray(value) && value[0]) return String(value[0]);
if (typeof value === "string" && value.trim()) return value;
}
} catch {
return fallback;
}
}
return fallback;
}
const getErrorMessage = extractDepartmentApiErrorMessage;
function sortEmployeesByName(items: User[]) {
return [...items].sort((a, b) => {