diff --git a/electron/servers/nextjs/app/(presentation-generator)/custom-template/components/FileUploadSection.tsx b/electron/servers/nextjs/app/(presentation-generator)/custom-template/components/FileUploadSection.tsx index 10fe87a8..b73d69fd 100644 --- a/electron/servers/nextjs/app/(presentation-generator)/custom-template/components/FileUploadSection.tsx +++ b/electron/servers/nextjs/app/(presentation-generator)/custom-template/components/FileUploadSection.tsx @@ -113,10 +113,10 @@ export const FileUploadSection: React.FC = ({ Click to Upload or drag & drop.

- :
-
+ :
+
-
+
-
-

{selectedFile.name}

+
+

{selectedFile.name}

Presentation ( {(selectedFile.size / (1024 * 1024)).toFixed(2)} MB)

diff --git a/servers/nextjs/app/(presentation-generator)/custom-template/components/FileUploadSection.tsx b/servers/nextjs/app/(presentation-generator)/custom-template/components/FileUploadSection.tsx index 10fe87a8..b73d69fd 100644 --- a/servers/nextjs/app/(presentation-generator)/custom-template/components/FileUploadSection.tsx +++ b/servers/nextjs/app/(presentation-generator)/custom-template/components/FileUploadSection.tsx @@ -113,10 +113,10 @@ export const FileUploadSection: React.FC = ({ Click to Upload or drag & drop.

- :
-
+ :
+
-
+
-
-

{selectedFile.name}

+
+

{selectedFile.name}

Presentation ( {(selectedFile.size / (1024 * 1024)).toFixed(2)} MB)

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 index eae66bf0..b7949572 100644 --- a/servers/nextjs/app/(presentation-generator)/services/api/api-error-handler.ts +++ b/servers/nextjs/app/(presentation-generator)/services/api/api-error-handler.ts @@ -1,12 +1,49 @@ // API Error Response Interface interface ApiErrorResponse { - detail?: string; + detail?: unknown; message?: string; error?: string; } // API Response Handler Utility export class ApiResponseHandler { + private static normalizeErrorDetail(detail: unknown): string | null { + if (!detail) return null; + + if (typeof detail === "string") { + return detail; + } + + if (Array.isArray(detail)) { + const parts = detail + .map((item) => { + if (typeof item === "string") return item; + if (item && typeof item === "object") { + const maybeMsg = (item as { msg?: unknown }).msg; + const maybeLoc = (item as { loc?: unknown }).loc; + const locPath = Array.isArray(maybeLoc) + ? maybeLoc + .filter((v) => typeof v === "string" || typeof v === "number") + .join(".") + : ""; + if (typeof maybeMsg === "string") { + return locPath ? `${locPath}: ${maybeMsg}` : maybeMsg; + } + } + return null; + }) + .filter((v): v is string => Boolean(v)); + + return parts.length ? parts.join("; ") : JSON.stringify(detail); + } + + if (typeof detail === "object") { + return JSON.stringify(detail); + } + + return String(detail); + } + static async handleResponse(response: Response, defaultErrorMessage: string): Promise { // Handle successful responses @@ -32,8 +69,9 @@ export class ApiResponseHandler { const errorData: ApiErrorResponse = await response.json(); // Extract error message in order of preference - if (errorData.detail) { - errorMessage = errorData.detail; + const normalizedDetail = this.normalizeErrorDetail(errorData.detail); + if (normalizedDetail) { + errorMessage = normalizedDetail; } else if (errorData.message) { errorMessage = errorData.message; } else if (errorData.error) { @@ -63,8 +101,9 @@ export class ApiResponseHandler { const errorData: ApiErrorResponse = await response.json(); // Extract error message in order of preference - if (errorData.detail) { - errorMessage = errorData.detail; + const normalizedDetail = this.normalizeErrorDetail(errorData.detail); + if (normalizedDetail) { + errorMessage = normalizedDetail; } else if (errorData.message) { errorMessage = errorData.message; } else if (errorData.error) { diff --git a/servers/nextjs/app/(presentation-generator)/services/api/dashboard.ts b/servers/nextjs/app/(presentation-generator)/services/api/dashboard.ts index dcabc907..8f287afb 100644 --- a/servers/nextjs/app/(presentation-generator)/services/api/dashboard.ts +++ b/servers/nextjs/app/(presentation-generator)/services/api/dashboard.ts @@ -1,8 +1,8 @@ -import { getApiUrl } from "@/utils/api"; import { getHeader, } from "@/app/(presentation-generator)/services/api/header"; import { ApiResponseHandler } from "@/app/(presentation-generator)/services/api/api-error-handler"; +import { getApiUrl } from "@/utils/api"; export interface PresentationResponse { id: string; diff --git a/servers/nextjs/app/(presentation-generator)/services/api/images.ts b/servers/nextjs/app/(presentation-generator)/services/api/images.ts index be56cc97..59adbd52 100644 --- a/servers/nextjs/app/(presentation-generator)/services/api/images.ts +++ b/servers/nextjs/app/(presentation-generator)/services/api/images.ts @@ -1,7 +1,13 @@ -import { getApiUrl } from "@/utils/api"; import { getHeaderForFormData } from "./header"; import { ApiResponseHandler } from "./api-error-handler"; import { ImageAssetResponse } from "./types"; +import { getApiUrl } from "@/utils/api"; + +interface StockSearchOptions { + provider?: string; + apiKey?: string; + strictApiKey?: boolean; +} export class ImagesApi { @@ -43,6 +49,41 @@ export class ImagesApi { throw error; } } + + static async searchStockImages( + query: string, + limit: number = 12, + options: StockSearchOptions = {} + ): Promise { + try { + const params = new URLSearchParams({ + query, + limit: String(limit), + }); + const normalizedProvider = (options.provider || "").trim().toLowerCase(); + if (normalizedProvider) { + params.set("provider", normalizedProvider); + } + if (options.strictApiKey) { + params.set("strict_api_key", "true"); + } + + const headers: Record = {}; + const trimmedApiKey = (options.apiKey || "").trim(); + if (trimmedApiKey) { + headers["X-Provider-Api-Key"] = trimmedApiKey; + } + + const response = await fetch(getApiUrl(`/api/v1/ppt/images/search?${params.toString()}`), { + method: "GET", + headers, + }); + return await ApiResponseHandler.handleResponse(response, "Failed to search stock images") as string[]; + } catch (error:any) { + console.log("Stock image search error:", error); + throw error; + } + } } 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 24349a9e..675c1fcf 100644 --- a/servers/nextjs/app/(presentation-generator)/services/api/presentation-generation.ts +++ b/servers/nextjs/app/(presentation-generator)/services/api/presentation-generation.ts @@ -1,11 +1,9 @@ -import { getApiUrl } from "@/utils/api"; import { getHeader, getHeaderForFormData } from "./header"; import { IconSearch, ImageGenerate, ImageSearch, PreviousGeneratedImagesResponse } from "./params"; import { ApiResponseHandler } from "./api-error-handler"; +import { getApiUrl } from "@/utils/api"; export class PresentationGenerationApi { - private static readonly DECOMPOSE_TIMEOUT_MS = 10 * 60 * 1000; - static async uploadDoc(documents: File[]) { const formData = new FormData(); @@ -31,10 +29,10 @@ export class PresentationGenerationApi { } } - static async decomposeDocuments(documentKeys: string[]) { - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), this.DECOMPOSE_TIMEOUT_MS); - + static async decomposeDocuments( + documentKeys: string[], + language?: string | null + ) { try { const response = await fetch( getApiUrl(`/api/v1/ppt/files/decompose`), @@ -43,21 +41,16 @@ export class PresentationGenerationApi { headers: getHeader(), body: JSON.stringify({ file_paths: documentKeys, + language: language ?? null, }), cache: "no-cache", - signal: controller.signal, } ); return await ApiResponseHandler.handleResponse(response, "Failed to decompose documents"); } catch (error) { - if (error instanceof DOMException && error.name === "AbortError") { - throw new Error("File decomposition timed out after 10 minutes"); - } console.error("Error in Decompose Files", error); throw error; - } finally { - clearTimeout(timeoutId); } } diff --git a/servers/nextjs/app/(presentation-generator)/services/api/template.ts b/servers/nextjs/app/(presentation-generator)/services/api/template.ts index 4a79a2f5..e7b8b341 100644 --- a/servers/nextjs/app/(presentation-generator)/services/api/template.ts +++ b/servers/nextjs/app/(presentation-generator)/services/api/template.ts @@ -1,11 +1,24 @@ import { getApiUrl } from "@/utils/api"; import { ApiResponseHandler } from "./api-error-handler"; +import { getHeader } from "./header"; + +export interface CloneTemplatePayload { + id: string; + name?: string; + description?: string; +} + +export interface CloneLayoutPayload { + template_id: string; + layout_id: string; + layout_name?: string; +} class TemplateService { static async getCustomTemplateSummaries() { try { - const response = await fetch(getApiUrl(`/api/v1/ppt/template-management/summary`)); + const response = await fetch(getApiUrl(`/api/v1/ppt/template/all`),); return await ApiResponseHandler.handleResponse(response, "Failed to get custom template summaries"); } catch (error) { console.error("Failed to get custom template summaries", error); @@ -15,7 +28,7 @@ class TemplateService { static async getCustomTemplateDetails(templateId: string) { try { - const response = await fetch(getApiUrl(`/api/v1/ppt/template-management/get-templates/${templateId}`)); + const response = await fetch(getApiUrl(`/api/v1/ppt/template/${templateId}/layouts`),); return await ApiResponseHandler.handleResponse(response, "Failed to get custom template details"); } catch (error) { console.error("Failed to get custom template details", error); @@ -25,13 +38,41 @@ class TemplateService { static async deleteCustomTemplate(presentationId: string) { try { - const response = await fetch(getApiUrl(`/api/v1/ppt/template-management/delete-templates/${presentationId}`), { method: "DELETE" }); + const response = await fetch(getApiUrl(`/api/v1/ppt/template-management/delete-templates/${presentationId}`), { method: "DELETE", headers: getHeader() }); return await ApiResponseHandler.handleResponseWithResult(response, "Failed to delete custom template"); } catch (error) { console.error("Failed to delete custom template", error); throw error; } } + + static async cloneCustomTemplate(payload: CloneTemplatePayload) { + try { + const response = await fetch(getApiUrl(`/api/v1/ppt/template/clone`), { + method: "POST", + headers: getHeader(), + body: JSON.stringify(payload), + }); + return await ApiResponseHandler.handleResponse(response, "Failed to clone template"); + } catch (error) { + console.error("Failed to clone template", error); + throw error; + } + } + + static async cloneTemplateLayout(payload: CloneLayoutPayload) { + try { + const response = await fetch(getApiUrl(`/api/v1/ppt/template/slide-layout/clone`), { + method: "POST", + headers: getHeader(), + body: JSON.stringify(payload), + }); + return await ApiResponseHandler.handleResponse(response, "Failed to clone layout"); + } catch (error) { + console.error("Failed to clone layout", error); + throw error; + } + } } export default TemplateService; \ No newline at end of file diff --git a/servers/nextjs/app/(presentation-generator)/services/api/theme.ts b/servers/nextjs/app/(presentation-generator)/services/api/theme.ts index b7a0184f..6d80d35d 100644 --- a/servers/nextjs/app/(presentation-generator)/services/api/theme.ts +++ b/servers/nextjs/app/(presentation-generator)/services/api/theme.ts @@ -1,7 +1,7 @@ -import { getApiUrl } from "@/utils/api" import { ApiResponseHandler } from "./api-error-handler" import { getHeader, getHeaderForFormData } from "./header" import { Theme, ThemeParams } from "./types" +import { getApiUrl } from "@/utils/api" @@ -90,8 +90,8 @@ class ThemeApi { static async uploadFont(font: File) { try { const formData = new FormData(); - formData.append("file", font); - const response = await fetch(getApiUrl(`/api/v1/ppt/fonts/upload`), { + formData.append("font_file", font); + const response: any = await fetch(getApiUrl(`/api/v1/ppt/fonts/upload`), { method: "POST", headers: getHeaderForFormData(), body: formData, diff --git a/servers/nextjs/app/(presentation-generator)/services/api/types.ts b/servers/nextjs/app/(presentation-generator)/services/api/types.ts index f1f45afd..ac5b6108 100644 --- a/servers/nextjs/app/(presentation-generator)/services/api/types.ts +++ b/servers/nextjs/app/(presentation-generator)/services/api/types.ts @@ -25,10 +25,10 @@ export interface DeplotResponse { } export interface ImageAssetResponse { - message: string; - path: string; - id: string; - file_url?: string; + message: string; + path: string; + id: string; + file_url?: string; }