From 5f58828d021feec8bd8a3408ada4640c8f8b46ae Mon Sep 17 00:00:00 2001 From: shiva raj badu Date: Sat, 19 Jul 2025 22:11:18 +0545 Subject: [PATCH] feat(nextjs): New slide add featture added --- .../components/TiptapTextReplacer.tsx | 1 + .../components/slide_layouts/NewSlide.tsx | 199 ++++-------------- .../hooks/useGroupLayouts.tsx | 3 +- .../components/PresentationPage.tsx | 8 +- .../presentation/components/SlideContent.tsx | 53 ++--- .../presentation/hooks/usePresentationData.ts | 22 +- .../dashboard/components/PresentationCard.tsx | 13 +- .../nextjs/app/layout-preview/[slug]/page.tsx | 2 +- .../hooks/useGroupLayoutLoader.ts | 44 +++- .../layout-preview/hooks/useLayoutLoader.ts | 8 +- .../nextjs/app/layout-preview/types/index.ts | 1 + .../store/slices/presentationGeneration.ts | 16 ++ 12 files changed, 138 insertions(+), 232 deletions(-) diff --git a/servers/nextjs/app/(presentation-generator)/components/TiptapTextReplacer.tsx b/servers/nextjs/app/(presentation-generator)/components/TiptapTextReplacer.tsx index 5482b702..0fd75f3a 100644 --- a/servers/nextjs/app/(presentation-generator)/components/TiptapTextReplacer.tsx +++ b/servers/nextjs/app/(presentation-generator)/components/TiptapTextReplacer.tsx @@ -107,6 +107,7 @@ const TiptapTextReplacer: React.FC = ({ const root = ReactDOM.createRoot(tiptapContainer); root.render( { if (dataPath && onContentChange) { diff --git a/servers/nextjs/app/(presentation-generator)/components/slide_layouts/NewSlide.tsx b/servers/nextjs/app/(presentation-generator)/components/slide_layouts/NewSlide.tsx index c1ee87b2..4ff3c840 100644 --- a/servers/nextjs/app/(presentation-generator)/components/slide_layouts/NewSlide.tsx +++ b/servers/nextjs/app/(presentation-generator)/components/slide_layouts/NewSlide.tsx @@ -1,152 +1,46 @@ import { Trash2 } from 'lucide-react'; import React from 'react' +import { useGroupLayoutLoader } from '@/app/layout-preview/hooks/useGroupLayoutLoader'; +import { useDispatch } from 'react-redux'; +import { addNewSlide } from '@/store/slices/presentationGeneration'; +import { Loader2 } from 'lucide-react'; interface NewSlideProps { - onSelectLayout: (type: number) => void; setShowNewSlideSelection: (show: boolean) => void; + group: string; + index: number; + presentationId: string; } - -const LayoutPreview = ({ type }: { type: string }) => { - switch (type) { - case 'type1': - return ( -
-
-
-
-
-
-

image

-
-
- ) - case 'type2': - return ( -
-
-
- {[1, 2, 3].map((i) => ( -
-
-
-
- ))} -
-
- ) - case 'type4': - return ( -
-
-
- {[1, 2, 3].map((i) => ( -
-
-

image

-
-
-
- ))} -
-
- ) - case 'type5': - return ( -
-
-
-
-

chart

-
-
-
-
- ) - case 'type6': - return ( -
-
-
-
-
-
- {[1, 2].map((i) => ( -
-
-
-
-
-
- ))} -
-
- ) - case 'type7': - return ( -
-
-
- {[1, 2, 3].map((i) => ( -
-
-

Icon

-
-
-
-
- ))} -
-
- ) - case 'type8': - return ( -
-
-
-
-
-
- {[1, 2].map((i) => ( -
-
-

Icon

-
-
-
-
-
- ))} -
-
- ) - case 'type9': - return ( -
-
-
-
-

chart

-
-
-
- {[1, 2, 3].map((i) => ( -
-
-
-
-
-
- ))} -
-
- ) - - default: - return null +const NewSlide = ({ setShowNewSlideSelection, group, index, presentationId }: NewSlideProps) => { + const dispatch = useDispatch(); + const handleNewSlide = (sampleData: any, id: string) => { + const newSlide = { + id: crypto.randomUUID(), + index: index, + content: sampleData, + layout_group: group, + layout: `${group}:${id}`, + presentation: presentationId + } + dispatch(addNewSlide({ slideData: newSlide, index })); + setShowNewSlideSelection(false); + } + const { layoutGroup, loading } = useGroupLayoutLoader(group) + + if (loading) { + return ( +
+
+

Select a Slide Layout

+ setShowNewSlideSelection(false)} className='text-gray-500 text-2xl cursor-pointer' /> +
+
+ +
+
+ ) } -} -const NewSlide = ({ onSelectLayout, setShowNewSlideSelection }: NewSlideProps) => { return (
@@ -155,22 +49,17 @@ const NewSlide = ({ onSelectLayout, setShowNewSlideSelection }: NewSlideProps) = setShowNewSlideSelection(false)} className='text-gray-500 text-2xl cursor-pointer' />
- {['type1', 'type2', 'type4', 'type5', 'type6', 'type7', 'type8', 'type9'].map((type) => ( -
onSelectLayout(parseInt(type.replace('type', '')))} - > -
-
-

Layout {type.replace('type', '')}

-
-
- + {layoutGroup && layoutGroup?.layouts.map((layout: any, index: number) => { + const { component: LayoutComponent, sampleData, layoutId } = layout + return ( +
handleNewSlide(sampleData, layoutId)} key={`${layoutGroup?.groupName}-${index}`} className=" relative cursor-pointer overflow-hidden aspect-video"> +
+
+
-
- ))} + ) + })}
) diff --git a/servers/nextjs/app/(presentation-generator)/hooks/useGroupLayouts.tsx b/servers/nextjs/app/(presentation-generator)/hooks/useGroupLayouts.tsx index 29c5e29a..8208530d 100644 --- a/servers/nextjs/app/(presentation-generator)/hooks/useGroupLayouts.tsx +++ b/servers/nextjs/app/(presentation-generator)/hooks/useGroupLayouts.tsx @@ -35,7 +35,7 @@ export const useGroupLayouts = () => { // Render slide content with group validation, automatic Tiptap text editing, and editable images/icons const renderSlideContent = useMemo(() => { - return (slide: any, isEditMode: boolean = true) => { + return (slide: any, isEditMode: boolean) => { const Layout = getGroupLayout(slide.layout, slide.layout_group); if (!Layout) { return ( @@ -55,6 +55,7 @@ export const useGroupLayouts = () => { isEditMode={isEditMode} > = ({ presentation_id }) }); // Custom hooks - const { fetchUserSlides, handleDeleteSlide } = usePresentationData( + const { fetchUserSlides } = usePresentationData( presentation_id, setLoading, setError @@ -72,9 +72,7 @@ const PresentationPage: React.FC = ({ presentation_id }) fetchUserSlides ); - const onDeleteSlide = (index: number) => { - handleDeleteSlide(index, presentationData); - }; + const onSlideChange = (newSlide: number) => { handleSlideChange(newSlide, presentationData); @@ -172,7 +170,7 @@ const PresentationPage: React.FC = ({ presentation_id }) slide={slide} index={index} presentationId={presentation_id} - onDeleteSlide={onDeleteSlide} + /> ))} diff --git a/servers/nextjs/app/(presentation-generator)/presentation/components/SlideContent.tsx b/servers/nextjs/app/(presentation-generator)/presentation/components/SlideContent.tsx index 5c33adf5..9fdb7dec 100644 --- a/servers/nextjs/app/(presentation-generator)/presentation/components/SlideContent.tsx +++ b/servers/nextjs/app/(presentation-generator)/presentation/components/SlideContent.tsx @@ -13,23 +13,21 @@ import { PresentationGenerationApi } from "../../services/api/presentation-gener import ToolTip from "@/components/ToolTip"; import { RootState } from "@/store/store"; import { useDispatch, useSelector } from "react-redux"; -import { addSlide, updateSlide } from "@/store/slices/presentationGeneration"; +import { deletePresentationSlide, updateSlide } from "@/store/slices/presentationGeneration"; import NewSlide from "../../components/slide_layouts/NewSlide"; -import { getEmptySlideContent } from "../../utils/NewSlideContent"; import { useGroupLayouts } from "../../hooks/useGroupLayouts"; interface SlideContentProps { slide: any; index: number; presentationId: string; - onDeleteSlide: (index: number) => void; } const SlideContent = ({ slide, index, presentationId, - onDeleteSlide, + }: SlideContentProps) => { const dispatch = useDispatch(); const [isUpdating, setIsUpdating] = useState(false); @@ -82,29 +80,16 @@ const SlideContent = ({ setIsUpdating(false); } }; - - const handleNewSlide = (type: number, index: number) => { - const newSlide: Slide = getEmptySlideContent( - type, - index + 1, - presentationData?.id! - ); - - dispatch(addSlide({ slide: newSlide, index: index + 1 })); - setShowNewSlideSelection(false); - - // Scroll to the newly added slide after a short delay to ensure it's rendered - setTimeout(() => { - const newSlideElement = document.getElementById(`slide-${newSlide.id}`); - if (newSlideElement) { - newSlideElement.scrollIntoView({ - behavior: "smooth", - block: "center", - }); - } - }, 100); + const onDeleteSlide = async () => { + try { + dispatch(deletePresentationSlide(slide.index)); + } catch (error) { + console.error("Error deleting slide:", error); + } }; + + // Scroll to the new slide when streaming and new slides are being generated useEffect(() => { if ( @@ -139,16 +124,16 @@ const SlideContent = ({ {isStreaming && ( )} -
+
{/* render slides */} {loading ?
: slideContent} - {/* {!showNewSlideSelection && ( + {!showNewSlideSelection && (
- {!isStreaming && ( + {!isStreaming && !loading && (
setShowNewSlideSelection(true)} className=" bg-white shadow-md w-[80px] py-2 border hover:border-[#5141e5] duration-300 flex items-center justify-center rounded-lg cursor-pointer mx-auto" @@ -159,16 +144,18 @@ const SlideContent = ({
)} - {showNewSlideSelection && ( + {showNewSlideSelection && !loading && ( handleNewSlide(type, slide.index)} + index={index} + group={slide.layout_group} setShowNewSlideSelection={setShowNewSlideSelection} + presentationId={presentationId} /> - )} */} - {!isStreaming && ( + )} + {!isStreaming && !loading && (
onDeleteSlide(slide.index)} + onClick={onDeleteSlide} className="absolute top-2 z-20 sm:top-4 right-2 sm:right-4 hidden md:block transition-transform" > diff --git a/servers/nextjs/app/(presentation-generator)/presentation/hooks/usePresentationData.ts b/servers/nextjs/app/(presentation-generator)/presentation/hooks/usePresentationData.ts index 4c7681f9..0369c342 100644 --- a/servers/nextjs/app/(presentation-generator)/presentation/hooks/usePresentationData.ts +++ b/servers/nextjs/app/(presentation-generator)/presentation/hooks/usePresentationData.ts @@ -2,8 +2,7 @@ import { useCallback } from "react"; import { useDispatch } from "react-redux"; import { toast } from "@/hooks/use-toast"; import { DashboardApi } from "@/app/dashboard/api/dashboard"; -import { PresentationGenerationApi } from "../../services/api/presentation-generation"; -import { setPresentationData, deletePresentationSlide } from "@/store/slices/presentationGeneration"; +import { setPresentationData } from "@/store/slices/presentationGeneration"; export const usePresentationData = ( presentationId: string, @@ -32,25 +31,10 @@ export const usePresentationData = ( } }, [presentationId, dispatch, setLoading, setError]); - const handleDeleteSlide = useCallback(async (index: number, presentationData: any) => { - dispatch(deletePresentationSlide(index)); - try { - await PresentationGenerationApi.deleteSlide( - presentationId, - presentationData?.slides[index].id! - ); - } catch (error) { - console.error("Error deleting slide:", error); - toast({ - title: "Error", - description: "Failed to delete slide", - variant: "destructive", - }); - } - }, [presentationId, dispatch]); + return { fetchUserSlides, - handleDeleteSlide, + }; }; \ No newline at end of file diff --git a/servers/nextjs/app/dashboard/components/PresentationCard.tsx b/servers/nextjs/app/dashboard/components/PresentationCard.tsx index b1497c11..720bce3f 100644 --- a/servers/nextjs/app/dashboard/components/PresentationCard.tsx +++ b/servers/nextjs/app/dashboard/components/PresentationCard.tsx @@ -63,11 +63,6 @@ export const PresentationCard = ({ }); } }; - - - - - return ( - e.stopPropagation()}> -