diff --git a/servers/nextjs/app/(presentation-generator)/outline/components/OutlineItem.tsx b/servers/nextjs/app/(presentation-generator)/outline/components/OutlineItem.tsx
index 6c62d2da..1c18a134 100644
--- a/servers/nextjs/app/(presentation-generator)/outline/components/OutlineItem.tsx
+++ b/servers/nextjs/app/(presentation-generator)/outline/components/OutlineItem.tsx
@@ -11,20 +11,21 @@ import MarkdownEditor from "../../components/MarkdownEditor"
interface OutlineItemProps {
slideOutline: SlideOutline,
index: number
+ isStreaming: boolean
}
export function OutlineItem({
index,
slideOutline,
+ isStreaming,
}: OutlineItemProps) {
const {
- presentation_id,
outlines,
} = useSelector((state: RootState) => state.presentationGeneration);
const dispatch = useDispatch()
const handleSlideChange = (newOutline: SlideOutline) => {
-
+ if (isStreaming) return;
const newData = outlines?.map((each, idx) => {
if (idx === index - 1) {
return newOutline
@@ -45,7 +46,7 @@ export function OutlineItem({
transform,
transition,
isDragging,
- } = useSortable({ id: slideOutline.title })
+ } = useSortable({ id: slideOutline.title || index })
const style = {
transform: CSS.Transform.toString(transform),
@@ -54,6 +55,7 @@ export function OutlineItem({
const handleSlideDelete = () => {
+ if (isStreaming) return;
dispatch(deleteSlideOutline({ index: index - 1 }))
}
@@ -83,19 +85,17 @@ export function OutlineItem({
{/* Main Title Input - Add onFocus handler */}
-
handleSlideChange({ ...slideOutline, title: e.target.value })}
-
className="text-md sm:text-lg flex-1 font-semibold bg-transparent outline-none"
placeholder="Title goes here"
/>
{/* Editable Markdown Content */}
handleSlideChange({ ...slideOutline, body: content })}
/>
diff --git a/servers/nextjs/app/(presentation-generator)/outline/components/OutlinePage.tsx b/servers/nextjs/app/(presentation-generator)/outline/components/OutlinePage.tsx
index b343f858..e90f530f 100644
--- a/servers/nextjs/app/(presentation-generator)/outline/components/OutlinePage.tsx
+++ b/servers/nextjs/app/(presentation-generator)/outline/components/OutlinePage.tsx
@@ -24,6 +24,7 @@ import { toast } from "@/hooks/use-toast";
import {
setPresentationData,
setOutlines,
+ SlideOutline,
} from "@/store/slices/presentationGeneration";
import { OverlayLoader } from "@/components/ui/overlay-loader";
import Wrapper from "@/components/Wrapper";
@@ -39,6 +40,7 @@ const OutlinePage = () => {
coordinateGetter: sortableKeyboardCoordinates,
})
);
+
const { presentation_id, outlines } = useSelector(
(state: RootState) => state.presentationGeneration
);
@@ -47,7 +49,6 @@ const OutlinePage = () => {
(state: RootState) => state.theme
);
-
const [loadingState, setLoadingState] = useState({
message: "",
isLoading: false,
@@ -58,77 +59,100 @@ const OutlinePage = () => {
const [isStreaming, setStreaming] = useState(false);
const [isLoading, setLoading] = useState(true);
-
-
useEffect(() => {
let evtSource: EventSource;
let accumulatedChunks = "";
+
const fetchSlides = async () => {
setStreaming(true);
- evtSource = new EventSource(
- `/api/v1/ppt/outlines/stream?presentation_id=${presentation_id}`
- );
- evtSource.onopen = () => {
- console.log('connection open');
- };
+ setLoading(true);
- evtSource.addEventListener("response", (event) => {
- const data = JSON.parse(event.data);
- console.log(data)
- if (data.type === "chunk") {
- accumulatedChunks += data.chunk;
+ try {
+ evtSource = new EventSource(
+ `/api/v1/ppt/outlines/stream?presentation_id=${presentation_id}`
+ );
- // try {
- // const repairedJson = jsonrepair(accumulatedChunks);
- // const partialData = JSON.parse(repairedJson);
- // if (partialData.slides) {
- // // Check if the length of slides has changed
- // if (
- // partialData.slides.length !== previousSlidesLength.current &&
- // partialData.slides.length > 1
- // ) {
- // partialData.slides.splice(-1);
+ evtSource.onopen = () => {
+ console.log('connection open');
+ };
- // previousSlidesLength.current = partialData.slides.length + 1; // Update the previous length
- // setLoading(false);
- // }
- // }
- // } catch (error) {
- // // console.error('error while repairing json', error)
- // // It's okay if this fails, it just means the JSON isn't complete yet
- // }
- } else if (data.type === "complete") {
- try {
+ evtSource.addEventListener("response", (event) => {
+ const data = JSON.parse(event.data);
+
+ if (data.type === "chunk") {
+ accumulatedChunks += data.chunk;
+
+ try {
+ const repairedJson = jsonrepair(accumulatedChunks);
+ const partialData = JSON.parse(repairedJson);
+
+ if (partialData.slides) {
+ dispatch(setOutlines(partialData.slides));
+ setLoading(false);
+ }
+ } catch (error) {
+ // It's okay if this fails, it just means the JSON isn't complete yet
+ }
+ } else if (data.type === "complete") {
+ try {
+ setLoading(false);
+ setStreaming(false);
+ const outlinesData: SlideOutline[] = JSON.parse(data.presentation).outlines;
+ dispatch(setOutlines(outlinesData));
+ evtSource.close();
+ } catch (error) {
+ evtSource.close();
+ console.error("Error parsing accumulated chunks:", error);
+ toast({
+ title: "Error",
+ description: "Failed to parse presentation data",
+ variant: "destructive",
+ });
+ }
+ accumulatedChunks = "";
+ } else if (data.type === "closing") {
setLoading(false);
setStreaming(false);
-
evtSource.close();
-
- } catch (error) {
- evtSource.close();
- console.error("Error parsing accumulated chunks:", error);
}
- accumulatedChunks = "";
- } else if (data.type === "closing") {
+ });
+
+ evtSource.onerror = (error) => {
+
setLoading(false);
setStreaming(false);
evtSource.close();
- }
- });
- evtSource.onerror = (error) => {
- console.error("EventSource failed:", error);
+
+ toast({
+ title: "Connection Error",
+ description: "Failed to connect to the server. Please try again.",
+ variant: "destructive",
+ });
+ };
+ } catch (error) {
setLoading(false);
setStreaming(false);
- evtSource.close();
- };
+
+ toast({
+ title: "Error",
+ description: "Failed to initialize connection",
+ variant: "destructive",
+ });
+ }
};
- fetchSlides();
-
- }, [])
-
+ if (presentation_id) {
+ fetchSlides();
+ }
+ // Cleanup function
+ return () => {
+ if (evtSource) {
+ evtSource.close();
+ }
+ };
+ }, [presentation_id, dispatch]);
const handleDragEnd = (event: any) => {
const { active, over } = event;
@@ -140,50 +164,55 @@ const OutlinePage = () => {
const oldIndex = outlines.findIndex((item) => item.title === active.id);
const newIndex = outlines.findIndex((item) => item.title === over.id);
- // Create new array with reordered items and updated indices
-
// Reorder the array
const reorderedArray = arrayMove(outlines, oldIndex, newIndex);
+ // Update local state
+ setOutlines(reorderedArray);
// Update the store with new order
dispatch(setOutlines(reorderedArray));
}
};
const handleSubmit = async () => {
+ if (!outlines || outlines.length === 0) {
+ toast({
+ title: "No Outlines",
+ description: "Please wait for outlines to load before generating presentation",
+ variant: "destructive",
+ });
+ return;
+ }
// Generate data
setLoadingState({
- message: "Generating data...",
+ message: "Generating presentation data...",
isLoading: true,
- showProgress: false,
- duration: 10,
+ showProgress: true,
+ duration: 30,
});
+
try {
-
-
- const response = await PresentationGenerationApi.generateData({
+ const response = await PresentationGenerationApi.presentationPrepare({
presentation_id: presentation_id,
- theme: {
- name: currentTheme.toLocaleLowerCase(),
- colors: currentColors,
- },
-
outlines: outlines,
-
});
if (response) {
dispatch(setPresentationData(response));
- router.push(
- `/presentation?id=${presentation_id}&stream=true`
- );
+ toast({
+ title: "Success",
+ description: "Presentation generated successfully!",
+ variant: "default",
+ });
+
+ router.push(`/presentation?id=${presentation_id}&stream=true`);
}
} catch (error) {
console.error("error in data generation", error);
toast({
- title: "Error Adding Charts",
- description: "Something went wrong, Try again",
+ title: "Generation Error",
+ description: "Failed to generate presentation. Please try again.",
variant: "destructive",
});
} finally {
@@ -197,17 +226,33 @@ const OutlinePage = () => {
};
const handleAddSlide = () => {
+ if (!outlines) return;
+ const newSlide: SlideOutline = {
+ title: "New Slide",
+ body: "",
+ // Add any other required properties based on your SlideOutline type
+ };
-
-
- // const newTitleWithCharts = [...outlines, { title: "New Slide", body: "" }];
-
- // dispatch(setOutlines(newTitleWithCharts));
+ const updatedOutlines = [...outlines, newSlide];
+ setOutlines(updatedOutlines);
+ dispatch(setOutlines(updatedOutlines));
};
+
if (!presentation_id) {
- return null;
+ return (
+
+
+
+
+ No Presentation ID Found
+
+
Please start a new presentation.
+
+
+
+ );
}
return (
@@ -219,41 +264,102 @@ const OutlinePage = () => {
duration={loadingState.duration}
/>
-
+
-
- Outline
-
- {/*
-
- ({ id: item.title })) || []}
- strategy={verticalListSortingStrategy}
+
+
+ Outline
+
+ {isStreaming && (
+
+
+ Generating outlines...
+
+ )}
+
+
+ {/* Skeleton loading state */}
+ {isLoading && (
+
+ {[...Array(5)].map((_, index) => (
+
+ ))}
+
+
+ )}
+
+ {/* Outlines content */}
+ {outlines && outlines.length > 0 && (
+
+
- {outlines?.map((item, index) => (
-
- ))}
-
-
-
-
*/}
+ ({ id: item.title || `slide-${index}` })) || []}
+ strategy={verticalListSortingStrategy}
+ >
+ {outlines?.map((item, index) => (
+
+ ))}
+
+
+
+
+
+ )}
+
+ {/* Empty state */}
+ {!isLoading && outlines && outlines.length === 0 && (
+
+
No outlines available
+
+
+ )}
-
+ : isLoading || isStreaming
+ ? "Loading..."
+ : "Generate Presentation"}
+ }
);
};
-export default OutlinePage;
+export default OutlinePage;
\ No newline at end of file
diff --git a/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationPage.tsx b/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationPage.tsx
index 4493e33b..34e52124 100644
--- a/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationPage.tsx
+++ b/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationPage.tsx
@@ -131,7 +131,7 @@ const PresentationPage = ({ presentation_id }: { presentation_id: string }) => {
dispatch(setStreaming(true));
evtSource = new EventSource(
- `/api/v1/ppt/generate/stream?presentation_id=${presentation_id}`
+ `/api/v1/ppt/presentation/stream?presentation_id=${presentation_id}`
);
evtSource.onopen = () => {
@@ -147,6 +147,7 @@ const PresentationPage = ({ presentation_id }: { presentation_id: string }) => {
try {
const repairedJson = jsonrepair(accumulatedChunks);
const partialData = JSON.parse(repairedJson);
+ console.log(partialData);
if (partialData.slides) {
// Check if the length of slides has changed
if (
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 d4eb0710..73fef649 100644
--- a/servers/nextjs/app/(presentation-generator)/services/api/presentation-generation.ts
+++ b/servers/nextjs/app/(presentation-generator)/services/api/presentation-generation.ts
@@ -195,10 +195,10 @@ export class PresentationGenerationApi {
}
}
- static async generateData(presentationData: any) {
+ static async presentationPrepare(presentationData: any) {
try {
const response = await fetch(
- `/api/v1/ppt/generate/data`,
+ `/api/v1/ppt/presentation/prepare`,
{
method: "POST",
headers: getHeader(),
diff --git a/servers/nextjs/components/layouts/BulletPointSlideLayout.tsx b/servers/nextjs/components/layouts/BulletPointSlideLayout.tsx
index b7c1824a..5aac05e6 100644
--- a/servers/nextjs/components/layouts/BulletPointSlideLayout.tsx
+++ b/servers/nextjs/components/layouts/BulletPointSlideLayout.tsx
@@ -22,6 +22,10 @@ const bulletPointSlideSchema = z.object({
export const Schema = bulletPointSlideSchema
+console.log(zodToJsonSchema(Schema, {
+ removeAdditionalStrategy: 'strict',
+}))
+
export type BulletPointSlideData = z.infer
interface BulletPointSlideLayoutProps {
diff --git a/servers/nextjs/store/slices/presentationGeneration.ts b/servers/nextjs/store/slices/presentationGeneration.ts
index 33a6bde6..04f0b683 100644
--- a/servers/nextjs/store/slices/presentationGeneration.ts
+++ b/servers/nextjs/store/slices/presentationGeneration.ts
@@ -18,8 +18,8 @@ export interface ChartSettings {
}
export interface SlideOutline {
- title: string;
- body: string;
+ title?: string;
+ body?: string;
}
export interface Chart {
@@ -48,7 +48,7 @@ export interface PresentationData {
theme: string | null;
title: string;
titles: string[];
- vector_store: string | null;
+
thumbnail: string | null;
language: string;
} | null;