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>
173 lines
5.3 KiB
TypeScript
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");
|
|
}
|
|
}
|