ppt-tool/frontend/app/(presentation-generator)/services/api/wizard.ts
Vadym Samoilenko a74f533043 Fix URL fetch to extract text from JSON response
Backend returns {text, url} object but frontend expected string.
Now properly extracts data.text field.
Fixes [object Object] display issue.

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
2026-02-27 22:36:34 +00:00

173 lines
5.3 KiB
TypeScript

import { getHeader, getHeaderForFormData } from "./header";
import { ApiResponseHandler } from "./api-error-handler";
export interface ClientOption {
id: string;
name: string;
}
export interface MasterDeckOption {
id: string;
name: string;
thumbnail_url?: string;
layout_count: number;
}
export interface JobStatus {
id: string;
job_type: string;
status: string;
progress: number;
progress_message: string | null;
error_message: string | null;
presentation_id: string | null;
created_at: string | null;
started_at: string | null;
completed_at: string | null;
}
export class WizardApi {
/** Fetch clients available to the current user */
static async getClients(): Promise<ClientOption[]> {
try {
const response = await fetch("/api/v1/admin/clients", {
method: "GET",
headers: getHeader(),
});
const data = await ApiResponseHandler.handleResponse(response, "Failed to fetch clients");
return data.items ?? data;
} catch (error) {
console.error("Error fetching clients:", error);
return [];
}
}
/** Fetch master decks, optionally filtered by client */
static async getMasterDecks(clientId?: string): Promise<MasterDeckOption[]> {
try {
const params = clientId ? `?client_id=${clientId}` : "";
const response = await fetch(`/api/v1/admin/master-decks${params}`, {
method: "GET",
headers: getHeader(),
});
const data = await ApiResponseHandler.handleResponse(response, "Failed to fetch master decks");
return data.items ?? data;
} catch (error) {
console.error("Error fetching master decks:", error);
return [];
}
}
/** Upload files for the wizard */
static async uploadFiles(files: File[]): Promise<string[]> {
const formData = new FormData();
files.forEach((file) => formData.append("files", file));
const response = await fetch("/api/v1/ppt/files/upload", {
method: "POST",
headers: getHeaderForFormData(),
body: formData,
cache: "no-cache",
});
return await ApiResponseHandler.handleResponse(response, "Failed to upload files");
}
/** Decompose uploaded documents */
static async decomposeFiles(filePaths: string[]): Promise<any[]> {
const response = await fetch("/api/v1/ppt/files/decompose", {
method: "POST",
headers: getHeader(),
body: JSON.stringify({ file_paths: filePaths }),
cache: "no-cache",
});
return await ApiResponseHandler.handleResponse(response, "Failed to decompose files");
}
/** Create presentation and start generation via ARQ job queue */
static async createPresentationAsync(params: {
content: string;
n_slides: number;
file_paths: string[];
language: string;
tone: string;
instructions: string;
client_id?: string;
master_deck_id?: string;
}) {
const response = await fetch("/api/v1/ppt/presentation/generate/async", {
method: "POST",
headers: getHeader(),
body: JSON.stringify(params),
cache: "no-cache",
});
return await ApiResponseHandler.handleResponse(response, "Failed to start generation");
}
/** Poll job status */
static async getJobStatus(jobId: string): Promise<JobStatus> {
const response = await fetch(`/api/v1/ppt/jobs/${jobId}`, {
method: "GET",
headers: getHeader(),
});
return await ApiResponseHandler.handleResponse(response, "Failed to fetch job status");
}
/** Cancel a job */
static async cancelJob(jobId: string): Promise<void> {
const response = await fetch(`/api/v1/ppt/jobs/${jobId}`, {
method: "DELETE",
headers: getHeader(),
});
await ApiResponseHandler.handleResponse(response, "Failed to cancel job");
}
/** Fetch URL content and extract text */
static async fetchUrl(url: string): Promise<string> {
const response = await fetch("/api/v1/ppt/files/fetch-url", {
method: "POST",
headers: getHeader(),
body: JSON.stringify({ url }),
cache: "no-cache",
});
const data = await ApiResponseHandler.handleResponse(response, "Failed to fetch URL");
// Backend returns {text: string, url: string}, extract just the text
return typeof data === 'string' ? data : (data.text || JSON.stringify(data));
}
/** Check if brief needs follow-up questions */
static async checkFollowUpQuestions(content: string): Promise<string[]> {
if (!content || content.trim().length < 10) return [];
try {
const response = await fetch("/api/v1/ppt/content/follow-up-questions", {
method: "POST",
headers: getHeader(),
body: JSON.stringify({ content }),
cache: "no-cache",
});
const data = await ApiResponseHandler.handleResponse(response, "");
return data.questions ?? [];
} catch {
return [];
}
}
/** Create presentation (outline-only, like existing flow) */
static async createPresentation(params: {
content: string;
n_slides: number;
file_paths: string[];
language: string;
tone: string;
instructions: string;
client_id?: string;
master_deck_id?: string;
}) {
const response = await fetch("/api/v1/ppt/presentation/create", {
method: "POST",
headers: getHeader(),
body: JSON.stringify(params),
cache: "no-cache",
});
return await ApiResponseHandler.handleResponse(response, "Failed to create presentation");
}
}