fix(campaigns): fix start_date modal — interceptor was losing structured detail
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled

client.ts: when FastAPI detail is an object, extract .message for the error
string and preserve the full detail on enhancedError.detail so consumers
can inspect structured error payloads (e.g. 409 start_date_in_future).

CampaignDetailPage: use enhancedErr.status (not response.status) and
enhancedErr.detail (not response.data.detail) to detect 409 and show
the confirmation modal instead of the toast.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
kitos
2026-06-04 16:22:17 +02:00
parent 910c198545
commit 019924f78c
2 changed files with 33 additions and 27 deletions

View File

@@ -64,15 +64,27 @@ client.interceptors.response.use(
} }
} }
// Extract error message from response // Extract error message — detail can be a string or a structured object
const rawDetail = error.response?.data?.detail;
const message = const message =
error.response?.data?.detail || typeof rawDetail === "string"
error.message || ? rawDetail
"An unexpected error occurred"; : typeof rawDetail === "object" && rawDetail !== null && (rawDetail as { message?: string }).message
? (rawDetail as { message: string }).message
: error.message || "An unexpected error occurred";
const enhancedError = new Error(message); const enhancedError = new Error(message) as Error & {
(enhancedError as Error & { status?: number }).status = status; status?: number;
(enhancedError as Error & { code?: string }).code = error.response?.data?.code; code?: string;
detail?: unknown;
};
enhancedError.status = status;
// Preserve the full detail object so consumers can inspect it (e.g. for modal flows)
enhancedError.detail = rawDetail;
enhancedError.code =
typeof rawDetail === "object" && rawDetail !== null
? (rawDetail as { code?: string }).code
: error.response?.data?.code;
return Promise.reject(enhancedError); return Promise.reject(enhancedError);
}, },

View File

@@ -98,29 +98,23 @@ export default function CampaignDetailPage() {
showToast("Campaign activated", "success"); showToast("Campaign activated", "success");
}, },
onError: (err: unknown) => { onError: (err: unknown) => {
// FastAPI wraps error bodies as: { detail: string | object } // The Axios interceptor (client.ts) transforms errors into enhanced Error objects
type AxiosLike = { // with .status (HTTP status), .detail (raw FastAPI detail), and .message (readable string)
response?: { type EnhancedError = Error & {
status?: number; status?: number;
data?: { detail?: { code?: string; message?: string } | string }; detail?: { code?: string; message?: string } | string;
};
}; };
const axiosErr = err as AxiosLike; const e = err as EnhancedError;
const status = axiosErr?.response?.status;
const detail = axiosErr?.response?.data?.detail;
if (status === 409 && detail && typeof detail === "object" && detail.message) { if (e.status === 409) {
// Future start_date show confirmation modal // Future start_date show confirmation modal using the message from detail
setStartDateWarning(detail.message); const warningMsg =
typeof e.detail === "object" && e.detail?.message
? e.detail.message
: e.message;
setStartDateWarning(warningMsg);
} else { } else {
// Any other error → extract readable message from FastAPI detail showToast(e.message || "Failed to activate campaign", "error");
const msg =
typeof detail === "string"
? detail
: typeof detail === "object" && detail?.message
? detail.message
: "Failed to activate campaign";
showToast(msg, "error");
} }
}, },
}); });