diff --git a/.gitignore b/.gitignore index fdc8b1ba..81923a11 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ user_data app_data tmp debug -.fastembed_cache \ No newline at end of file +.fastembed_cache +my-doc.txt \ No newline at end of file diff --git a/servers/nextjs/app/(presentation-generator)/components/EditableLayoutWrapper.tsx b/servers/nextjs/app/(presentation-generator)/components/EditableLayoutWrapper.tsx index 9e0117b2..ee02c096 100644 --- a/servers/nextjs/app/(presentation-generator)/components/EditableLayoutWrapper.tsx +++ b/servers/nextjs/app/(presentation-generator)/components/EditableLayoutWrapper.tsx @@ -298,7 +298,7 @@ const EditableLayoutWrapper: React.FC = ({ onClose={handleEditorClose} onImageChange={handleImageChange} > -
+ )} @@ -311,7 +311,7 @@ const EditableLayoutWrapper: React.FC = ({ onClose={handleEditorClose} onIconChange={handleIconChange} > -
+ )}
diff --git a/servers/nextjs/app/(presentation-generator)/components/IconsEditor.tsx b/servers/nextjs/app/(presentation-generator)/components/IconsEditor.tsx index 1cfb6568..2dce96e8 100644 --- a/servers/nextjs/app/(presentation-generator)/components/IconsEditor.tsx +++ b/servers/nextjs/app/(presentation-generator)/components/IconsEditor.tsx @@ -31,23 +31,20 @@ const IconsEditor = ({ }: IconsEditorProps) => { // State management - const [icon, setIcon] = useState(initialIcon); const [icons, setIcons] = useState([]); const [searchQuery, setSearchQuery] = useState( icon_prompt?.[0] || "" ); const [loading, setLoading] = useState(true); - const searchParams = useSearchParams(); - // Update local state when initial icon changes - useEffect(() => { - setIcon(initialIcon); - }, [initialIcon]); + // Search for icons when component opens useEffect(() => { - handleIconSearch(); + if (icon_prompt && icon_prompt.length > 0 && icons.length === 0) { + handleIconSearch(); + } }, []); /** @@ -55,17 +52,15 @@ const IconsEditor = ({ */ const handleIconSearch = async () => { setLoading(true); - const presentation_id = searchParams.get("id"); const query = searchQuery.length > 0 ? searchQuery : icon_prompt?.[0] || ""; try { const data = await PresentationGenerationApi.searchIcons({ - presentation_id: presentation_id!, query, - page: 1, limit: 40, }); - setIcons(data.paths); + console.log("icons search data", data); + setIcons(data); } catch (error) { console.error("Error fetching icons:", error); setIcons([]); @@ -78,7 +73,6 @@ const IconsEditor = ({ * Handles icon selection and calls the parent callback */ const handleIconChange = (newIcon: string) => { - setIcon(newIcon); if (onIconChange) { onIconChange(newIcon, searchQuery || icon_prompt?.[0] || ''); @@ -137,7 +131,7 @@ const IconsEditor = ({ ))}
- ) : icons.length > 0 ? ( + ) : icons && icons.length > 0 ? (
{icons.map((iconSrc, idx) => (
{ - const { currentTheme } = useSelector((state: RootState) => state.theme); - const searchParams = useSearchParams(); // State management const [image, setImage] = useState(initialImage); @@ -186,23 +184,21 @@ const ImageEditor = ({ * Generates new images using AI */ const handleGenerateImage = async () => { + if (!prompt) { + setError("Please enter a prompt"); + return; + } + console.log("prompt", prompt); try { setIsGenerating(true); setError(null); - - const presentation_id = searchParams.get("id"); - const response = await PresentationGenerationApi.generateImage({ - presentation_id: presentation_id!, - prompt: { - theme_prompt: ThemeImagePrompt[currentTheme], - image_prompt: prompt, - aspect_ratio: "4:5", - }, + prompt: prompt, }); setPreviewImages(response.paths); } catch (err) { + console.error("Error in image generation", err); setError("Failed to generate image. Please try again."); } finally { setIsGenerating(false); diff --git a/servers/nextjs/app/(presentation-generator)/outline/hooks/useOutlineStreaming.ts b/servers/nextjs/app/(presentation-generator)/outline/hooks/useOutlineStreaming.ts index 5ac7219b..bd84a275 100644 --- a/servers/nextjs/app/(presentation-generator)/outline/hooks/useOutlineStreaming.ts +++ b/servers/nextjs/app/(presentation-generator)/outline/hooks/useOutlineStreaming.ts @@ -1,9 +1,10 @@ import { useEffect, useState } from "react"; -import { useDispatch } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; import { toast } from "@/hooks/use-toast"; import { setOutlines, SlideOutline } from "@/store/slices/presentationGeneration"; import { jsonrepair } from "jsonrepair"; import { StreamState } from "../types/index"; +import { RootState } from "@/store/store"; const DEFAULT_STREAM_STATE: StreamState = { isStreaming: false, @@ -12,10 +13,11 @@ const DEFAULT_STREAM_STATE: StreamState = { export const useOutlineStreaming = (presentationId: string | null) => { const dispatch = useDispatch(); + const {outlines} = useSelector((state: RootState) => state.presentationGeneration); const [streamState, setStreamState] = useState(DEFAULT_STREAM_STATE); useEffect(() => { - if (!presentationId) return; + if (!presentationId || outlines.length > 0) return; let eventSource: EventSource; let accumulatedChunks = ""; diff --git a/servers/nextjs/app/(presentation-generator)/outline/hooks/usePresentationGeneration.ts b/servers/nextjs/app/(presentation-generator)/outline/hooks/usePresentationGeneration.ts index 82b454f3..4bc05cfa 100644 --- a/servers/nextjs/app/(presentation-generator)/outline/hooks/usePresentationGeneration.ts +++ b/servers/nextjs/app/(presentation-generator)/outline/hooks/usePresentationGeneration.ts @@ -2,7 +2,7 @@ import { useState, useCallback } from "react"; import { useDispatch } from "react-redux"; import { useRouter } from "next/navigation"; import { toast } from "@/hooks/use-toast"; -import { setPresentationData, SlideOutline } from "@/store/slices/presentationGeneration"; +import { clearPresentationData, setPresentationData, SlideOutline } from "@/store/slices/presentationGeneration"; import { PresentationGenerationApi } from "../../services/api/presentation-generation"; import { useLayout } from "../../context/LayoutContext"; import { LayoutGroup, LoadingState } from "../types/index"; @@ -69,8 +69,11 @@ export const usePresentationGeneration = ( }, [selectedLayoutGroup, getLayoutById]); const handleSubmit = useCallback(async () => { + if (!validateInputs()) return; + + setLoadingState({ message: "Generating presentation data...", isLoading: true, @@ -89,8 +92,8 @@ export const usePresentationGeneration = ( }); if (response) { - dispatch(setPresentationData(response)); - router.push(`/presentation?id=${presentationId}&stream=true`); + dispatch(clearPresentationData()); + router.push(`/presentation?id=${presentationId}&stream=true`); } } catch (error) { console.error("Error in data generation", error); diff --git a/servers/nextjs/app/(presentation-generator)/services/api/params.ts b/servers/nextjs/app/(presentation-generator)/services/api/params.ts index 75a9f930..8e7003de 100644 --- a/servers/nextjs/app/(presentation-generator)/services/api/params.ts +++ b/servers/nextjs/app/(presentation-generator)/services/api/params.ts @@ -6,18 +6,14 @@ export interface ImageSearch { } export interface ImageGenerate { - presentation_id: string; - prompt: { - theme_prompt: string; - image_prompt: string; - aspect_ratio: string; - }; + + + prompt: string; } export interface IconSearch { - presentation_id: string; + query: string; - category?: string; - page: number; + limit: number; } 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 801e1c31..f64e14f0 100644 --- a/servers/nextjs/app/(presentation-generator)/services/api/presentation-generation.ts +++ b/servers/nextjs/app/(presentation-generator)/services/api/presentation-generation.ts @@ -172,11 +172,10 @@ export class PresentationGenerationApi { static async generateImage(imageGenerate: ImageGenerate) { try { const response = await fetch( - `/api/v1/ppt/image/generate`, + `/api/v1/ppt/images/generate?prompt=${imageGenerate.prompt}`, { - method: "POST", + method: "GET", headers: getHeader(), - body: JSON.stringify(imageGenerate), cache: "no-cache", } ); @@ -195,11 +194,10 @@ export class PresentationGenerationApi { static async searchIcons(iconSearch: IconSearch) { try { const response = await fetch( - `/api/v1/ppt/icon/search`, + `/api/v1/ppt/icons/search?query=${iconSearch.query}&limit=${iconSearch.limit}`, { - method: "POST", + method: "GET", headers: getHeader(), - body: JSON.stringify(iconSearch), cache: "no-cache", } ); diff --git a/servers/nextjs/app/(presentation-generator)/upload/components/UploadPage.tsx b/servers/nextjs/app/(presentation-generator)/upload/components/UploadPage.tsx index 398b180f..bd513217 100644 --- a/servers/nextjs/app/(presentation-generator)/upload/components/UploadPage.tsx +++ b/servers/nextjs/app/(presentation-generator)/upload/components/UploadPage.tsx @@ -13,7 +13,7 @@ import React, { useState } from "react"; import { useRouter } from "next/navigation"; import { useDispatch } from "react-redux"; -import { setPresentationId } from "@/store/slices/presentationGeneration"; +import { clearOutlines, setPresentationId } from "@/store/slices/presentationGeneration"; import { ConfigurationSelects } from "./ConfigurationSelects"; import { PromptInput } from "./PromptInput"; import { LanguageType, PresentationConfig } from "../type"; @@ -154,8 +154,6 @@ const UploadPage = () => { }); // Use the first available layout group for direct generation - - const createResponse = await PresentationGenerationApi.createPresentation({ prompt: config?.prompt ?? "", n_slides: config?.slides ? parseInt(config.slides) : null, @@ -164,6 +162,7 @@ const UploadPage = () => { }); dispatch(setPresentationId(createResponse.id)); + dispatch(clearOutlines()); router.push("/outline"); }; diff --git a/servers/nextjs/app/api/upload-image/route.ts b/servers/nextjs/app/api/upload-image/route.ts index af58449d..24ab96e1 100644 --- a/servers/nextjs/app/api/upload-image/route.ts +++ b/servers/nextjs/app/api/upload-image/route.ts @@ -22,12 +22,14 @@ export async function POST(request: NextRequest) { const buffer = Buffer.from(bytes); // Create uploads directory if it doesn't exist - const uploadsDir = path.join(userDataDir, "uploads"); + const uploadsDir = path.join(userDataDir, "images"); fs.mkdirSync(uploadsDir, { recursive: true }); + console.log("uploadsDir", uploadsDir); // Generate unique filename const filename = `${crypto.randomBytes(16).toString("hex")}.png`; const filePath = path.join(uploadsDir, filename); + console.log("filePath", filePath); // Write file to disk fs.writeFileSync(filePath, buffer); @@ -35,7 +37,7 @@ export async function POST(request: NextRequest) { // Return the relative path that can be used in the frontend return NextResponse.json({ success: true, - filePath: `${userDataDir}/uploads/${filename}` + filePath: `${uploadsDir}/${filename}` }); } catch (error) { console.error("Error saving image:", error); diff --git a/servers/nextjs/app/dashboard/api/dashboard.ts b/servers/nextjs/app/dashboard/api/dashboard.ts index 9de09536..498d136d 100644 --- a/servers/nextjs/app/dashboard/api/dashboard.ts +++ b/servers/nextjs/app/dashboard/api/dashboard.ts @@ -67,20 +67,29 @@ export class DashboardApi { static async deletePresentation(presentation_id: string) { try { const response = await fetch( - `/api/v1/ppt/delete?id=${presentation_id}`, + `/api/v1/ppt/presentation/?id=${presentation_id}`, { method: "DELETE", headers: getHeader(), } ); + const data = await response.json(); if (response.status === 204) { - return true; + return { + success: true, + }; } - return false; + return { + success: false, + message: data.detail || "Failed to delete presentation", + }; } catch (error) { console.error("Error deleting presentation:", error); - throw error; + return { + success: false, + message: error instanceof Error ? error.message : "Failed to delete presentation", + }; } } diff --git a/servers/nextjs/app/dashboard/components/DashboardPage.tsx b/servers/nextjs/app/dashboard/components/DashboardPage.tsx index 8dda5c7b..0546522f 100644 --- a/servers/nextjs/app/dashboard/components/DashboardPage.tsx +++ b/servers/nextjs/app/dashboard/components/DashboardPage.tsx @@ -35,6 +35,11 @@ const DashboardPage: React.FC = () => { } }; + const removePresentation = (presentationId: string) => { + setPresentations((prev: any) => + prev ? prev.filter((p: any) => p.id !== presentationId) : [] + ); + }; return (
@@ -50,6 +55,7 @@ const DashboardPage: React.FC = () => { type="slide" isLoading={isLoading} error={error} + onPresentationDeleted={removePresentation} /> diff --git a/servers/nextjs/app/dashboard/components/PresentationCard.tsx b/servers/nextjs/app/dashboard/components/PresentationCard.tsx index e5759a76..e978ad72 100644 --- a/servers/nextjs/app/dashboard/components/PresentationCard.tsx +++ b/servers/nextjs/app/dashboard/components/PresentationCard.tsx @@ -16,12 +16,14 @@ export const PresentationCard = ({ id, title, created_at, - slide + slide, + onDeleted }: { id: string; title: string; created_at: string; - slide: any + slide: any; + onDeleted?: (presentationId: string) => void; }) => { const router = useRouter(); const { renderSlideContent } = useGroupLayouts(); @@ -50,6 +52,10 @@ export const PresentationCard = ({ description: "The presentation has been deleted successfully", variant: "default", }); + // Call the onDeleted callback to update the parent state + if (onDeleted) { + onDeleted(id); + } } else { toast({ title: "Error", @@ -57,7 +63,7 @@ export const PresentationCard = ({ variant: "destructive", }); } - window.location.reload(); + // Removed window.location.reload() - no longer needed }; @@ -79,7 +85,10 @@ export const PresentationCard = ({

e.stopPropagation()}> - +
diff --git a/servers/nextjs/store/slices/presentationGeneration.ts b/servers/nextjs/store/slices/presentationGeneration.ts index 5f4494f4..50578a2e 100644 --- a/servers/nextjs/store/slices/presentationGeneration.ts +++ b/servers/nextjs/store/slices/presentationGeneration.ts @@ -72,6 +72,10 @@ const presentationGenerationSlice = createSlice({ state.presentation_id = null; state.error = null; state.isLoading = false; + state.presentationData = null; + }, + clearOutlines: (state) => { + state.outlines = []; }, // Set outlines setOutlines: (state, action: PayloadAction) => { @@ -341,6 +345,7 @@ export const { setSlidesRendered, setError, clearPresentationData, + clearOutlines, deleteSlideOutline, setPresentationData, setOutlines,