mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
fix(backoffice): map raw IAM error codes to friendly messages (EDRFREIGHT-223, EDRFREIGHT-225)
@tria-plc/iamapi-common throws BadRequestException with a raw, untranslated code as the message (e.g. "user_role_not_found", "unit_employee_limit_reached:5") — useErrorHandler's extractMessage returned that string verbatim, so removing an org/unit admin whose role row didn't match, or inviting past a unit's employee cap, toasted the raw backend code instead of an explanation. Adds a small code→i18n map checked before the raw-message fallback, shared by both the async and sync error handlers so every caller (org-admin removal, employee invites, etc.) picks it up for free. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1649,6 +1649,8 @@
|
||||
"notFoundError": "የፈለጉትን መረጃ አልተገኘም።",
|
||||
"fileTooLarge": "ፋይሉ በጣም ትልቅ ነው። እባክዎ ፋይሉን አሳንሰው ዳግም ይሞክሩ።",
|
||||
"serverError": "ከአገልጋይ በኩል ችግር አለ። እባክዎ ዳግመኛ ይሞክሩ።",
|
||||
"userRoleNotFound": "ይህ የአስተዳዳሪ ሚና ምደባ አልተገኘም — ቀደም ብሎ ተወግዶ ሊሆን ይችላል።",
|
||||
"unitEmployeeLimitReached": "ይህ ክፍል የ{{limit}} ሰራተኞች ገደብ ላይ ደርሷል።",
|
||||
"attachmentDeleted": "አባሪው በተሳካ ሁኔታ ተሰርዟል።",
|
||||
"replyAdded": "ምላሹ በተሳካ ሁኔታ ታክሏል!",
|
||||
"replyError": "ምላሹን በመጨመር ላይ ስህተት አጋጥሟል።",
|
||||
|
||||
@@ -1669,6 +1669,8 @@
|
||||
"notFoundError": "We couldn't find what you were looking for.",
|
||||
"fileTooLarge": "The file is too large. Please reduce the file size and try again.",
|
||||
"serverError": "Something went wrong on our side. Please try again in a moment.",
|
||||
"userRoleNotFound": "This admin role assignment could not be found — it may have already been removed.",
|
||||
"unitEmployeeLimitReached": "This unit has reached its limit of {{limit}} employees.",
|
||||
"attachmentDeleted": "Attachment deleted successfully.",
|
||||
"replyAdded": "Reply added successfully!",
|
||||
"replyError": "An error occurred while adding the reply.",
|
||||
|
||||
@@ -1169,6 +1169,8 @@
|
||||
"notFoundError": "Nous n’avons pas trouvé ce que vous cherchiez.",
|
||||
"fileTooLarge": "Le fichier est trop volumineux. Veuillez réduire sa taille et réessayer.",
|
||||
"serverError": "Un problème est survenu de notre côté. Veuillez réessayer dans un instant.",
|
||||
"userRoleNotFound": "Cette attribution de rôle d'administrateur est introuvable — elle a peut-être déjà été supprimée.",
|
||||
"unitEmployeeLimitReached": "Cette unité a atteint sa limite de {{limit}} employés.",
|
||||
"attachmentDeleted": "Pièce jointe supprimée avec succès.",
|
||||
"replyAdded": "Réponse ajoutée avec succès !",
|
||||
"replyError": "Une erreur s’est produite lors de l’ajout de la réponse.",
|
||||
|
||||
@@ -54,6 +54,28 @@ const extractMessage = (value: unknown): string | null => {
|
||||
}
|
||||
};
|
||||
|
||||
// IAM (@tria-plc/iamapi-common) throws BadRequestException with a raw,
|
||||
// untranslated code string as the message — no i18n on that side — so it
|
||||
// would otherwise reach the UI verbatim (e.g. "user_role_not_found"). Map
|
||||
// known codes to a friendly, translated message before falling back to the
|
||||
// raw text. `unit_employee_limit_reached` carries its configured limit after
|
||||
// a colon (e.g. "unit_employee_limit_reached:5").
|
||||
const UNIT_EMPLOYEE_LIMIT_PREFIX = "unit_employee_limit_reached:";
|
||||
|
||||
const mapIamErrorCode = (
|
||||
raw: string | null,
|
||||
t: (key: string, options?: Record<string, unknown>) => string,
|
||||
): string | null => {
|
||||
if (!raw) return null;
|
||||
if (raw.startsWith(UNIT_EMPLOYEE_LIMIT_PREFIX)) {
|
||||
return t("msg.unitEmployeeLimitReached", {
|
||||
limit: raw.slice(UNIT_EMPLOYEE_LIMIT_PREFIX.length),
|
||||
});
|
||||
}
|
||||
if (raw === "user_role_not_found") return t("msg.userRoleNotFound");
|
||||
return null;
|
||||
};
|
||||
|
||||
// Maps an HTTP status code to the i18n key used when no backend message is available.
|
||||
const statusKeyFor = (status: number | undefined): string => {
|
||||
if (status === 400 || status === 422) return "msg.validationError";
|
||||
@@ -111,7 +133,7 @@ const parseBlobBody = async (blob: Blob): Promise<unknown> => {
|
||||
};
|
||||
|
||||
export const useErrorHandler = (
|
||||
t: (key: string) => string,
|
||||
t: (key: string, options?: Record<string, unknown>) => string,
|
||||
) => {
|
||||
const getErrorMessage = useCallback(
|
||||
async (err: unknown): Promise<string> => {
|
||||
@@ -134,15 +156,15 @@ export const useErrorHandler = (
|
||||
const fromException =
|
||||
extractMessage((data as any)?.exception?.response) ??
|
||||
extractMessage((data as any)?.exception);
|
||||
if (fromException) return fromException;
|
||||
if (fromException) return mapIamErrorCode(fromException, t) ?? fromException;
|
||||
|
||||
const fromData = extractMessage(data);
|
||||
if (fromData) return fromData;
|
||||
if (fromData) return mapIamErrorCode(fromData, t) ?? fromData;
|
||||
}
|
||||
|
||||
if (err instanceof Error) {
|
||||
const fromError = extractMessage(err.message);
|
||||
if (fromError) return fromError;
|
||||
if (fromError) return mapIamErrorCode(fromError, t) ?? fromError;
|
||||
}
|
||||
|
||||
return t(statusKeyFor(status));
|
||||
@@ -186,7 +208,7 @@ export const useErrorHandler = (
|
||||
};
|
||||
|
||||
export const useClientErrorHandler = (
|
||||
t: (key: string) => string,
|
||||
t: (key: string, options?: Record<string, unknown>) => string,
|
||||
) => {
|
||||
const getErrorMessage = useCallback(
|
||||
(err: unknown): string => {
|
||||
@@ -205,13 +227,13 @@ export const useClientErrorHandler = (
|
||||
const fromException =
|
||||
extractMessage(data?.exception?.response) ??
|
||||
extractMessage(data?.exception);
|
||||
if (fromException) return fromException;
|
||||
if (fromException) return mapIamErrorCode(fromException, t) ?? fromException;
|
||||
|
||||
const fromData = extractMessage(data);
|
||||
if (fromData) return fromData;
|
||||
if (fromData) return mapIamErrorCode(fromData, t) ?? fromData;
|
||||
|
||||
const fromError = extractMessage((err as any).message);
|
||||
if (fromError) return fromError;
|
||||
if (fromError) return mapIamErrorCode(fromError, t) ?? fromError;
|
||||
}
|
||||
|
||||
return t(statusKeyFor(status));
|
||||
|
||||
Reference in New Issue
Block a user