html to pdf page added
This commit is contained in:
parent
abe4226147
commit
43062f7e6f
6 changed files with 278 additions and 71 deletions
|
|
@ -26,7 +26,7 @@ const Type1Layout = ({
|
|||
const { currentColors } = useSelector((state: RootState) => state.theme);
|
||||
return (
|
||||
<div
|
||||
className="slide-container font-inter w-full rounded-sm max-w-[1280px] shadow-lg px-3 sm:px-12 lg:px-20 py-[10px] sm:py-[40px] lg:py-[86px] max-h-[720px] flex items-center aspect-video bg-white relative z-20"
|
||||
className="slide-container w-full rounded-sm max-w-[1280px] shadow-lg px-3 sm:px-12 lg:px-20 py-[10px] sm:py-[40px] lg:py-[86px] max-h-[720px] flex items-center aspect-video bg-white relative z-20"
|
||||
data-slide-element
|
||||
data-slide-id={slideId}
|
||||
data-slide-index={slideIndex}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ const Type8Layout = ({
|
|||
|
||||
return (
|
||||
<div
|
||||
className="slide-container shadow-lg w-full rounded-sm font-inter px-3 sm:px-12 lg:px-20 py-[10px] sm:py-[40px] lg:py-[86px] flex items-center justify-center max-h-[720px] aspect-video bg-white relative z-20"
|
||||
className="slide-container shadow-lg w-full max-w-[1280px] rounded-sm font-inter px-3 sm:px-12 lg:px-20 py-[10px] sm:py-[40px] lg:py-[86px] flex items-center justify-center max-h-[720px] aspect-video bg-white relative z-20"
|
||||
data-slide-element
|
||||
data-slide-index={slideIndex}
|
||||
data-slide-id={slideId}
|
||||
|
|
@ -89,29 +89,73 @@ const Type8Layout = ({
|
|||
<div className="space-y-4 lg:space-y-8">
|
||||
{body && body.length > 0 && body.length === 2
|
||||
? body.map((item, index) => (
|
||||
<div
|
||||
data-slide-element
|
||||
data-slide-index={slideIndex}
|
||||
data-element-type="slide-box"
|
||||
data-element-id={`slide-${slideIndex}-item-${index}-box`}
|
||||
style={{
|
||||
boxShadow: "0 2px 10px 0 rgba(43, 43, 43, 0.2)",
|
||||
}}
|
||||
key={`${body.length}-${index}`}
|
||||
className="slide-box rounded-lg p-3 lg:p-6 relative group"
|
||||
>
|
||||
<ElementMenu index={index} handleDeleteItem={DeleteItem} />
|
||||
<IconsEditor
|
||||
icon={icons[index]}
|
||||
index={index}
|
||||
backgroundColor={currentColors.iconBg}
|
||||
hasBg={true}
|
||||
slideIndex={slideIndex}
|
||||
elementId={`slide-${slideIndex}-icon-${index}`}
|
||||
icon_prompt={icon_queries?.[index]?.queries || []}
|
||||
/>
|
||||
<div
|
||||
data-slide-element
|
||||
data-slide-index={slideIndex}
|
||||
data-element-type="slide-box"
|
||||
data-element-id={`slide-${slideIndex}-item-${index}-box`}
|
||||
style={{
|
||||
boxShadow: "0 2px 10px 0 rgba(43, 43, 43, 0.2)",
|
||||
}}
|
||||
key={`${body.length}-${index}`}
|
||||
className="slide-box rounded-lg p-3 lg:p-6 relative group"
|
||||
>
|
||||
<ElementMenu index={index} handleDeleteItem={DeleteItem} />
|
||||
<IconsEditor
|
||||
icon={icons[index]}
|
||||
index={index}
|
||||
backgroundColor={currentColors.iconBg}
|
||||
hasBg={true}
|
||||
slideIndex={slideIndex}
|
||||
elementId={`slide-${slideIndex}-icon-${index}`}
|
||||
icon_prompt={icon_queries?.[index]?.queries || []}
|
||||
/>
|
||||
|
||||
<div className="space-y-1 lg:space-y-3 lg:mt-3">
|
||||
<div className="space-y-1 lg:space-y-3 lg:mt-3">
|
||||
<EditableText
|
||||
slideIndex={slideIndex}
|
||||
elementId={`slide-${slideIndex}-item-${index}-heading`}
|
||||
type="heading"
|
||||
bodyIdx={index}
|
||||
content={item.heading}
|
||||
/>
|
||||
<EditableText
|
||||
slideIndex={slideIndex}
|
||||
elementId={`slide-${slideIndex}-item-${index}-description`}
|
||||
type="heading-description"
|
||||
bodyIdx={index}
|
||||
content={item.description}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
: body.map((item, index) => (
|
||||
<div
|
||||
data-slide-element
|
||||
data-slide-index={slideIndex}
|
||||
data-element-type="slide-box"
|
||||
data-element-id={`slide-${slideIndex}-item-${index}-box`}
|
||||
style={{
|
||||
boxShadow: "0 2px 10px 0 rgba(43, 43, 43, 0.2)",
|
||||
}}
|
||||
key={`${body.length}-${index}`}
|
||||
className="slide-box rounded-lg p-3 lg:p-6 relative group"
|
||||
>
|
||||
<ElementMenu index={index} handleDeleteItem={DeleteItem} />
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-[32px] md:w-[64px] h-[32px] md:h-[64px]">
|
||||
<IconsEditor
|
||||
className="rounded-lg"
|
||||
icon={icons[index]}
|
||||
index={index}
|
||||
backgroundColor={currentColors.iconBg}
|
||||
hasBg={true}
|
||||
slideIndex={slideIndex}
|
||||
elementId={`slide-${slideIndex}-icon-${index}`}
|
||||
icon_prompt={icon_queries?.[index]?.queries || []}
|
||||
/>
|
||||
</div>
|
||||
<div className="lg:space-y-3 ">
|
||||
<EditableText
|
||||
slideIndex={slideIndex}
|
||||
elementId={`slide-${slideIndex}-item-${index}-heading`}
|
||||
|
|
@ -128,52 +172,8 @@ const Type8Layout = ({
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
: body.map((item, index) => (
|
||||
<div
|
||||
data-slide-element
|
||||
data-slide-index={slideIndex}
|
||||
data-element-type="slide-box"
|
||||
data-element-id={`slide-${slideIndex}-item-${index}-box`}
|
||||
style={{
|
||||
boxShadow: "0 2px 10px 0 rgba(43, 43, 43, 0.2)",
|
||||
}}
|
||||
key={`${body.length}-${index}`}
|
||||
className="slide-box rounded-lg p-3 lg:p-6 relative group"
|
||||
>
|
||||
<ElementMenu index={index} handleDeleteItem={DeleteItem} />
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-[32px] md:w-[64px] h-[32px] md:h-[64px]">
|
||||
<IconsEditor
|
||||
className="rounded-lg"
|
||||
icon={icons[index]}
|
||||
index={index}
|
||||
backgroundColor={currentColors.iconBg}
|
||||
hasBg={true}
|
||||
slideIndex={slideIndex}
|
||||
elementId={`slide-${slideIndex}-icon-${index}`}
|
||||
icon_prompt={icon_queries?.[index]?.queries || []}
|
||||
/>
|
||||
</div>
|
||||
<div className="lg:space-y-3 ">
|
||||
<EditableText
|
||||
slideIndex={slideIndex}
|
||||
elementId={`slide-${slideIndex}-item-${index}-heading`}
|
||||
type="heading"
|
||||
bodyIdx={index}
|
||||
content={item.heading}
|
||||
/>
|
||||
<EditableText
|
||||
slideIndex={slideIndex}
|
||||
elementId={`slide-${slideIndex}-item-${index}-description`}
|
||||
type="heading-description"
|
||||
bodyIdx={index}
|
||||
content={item.description}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,176 @@
|
|||
"use client";
|
||||
import React, { useEffect, useState, useCallback, useRef } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { RootState } from "@/store/store";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
|
||||
|
||||
import { DashboardApi } from "@/app/dashboard/api/dashboard";
|
||||
|
||||
import SlideContent from "../presentation/components/SlideContent";
|
||||
|
||||
import {
|
||||
deletePresentationSlide,
|
||||
setPresentationData,
|
||||
} from "@/store/slices/presentationGeneration";
|
||||
import { toast } from "@/hooks/use-toast";
|
||||
|
||||
|
||||
import { Loader2 } from "lucide-react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { AlertCircle } from "lucide-react";
|
||||
import { setThemeColors, ThemeColors } from "../store/themeSlice";
|
||||
import { ThemeType } from "../upload/type";
|
||||
import { PresentationGenerationApi } from "../services/api/presentation-generation";
|
||||
import { renderSlideContent } from "../components/slide_config";
|
||||
|
||||
|
||||
|
||||
// Custom debounce function
|
||||
function useDebounce<T extends (...args: any[]) => void>(
|
||||
callback: T,
|
||||
delay: number
|
||||
) {
|
||||
const timeoutRef = useRef<NodeJS.Timeout>();
|
||||
|
||||
return useCallback(
|
||||
(...args: Parameters<T>) => {
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current);
|
||||
}
|
||||
|
||||
timeoutRef.current = setTimeout(() => {
|
||||
callback(...args);
|
||||
}, delay);
|
||||
},
|
||||
[callback, delay]
|
||||
);
|
||||
}
|
||||
|
||||
const PresentationPage = ({ presentation_id }: { presentation_id: string }) => {
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const { currentTheme, currentColors } = useSelector(
|
||||
(state: RootState) => state.theme
|
||||
);
|
||||
const { presentationData } = useSelector(
|
||||
(state: RootState) => state.presentationGeneration
|
||||
);
|
||||
const [error, setError] = useState(false);
|
||||
// Function to fetch the slides
|
||||
useEffect(() => {
|
||||
fetchUserSlides();
|
||||
}, []);
|
||||
|
||||
// Function to fetch the user slides
|
||||
const fetchUserSlides = async () => {
|
||||
try {
|
||||
const data = await DashboardApi.getPresentation(presentation_id);
|
||||
if (data) {
|
||||
if (data.presentation.theme) {
|
||||
dispatch(
|
||||
setThemeColors({
|
||||
...data.presentation.theme.colors,
|
||||
theme: data.presentation.theme.name as ThemeType,
|
||||
})
|
||||
);
|
||||
setColorsVariables(
|
||||
data.presentation.theme.colors,
|
||||
data.presentation.theme.name as ThemeType
|
||||
);
|
||||
}
|
||||
dispatch(setPresentationData(data));
|
||||
setLoading(false);
|
||||
}
|
||||
} catch (error) {
|
||||
setError(true);
|
||||
toast({
|
||||
title: "Error",
|
||||
description: "Failed to load presentation",
|
||||
variant: "destructive",
|
||||
});
|
||||
|
||||
console.error("Error fetching user slides:", error);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
const setColorsVariables = (colors: ThemeColors, theme: ThemeType) => {
|
||||
const root = document.documentElement;
|
||||
Object.entries(colors).forEach(([key, value]) => {
|
||||
const cssKey = key.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
|
||||
root.style.setProperty(`--${theme}-${cssKey}`, value);
|
||||
});
|
||||
};
|
||||
const language = presentationData?.presentation?.language || "English";
|
||||
// Regular view
|
||||
return (
|
||||
<div className="h-screen flex overflow-hidden flex-col">
|
||||
{error ? (
|
||||
<div className="flex flex-col items-center justify-center h-screen bg-gray-100">
|
||||
<div
|
||||
className="bg-white border border-red-300 text-red-700 px-6 py-8 rounded-lg shadow-lg flex flex-col items-center"
|
||||
role="alert"
|
||||
>
|
||||
<AlertCircle className="w-16 h-16 mb-4 text-red-500" />
|
||||
<strong className="font-bold text-4xl mb-2">Oops!</strong>
|
||||
<p className="block text-2xl py-2">
|
||||
We encountered an issue loading your presentation.
|
||||
</p>
|
||||
<p className="text-lg py-2">
|
||||
Please check your internet connection or try again later.
|
||||
</p>
|
||||
<Button
|
||||
className="mt-4 bg-red-500 text-white hover:bg-red-600 focus:ring-4 focus:ring-red-300"
|
||||
onClick={() => window.location.reload()}
|
||||
>
|
||||
Retry
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
|
||||
<div style={{
|
||||
background: currentColors.background,
|
||||
}} className="flex-1 h-[calc(100vh-100px)] overflow-y-auto">
|
||||
<div
|
||||
className="mx-auto flex flex-col items-center overflow-hidden justify-center slide-theme"
|
||||
data-theme={currentTheme}
|
||||
>
|
||||
{!presentationData ||
|
||||
loading ||
|
||||
!presentationData?.slides ||
|
||||
presentationData?.slides.length === 0 ? (
|
||||
<div className="relative w-full h-[calc(100vh-120px)] mx-auto ">
|
||||
<div className=" ">
|
||||
{Array.from({ length: 2 }).map((_, index) => (
|
||||
<Skeleton
|
||||
key={index}
|
||||
className="aspect-video bg-gray-400 my-4 w-full mx-auto max-w-[1280px]"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{presentationData &&
|
||||
presentationData.slides &&
|
||||
presentationData.slides.length > 0 &&
|
||||
presentationData.slides.map((slide, index) => (
|
||||
renderSlideContent(slide, language)
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PresentationPage;
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
'use client'
|
||||
import React from "react";
|
||||
import { FooterProvider } from "../context/footerContext";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import PdfMakerPage from "./PdfMakerPage";
|
||||
const page = () => {
|
||||
|
||||
const router = useRouter();
|
||||
const params = useSearchParams();
|
||||
const queryId = params.get("id");
|
||||
if (!queryId) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-screen">
|
||||
<h1 className="text-2xl font-bold">No presentation id found</h1>
|
||||
<p className="text-gray-500 pb-4">Please try again</p>
|
||||
<Button onClick={() => router.push("/dashboard")}>Go to home</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<FooterProvider>
|
||||
<PdfMakerPage presentation_id={queryId} />
|
||||
</FooterProvider>
|
||||
);
|
||||
};
|
||||
export default page;
|
||||
|
|
@ -324,6 +324,9 @@ const Header = ({
|
|||
{isStreaming && (
|
||||
<Loader2 className="animate-spin text-white font-bold w-6 h-6" />
|
||||
)}
|
||||
<button onClick={() => router.push(`/pdf-maker?id=${presentation_id}`)}>
|
||||
go there
|
||||
</button>
|
||||
<Select value={currentTheme} onValueChange={handleThemeSelect}>
|
||||
<SelectTrigger className="w-[160px] bg-[#6358fd] text-white border-none hover:bg-[#5146E5] transition-colors">
|
||||
<div className="flex items-center gap-2">
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import PresentationPage from "./components/PresentationPage";
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
const page = () => {
|
||||
console.log("presentation page", window.location.href);
|
||||
|
||||
const router = useRouter();
|
||||
const params = useSearchParams();
|
||||
const queryId = params.get("id");
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue