diff --git a/servers/nextjs/app/(presentation-generator)/components/IconsEditor.tsx b/servers/nextjs/app/(presentation-generator)/components/IconsEditor.tsx index 4bb01e51..09cbae06 100644 --- a/servers/nextjs/app/(presentation-generator)/components/IconsEditor.tsx +++ b/servers/nextjs/app/(presentation-generator)/components/IconsEditor.tsx @@ -12,7 +12,7 @@ import { Skeleton } from "@/components/ui/skeleton"; import { Button } from "@/components/ui/button"; import { PresentationGenerationApi } from "../services/api/presentation-generation"; import { getStaticFileUrl } from "../utils/others"; - +import { toast } from "sonner"; interface IconsEditorProps { icon_prompt?: string[] | null; onClose?: () => void; @@ -53,8 +53,9 @@ const IconsEditor = ({ limit: 40, }); setIcons(data); - } catch (error) { + } catch (error: any) { console.error("Error fetching icons:", error); + toast.error(error.message || "Failed to fetch icons. Please try again."); setIcons([]); } finally { setLoading(false); diff --git a/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx b/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx index 88b0b18d..83cc9906 100644 --- a/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx +++ b/servers/nextjs/app/(presentation-generator)/components/ImageEditor.tsx @@ -98,10 +98,10 @@ const ImageEditor = ({ try { const response = await PresentationGenerationApi.getPreviousGeneratedImages(); setPreviousGeneratedImages(response); - } catch (error) { + } catch (error: any) { toast.error("Failed to get previous generated images. Please try again."); console.error("error in getting previous generated images", error); - setError("Failed to get previous generated images. Please try again."); + setError(error.message || "Failed to get previous generated images. Please try again."); } } @@ -218,9 +218,9 @@ const ImageEditor = ({ }); setPreviewImages(response); - } catch (err) { + } catch (err: any) { console.error("Error in image generation", err); - setError("Failed to generate image. Please try again."); + setError(err.message || "Failed to generate image. Please try again."); } finally { setIsGenerating(false); } diff --git a/servers/nextjs/app/(presentation-generator)/documents-preview/components/DocumentPreviewPage.tsx b/servers/nextjs/app/(presentation-generator)/documents-preview/components/DocumentPreviewPage.tsx index 7b106d23..18a0babd 100644 --- a/servers/nextjs/app/(presentation-generator)/documents-preview/components/DocumentPreviewPage.tsx +++ b/servers/nextjs/app/(presentation-generator)/documents-preview/components/DocumentPreviewPage.tsx @@ -149,11 +149,13 @@ const DocumentsPreviewPage: React.FC = () => { dispatch(setPresentationId(createResponse.id)); router.push("/outline"); - } catch (error) { - console.error("Error in presentation creation:", error); - toast.error("Error in presentation creation. Please try again."); + } catch (error: any) { + console.error("Error in radar presentation creation:", error); + toast.error('Error', { + description: error.message || "Error in radar presentation creation.", + }); setShowLoading({ - message: "Error in presentation creation.", + message: "Error in radar presentation creation.", show: true, duration: 10, progress: false, diff --git a/servers/nextjs/app/(presentation-generator)/outline/hooks/usePresentationGeneration.ts b/servers/nextjs/app/(presentation-generator)/outline/hooks/usePresentationGeneration.ts index d0f56980..def04fe7 100644 --- a/servers/nextjs/app/(presentation-generator)/outline/hooks/usePresentationGeneration.ts +++ b/servers/nextjs/app/(presentation-generator)/outline/hooks/usePresentationGeneration.ts @@ -86,10 +86,10 @@ export const usePresentationGeneration = ( dispatch(clearPresentationData()); router.push(`/presentation?id=${presentationId}&stream=true`); } - } catch (error) { - console.error("Error in data generation", error); + } catch (error: any) { + console.error('Error In Presentation Generation(prepare).', error); toast.error("Generation Error", { - description: "Failed to generate presentation. Please try again.", + description: error.message || "Error In Presentation Generation(prepare).", }); } finally { setLoadingState(DEFAULT_LOADING_STATE); diff --git a/servers/nextjs/app/(presentation-generator)/presentation/components/SlideContent.tsx b/servers/nextjs/app/(presentation-generator)/presentation/components/SlideContent.tsx index 2f2bd6e9..3abf1649 100644 --- a/servers/nextjs/app/(presentation-generator)/presentation/components/SlideContent.tsx +++ b/servers/nextjs/app/(presentation-generator)/presentation/components/SlideContent.tsx @@ -60,9 +60,11 @@ const SlideContent = ({ dispatch(updateSlide({ index: slide.index, slide: response })); toast.success("Slide updated successfully"); } - } catch (error) { - console.error("Error updating slide:", error); - toast.error("Failed to update slide. Please try again."); + } catch (error: any) { + console.error("Error in slide editing:", error); + toast.error("Error in slide editing.", { + description: error.message || "Error in slide editing.", + }); } finally { setIsUpdating(false); } @@ -70,8 +72,11 @@ const SlideContent = ({ const onDeleteSlide = async () => { try { dispatch(deletePresentationSlide(slide.index)); - } catch (error) { + } catch (error: any) { console.error("Error deleting slide:", error); + toast.error("Error deleting slide.", { + description: error.message || "Error deleting slide.", + }); } }; // Scroll to the new slide when streaming and new slides are being generated diff --git a/servers/nextjs/app/(presentation-generator)/presentation/hooks/useAutoSave.tsx b/servers/nextjs/app/(presentation-generator)/presentation/hooks/useAutoSave.tsx index 75e9273d..cb4a5107 100644 --- a/servers/nextjs/app/(presentation-generator)/presentation/hooks/useAutoSave.tsx +++ b/servers/nextjs/app/(presentation-generator)/presentation/hooks/useAutoSave.tsx @@ -53,6 +53,7 @@ export const useAutoSave = ({ } catch (error) { console.error('❌ Auto-save failed:', error); + } finally { setIsSaving(false); } diff --git a/servers/nextjs/app/(presentation-generator)/services/api/api-error-handler.ts b/servers/nextjs/app/(presentation-generator)/services/api/api-error-handler.ts new file mode 100644 index 00000000..eae66bf0 --- /dev/null +++ b/servers/nextjs/app/(presentation-generator)/services/api/api-error-handler.ts @@ -0,0 +1,121 @@ +// API Error Response Interface +interface ApiErrorResponse { + detail?: string; + message?: string; + error?: string; +} + +// API Response Handler Utility +export class ApiResponseHandler { + + static async handleResponse(response: Response, defaultErrorMessage: string): Promise { + // Handle successful responses + if (response.ok) { + // Handle 204 No Content responses + if (response.status === 204) { + return true; + } + + // Try to parse JSON response + try { + return await response.json(); + } catch (error) { + // If JSON parsing fails but response is ok, return empty object + return {}; + } + } + + // Handle error responses + let errorMessage = defaultErrorMessage; + + try { + const errorData: ApiErrorResponse = await response.json(); + + // Extract error message in order of preference + if (errorData.detail) { + errorMessage = errorData.detail; + } else if (errorData.message) { + errorMessage = errorData.message; + } else if (errorData.error) { + errorMessage = errorData.error; + } + } catch (parseError) { + // If JSON parsing fails, use status-based messages + errorMessage = this.getStatusBasedErrorMessage(response.status, defaultErrorMessage); + } + + // Throw error with appropriate message + throw new Error(errorMessage); + } + + + static async handleResponseWithResult(response: Response, defaultErrorMessage: string): Promise<{success: boolean, message?: string}> { + try { + // Handle successful responses + if (response.ok) { + return { success: true }; + } + + // Handle error responses + let errorMessage = defaultErrorMessage; + + try { + const errorData: ApiErrorResponse = await response.json(); + + // Extract error message in order of preference + if (errorData.detail) { + errorMessage = errorData.detail; + } else if (errorData.message) { + errorMessage = errorData.message; + } else if (errorData.error) { + errorMessage = errorData.error; + } + } catch (parseError) { + // If JSON parsing fails, use status-based messages + errorMessage = this.getStatusBasedErrorMessage(response.status, defaultErrorMessage); + } + + return { + success: false, + message: errorMessage, + }; + } catch (error) { + return { + success: false, + message: error instanceof Error ? error.message : defaultErrorMessage, + }; + } + } + + + private static getStatusBasedErrorMessage(status: number, defaultMessage: string): string { + switch (status) { + case 400: + return "Bad request. Please check your input and try again."; + case 401: + return "Unauthorized. Please log in and try again."; + case 403: + return "Access forbidden. You don't have permission to perform this action."; + case 404: + return "Resource not found. The requested item may have been deleted or moved."; + case 409: + return "Conflict. The resource already exists or there's a conflict with the current state."; + case 422: + return "Validation error. Please check your input and try again."; + case 429: + return "Too many requests. Please wait a moment and try again."; + case 500: + return "Internal server error. Please try again later."; + case 502: + return "Bad gateway. The server is temporarily unavailable."; + case 503: + return "Service unavailable. Please try again later."; + case 504: + return "Gateway timeout. The request took too long to process."; + default: + return defaultMessage; + } + } +} + +export type { ApiErrorResponse }; \ No newline at end of file diff --git a/servers/nextjs/app/(presentation-generator)/services/api/presentation-generation.ts b/servers/nextjs/app/(presentation-generator)/services/api/presentation-generation.ts index 079a6bc0..ec27d9e3 100644 --- a/servers/nextjs/app/(presentation-generator)/services/api/presentation-generation.ts +++ b/servers/nextjs/app/(presentation-generator)/services/api/presentation-generation.ts @@ -1,5 +1,7 @@ import { getHeader, getHeaderForFormData } from "./header"; import { IconSearch, ImageGenerate, ImageSearch, PreviousGeneratedImagesResponse } from "./params"; +import { ApiResponseHandler } from "./api-error-handler"; + export class PresentationGenerationApi { static async uploadDoc(documents: File[]) { const formData = new FormData(); @@ -19,20 +21,13 @@ export class PresentationGenerationApi { } ); - if (!response.ok) { - throw new Error(`Upload failed: ${response.statusText}`); - } - - const data = await response.json(); - return data; + return await ApiResponseHandler.handleResponse(response, "Failed to upload documents"); } catch (error) { console.error("Upload error:", error); throw error; } } - - static async decomposeDocuments(documentKeys: string[]) { try { const response = await fetch( @@ -46,28 +41,53 @@ export class PresentationGenerationApi { cache: "no-cache", } ); - if (response.status === 200) { - const data = await response.json(); - - return data; - } else { - throw new Error(`Failed to decompose files: ${response.statusText}`); - } + + return await ApiResponseHandler.handleResponse(response, "Failed to decompose documents"); } catch (error) { console.error("Error in Decompose Files", error); throw error; } } + static async createPresentation({ + prompt, + n_slides, + file_paths, + language, + }: { + prompt: string; + n_slides: number | null; + file_paths?: string[]; + language: string | null; + }) { + try { + const response = await fetch( + `/api/v1/ppt/presentation/create`, + { + method: "POST", + headers: getHeader(), + body: JSON.stringify({ + prompt, + n_slides, + file_paths, + language, + }), + cache: "no-cache", + } + ); + + return await ApiResponseHandler.handleResponse(response, "Failed to create presentation"); + } catch (error) { + console.error("error in presentation creation", error); + throw error; + } + } - static async editSlide( slide_id: string, - prompt: string ) { try { - const response = await fetch( `/api/v1/ppt/slide/edit`, { @@ -76,18 +96,12 @@ export class PresentationGenerationApi { body: JSON.stringify({ id: slide_id, prompt, - }), cache: "no-cache", } ); - if (!response.ok) { - throw new Error("Failed to update slides"); - } - - const data = await response.json(); - return data; + return await ApiResponseHandler.handleResponse(response, "Failed to update slide"); } catch (error) { console.error("error in slide update", error); throw error; @@ -105,15 +119,8 @@ export class PresentationGenerationApi { cache: "no-cache", } ); - if (response.ok) { - const data = await response.json(); - - return data; - } else { - throw new Error( - `Failed to update presentation content: ${response.statusText}` - ); - } + + return await ApiResponseHandler.handleResponse(response, "Failed to update presentation content"); } catch (error) { console.error("error in presentation content update", error); throw error; @@ -131,41 +138,17 @@ export class PresentationGenerationApi { cache: "no-cache", } ); - if (response.ok) { - const data = await response.json(); - - return data; - } else { - throw new Error(`Failed to generate data: ${response.statusText}`); - } + + return await ApiResponseHandler.handleResponse(response, "Failed to prepare presentation"); } catch (error) { console.error("error in data generation", error); throw error; } } + // IMAGE AND ICON SEARCH - static async imageSearch(imageSearch: ImageSearch) { - try { - const response = await fetch( - `/api/v1/ppt/image/search`, - { - method: "POST", - headers: getHeader(), - body: JSON.stringify(imageSearch), - cache: "no-cache", - } - ); - if (response.ok) { - const data = await response.json(); - return data; - } else { - throw new Error(`Failed to search images: ${response.statusText}`); - } - } catch (error) { - console.error("error in image search", error); - throw error; - } - } + + static async generateImage(imageGenerate: ImageGenerate) { try { const response = await fetch( @@ -176,20 +159,15 @@ export class PresentationGenerationApi { cache: "no-cache", } ); - if (response.ok) { - const data = await response.json(); - - return data; - } else { - throw new Error(`Failed to generate images: ${response.statusText}`); - } + + return await ApiResponseHandler.handleResponse(response, "Failed to generate image"); } catch (error) { console.error("error in image generation", error); throw error; } } - static getPreviousGeneratedImages = async():Promise=>{ + static getPreviousGeneratedImages = async (): Promise => { try { const response = await fetch( `/api/v1/ppt/images/generated`, @@ -198,17 +176,14 @@ export class PresentationGenerationApi { headers: getHeader(), } ); - if (response.ok) { - const data = await response.json(); - return data; - } else { - throw new Error(`Failed to get previous generated images: ${response.statusText}`); - } + + return await ApiResponseHandler.handleResponse(response, "Failed to get previous generated images"); } catch (error) { console.error("error in getting previous generated images", error); throw error; } } + static async searchIcons(iconSearch: IconSearch) { try { const response = await fetch( @@ -219,41 +194,15 @@ export class PresentationGenerationApi { cache: "no-cache", } ); - if (response.ok) { - const data = await response.json(); - - return data; - } else { - throw new Error(`Failed to search icons: ${response.statusText}`); - } + + return await ApiResponseHandler.handleResponse(response, "Failed to search icons"); } catch (error) { console.error("error in icon search", error); throw error; } } - static async updateDocuments(body: any) { - try { - const response = await fetch( - `/api/v1/ppt/document/update`, - { - method: "POST", - headers: getHeaderForFormData(), - body: body, - cache: "no-cache", - } - ); - if (response.ok) { - const data = await response.json(); - return data; - } else { - throw new Error(`Failed to update documents: ${response.statusText}`); - } - } catch (error) { - console.error("error in document update", error); - throw error; - } - } + // EXPORT PRESENTATION static async exportAsPPTX(presentationData: any) { @@ -267,100 +216,13 @@ export class PresentationGenerationApi { cache: "no-cache", } ); - if (response.ok) { - return await response.json(); - } else { - throw new Error(`Failed to export as pptx: ${response.statusText}`); - } + return await ApiResponseHandler.handleResponse(response, "Failed to export as PowerPoint"); } catch (error) { console.error("error in pptx export", error); throw error; } } - static async exportAsPDF(presentationData: any) { - try { - const response = await fetch( - `/api/v1/ppt/presentation/export_as_pdf`, - { - method: "POST", - headers: getHeader(), - body: JSON.stringify(presentationData), - } - ); - if (response.ok) { - const data = await response.json(); - - return data; - } else { - throw new Error(`Failed to export as pdf: ${response.statusText}`); - } - } catch (error) { - console.error("error in pdf export", error); - throw error; - } - } - static async deleteSlide(presentation_id: string, slide_id: string) { - try { - const response = await fetch( - `/api/v1/ppt/slide/delete?presentation_id=${presentation_id}&slide_id=${slide_id}`, - { - method: "DELETE", - headers: getHeader(), - cache: "no-cache", - } - ); - if (response.status === 204) { - return true; - } else { - throw new Error(`Failed to delete slide: ${response.statusText}`); - } - } catch (error) { - console.error("error in slide deletion", error); - throw error; - } - } - - - static async createPresentation({ - prompt, - n_slides, - file_paths, - language, + - }: { - prompt: string; - n_slides: number | null; - file_paths?: string[]; - language: string | null; - - }) { - try { - const response = await fetch( - `/api/v1/ppt/presentation/create`, - { - method: "POST", - headers: getHeader(), - body: JSON.stringify({ - prompt, - n_slides, - file_paths, - language, - - - }), - cache: "no-cache", - } - ); - if (response.ok) { - const data = await response.json(); - return data; - } else { - throw new Error(`Failed to get questions: ${response.statusText}`); - } - } catch (error) { - console.error("error in question generation", error); - throw error; - } - } -} +} \ No newline at end of file diff --git a/servers/nextjs/app/(presentation-generator)/upload/components/UploadPage.tsx b/servers/nextjs/app/(presentation-generator)/upload/components/UploadPage.tsx index 45b196b4..0c16b25f 100644 --- a/servers/nextjs/app/(presentation-generator)/upload/components/UploadPage.tsx +++ b/servers/nextjs/app/(presentation-generator)/upload/components/UploadPage.tsx @@ -162,7 +162,7 @@ const UploadPage = () => { * Handles errors during presentation generation */ const handleGenerationError = (error: any) => { - console.error("Error in presentation generation:", error); + console.error("Error in upload page", error); setLoadingState({ isLoading: false, message: "", @@ -170,7 +170,7 @@ const UploadPage = () => { showProgress: false, }); toast.error("Error", { - description: "Failed to generate presentation. Please try again.", + description: error.message || "Error in upload page.", }); }; diff --git a/servers/nextjs/app/dashboard/api/dashboard.ts b/servers/nextjs/app/dashboard/api/dashboard.ts index 9e04d77f..aed0b19d 100644 --- a/servers/nextjs/app/dashboard/api/dashboard.ts +++ b/servers/nextjs/app/dashboard/api/dashboard.ts @@ -1,8 +1,7 @@ import { getHeader, } from "@/app/(presentation-generator)/services/api/header"; - - +import { ApiResponseHandler } from "@/app/(presentation-generator)/services/api/api-error-handler"; export interface PresentationResponse { id: string; @@ -33,38 +32,36 @@ export class DashboardApi { method: "GET", } ); - if (response.status === 200) { - const data = await response.json(); - return data; - } else if (response.status === 404) { + + // Handle the special case where 404 means "no presentations found" + if (response.status === 404) { console.log("No presentations found"); return []; } - return []; + + return await ApiResponseHandler.handleResponse(response, "Failed to fetch presentations"); } catch (error) { console.error("Error fetching presentations:", error); throw error; } } + static async getPresentation(id: string) { try { const response = await fetch( `/api/v1/ppt/presentation/?id=${id}`, { method: "GET", - } ); - if (response.status === 200) { - const data = await response.json(); - return data; - } - throw new Error("Presentation not found"); + + return await ApiResponseHandler.handleResponse(response, "Presentation not found"); } catch (error) { - console.error("Error fetching presentations:", error); + console.error("Error fetching presentation:", error); throw error; } } + static async deletePresentation(presentation_id: string) { try { const response = await fetch( @@ -74,17 +71,8 @@ export class DashboardApi { headers: getHeader(), } ); - const data = await response.json(); - if (response.status === 204) { - return { - success: true, - }; - } - return { - success: false, - message: data.detail || "Failed to delete presentation", - }; + return await ApiResponseHandler.handleResponseWithResult(response, "Failed to delete presentation"); } catch (error) { console.error("Error deleting presentation:", error); return { @@ -93,5 +81,4 @@ export class DashboardApi { }; } } - }