From 019924f78cfe2e860b9184fac4e611d5f190db02 Mon Sep 17 00:00:00 2001 From: kitos Date: Thu, 4 Jun 2026 16:22:17 +0200 Subject: [PATCH] =?UTF-8?q?fix(campaigns):=20fix=20start=5Fdate=20modal=20?= =?UTF-8?q?=E2=80=94=20interceptor=20was=20losing=20structured=20detail?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- frontend/src/api/client.ts | 26 ++++++++++++----- frontend/src/pages/CampaignDetailPage.tsx | 34 ++++++++++------------- 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 536ef8a..14dd9f8 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -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 = - error.response?.data?.detail || - error.message || - "An unexpected error occurred"; + typeof rawDetail === "string" + ? rawDetail + : 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); - (enhancedError as Error & { status?: number }).status = status; - (enhancedError as Error & { code?: string }).code = error.response?.data?.code; + const enhancedError = new Error(message) as Error & { + status?: number; + 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); }, diff --git a/frontend/src/pages/CampaignDetailPage.tsx b/frontend/src/pages/CampaignDetailPage.tsx index 3a9e4e4..41f9af9 100644 --- a/frontend/src/pages/CampaignDetailPage.tsx +++ b/frontend/src/pages/CampaignDetailPage.tsx @@ -98,29 +98,23 @@ export default function CampaignDetailPage() { showToast("Campaign activated", "success"); }, onError: (err: unknown) => { - // FastAPI wraps error bodies as: { detail: string | object } - type AxiosLike = { - response?: { - status?: number; - data?: { detail?: { code?: string; message?: string } | string }; - }; + // The Axios interceptor (client.ts) transforms errors into enhanced Error objects + // with .status (HTTP status), .detail (raw FastAPI detail), and .message (readable string) + type EnhancedError = Error & { + status?: number; + detail?: { code?: string; message?: string } | string; }; - const axiosErr = err as AxiosLike; - const status = axiosErr?.response?.status; - const detail = axiosErr?.response?.data?.detail; + const e = err as EnhancedError; - if (status === 409 && detail && typeof detail === "object" && detail.message) { - // Future start_date → show confirmation modal - setStartDateWarning(detail.message); + if (e.status === 409) { + // Future start_date — show confirmation modal using the message from detail + const warningMsg = + typeof e.detail === "object" && e.detail?.message + ? e.detail.message + : e.message; + setStartDateWarning(warningMsg); } else { - // Any other error → extract readable message from FastAPI detail - const msg = - typeof detail === "string" - ? detail - : typeof detail === "object" && detail?.message - ? detail.message - : "Failed to activate campaign"; - showToast(msg, "error"); + showToast(e.message || "Failed to activate campaign", "error"); } }, });