From cb8ad52f0c8b3963dacf30817035d94206f814c8 Mon Sep 17 00:00:00 2001 From: shiva raj badu Date: Thu, 12 Feb 2026 12:26:38 +0545 Subject: [PATCH 01/10] feat: Implement dashboard design & templates page --- .../(dashboard)/Components/DashboardNav.tsx | 64 + .../Components/DashboardSidebar.tsx | 162 + .../dashboard/components/DashboardPage.tsx | 30 +- .../dashboard/components/EmptyState.tsx | 0 .../dashboard/components/Header.tsx | 0 .../dashboard/components/PresentationCard.tsx | 105 + .../dashboard/components/PresentationGrid.tsx | 47 +- .../components/PresentationListItem.tsx | 0 .../app/(dashboard)/dashboard/loading.tsx | 25 + .../dashboard/page.tsx | 0 .../dashboard/types.ts | 0 servers/nextjs/app/(dashboard)/layout.tsx | 17 + .../components/CreateCustomTemplate.tsx | 40 + .../templates/components/TemplatePanel.tsx | 247 + .../app/(dashboard)/templates/loading.tsx | 58 + .../nextjs/app/(dashboard)/templates/page.tsx | 10 + .../components/APIKeyWarning.tsx | 4 +- .../components/LoadingSpinner.tsx | 2 +- .../custom-template/page.tsx | 2 +- .../dashboard/components/PresentationCard.tsx | 120 - .../dashboard/loading.tsx | 29 - .../components/DocumentPreviewPage.tsx | 9 +- .../(presentation-generator)/outline/page.tsx | 2 +- .../settings/SettingPage.tsx | 7 +- .../template-preview/[slug]/page.tsx | 2 +- .../template-preview/page.tsx | 2 +- .../upload/loading.tsx | 2 +- .../(presentation-generator)/upload/page.tsx | 2 +- servers/nextjs/public/card_bg.svg | 5311 +++++++++++++++++ servers/nextjs/public/create_presentation.png | Bin 0 -> 232862 bytes servers/nextjs/public/logo-with-bg.png | Bin 0 -> 5974 bytes 31 files changed, 6092 insertions(+), 207 deletions(-) create mode 100644 servers/nextjs/app/(dashboard)/Components/DashboardNav.tsx create mode 100644 servers/nextjs/app/(dashboard)/Components/DashboardSidebar.tsx rename servers/nextjs/app/{(presentation-generator) => (dashboard)}/dashboard/components/DashboardPage.tsx (59%) rename servers/nextjs/app/{(presentation-generator) => (dashboard)}/dashboard/components/EmptyState.tsx (100%) rename servers/nextjs/app/{(presentation-generator) => (dashboard)}/dashboard/components/Header.tsx (100%) create mode 100644 servers/nextjs/app/(dashboard)/dashboard/components/PresentationCard.tsx rename servers/nextjs/app/{(presentation-generator) => (dashboard)}/dashboard/components/PresentationGrid.tsx (53%) rename servers/nextjs/app/{(presentation-generator) => (dashboard)}/dashboard/components/PresentationListItem.tsx (100%) create mode 100644 servers/nextjs/app/(dashboard)/dashboard/loading.tsx rename servers/nextjs/app/{(presentation-generator) => (dashboard)}/dashboard/page.tsx (100%) rename servers/nextjs/app/{(presentation-generator) => (dashboard)}/dashboard/types.ts (100%) create mode 100644 servers/nextjs/app/(dashboard)/layout.tsx create mode 100644 servers/nextjs/app/(dashboard)/templates/components/CreateCustomTemplate.tsx create mode 100644 servers/nextjs/app/(dashboard)/templates/components/TemplatePanel.tsx create mode 100644 servers/nextjs/app/(dashboard)/templates/loading.tsx create mode 100644 servers/nextjs/app/(dashboard)/templates/page.tsx delete mode 100644 servers/nextjs/app/(presentation-generator)/dashboard/components/PresentationCard.tsx delete mode 100644 servers/nextjs/app/(presentation-generator)/dashboard/loading.tsx create mode 100644 servers/nextjs/public/card_bg.svg create mode 100644 servers/nextjs/public/create_presentation.png create mode 100644 servers/nextjs/public/logo-with-bg.png diff --git a/servers/nextjs/app/(dashboard)/Components/DashboardNav.tsx b/servers/nextjs/app/(dashboard)/Components/DashboardNav.tsx new file mode 100644 index 00000000..6185cd7f --- /dev/null +++ b/servers/nextjs/app/(dashboard)/Components/DashboardNav.tsx @@ -0,0 +1,64 @@ +"use client"; + +import { ChevronRight } from 'lucide-react'; +import Link from 'next/link'; +import React, { } from 'react' +import { defaultNavItems } from './DashboardSidebar'; +import { usePathname } from 'next/navigation'; + +const DashboardNav = () => { + const pathname = usePathname(); + const activeTab = pathname.split("?")[0].split("/").pop(); + const activeItem = defaultNavItems.find((i: any) => i.key === activeTab); + + + + + + return ( +
+
+

+ + {activeItem?.label ?? (activeTab && activeTab?.charAt(0).toUpperCase() + activeTab?.slice(1))} +

+
+ + + + {activeTab !== "playground" && activeTab !== "theme" && + + New presentation + New + + } + {activeTab === "theme" && + + New Themes + New + + + } +
+
+
+ ) +} + +export default DashboardNav diff --git a/servers/nextjs/app/(dashboard)/Components/DashboardSidebar.tsx b/servers/nextjs/app/(dashboard)/Components/DashboardSidebar.tsx new file mode 100644 index 00000000..cacbda1f --- /dev/null +++ b/servers/nextjs/app/(dashboard)/Components/DashboardSidebar.tsx @@ -0,0 +1,162 @@ +"use client"; + +import React from "react"; +import { LayoutDashboard, Star, Brain, Settings } from "lucide-react"; +import { usePathname } from "next/navigation"; +import Link from "next/link"; +import { useRouter } from "next/navigation"; + + + +export const defaultNavItems = [ + { key: "dashboard" as const, label: "Dashboard", icon: LayoutDashboard }, + { key: "templates" as const, label: "Standard", icon: Star }, + { key: "designs" as const, label: "Smart", icon: Brain }, + + + +]; +export const BelongingNavItems = [ + { key: "settings" as const, label: "Settings", icon: Settings }, +] + +const DashboardSidebar = () => { + + + const pathname = usePathname(); + const activeTab = pathname.split("?")[0].split("/").pop(); + const router = useRouter(); + const [mounted, setMounted] = React.useState(false); + const [profileMenuOpen, setProfileMenuOpen] = React.useState(false); + + + React.useEffect(() => { + setMounted(true); + }, []); + + + const handleMenuNavigate = (href: string) => { + setProfileMenuOpen(false); + router.push(href); + }; + + + + + return ( + + ); +}; + +export default DashboardSidebar; + + diff --git a/servers/nextjs/app/(presentation-generator)/dashboard/components/DashboardPage.tsx b/servers/nextjs/app/(dashboard)/dashboard/components/DashboardPage.tsx similarity index 59% rename from servers/nextjs/app/(presentation-generator)/dashboard/components/DashboardPage.tsx rename to servers/nextjs/app/(dashboard)/dashboard/components/DashboardPage.tsx index c7ad86dd..be70803b 100644 --- a/servers/nextjs/app/(presentation-generator)/dashboard/components/DashboardPage.tsx +++ b/servers/nextjs/app/(dashboard)/dashboard/components/DashboardPage.tsx @@ -2,12 +2,10 @@ import React, { useState, useEffect } from "react"; -import Wrapper from "@/components/Wrapper"; import { DashboardApi } from "@/app/(presentation-generator)/services/api/dashboard"; -import { PresentationGrid } from "@/app/(presentation-generator)/dashboard/components/PresentationGrid"; +import { PresentationGrid } from "@/app/(dashboard)/dashboard/components/PresentationGrid"; -import Header from "@/app/(presentation-generator)/dashboard/components/Header"; const DashboardPage: React.FC = () => { const [presentations, setPresentations] = useState(null); @@ -46,24 +44,14 @@ const DashboardPage: React.FC = () => { }; return ( -
-
- -
-
-

- Slide Presentation -

- -
-
-
+
+
); }; diff --git a/servers/nextjs/app/(presentation-generator)/dashboard/components/EmptyState.tsx b/servers/nextjs/app/(dashboard)/dashboard/components/EmptyState.tsx similarity index 100% rename from servers/nextjs/app/(presentation-generator)/dashboard/components/EmptyState.tsx rename to servers/nextjs/app/(dashboard)/dashboard/components/EmptyState.tsx diff --git a/servers/nextjs/app/(presentation-generator)/dashboard/components/Header.tsx b/servers/nextjs/app/(dashboard)/dashboard/components/Header.tsx similarity index 100% rename from servers/nextjs/app/(presentation-generator)/dashboard/components/Header.tsx rename to servers/nextjs/app/(dashboard)/dashboard/components/Header.tsx diff --git a/servers/nextjs/app/(dashboard)/dashboard/components/PresentationCard.tsx b/servers/nextjs/app/(dashboard)/dashboard/components/PresentationCard.tsx new file mode 100644 index 00000000..c785ea51 --- /dev/null +++ b/servers/nextjs/app/(dashboard)/dashboard/components/PresentationCard.tsx @@ -0,0 +1,105 @@ +'use client' +import React from "react"; + +import { Card } from "@/components/ui/card"; +import { DashboardApi } from "@/app/(presentation-generator)/services/api/dashboard"; +import { EllipsisVertical, Star, Trash } from "lucide-react"; +import { + Popover, + PopoverTrigger, + PopoverContent, +} from "@/components/ui/popover"; +import { useRouter } from "next/navigation"; +import { toast } from "sonner"; + +import { useFontLoader } from "@/app/(presentation-generator)/hooks/useFontLoader"; +import SlideScale from "@/app/(presentation-generator)/components/PresentationRender"; +import MarkdownRenderer from "@/app/(presentation-generator)/documents-preview/components/MarkdownRenderer"; + +export const PresentationCard = ({ + id, + title, + presentation, + onDeleted +}: { + id: string; + title: string; + presentation: any; + onDeleted?: (presentationId: string) => void; +}) => { + const router = useRouter(); + // useFontLoader(presentation.fonts || []); + const handlePreview = (e: React.MouseEvent) => { + e.preventDefault(); + router.push(`/presentation?id=${id}&type=${presentation.type}`); + }; + + const handleDelete = async (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + + + const response = await DashboardApi.deletePresentation(id); + + if (response) { + toast.success("Presentation deleted", { + description: "The presentation has been deleted successfully", + }); + if (onDeleted) { + onDeleted(id); + } + } else { + toast.error("Error deleting presentation"); + } + }; + const firstSlide = presentation?.slides?.[0]; + return ( + +
+ {/*

+ + {presentation.type} +

*/} + + +
+ + +
+ +
+
+
+
+ {} +
+

+ {new Date(presentation?.created_at).toLocaleDateString()} +

+ +
+ + e.stopPropagation()}> + + + + + + +
+ +
+
+
+ ); +}; diff --git a/servers/nextjs/app/(presentation-generator)/dashboard/components/PresentationGrid.tsx b/servers/nextjs/app/(dashboard)/dashboard/components/PresentationGrid.tsx similarity index 53% rename from servers/nextjs/app/(presentation-generator)/dashboard/components/PresentationGrid.tsx rename to servers/nextjs/app/(dashboard)/dashboard/components/PresentationGrid.tsx index 1a6ca543..559487bd 100644 --- a/servers/nextjs/app/(presentation-generator)/dashboard/components/PresentationGrid.tsx +++ b/servers/nextjs/app/(dashboard)/dashboard/components/PresentationGrid.tsx @@ -3,6 +3,7 @@ import { PresentationCard } from "./PresentationCard"; import { PlusIcon } from "@radix-ui/react-icons"; import { useRouter } from "next/navigation"; import { PresentationResponse } from "@/app/(presentation-generator)/services/api/dashboard"; +import { ArrowRight } from "lucide-react"; interface PresentationGridProps { presentations: PresentationResponse[]; @@ -41,35 +42,44 @@ export const PresentationGrid = ({ const CreateNewCard = () => (
-
- -
-
-

- Create {type === "slide" ? "New" : "Video"} Presentation -

-

- Start from scratch and bring your ideas to life -

+ New Presentation +
+ + + + + + + + + + + + + +
+

Create New Presentation

+

Get Started

+
); if (isLoading) { return ( -
-
-
+
+
+
-
-
+
+
- {[...Array(3)].map((_, i) => ( + {[...Array(15)].map((_, i) => ( ))}
@@ -105,8 +115,7 @@ export const PresentationGrid = ({ key={presentation.id} id={presentation.id} title={presentation.title} - created_at={presentation.created_at} - slide={presentation.slides[0]} + presentation={presentation} onDeleted={onPresentationDeleted} /> ))} diff --git a/servers/nextjs/app/(presentation-generator)/dashboard/components/PresentationListItem.tsx b/servers/nextjs/app/(dashboard)/dashboard/components/PresentationListItem.tsx similarity index 100% rename from servers/nextjs/app/(presentation-generator)/dashboard/components/PresentationListItem.tsx rename to servers/nextjs/app/(dashboard)/dashboard/components/PresentationListItem.tsx diff --git a/servers/nextjs/app/(dashboard)/dashboard/loading.tsx b/servers/nextjs/app/(dashboard)/dashboard/loading.tsx new file mode 100644 index 00000000..41cd811c --- /dev/null +++ b/servers/nextjs/app/(dashboard)/dashboard/loading.tsx @@ -0,0 +1,25 @@ +import { Skeleton } from '@/components/ui/skeleton' +import React from 'react' + +const loading = () => { + return ( +
+ + +
+ + +
+ { + Array.from({ length: 8 }).map((_, index) => ( + + )) + } +
+
+ +
+ ) +} + +export default loading diff --git a/servers/nextjs/app/(presentation-generator)/dashboard/page.tsx b/servers/nextjs/app/(dashboard)/dashboard/page.tsx similarity index 100% rename from servers/nextjs/app/(presentation-generator)/dashboard/page.tsx rename to servers/nextjs/app/(dashboard)/dashboard/page.tsx diff --git a/servers/nextjs/app/(presentation-generator)/dashboard/types.ts b/servers/nextjs/app/(dashboard)/dashboard/types.ts similarity index 100% rename from servers/nextjs/app/(presentation-generator)/dashboard/types.ts rename to servers/nextjs/app/(dashboard)/dashboard/types.ts diff --git a/servers/nextjs/app/(dashboard)/layout.tsx b/servers/nextjs/app/(dashboard)/layout.tsx new file mode 100644 index 00000000..3a67a294 --- /dev/null +++ b/servers/nextjs/app/(dashboard)/layout.tsx @@ -0,0 +1,17 @@ +import React from 'react' +import DashboardSidebar from './Components/DashboardSidebar' +import DashboardNav from './Components/DashboardNav' + +const layout = ({ children }: { children: React.ReactNode }) => { + return ( +
+ +
+ + {children} +
+
+ ) +} + +export default layout \ No newline at end of file diff --git a/servers/nextjs/app/(dashboard)/templates/components/CreateCustomTemplate.tsx b/servers/nextjs/app/(dashboard)/templates/components/CreateCustomTemplate.tsx new file mode 100644 index 00000000..9cb0cf39 --- /dev/null +++ b/servers/nextjs/app/(dashboard)/templates/components/CreateCustomTemplate.tsx @@ -0,0 +1,40 @@ +import { Plus, Sparkles } from 'lucide-react' +import { useRouter } from 'next/navigation'; +import React from 'react' + +const CreateCustomTemplate = () => { + const router = useRouter(); + return ( +
{ + router.push('/custom-template') + }} + className='w-full rounded-xl border border-[#EDEEEF] cursor-pointer'> +
+ +
+ + +
+
+
+
+
+ + +
+
+

Build Template

+

Build Your Own Template

+
+ +
+
+ ) +} + +export default CreateCustomTemplate diff --git a/servers/nextjs/app/(dashboard)/templates/components/TemplatePanel.tsx b/servers/nextjs/app/(dashboard)/templates/components/TemplatePanel.tsx new file mode 100644 index 00000000..270d0e10 --- /dev/null +++ b/servers/nextjs/app/(dashboard)/templates/components/TemplatePanel.tsx @@ -0,0 +1,247 @@ +"use client"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; +import { useRouter } from "next/navigation"; +import { Card } from "@/components/ui/card"; +import { ExternalLink, Loader2, Plus } from "lucide-react"; +import { templates } from "@/app/presentation-templates"; +import { TemplateWithData, TemplateLayoutsWithSettings } from "@/app/presentation-templates/utils"; +import { + useCustomTemplateSummaries, + useCustomTemplatePreview, + CustomTemplates, +} from "@/app/hooks/useCustomTemplates"; +import { CompiledLayout } from "@/app/hooks/compileLayout"; +import CreateCustomTemplate from "./CreateCustomTemplate"; + +// Component for rendering custom template card with lazy-loaded previews +export const CustomTemplateCard = React.memo(function CustomTemplateCard({ template }: { template: CustomTemplates }) { + const router = useRouter(); + const { previewLayouts, loading } = useCustomTemplatePreview(`${template.id}`); + const handleOpen = useCallback(() => { + if (template.id.startsWith('custom-')) { + router.push(`/template-preview/${template.id}`) + } else { + router.push(`/template-preview/custom-${template.id}`) + } + } + , [router, template.id]); + + return ( + +
+
+

+ {template.name} +

+
+ + {template.layoutCount} + + +
+
+ + + + {/* Layout previews */} +
+ {loading ? ( + // Loading placeholders + [...Array(Math.min(4, template.layoutCount))].map((_, index) => ( +
+ +
+ )) + ) : previewLayouts.length > 0 ? ( + // Actual layout previews + previewLayouts.slice(0, 4).map((layout: CompiledLayout, index: number) => { + const LayoutComponent = layout.component; + return ( +
+
+
+ +
+
+ ); + }) + ) : ( + // Empty state placeholders + [...Array(Math.min(4, template.layoutCount))].map((_, index) => ( +
+ No preview +
+ )) + )} +
+ + +
+ + ); +}, (prev, next) => { + // Custom templates may be refetched, producing new object references; compare on fields we render/use. + return ( + prev.template.id === next.template.id && + prev.template.id === next.template.id && + prev.template.name === next.template.name && + prev.template.layoutCount === next.template.layoutCount + ); +}); + +const InbuiltTemplateCard = React.memo(function InbuiltTemplateCard({ + template, + onOpen, +}: { + template: TemplateLayoutsWithSettings; + onOpen: (id: string) => void; +}) { + const previewLayouts = useMemo(() => template.layouts.slice(0, 4), [template.layouts]); + const handleOpen = useCallback(() => onOpen(template.id), [onOpen, template.id]); + + return ( + +
+
+

+ {template.name} +

+
+ + {template.layouts.length} + + +
+
+ +

+ {template.description} +

+ +
+ {previewLayouts.map((layout: TemplateWithData, index: number) => { + const LayoutComponent = layout.component; + return ( +
+
+
+ +
+
+ ); + })} +
+
+ + ); +}); + +const LayoutPreview = () => { + const [tab, setTab] = useState<'custom' | 'default'>('custom'); + const router = useRouter(); + const { templates: customTemplates, loading: customLoading } = useCustomTemplateSummaries(); + + useEffect(() => { + const existingScript = document.querySelector('script[src*="tailwindcss.com"]'); + if (!existingScript) { + const script = document.createElement("script"); + script.src = "https://cdn.tailwindcss.com"; + script.async = true; + document.head.appendChild(script); + } + }, []); + + const handleOpenPreview = useCallback((id: string) => router.push(`/template-preview/${id}`), [router]); + + + + + const inbuiltTemplateCards = useMemo( + () => + templates.map((template: TemplateLayoutsWithSettings) => ( + + )), + [handleOpenPreview], + ); + + const customTemplateCards = useMemo( + () => customTemplates.map((template: CustomTemplates) => ), + [customTemplates], + ); + + return ( +
+ +
+
+ + + + + +
+ + {/* Inbuilt Templates Section */} + {tab === 'default' &&
+
+ {inbuiltTemplateCards} +
+
} + + + {tab === 'custom' &&
+ {customLoading ? ( +
+ + Loading custom templates... +
+ ) : ( +
+ + {customTemplateCards} +
+ )} +
} +
+
+ ); +}; + +export default LayoutPreview; diff --git a/servers/nextjs/app/(dashboard)/templates/loading.tsx b/servers/nextjs/app/(dashboard)/templates/loading.tsx new file mode 100644 index 00000000..a6a5d2ca --- /dev/null +++ b/servers/nextjs/app/(dashboard)/templates/loading.tsx @@ -0,0 +1,58 @@ +import { Skeleton } from '@/components/ui/skeleton' +import { Card } from '@/components/ui/card' + +const Loading = () => { + return ( +
+
+ {/* Header */} +
+ + +
+ + {/* Inbuilt Templates Section */} +
+ +
+ {Array.from({ length: 4 }).map((_, idx) => ( + +
+
+ +
+ + +
+
+ + +
+ {Array.from({ length: 4 }).map((_, i) => ( + + ))} +
+
+
+ ))} +
+
+ + {/* Custom Templates Section */} +
+
+ + +
+
+ + +
+
+
+
+ ) +} + +export default Loading + diff --git a/servers/nextjs/app/(dashboard)/templates/page.tsx b/servers/nextjs/app/(dashboard)/templates/page.tsx new file mode 100644 index 00000000..b4bf6695 --- /dev/null +++ b/servers/nextjs/app/(dashboard)/templates/page.tsx @@ -0,0 +1,10 @@ +import React from 'react' +import TemplatePanel from './components/TemplatePanel' + +const page = () => { + return ( + + ) +} + +export default page diff --git a/servers/nextjs/app/(presentation-generator)/custom-template/components/APIKeyWarning.tsx b/servers/nextjs/app/(presentation-generator)/custom-template/components/APIKeyWarning.tsx index 60a43e59..07977556 100644 --- a/servers/nextjs/app/(presentation-generator)/custom-template/components/APIKeyWarning.tsx +++ b/servers/nextjs/app/(presentation-generator)/custom-template/components/APIKeyWarning.tsx @@ -1,5 +1,5 @@ import React from "react"; -import Header from "@/app/(presentation-generator)/dashboard/components/Header"; +import Header from "@/app/(dashboard)/dashboard/components/Header"; export const APIKeyWarning: React.FC = () => { return ( @@ -8,7 +8,7 @@ export const APIKeyWarning: React.FC = () => {

- Please add "GOOGLE_API_KEY" to enable template creation via AI. + Please add "GOOGLE_API_KEY" to enable template creation via AI.

Please add your OpenAI API Key to process the layout

diff --git a/servers/nextjs/app/(presentation-generator)/custom-template/components/LoadingSpinner.tsx b/servers/nextjs/app/(presentation-generator)/custom-template/components/LoadingSpinner.tsx index e379f73f..6241110e 100644 --- a/servers/nextjs/app/(presentation-generator)/custom-template/components/LoadingSpinner.tsx +++ b/servers/nextjs/app/(presentation-generator)/custom-template/components/LoadingSpinner.tsx @@ -1,6 +1,6 @@ import React from "react"; import { Loader2 } from "lucide-react"; -import Header from "@/app/(presentation-generator)/dashboard/components/Header"; +import Header from "@/app/(dashboard)/dashboard/components/Header"; interface LoadingSpinnerProps { message: string; diff --git a/servers/nextjs/app/(presentation-generator)/custom-template/page.tsx b/servers/nextjs/app/(presentation-generator)/custom-template/page.tsx index cd355765..dc8ee5e0 100644 --- a/servers/nextjs/app/(presentation-generator)/custom-template/page.tsx +++ b/servers/nextjs/app/(presentation-generator)/custom-template/page.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from "react"; import FontManager from "./components/FontManager"; -import Header from "../dashboard/components/Header"; +import Header from "../../(dashboard)/dashboard/components/Header"; import { useCustomLayout } from "./hooks/useCustomLayout"; import { useFontManagement } from "./hooks/useFontManagement"; diff --git a/servers/nextjs/app/(presentation-generator)/dashboard/components/PresentationCard.tsx b/servers/nextjs/app/(presentation-generator)/dashboard/components/PresentationCard.tsx deleted file mode 100644 index 588f61dd..00000000 --- a/servers/nextjs/app/(presentation-generator)/dashboard/components/PresentationCard.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import React, { useMemo } from "react"; - -import { Card } from "@/components/ui/card"; -import { DashboardApi } from "@/app/(presentation-generator)/services/api/dashboard"; -import { DotsVerticalIcon, TrashIcon } from "@radix-ui/react-icons"; -import { - Popover, - PopoverTrigger, - PopoverContent, -} from "@/components/ui/popover"; -import { useRouter } from "next/navigation"; -import { toast } from "sonner"; -import SlideScale from "../../components/PresentationRender"; - -export const PresentationCard = ({ - id, - title, - created_at, - slide, - onDeleted -}: { - id: string; - title: string; - created_at: string; - slide: any; - onDeleted?: (presentationId: string) => void; -}) => { - const router = useRouter(); - - - - - const handlePreview = (e: React.MouseEvent) => { - e.preventDefault(); - router.push(`/presentation?id=${id}`); - }; - - const handleDelete = async (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - - - const response = await DashboardApi.deletePresentation(id); - - if (response) { - toast.success("Presentation deleted", { - description: "The presentation has been deleted successfully", - }); - if (onDeleted) { - onDeleted(id); - } - } else { - toast.error("Error deleting presentation"); - } - }; - return ( - -

- {/* Date */} -
-

- {new Date(created_at).toLocaleDateString()} -

- - e.stopPropagation()}> - - - - - - - - - -
- -
-
-
- -
-
- - {/* Icon and Title */} -
- - - -

- {title} -

-
-
- - ); -}; diff --git a/servers/nextjs/app/(presentation-generator)/dashboard/loading.tsx b/servers/nextjs/app/(presentation-generator)/dashboard/loading.tsx deleted file mode 100644 index d3350f27..00000000 --- a/servers/nextjs/app/(presentation-generator)/dashboard/loading.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { Skeleton } from '@/components/ui/skeleton' -import React from 'react' -import Header from './components/Header' -import Wrapper from '@/components/Wrapper' - -const loading = () => { - return ( -
-
- -
- -

- Slide Presentation -

-
- { - Array.from({ length: 8 }).map((_, index) => ( - - )) - } -
-
-
-
- ) -} - -export default loading \ No newline at end of file diff --git a/servers/nextjs/app/(presentation-generator)/documents-preview/components/DocumentPreviewPage.tsx b/servers/nextjs/app/(presentation-generator)/documents-preview/components/DocumentPreviewPage.tsx index 06d44619..8abd3492 100644 --- a/servers/nextjs/app/(presentation-generator)/documents-preview/components/DocumentPreviewPage.tsx +++ b/servers/nextjs/app/(presentation-generator)/documents-preview/components/DocumentPreviewPage.tsx @@ -27,7 +27,7 @@ import MarkdownRenderer from "./MarkdownRenderer"; import { getIconFromFile } from "../../utils/others"; import { ChevronRight, PanelRightOpen, X } from "lucide-react"; import ToolTip from "@/components/ToolTip"; -import Header from "@/app/(presentation-generator)/dashboard/components/Header"; +import Header from "@/app/(dashboard)/dashboard/components/Header"; import { trackEvent, MixpanelEvent } from "@/utils/mixpanel"; // Types @@ -147,7 +147,7 @@ const DocumentsPreviewPage: React.FC = () => { (fileItem: FileItem) => fileItem.file_path ); trackEvent(MixpanelEvent.DocumentsPreview_Create_Presentation_API_Call); - const createResponse = await PresentationGenerationApi.createPresentation( + const createResponse = await PresentationGenerationApi.createPresentation( { content: config?.prompt ?? "", n_slides: config?.slides ? parseInt(config.slides) : null, @@ -240,9 +240,8 @@ const DocumentsPreviewPage: React.FC = () => {
updateSelectedDocument(key)} - className={`${ - selectedDocument === key ? "border border-blue-500" : "" - } flex p-2 rounded-sm gap-2 items-center cursor-pointer`} + className={`${selectedDocument === key ? "border border-blue-500" : "" + } flex p-2 rounded-sm gap-2 items-center cursor-pointer`} > { + ))} */} +
+
+ ); + } + + if (llmConfig.IMAGE_PROVIDER === "gpt-image-1.5") { + return ( +
+ +
+ + {/* {GPT_IMAGE_1_5_QUALITY_OPTIONS.map((option) => ( + + ))} */} +
+
+ ); + } + + return null; +}; + +const ImageSelectionConfig = ({ isImageGenerationDisabled, openImageProviderSelect, setOpenImageProviderSelect, llmConfig, input_field_changed, getApiKeyValue, handleApiKeyInputChange }: { isImageGenerationDisabled: boolean, openImageProviderSelect: boolean, setOpenImageProviderSelect: (open: boolean) => void, llmConfig: LLMConfig, input_field_changed: (value: string, field: string) => void, getApiKeyValue: (field: string) => string, handleApiKeyInputChange: (field: string, value: string) => void }) => { + return ( +
+
+
+

Image Generation Settings

+

+ Choosing where images come from. +

+
+
+ + + {!isImageGenerationDisabled && ( + <> + {/* Image Provider Selection */} +
+ +
+ + + + + + + + + No provider found. + + {Object.values(IMAGE_PROVIDERS).map( + (provider, index) => ( + { + input_field_changed(value, "image_provider"); + setOpenImageProviderSelect(false); + }} + > + +
+
+
+ + {provider.label} + +
+ + {provider.description} + +
+
+
+ ) + )} +
+
+
+
+
+
+
+ + {renderQualitySelector(llmConfig, input_field_changed)} + + {/* Dynamic API Key Input for Image Provider */} + {llmConfig.IMAGE_PROVIDER && + IMAGE_PROVIDERS[llmConfig.IMAGE_PROVIDER] && + (() => { + const provider = IMAGE_PROVIDERS[llmConfig.IMAGE_PROVIDER]; + + // Show info message when using same API key as main provider + if ( + provider.value === "dall-e-3" && + llmConfig.LLM === "openai" + ) { + return <>; + } + + if ( + provider.value === "gpt-image-1.5" && + llmConfig.LLM === "openai" + ) { + return <>; + } + + if ( + provider.value === "gemini_flash" && + llmConfig.LLM === "google" + ) { + return <>; + } + + if ( + provider.value === "nanobanana_pro" && + llmConfig.LLM === "google" + ) { + return <>; + } + + // Show ComfyUI configuration + if (provider.value === "comfyui") { + return ( +
+
+ +
+ { + input_field_changed( + e.target.value, + "comfyui_url" + ); + }} + /> +
+

+ + Use your machine IP address (not localhost) when + running in Docker +

+
+
+ +
+