diff --git a/docs/error-handling-guidelines.md b/docs/error-handling-guidelines.md
new file mode 100644
index 00000000..3a427d81
--- /dev/null
+++ b/docs/error-handling-guidelines.md
@@ -0,0 +1,165 @@
+# Error Handling Guidelines
+
+## Overview
+
+The CCF UI exposes a consistent set of utilities for surfacing errors. These helpers separate transport concerns (Axios responses) from application-level failures and give developers the choice of presenting feedback as a toast or a modal dialog.
+
+## Available Utilities
+
+### `formatAxiosError`
+
+**Location:** `src/utils/error-formatting.ts`
+
+Converts an `AxiosError` into `{ summary, detail, statusCode }`, pulling useful information from the HTTP response.
+
+### `formatError`
+
+**Location:** `src/utils/error-formatting.ts`
+
+Normalizes any thrown value (plain `Error`, string, custom object) into the same structure as `formatAxiosError`. Use this when the failure is not an Axios response.
+
+### `useErrorToast`
+
+**Location:** `src/composables/useErrorToast.ts`
+
+Watches a reactive Axios error ref (e.g., from `useDataApi`) and automatically emits a PrimeVue toast with formatted details. Returns a `showErrorToast` helper for manual invocation.
+
+### `useAxiosErrorDialog`
+
+**Location:** `src/composables/useAxiosErrorDialog.ts`
+
+Watches an Axios error ref and opens the shared error dialog when a failure occurs. Also exposes `showAxiosErrorDialog` for manual use.
+
+### `errorDialog` service
+
+**Location:** `src/services/error-dialog.ts`
+
+Service-style API with methods:
+
+- `errorDialog.show({...})` – display a dialog given explicit summary/detail.
+- `errorDialog.showAxiosError(error, options)` – convenience wrapper around `formatAxiosError`.
+- `errorDialog.showError(error, options)` – convenience wrapper around `formatError`.
+- `errorDialog.hide()` – close the dialog programmatically.
+
+### `ErrorDialogHost` component
+
+**Location:** `src/components/notifications/ErrorDialogHost.vue`
+
+Renders a global dialog surface driven by the service above. It is already mounted in `src/App.vue`.
+
+---
+
+## Choosing a Tool
+
+- Use **`useErrorToast`** for straightforward Axios error refs (list/detail views, data tables) when a toast is sufficient.
+- Use **`useAxiosErrorDialog`** when the user must acknowledge a failure before continuing.
+- Call **`errorDialog.showAxiosError`** inside `catch` blocks for manual flows (e.g., form submissions, CRUD actions).
+- Use **`formatError`** for non-Axios failures before calling `toast.add` or `errorDialog.showError`.
+
+---
+
+## Usage Examples
+
+### 1. Toast on List Fetch Failure
+
+```vue
+
+```
+
+### 2. Dialog on Critical Fetch Failure
+
+```vue
+
+```
+
+### 3. Form Submission with Toast
+
+```ts
+import { useToast } from 'primevue/usetoast';
+import { formatAxiosError } from '@/utils/error-formatting';
+import type { AxiosError } from 'axios';
+import type { ErrorBody, ErrorResponse } from '@/stores/types';
+
+const toast = useToast();
+
+async function saveUser() {
+ try {
+ await apiCall();
+ toast.add({
+ severity: 'success',
+ summary: 'Success',
+ detail: 'User updated successfully.',
+ life: 3000,
+ });
+ } catch (err) {
+ const formatted = formatAxiosError(
+ err as AxiosError>,
+ );
+ toast.add({
+ severity: 'error',
+ summary: `Error updating user: ${formatted.summary}`,
+ detail: formatted.detail,
+ life: 4000,
+ });
+ }
+}
+```
+
+### 4. Manual Dialog from Catch Block
+
+```ts
+import { errorDialog } from '@/services/error-dialog';
+
+try {
+ await deleteThing();
+} catch (err) {
+ errorDialog.showError(err, {
+ summary: 'Delete failed',
+ onClose: () => console.log('Dialog dismissed'),
+ });
+}
+```
+
+---
+
+## Checklist Before Shipping
+
+- [ ] Decide between toast or dialog based on UX (non-blocking vs requiring acknowledgment).
+- [ ] Use `useErrorToast` or `useAxiosErrorDialog` when working with `useDataApi` error refs.
+- [ ] In manual `try/catch` blocks:
+ - [ ] Call `formatAxiosError` for Axios failures.
+ - [ ] Call `formatError` for non-Axios failures.
+ - [ ] Pass the formatted output to `toast.add` **or** `errorDialog.show`.
+- [ ] Ensure any navigation or recovery logic runs after the toast/dialog logic so the user sees the feedback.
diff --git a/src/App.vue b/src/App.vue
index 7ade6044..e1f431bb 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -2,10 +2,12 @@
import { RouterView } from 'vue-router';
import Toast from '@/volt/Toast.vue';
import ConfirmDialog from '@/volt/ConfirmDialog.vue';
+import ErrorDialogHost from '@/components/notifications/ErrorDialogHost.vue';
+
diff --git a/src/components/notifications/ErrorDialogHost.vue b/src/components/notifications/ErrorDialogHost.vue
new file mode 100644
index 00000000..6c9df38b
--- /dev/null
+++ b/src/components/notifications/ErrorDialogHost.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
diff --git a/src/components/users/UserCreateForm.vue b/src/components/users/UserCreateForm.vue
index f77e92e9..7f0a1981 100644
--- a/src/components/users/UserCreateForm.vue
+++ b/src/components/users/UserCreateForm.vue
@@ -62,6 +62,7 @@ import PrimaryButton from '../PrimaryButton.vue';
import { useDataApi } from '@/composables/axios';
import type { AxiosError } from 'axios';
import { useToast } from 'primevue/usetoast';
+import { formatAxiosError } from '@/utils/error-formatting';
const passwords = reactive({
password: '',
@@ -120,11 +121,11 @@ async function createUser() {
emit('create', createdUser.value);
} catch (error) {
const errorResponse = error as AxiosError>;
+ const formattedError = formatAxiosError(errorResponse);
toast.add({
severity: 'error',
- summary: 'Error creating user',
- detail:
- errorResponse.response?.data.errors.body ?? 'Unknown error occurred',
+ summary: `Error creating user${formattedError.statusCode ? `: ${formattedError.summary}` : ''}`,
+ detail: formattedError.detail,
life: 3000,
});
return;
diff --git a/src/components/users/UserEditForm.vue b/src/components/users/UserEditForm.vue
index 1ae75746..d278c32d 100644
--- a/src/components/users/UserEditForm.vue
+++ b/src/components/users/UserEditForm.vue
@@ -43,6 +43,7 @@ import PrimaryButton from '../PrimaryButton.vue';
import { useDataApi } from '@/composables/axios';
import type { AxiosError } from 'axios';
import { useToast } from 'primevue/usetoast';
+import { formatAxiosError } from '@/utils/error-formatting';
const props = defineProps<{
user: CCFUser;
@@ -79,12 +80,11 @@ async function updateUser() {
emit('saved', updatedUser.value);
} catch (error) {
const errorResponse = error as AxiosError>;
+ const formattedError = formatAxiosError(errorResponse);
toast.add({
severity: 'error',
- summary: 'Error updating user',
- detail:
- errorResponse.response?.data.errors.body ??
- 'An error occurred while updating the user.',
+ summary: `Error updating user${formattedError.statusCode ? `: ${formattedError.summary}` : ''}`,
+ detail: formattedError.detail,
life: 3000,
});
}
diff --git a/src/composables/useAxiosErrorDialog.ts b/src/composables/useAxiosErrorDialog.ts
new file mode 100644
index 00000000..d1e97408
--- /dev/null
+++ b/src/composables/useAxiosErrorDialog.ts
@@ -0,0 +1,70 @@
+import { watch, type Ref } from 'vue';
+import type { AxiosError } from 'axios';
+import type { ErrorResponse, ErrorBody } from '@/stores/types';
+import { errorDialog } from '@/services/error-dialog';
+
+export interface AxiosErrorDialogOptions {
+ /**
+ * Summary prefix displayed before the formatted status message.
+ */
+ summary?: string;
+
+ /**
+ * Whether to automatically show the dialog when error occurs.
+ * @default true
+ */
+ autoShow?: boolean;
+
+ /**
+ * Custom detail to override the formatted error detail.
+ */
+ detailOverride?: string;
+
+ /**
+ * Optional label for the close button.
+ * @default 'Close'
+ */
+ closeLabel?: string;
+
+ /**
+ * Optional callback invoked after the dialog is dismissed.
+ */
+ onClose?: () => void;
+}
+
+/**
+ * Composable to automatically display error dialogs for Axios errors.
+ *
+ * Watches a reactive Axios error ref and opens the shared error dialog when an error occurs.
+ */
+export function useAxiosErrorDialog(
+ errorRef: Ref> | null | undefined>,
+ options: AxiosErrorDialogOptions = {},
+) {
+ const {
+ summary,
+ autoShow = true,
+ detailOverride,
+ closeLabel,
+ onClose,
+ } = options;
+
+ watch(errorRef, (error) => {
+ if (error && autoShow) {
+ showAxiosErrorDialog(error);
+ }
+ });
+
+ function showAxiosErrorDialog(error: AxiosError>) {
+ errorDialog.showAxiosError(error, {
+ summary,
+ detailOverride,
+ closeLabel,
+ onClose,
+ });
+ }
+
+ return {
+ showAxiosErrorDialog,
+ };
+}
diff --git a/src/composables/useErrorToast.ts b/src/composables/useErrorToast.ts
new file mode 100644
index 00000000..123c81c0
--- /dev/null
+++ b/src/composables/useErrorToast.ts
@@ -0,0 +1,99 @@
+import { watch, type Ref } from 'vue';
+import { useToast } from 'primevue/usetoast';
+import type { AxiosError } from 'axios';
+import type { ErrorResponse, ErrorBody } from '@/stores/types';
+import { formatAxiosError } from '@/utils/error-formatting';
+
+/**
+ * Configuration options for the Axios error toast composable
+ */
+export interface ErrorToastOptions {
+ /**
+ * Summary prefix to show before the error status.
+ * If not provided, only the status code message will be shown.
+ *
+ * @example "Error loading user" becomes "Error loading user: 404 Not Found"
+ */
+ summary?: string;
+
+ /**
+ * Toast lifetime in milliseconds
+ * @default 5000
+ */
+ life?: number;
+
+ /**
+ * Whether to automatically show the toast when error occurs.
+ * Set to false if you need custom handling before showing the toast.
+ * @default true
+ */
+ autoShow?: boolean;
+
+ /**
+ * Custom error message extractor function.
+ * Use this to provide custom logic for extracting error messages
+ * based on your specific error scenarios.
+ *
+ * @param error - The AxiosError object
+ * @returns Custom error detail message
+ */
+ extractMessage?: (error: AxiosError>) => string;
+}
+
+/**
+ * Composable to automatically display toast notifications for Axios errors.
+ *
+ * This composable watches a reactive Axios error ref (typically from useDataApi or useGuestApi)
+ * and automatically displays a toast notification when an error occurs. It extracts
+ * HTTP status codes and API error messages to provide informative feedback to users.
+ *
+ * @param errorRef - Reactive reference to an AxiosError (typically from useDataApi)
+ * @param options - Configuration options for the error toast
+ * @returns Object with showErrorToast function for manual error toast display
+ */
+export function useErrorToast(
+ errorRef: Ref> | null | undefined>,
+ options: ErrorToastOptions = {},
+) {
+ const toast = useToast();
+
+ const { summary, life = 5000, autoShow = true, extractMessage } = options;
+
+ // Automatically watch the error ref and show toast when error occurs
+ watch(errorRef, (error) => {
+ if (error && autoShow) {
+ showErrorToast(error);
+ }
+ });
+
+ /**
+ * Manually show an error toast for the given Axios error.
+ *
+ * Use this when autoShow is false or when you need to show
+ * an error toast outside of the automatic watcher.
+ *
+ * @param error - The AxiosError to display
+ */
+ function showErrorToast(error: AxiosError>) {
+ const formatted = formatAxiosError(error);
+
+ // Use custom message extractor if provided
+ const detail = extractMessage ? extractMessage(error) : formatted.detail;
+
+ // Build summary with optional prefix
+ const toastSummary = summary
+ ? `${summary}: ${formatted.summary}`
+ : formatted.summary;
+
+ toast.add({
+ severity: 'error',
+ summary: toastSummary,
+ detail,
+ life,
+ });
+ }
+
+ return {
+ showErrorToast,
+ };
+}
diff --git a/src/services/error-dialog.ts b/src/services/error-dialog.ts
new file mode 100644
index 00000000..eb8df925
--- /dev/null
+++ b/src/services/error-dialog.ts
@@ -0,0 +1,134 @@
+import { reactive, readonly } from 'vue';
+import type { AxiosError } from 'axios';
+import type { ErrorResponse, ErrorBody } from '@/stores/types';
+import {
+ formatAxiosError,
+ formatError,
+ type FormattedError,
+} from '@/utils/error-formatting';
+
+export interface ErrorDialogPayload {
+ summary: string;
+ detail: string;
+ statusCode?: number;
+ closeLabel?: string;
+ onClose?: () => void;
+}
+
+interface ErrorDialogState
+ extends Omit {
+ visible: boolean;
+ summary: string;
+ detail: string;
+}
+
+const state = reactive({
+ visible: false,
+ summary: '',
+ detail: '',
+ statusCode: undefined,
+ closeLabel: 'Close',
+ onClose: undefined,
+});
+
+/**
+ * Reset the dialog state after dismissal.
+ */
+function resetState() {
+ state.summary = '';
+ state.detail = '';
+ state.statusCode = undefined;
+ state.closeLabel = 'Close';
+ state.onClose = undefined;
+}
+
+function buildSummary(
+ formatted: FormattedError,
+ overrideSummary?: string,
+): string {
+ if (!overrideSummary) {
+ return formatted.summary || 'Error';
+ }
+
+ if (!formatted.summary || formatted.summary === overrideSummary) {
+ return overrideSummary;
+ }
+
+ return `${overrideSummary}: ${formatted.summary}`;
+}
+
+function show(payload: ErrorDialogPayload) {
+ state.summary = payload.summary || 'Error';
+ state.detail = payload.detail;
+ state.statusCode = payload.statusCode;
+ state.closeLabel = payload.closeLabel ?? 'Close';
+ state.onClose = payload.onClose;
+ state.visible = true;
+}
+
+function hide(options: { invokeCallback?: boolean } = {}) {
+ const { invokeCallback = true } = options;
+ const callback = state.onClose;
+
+ state.visible = false;
+ resetState();
+
+ if (invokeCallback && typeof callback === 'function') {
+ callback();
+ }
+}
+
+function showAxiosError(
+ error: AxiosError>,
+ options: {
+ summary?: string;
+ detailOverride?: string;
+ closeLabel?: string;
+ onClose?: () => void;
+ } = {},
+) {
+ const formatted = formatAxiosError(error);
+ show({
+ summary: buildSummary(formatted, options.summary),
+ detail: options.detailOverride ?? formatted.detail,
+ statusCode: formatted.statusCode,
+ closeLabel: options.closeLabel,
+ onClose: options.onClose,
+ });
+}
+
+function showError(
+ error: unknown,
+ options: {
+ summary?: string;
+ closeLabel?: string;
+ onClose?: () => void;
+ defaultDetail?: string;
+ } = {},
+) {
+ const formatted = formatError(error, {
+ defaultSummary: options.summary,
+ defaultDetail: options.defaultDetail,
+ });
+ show({
+ summary: buildSummary(formatted, options.summary),
+ detail: formatted.detail,
+ statusCode: formatted.statusCode,
+ closeLabel: options.closeLabel,
+ onClose: options.onClose,
+ });
+}
+
+export const errorDialog = {
+ show,
+ hide,
+ showAxiosError,
+ showError,
+};
+
+export function useErrorDialogController() {
+ return {
+ state: readonly(state),
+ hide,
+ };
+}
diff --git a/src/utils/error-formatting.spec.ts b/src/utils/error-formatting.spec.ts
new file mode 100644
index 00000000..839f66f4
--- /dev/null
+++ b/src/utils/error-formatting.spec.ts
@@ -0,0 +1,40 @@
+import { describe, expect, it } from 'vitest';
+import type { AxiosError } from 'axios';
+import type { ErrorBody, ErrorResponse } from '@/stores/types';
+import { formatError } from './error-formatting';
+
+describe('formatError', () => {
+ it('formats axios errors with status code and API detail', () => {
+ const axiosError = {
+ isAxiosError: true,
+ response: {
+ status: 404,
+ data: {
+ errors: {
+ body: 'User not found',
+ },
+ },
+ },
+ message: 'Request failed',
+ } as AxiosError>;
+
+ const formatted = formatError(axiosError);
+
+ expect(formatted.summary).toBe('Not Found');
+ expect(formatted.detail).toBe('User not found');
+ expect(formatted.statusCode).toBe(404);
+ });
+
+ it('falls back to Error message when non-Axios error is thrown', () => {
+ const formatted = formatError(new Error('Something went wrong'));
+ expect(formatted.summary).toBe('Error');
+ expect(formatted.detail).toBe('Something went wrong');
+ expect(formatted.statusCode).toBeUndefined();
+ });
+
+ it('supports simple string errors', () => {
+ const formatted = formatError('plain string error');
+ expect(formatted.summary).toBe('Error');
+ expect(formatted.detail).toBe('plain string error');
+ });
+});
diff --git a/src/utils/error-formatting.ts b/src/utils/error-formatting.ts
new file mode 100644
index 00000000..ecf1aaac
--- /dev/null
+++ b/src/utils/error-formatting.ts
@@ -0,0 +1,195 @@
+import { isAxiosError } from 'axios';
+import type { AxiosError } from 'axios';
+import type { ErrorResponse, ErrorBody } from '@/stores/types';
+
+/**
+ * Formatted error information for display in toast notifications
+ */
+export interface FormattedError {
+ /**
+ * User-friendly error summary/title
+ */
+ summary: string;
+
+ /**
+ * Detailed error message
+ */
+ detail: string;
+
+ /**
+ * HTTP status code (if available)
+ */
+ statusCode?: number;
+}
+
+/**
+ * Format an AxiosError into user-friendly toast message components
+ *
+ * Extracts HTTP status codes, API error messages, and provides fallback messages
+ * for better user experience.
+ *
+ * @example
+ * ```typescript
+ * try {
+ * await api.post('/users', userData);
+ * } catch (err) {
+ * const formatted = formatAxiosError(err as AxiosError>);
+ * toast.add({
+ * severity: 'error',
+ * summary: formatted.summary,
+ * detail: formatted.detail,
+ * life: 5000,
+ * });
+ * }
+ * ```
+ *
+ * @param error - The AxiosError to format
+ * @returns Formatted error object with summary, detail, and status code
+ */
+export function formatAxiosError(
+ error: AxiosError>,
+): FormattedError {
+ const statusCode = error.response?.status;
+ const apiErrorMessage = error.response?.data?.errors?.body;
+
+ return {
+ summary: getErrorSummary(statusCode),
+ detail: apiErrorMessage || getErrorDetail(error),
+ statusCode,
+ };
+}
+
+/**
+ * Optional overrides when formatting unknown errors
+ */
+export interface FormatErrorOptions {
+ /**
+ * Override the default summary ("Error") when no better title is available.
+ */
+ defaultSummary?: string;
+
+ /**
+ * Override the default detail fallback when the error is not descriptive.
+ */
+ defaultDetail?: string;
+}
+
+/**
+ * Format unknown errors (Axios, Error, string, or otherwise) into a common structure.
+ *
+ * This helper lets any caller rely on consistent summary/detail strings regardless of the
+ * error origin. Axios errors continue to surface their status codes and response messages.
+ *
+ * @param error - The error to format (may be AxiosError, Error, string, or any value)
+ * @param options - Optional overrides for summary/detail defaults
+ * @returns Formatted error object with summary, detail, and optional status code
+ */
+export function formatError(
+ error: unknown,
+ options: FormatErrorOptions = {},
+): FormattedError {
+ if (isAxiosError>(error)) {
+ return formatAxiosError(error);
+ }
+
+ const defaultSummary = options.defaultSummary ?? 'Error';
+ const defaultDetail =
+ options.defaultDetail ?? 'An unexpected error occurred. Please try again.';
+
+ if (error instanceof Error) {
+ return {
+ summary: defaultSummary,
+ detail: error.message || defaultDetail,
+ };
+ }
+
+ if (typeof error === 'string') {
+ return {
+ summary: defaultSummary,
+ detail: error || defaultDetail,
+ };
+ }
+
+ if (
+ error &&
+ typeof error === 'object' &&
+ 'message' in error &&
+ typeof (error as { message?: unknown }).message === 'string'
+ ) {
+ return {
+ summary: defaultSummary,
+ detail: (error as { message: string }).message || defaultDetail,
+ };
+ }
+
+ return {
+ summary: defaultSummary,
+ detail: defaultDetail,
+ };
+}
+
+/**
+ * Get user-friendly error summary based on HTTP status code
+ *
+ * Maps common HTTP status codes to human-readable titles.
+ *
+ * @param statusCode - HTTP status code from the error response
+ * @returns User-friendly error title
+ *
+ * @internal
+ */
+function getErrorSummary(statusCode?: number): string {
+ const statusMessages: Record = {
+ 400: 'Bad Request',
+ 401: 'Unauthorized',
+ 403: 'Forbidden',
+ 404: 'Not Found',
+ 409: 'Conflict',
+ 422: 'Validation Error',
+ 429: 'Too Many Requests',
+ 500: 'Server Error',
+ 502: 'Bad Gateway',
+ 503: 'Service Unavailable',
+ 504: 'Gateway Timeout',
+ };
+
+ if (!statusCode) {
+ return 'Error';
+ }
+
+ return statusMessages[statusCode] || `Error ${statusCode}`;
+}
+
+/**
+ * Get detailed error message with fallbacks
+ *
+ * Attempts to extract error message from various parts of the AxiosError
+ * in order of preference:
+ * 1. API error body (response.data.errors.body)
+ * 2. Error message property
+ * 3. Generic fallback message
+ *
+ * @param error - The AxiosError to extract details from
+ * @returns Detailed error message
+ *
+ * @internal
+ */
+function getErrorDetail(error: AxiosError>): string {
+ // Try API error body (already checked in formatAxiosError, but kept for completeness)
+ if (error.response?.data?.errors?.body) {
+ return error.response.data.errors.body;
+ }
+
+ // Try error message
+ if (error.message) {
+ return error.message;
+ }
+
+ // Network error without response
+ if (!error.response) {
+ return 'Unable to connect to the server. Please check your network connection and try again.';
+ }
+
+ // Default fallback
+ return 'An unexpected error occurred. Please try again.';
+}
diff --git a/src/views/users/UserView.vue b/src/views/users/UserView.vue
index cd5a4437..91c5fbe1 100644
--- a/src/views/users/UserView.vue
+++ b/src/views/users/UserView.vue
@@ -71,6 +71,7 @@ import UserEditForm from '@/components/users/UserEditForm.vue';
import { useConfirm } from 'primevue/useconfirm';
import { useDataApi } from '@/composables/axios';
import type { AxiosError } from 'axios';
+import { formatAxiosError } from '@/utils/error-formatting';
const route = useRoute();
const router = useRouter();
@@ -96,12 +97,11 @@ const { data: updatedUserData, execute: lockExecute } = useDataApi(
watch(error, (err) => {
if (err) {
const errorResponse = err as AxiosError>;
+ const formattedError = formatAxiosError(errorResponse);
toast.add({
severity: 'error',
- summary: 'Error loading user',
- detail:
- errorResponse.response?.data.errors.body ||
- 'An error occurred while loading the user data.',
+ summary: `Error loading user${formattedError.statusCode ? `: ${formattedError.summary}` : ''}`,
+ detail: formattedError.detail,
life: 3000,
});
router.push({ name: 'users-list' });
@@ -133,12 +133,11 @@ async function updateLock() {
});
} catch (error) {
const errorResponse = error as AxiosError>;
+ const formattedError = formatAxiosError(errorResponse);
toast.add({
severity: 'error',
- summary: `Error ${newIsLocked ? 'locking' : 'unlocking'} user`,
- detail:
- errorResponse.response?.data.errors.body ||
- 'An error occurred while updating the user lock status.',
+ summary: `Error ${newIsLocked ? 'locking' : 'unlocking'} user${formattedError.statusCode ? `: ${formattedError.summary}` : ''}`,
+ detail: formattedError.detail,
life: 3000,
});
}
@@ -171,12 +170,11 @@ function deleteUser() {
router.push({ name: 'users-list' });
} catch (error) {
const errorResponse = error as AxiosError>;
+ const formattedError = formatAxiosError(errorResponse);
toast.add({
severity: 'error',
- summary: 'Error deleting user',
- detail:
- errorResponse.response?.data.errors.body ||
- 'An error occurred while deleting the user.',
+ summary: `Error deleting user${formattedError.statusCode ? `: ${formattedError.summary}` : ''}`,
+ detail: formattedError.detail,
life: 3000,
});
}