Refactored UI endpoint usage
This commit is contained in:
@@ -19,6 +19,7 @@ export interface FriendlyError {
|
||||
category: ErrorCategory;
|
||||
categoryLabel?: string;
|
||||
fieldErrors?: Array<{ field: string; message: string }>;
|
||||
errorsList?: string[];
|
||||
technicalDetails?: string;
|
||||
}
|
||||
|
||||
@@ -80,7 +81,7 @@ function humanizeFieldName(field: string): string {
|
||||
function cleanTechnicalJargon(text: string): string {
|
||||
if (!text) return '';
|
||||
|
||||
let cleaned = text
|
||||
const cleaned = text
|
||||
.replace(/^AxiosError:\s*/i, '')
|
||||
.replace(/^Error:\s*/i, '')
|
||||
.replace(/Request failed with status code \d+/i, '')
|
||||
@@ -90,33 +91,14 @@ function cleanTechnicalJargon(text: string): string {
|
||||
.replace(/at\s+[a-zA-Z0-9_.]+\s*\(.*?\)/g, '')
|
||||
.trim();
|
||||
|
||||
// If after cleaning it's empty or trivial, return empty
|
||||
if (cleaned.length < 3 || cleaned === '[object Object]') {
|
||||
// In this system, ProblemDetails title is always "Error" — ignore trivial or generic "Error" strings
|
||||
if (cleaned.length < 2 || cleaned === '[object Object]' || cleaned.toLowerCase() === 'error') {
|
||||
return '';
|
||||
}
|
||||
|
||||
return cleaned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a title is a standard framework generic title.
|
||||
*/
|
||||
function isGenericFrameworkTitle(title?: string): boolean {
|
||||
if (!title) return true;
|
||||
const lower = title.toLowerCase().trim();
|
||||
return (
|
||||
lower === 'bad request' ||
|
||||
lower === 'one or more validation errors occurred.' ||
|
||||
lower === 'one or more validation errors occurred' ||
|
||||
lower === 'forbidden' ||
|
||||
lower === 'not found' ||
|
||||
lower === 'internal server error' ||
|
||||
lower === 'an error occurred while processing your request.' ||
|
||||
lower === 'api request error' ||
|
||||
lower.startsWith('request failed with')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a response object conforms to RFC 7807 / RFC 9457 ProblemDetails.
|
||||
*/
|
||||
@@ -127,7 +109,9 @@ export function isProblemDetailsObject(data: any): data is ProblemDetails {
|
||||
'detail' in data ||
|
||||
'status' in data ||
|
||||
'type' in data ||
|
||||
'errors' in data
|
||||
'errors' in data ||
|
||||
'Errors' in data ||
|
||||
'errorList' in data
|
||||
);
|
||||
}
|
||||
|
||||
@@ -180,11 +164,8 @@ function detectOperationIntent(
|
||||
|
||||
/**
|
||||
* Extracts and formats human-friendly error guidance from any error,
|
||||
* prioritizing the standardised ProblemDetails model when present,
|
||||
* and handling BadRequest specifically for its multiple purposes:
|
||||
* - Input validation errors (field-level)
|
||||
* - Creation errors (prerequisites, business rules, duplicate identifiers)
|
||||
* - Update errors (lifecycle state restrictions, invalid transitions, permissions)
|
||||
* accessing the ProblemDetails `errors` array directly to display the error.
|
||||
* In this system the title field is always "Error", so no generic error testing is performed.
|
||||
*/
|
||||
export function parseFriendlyError(
|
||||
error: unknown,
|
||||
@@ -193,7 +174,7 @@ export function parseFriendlyError(
|
||||
let statusCode: number | undefined;
|
||||
let rawTitle: string | undefined;
|
||||
let rawDetail: string | undefined;
|
||||
let rawErrors: Record<string, string[] | string> | undefined;
|
||||
let rawErrors: any = undefined;
|
||||
let isProblemDetails = false;
|
||||
let rawResponseData: any = null;
|
||||
let technicalSummary = '';
|
||||
@@ -210,25 +191,31 @@ export function parseFriendlyError(
|
||||
technicalSummary = `${httpMethod || 'HTTP'} ${requestUrl} -> ${statusCode || axiosErr.code || 'ERR'}`;
|
||||
|
||||
if (rawResponseData && typeof rawResponseData === 'object') {
|
||||
if (isProblemDetailsObject(rawResponseData)) {
|
||||
const dataObj = rawResponseData as any;
|
||||
if (isProblemDetailsObject(dataObj)) {
|
||||
isProblemDetails = true;
|
||||
rawTitle = rawResponseData.title || undefined;
|
||||
rawDetail = rawResponseData.detail || undefined;
|
||||
statusCode = rawResponseData.status || statusCode;
|
||||
rawErrors = rawResponseData.errors;
|
||||
} else if (rawResponseData.message || rawResponseData.error) {
|
||||
rawDetail = rawResponseData.message || rawResponseData.error;
|
||||
rawTitle = dataObj.title || dataObj.Title || undefined;
|
||||
rawDetail = dataObj.detail || dataObj.Detail || undefined;
|
||||
statusCode = dataObj.status || dataObj.Status || statusCode;
|
||||
rawErrors = dataObj.errors ?? dataObj.Errors ?? dataObj.errorList ?? dataObj.ErrorList;
|
||||
} else if (dataObj.message || dataObj.error || dataObj.errors || dataObj.Errors) {
|
||||
rawDetail = dataObj.message || dataObj.error;
|
||||
rawErrors = dataObj.errors ?? dataObj.Errors;
|
||||
} else if (Array.isArray(dataObj)) {
|
||||
rawErrors = dataObj;
|
||||
}
|
||||
} else if (typeof rawResponseData === 'string') {
|
||||
// Check if response data is stringified JSON containing ProblemDetails
|
||||
try {
|
||||
const parsed = JSON.parse(rawResponseData);
|
||||
const parsed = JSON.parse(rawResponseData) as any;
|
||||
if (isProblemDetailsObject(parsed)) {
|
||||
isProblemDetails = true;
|
||||
rawTitle = parsed.title || undefined;
|
||||
rawDetail = parsed.detail || undefined;
|
||||
statusCode = parsed.status || statusCode;
|
||||
rawErrors = parsed.errors;
|
||||
rawTitle = parsed.title || parsed.Title || undefined;
|
||||
rawDetail = parsed.detail || parsed.Detail || undefined;
|
||||
statusCode = parsed.status || parsed.Status || statusCode;
|
||||
rawErrors = parsed.errors ?? parsed.Errors ?? parsed.errorList ?? parsed.ErrorList;
|
||||
} else if (Array.isArray(parsed)) {
|
||||
rawErrors = parsed;
|
||||
} else {
|
||||
rawDetail = cleanTechnicalJargon(rawResponseData);
|
||||
}
|
||||
@@ -267,16 +254,19 @@ export function parseFriendlyError(
|
||||
const obj = error as any;
|
||||
if (isProblemDetailsObject(obj)) {
|
||||
isProblemDetails = true;
|
||||
rawTitle = obj.title || undefined;
|
||||
rawDetail = obj.detail || undefined;
|
||||
statusCode = obj.status || undefined;
|
||||
rawErrors = obj.errors;
|
||||
rawTitle = obj.title || obj.Title || undefined;
|
||||
rawDetail = obj.detail || obj.Detail || undefined;
|
||||
statusCode = obj.status || obj.Status || undefined;
|
||||
rawErrors = obj.errors ?? obj.Errors ?? obj.errorList ?? obj.ErrorList;
|
||||
} else if (obj.response?.data && isProblemDetailsObject(obj.response.data)) {
|
||||
isProblemDetails = true;
|
||||
rawTitle = obj.response.data.title || undefined;
|
||||
rawDetail = obj.response.data.detail || undefined;
|
||||
statusCode = obj.response.status || obj.response.data.status;
|
||||
rawErrors = obj.response.data.errors;
|
||||
const respData = obj.response.data as any;
|
||||
rawTitle = respData.title || respData.Title || undefined;
|
||||
rawDetail = respData.detail || respData.Detail || undefined;
|
||||
statusCode = obj.response.status || respData.status || respData.Status;
|
||||
rawErrors = respData.errors ?? respData.Errors ?? respData.errorList ?? respData.ErrorList;
|
||||
} else if (Array.isArray(obj)) {
|
||||
rawErrors = obj;
|
||||
} else if (obj.message) {
|
||||
rawDetail = obj.message;
|
||||
const statusMatch = obj.message.match(/status code (\d{3})/i);
|
||||
@@ -285,7 +275,7 @@ export function parseFriendlyError(
|
||||
}
|
||||
}
|
||||
} else if (typeof error === 'string') {
|
||||
// 3. String error, e.g. String(err)
|
||||
// 3. String error
|
||||
const statusMatch = error.match(/status code (\d{3})/i);
|
||||
if (statusMatch) {
|
||||
statusCode = parseInt(statusMatch[1], 10);
|
||||
@@ -293,187 +283,188 @@ export function parseFriendlyError(
|
||||
rawDetail = cleanTechnicalJargon(error);
|
||||
}
|
||||
|
||||
// Parse field-level validation errors from ProblemDetails if present
|
||||
// Extract errors list directly from ProblemDetails errors array / dictionary
|
||||
const fieldErrors: Array<{ field: string; message: string }> = [];
|
||||
if (rawErrors && typeof rawErrors === 'object') {
|
||||
for (const [key, val] of Object.entries(rawErrors)) {
|
||||
const fieldTitle = humanizeFieldName(key);
|
||||
if (Array.isArray(val)) {
|
||||
val.forEach((msg) => {
|
||||
if (msg) fieldErrors.push({ field: fieldTitle, message: cleanTechnicalJargon(String(msg)) });
|
||||
});
|
||||
} else if (typeof val === 'string' && val) {
|
||||
fieldErrors.push({ field: fieldTitle, message: cleanTechnicalJargon(val) });
|
||||
const errorsList: string[] = [];
|
||||
|
||||
if (rawErrors) {
|
||||
if (Array.isArray(rawErrors)) {
|
||||
rawErrors.forEach((item) => {
|
||||
if (typeof item === 'string' && item.trim()) {
|
||||
const cleaned = cleanTechnicalJargon(item.trim());
|
||||
if (cleaned) errorsList.push(cleaned);
|
||||
} else if (item && typeof item === 'object') {
|
||||
const msg =
|
||||
item.message ||
|
||||
item.errorMessage ||
|
||||
item.detail ||
|
||||
item.error ||
|
||||
item.description ||
|
||||
JSON.stringify(item);
|
||||
const cleaned = cleanTechnicalJargon(String(msg));
|
||||
if (cleaned) errorsList.push(cleaned);
|
||||
}
|
||||
});
|
||||
} else if (typeof rawErrors === 'object') {
|
||||
for (const [key, val] of Object.entries(rawErrors)) {
|
||||
const isNumericKey = /^\d+$/.test(key);
|
||||
const fieldTitle = isNumericKey ? '' : humanizeFieldName(key);
|
||||
if (Array.isArray(val)) {
|
||||
val.forEach((msg) => {
|
||||
if (msg) {
|
||||
const cleaned = cleanTechnicalJargon(String(msg));
|
||||
if (cleaned) {
|
||||
if (fieldTitle && !cleaned.toLowerCase().includes(fieldTitle.toLowerCase())) {
|
||||
fieldErrors.push({ field: fieldTitle, message: cleaned });
|
||||
errorsList.push(`${fieldTitle}: ${cleaned}`);
|
||||
} else {
|
||||
errorsList.push(cleaned);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (typeof val === 'string' && val.trim()) {
|
||||
const cleaned = cleanTechnicalJargon(val.trim());
|
||||
if (cleaned) {
|
||||
if (fieldTitle && !cleaned.toLowerCase().includes(fieldTitle.toLowerCase())) {
|
||||
fieldErrors.push({ field: fieldTitle, message: cleaned });
|
||||
errorsList.push(`${fieldTitle}: ${cleaned}`);
|
||||
} else {
|
||||
errorsList.push(cleaned);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (typeof rawErrors === 'string' && rawErrors.trim()) {
|
||||
const cleaned = cleanTechnicalJargon(rawErrors.trim());
|
||||
if (cleaned) errorsList.push(cleaned);
|
||||
}
|
||||
}
|
||||
|
||||
// Detect whether the operation is creation, update, deletion, or general
|
||||
// Detect operation intent (creation, update, deletion, general)
|
||||
const operationIntent = detectOperationIntent(httpMethod, fallbackActionTitle, requestUrl);
|
||||
|
||||
// Build user-friendly title, message, and category based on standard HTTP codes and ProblemDetails
|
||||
let friendlyTitle = fallbackActionTitle;
|
||||
let friendlyMessage = '';
|
||||
let category: ErrorCategory = 'general';
|
||||
let categoryLabel: string | undefined = undefined;
|
||||
|
||||
switch (statusCode) {
|
||||
case 403:
|
||||
// Forbid: specifically occurs when the hashid used on a call is invalid or access denied
|
||||
category = 'security';
|
||||
categoryLabel = 'Invalid Identifier / Forbid';
|
||||
friendlyTitle = 'Access Not Permitted or Invalid Identifier';
|
||||
friendlyMessage =
|
||||
cleanTechnicalJargon(rawDetail || '') ||
|
||||
'The reference identifier (hash ID) is invalid, expired, or you do not have permission to access this item. Please verify your selection or contact your system administrator.';
|
||||
break;
|
||||
const cleanDetail = cleanTechnicalJargon(rawDetail || '');
|
||||
|
||||
case 404:
|
||||
// NotFound: item could not be located
|
||||
category = 'notFound';
|
||||
categoryLabel = 'Record Not Found';
|
||||
friendlyTitle = 'Record Not Found';
|
||||
friendlyMessage =
|
||||
cleanTechnicalJargon(rawDetail || '') ||
|
||||
'The requested record could not be found. It may have been archived, removed, or the link is outdated.';
|
||||
break;
|
||||
// In this system, ProblemDetails allows access to the errors array to display the error directly if available
|
||||
if (errorsList.length > 0) {
|
||||
category = 'validation';
|
||||
categoryLabel =
|
||||
operationIntent === 'creation'
|
||||
? 'Validation'
|
||||
: operationIntent === 'update'
|
||||
? 'Validation'
|
||||
: 'Validation';
|
||||
|
||||
case 400: {
|
||||
// BadRequest serves MULTIPLE purposes:
|
||||
// 1) Input validation errors (field-level errors dictionary)
|
||||
// 2) Creation errors (prerequisites, business rules, duplicate code, invalid entity state)
|
||||
// 3) Update errors (status transition restrictions, record locked, state mismatch)
|
||||
const hasSpecificTitle = rawTitle && !isGenericFrameworkTitle(rawTitle);
|
||||
const cleanDetail = cleanTechnicalJargon(rawDetail || '');
|
||||
|
||||
if (fieldErrors.length > 0) {
|
||||
// Purpose 1: Input Validation Errors
|
||||
category = 'validation';
|
||||
categoryLabel =
|
||||
operationIntent === 'creation'
|
||||
? 'Creation Validation'
|
||||
: operationIntent === 'update'
|
||||
? 'Update Validation'
|
||||
: 'Input Validation';
|
||||
|
||||
if (hasSpecificTitle) {
|
||||
friendlyTitle = cleanTechnicalJargon(rawTitle!);
|
||||
} else if (operationIntent === 'creation') {
|
||||
friendlyTitle = 'Please Check New Record Details';
|
||||
} else if (operationIntent === 'update') {
|
||||
friendlyTitle = 'Please Check Updated Values';
|
||||
} else {
|
||||
friendlyTitle = 'Please Check Submitted Details';
|
||||
}
|
||||
|
||||
const errorList = fieldErrors.map((f) => `• ${f.field}: ${f.message}`).join('\n');
|
||||
friendlyMessage = cleanDetail
|
||||
? `${cleanDetail}\n\n${errorList}`
|
||||
: `The submitted details could not be validated:\n${errorList}`;
|
||||
} else if (operationIntent === 'creation') {
|
||||
// Purpose 2: Creation Error (non-field-validation: business rule, duplicate, or prerequisite failure)
|
||||
category = 'creation';
|
||||
categoryLabel = 'Creation Error';
|
||||
|
||||
if (hasSpecificTitle) {
|
||||
friendlyTitle = cleanTechnicalJargon(rawTitle!);
|
||||
} else if (fallbackActionTitle && fallbackActionTitle !== 'Action Failed') {
|
||||
friendlyTitle = fallbackActionTitle;
|
||||
} else {
|
||||
friendlyTitle = 'Unable to Create Record';
|
||||
}
|
||||
|
||||
friendlyMessage =
|
||||
cleanDetail ||
|
||||
'The new record could not be created. This may be due to business rule prerequisites, existing duplicate identifiers, or missing related records.';
|
||||
} else if (operationIntent === 'update') {
|
||||
// Purpose 3: Update Error (non-field-validation: status transition, record locked, lifecycle restriction)
|
||||
category = 'update';
|
||||
categoryLabel = 'Update Error';
|
||||
|
||||
if (hasSpecificTitle) {
|
||||
friendlyTitle = cleanTechnicalJargon(rawTitle!);
|
||||
} else if (fallbackActionTitle && fallbackActionTitle !== 'Action Failed') {
|
||||
friendlyTitle = fallbackActionTitle;
|
||||
} else {
|
||||
friendlyTitle = 'Unable to Update Record';
|
||||
}
|
||||
|
||||
friendlyMessage =
|
||||
cleanDetail ||
|
||||
'The record could not be updated. It may be locked, in a status that does not allow modifications, or the requested status transition is not permitted.';
|
||||
} else {
|
||||
// General Bad Request
|
||||
category = 'general';
|
||||
categoryLabel = 'Request Error';
|
||||
|
||||
if (hasSpecificTitle) {
|
||||
friendlyTitle = cleanTechnicalJargon(rawTitle!);
|
||||
} else {
|
||||
friendlyTitle = fallbackActionTitle || 'Request Could Not Be Processed';
|
||||
}
|
||||
|
||||
friendlyMessage =
|
||||
cleanDetail ||
|
||||
'The request could not be processed due to invalid parameters or business rule requirements. Please review your input and try again.';
|
||||
}
|
||||
break;
|
||||
// Display the error directly
|
||||
if (errorsList.length === 1) {
|
||||
friendlyMessage = errorsList[0];
|
||||
} else {
|
||||
friendlyMessage = errorsList.map((err) => `• ${err}`).join('\n');
|
||||
}
|
||||
|
||||
case 409:
|
||||
// Conflict
|
||||
// If detail provides extra non-duplicate context, include it
|
||||
if (cleanDetail && !errorsList.some((e) => e.toLowerCase() === cleanDetail.toLowerCase())) {
|
||||
friendlyMessage = `${cleanDetail}\n\n${friendlyMessage}`;
|
||||
}
|
||||
} else if (cleanDetail) {
|
||||
// If no errors array, but detail is available, display detail directly
|
||||
friendlyMessage = cleanDetail;
|
||||
if (statusCode === 403) {
|
||||
category = 'security';
|
||||
categoryLabel = 'Access Denied';
|
||||
} else if (statusCode === 404) {
|
||||
category = 'notFound';
|
||||
categoryLabel = 'Not Found';
|
||||
} else if (statusCode === 409) {
|
||||
category = 'conflict';
|
||||
categoryLabel = 'Conflict';
|
||||
friendlyTitle = 'Update Conflict';
|
||||
friendlyMessage =
|
||||
cleanTechnicalJargon(rawDetail || '') ||
|
||||
'This action conflicts with the current status of the record (for instance, another user may have updated it or a duplicate entry exists). Please refresh the page and try again.';
|
||||
break;
|
||||
|
||||
case 422:
|
||||
// Unprocessable Entity
|
||||
category = 'validation';
|
||||
categoryLabel = 'Incomplete Data';
|
||||
friendlyTitle = 'Information Incomplete';
|
||||
friendlyMessage =
|
||||
cleanTechnicalJargon(rawDetail || '') ||
|
||||
'The submitted information could not be processed by the system. Please verify that all required fields are complete.';
|
||||
break;
|
||||
|
||||
case 429:
|
||||
// Too Many Requests
|
||||
category = 'server';
|
||||
categoryLabel = 'Rate Limit';
|
||||
friendlyTitle = 'System Busy';
|
||||
friendlyMessage = 'The system received too many requests in a short time. Please pause for a moment before trying again.';
|
||||
break;
|
||||
|
||||
case 500:
|
||||
case 502:
|
||||
case 503:
|
||||
case 504:
|
||||
// Server error
|
||||
} else if (statusCode && statusCode >= 500) {
|
||||
category = 'server';
|
||||
categoryLabel = 'Service Issue';
|
||||
friendlyTitle = 'Service Temporarily Unavailable';
|
||||
friendlyMessage =
|
||||
'The central service encountered an unexpected issue while processing your request. Please try again in a few moments.';
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Fallback status-specific messaging when neither errors array nor detail is available
|
||||
switch (statusCode) {
|
||||
case 403:
|
||||
category = 'security';
|
||||
categoryLabel = 'Invalid Identifier / Forbid';
|
||||
friendlyTitle = 'Access Not Permitted or Invalid Identifier';
|
||||
friendlyMessage =
|
||||
'The reference identifier (hash ID) is invalid, expired, or you do not have permission to access this item. Please verify your selection or contact your system administrator.';
|
||||
break;
|
||||
|
||||
default:
|
||||
if (rawTitle && isProblemDetails && !isGenericFrameworkTitle(rawTitle)) {
|
||||
friendlyTitle = cleanTechnicalJargon(rawTitle) || fallbackActionTitle;
|
||||
}
|
||||
if (rawDetail) {
|
||||
friendlyMessage = cleanTechnicalJargon(rawDetail);
|
||||
} else if (fieldErrors.length > 0) {
|
||||
case 404:
|
||||
category = 'notFound';
|
||||
categoryLabel = 'Record Not Found';
|
||||
friendlyTitle = 'Record Not Found';
|
||||
friendlyMessage =
|
||||
'The requested record could not be found. It may have been archived, removed, or the link is outdated.';
|
||||
break;
|
||||
|
||||
case 400:
|
||||
if (operationIntent === 'creation') {
|
||||
category = 'creation';
|
||||
categoryLabel = 'Creation Error';
|
||||
friendlyMessage =
|
||||
'The new record could not be created. Please review the input parameters and prerequisites.';
|
||||
} else if (operationIntent === 'update') {
|
||||
category = 'update';
|
||||
categoryLabel = 'Update Error';
|
||||
friendlyMessage =
|
||||
'The record could not be updated. It may be locked or the requested status transition is not permitted.';
|
||||
} else {
|
||||
category = 'general';
|
||||
categoryLabel = 'Request Error';
|
||||
friendlyMessage =
|
||||
'The request could not be processed due to invalid parameters or business rule requirements.';
|
||||
}
|
||||
break;
|
||||
|
||||
case 409:
|
||||
category = 'conflict';
|
||||
categoryLabel = 'Conflict';
|
||||
friendlyTitle = 'Update Conflict';
|
||||
friendlyMessage =
|
||||
'This action conflicts with the current status of the record. Please refresh the page and try again.';
|
||||
break;
|
||||
|
||||
case 422:
|
||||
category = 'validation';
|
||||
categoryLabel = 'Validation';
|
||||
friendlyMessage = fieldErrors.map((f) => `• ${f.field}: ${f.message}`).join('\n');
|
||||
} else {
|
||||
friendlyMessage = 'An unexpected issue occurred while processing this request. Please try again or contact support.';
|
||||
}
|
||||
break;
|
||||
categoryLabel = 'Incomplete Data';
|
||||
friendlyMessage =
|
||||
'The submitted information could not be processed. Please verify that all required fields are complete.';
|
||||
break;
|
||||
|
||||
case 429:
|
||||
category = 'server';
|
||||
categoryLabel = 'Rate Limit';
|
||||
friendlyTitle = 'System Busy';
|
||||
friendlyMessage =
|
||||
'The system received too many requests in a short time. Please pause for a moment before trying again.';
|
||||
break;
|
||||
|
||||
case 500:
|
||||
case 502:
|
||||
case 503:
|
||||
case 504:
|
||||
category = 'server';
|
||||
categoryLabel = 'Service Issue';
|
||||
friendlyTitle = 'Service Temporarily Unavailable';
|
||||
friendlyMessage =
|
||||
'The central service encountered an unexpected issue while processing your request. Please try again in a few moments.';
|
||||
break;
|
||||
|
||||
default:
|
||||
friendlyMessage =
|
||||
'An unexpected issue occurred while processing this request. Please try again or contact support.';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure title is friendly and not an uncaught technical string
|
||||
@@ -489,6 +480,7 @@ export function parseFriendlyError(
|
||||
category,
|
||||
categoryLabel,
|
||||
fieldErrors: fieldErrors.length > 0 ? fieldErrors : undefined,
|
||||
errorsList: errorsList.length > 0 ? errorsList : undefined,
|
||||
technicalDetails: technicalSummary || (statusCode ? `HTTP Status ${statusCode}` : undefined),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user