Refactor presentation components: integrate FooterProvider in layout, remove unused mini-slide components, and update slide rendering logic. Adjust theme handling in PresentationCard and SidePanel for improved styling and functionality.

This commit is contained in:
shiva raj badu 2025-07-12 00:01:41 +05:45
parent 31f47bffca
commit a95e8c148f
22 changed files with 126 additions and 738 deletions

View file

@ -1,12 +1,5 @@
import {
updateSlideDescription,
updateSlideBodyString,
updateSlideTitle,
updateSlideBodyHeading,
updateSlideBodyDescription,
} from "@/store/slices/presentationGeneration";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import React, { useEffect, useRef } from "react";
import { useSelector } from "react-redux";
import TipTapEditor from "./Tiptap";
import { RootState } from "@/store/store";
import Typewriter from "./TypeWriter";
@ -14,7 +7,7 @@ import Typewriter from "./TypeWriter";
interface EditableTextProps {
slideIndex: number;
bodyIdx?: number;
elementId: string; // Format: 'title' | 'body.0.heading' | 'body.0.description'
elementId: string;
type:
| "title"
| "heading"

View file

@ -1,18 +0,0 @@
import React from 'react'
import { Chart } from '@/store/slices/presentationGeneration';
import { renderChart } from '../slide_config';
import { RootState } from '@/store/store';
import { useSelector } from 'react-redux';
const MiniCharts = ({ chartData }: { chartData: Chart }) => {
const { currentColors } = useSelector((state: RootState) => state.theme);
return (
<div className="w-full h-full pointer-events-none">
{renderChart(chartData, true, currentColors)}
</div>
)
}
export default MiniCharts

View file

@ -1,17 +0,0 @@
import { useTypewriter } from "@/hooks/useTypeWriter";
import { RootState } from "@/store/store";
import { useSelector } from "react-redux";
const MiniTypeWriter = ({ text }: { text: string }) => {
const { isStreaming } = useSelector(
(state: RootState) => state.presentationGeneration
);
// Pass the isStreaming value directly to the hook as the enabled parameter
const { displayText } = useTypewriter(text, 20, isStreaming || false);
// Since the hook now handles both states, we can simply return displayText
return <span>{displayText}</span>;
};
export default MiniTypeWriter;

View file

@ -1,38 +0,0 @@
import { PresentationGenerationApi } from "../../services/api/presentation-generation";
import { getStaticFileUrl } from "../../utils/others";
import MiniTypeWriter from "./MiniTypeWriter";
interface Type1MiniProps {
title: string;
description: string;
image: string;
}
const Type1Mini = ({ title, description, image }: Type1MiniProps) => {
const updatedImage = getStaticFileUrl(image);
return (
<div className="slide-container w-full aspect-video bg-white p-2 flex items-center justify-center rounded-lg text-[6px] border shadow-xl">
<div className="grid grid-cols-2 gap-2 h-full">
<div className="flex flex-col justify-center space-y-1">
<div className="font-semibold text-[10px] line-clamp-2 slide-title ">
<MiniTypeWriter text={title} />
</div>
<div className="text-gray-600 text-[8px] line-clamp-3 slide-description">
<MiniTypeWriter text={description} />
</div>
</div>
<div className="bg-gray-100 rounded-sm overflow-hidden">
{image && (
<img
src={updatedImage}
alt={title}
className="w-full h-full object-cover"
/>
)}
</div>
</div>
</div>
);
};
export default Type1Mini;

View file

@ -1,94 +0,0 @@
import { RootState } from "@/store/store";
import { useSelector } from "react-redux";
import MiniTypeWriter from "./MiniTypeWriter";
interface Type2MiniProps {
title: string;
body: Array<{
heading: string;
description: string;
}>;
design_index: number;
}
const Type2Mini = ({ title, body, design_index }: Type2MiniProps) => {
const isGridLayout = body.length === 4;
const { currentColors } = useSelector((state: RootState) => state.theme);
const getGridCols = (length: number) => {
switch (length) {
case 1: return 'grid-cols-1';
case 2: return 'grid-cols-2';
case 3: return 'grid-cols-3';
case 4: return 'grid-cols-4';
// Add more cases as needed
default: return 'grid-cols-1';
}
}
return (
<div className="slide-container w-full aspect-video flex flex-col justify-center items-center bg-white p-2 rounded-lg text-[6px] border shadow-xl">
<div className="text-center mb-2">
<div className="font-semibold text-[10px] line-clamp-2 slide-title">
<MiniTypeWriter text={title} />
</div>
</div>
{design_index === 3 ? (
<div className="flex flex-col mt-2 ">
<div className="flex justify-between w-[85%] mx-auto items-center mb-1">
<div className="absolute w-[70%] h-[1px] "
style={{
backgroundColor: currentColors.iconBg
}}
/>
{body.map((_, index) => (
<div
style={{
backgroundColor: currentColors.iconBg
}}
key={index} className="w-2 h-2 rounded-full text-white flex items-center justify-center text-[4px] z-10">
{index + 1}
</div>
))}
</div>
<div className="flex justify-between gap-1">
{body.map((item, index) => (
<div key={index} className="flex-1 text-center">
<div className="truncate font-medium slide-heading">
<MiniTypeWriter text={item.heading} />
</div>
<div className="text-gray-600 line-clamp-2 text-[4px] slide-description">
<MiniTypeWriter text={item.description} />
</div>
</div>
))}
</div>
</div>
) : (
<div className={isGridLayout ? 'grid grid-cols-2 gap-1 ' : `grid ${getGridCols(body.length)} gap-1 `}>
{body.map((item, index) => (
<div
key={index}
className={`w-full ${design_index === 2 ? 'bg-gray-50 rounded-sm p-1 slide-box' : ''} `}
>
{design_index === 2 && (
<div
style={{
color: currentColors.iconBg
}}
className=" text-[5px] font-semibold">0{index + 1}</div>
)}
<div className="truncate font-medium slide-heading">
<MiniTypeWriter text={item.heading} />
</div>
<div className="text-gray-600 line-clamp-2 text-[4px] slide-description">
<MiniTypeWriter text={item.description} />
</div>
</div>
))}
</div>
)}
</div>
);
};
export default Type2Mini;

View file

@ -1,67 +0,0 @@
import { getStaticFileUrl } from "../../utils/others";
import MiniTypeWriter from "./MiniTypeWriter";
interface Type4MiniProps {
title: string;
body: Array<{
heading: string;
description: string;
}>;
images: string[];
}
const Type4Mini = ({ title, body, images }: Type4MiniProps) => {
const updatedImages = images.map((image) => {
if (image.startsWith("user")) {
return getStaticFileUrl(image);
}
return getStaticFileUrl(image);
});
const getGridCols = (length: number) => {
switch (length) {
case 1: return 'grid-cols-1';
case 2: return 'grid-cols-2';
case 3: return 'grid-cols-3';
case 4: return 'grid-cols-4';
// Add more cases as needed
default: return 'grid-cols-1';
}
}
return (
<div className="slide-container w-full aspect-video bg-white p-2 flex flex-col items-center justify-center rounded-lg text-[6px] border shadow-xl">
<div className="text-center mb-1">
<div className="font-semibold text-[10px] slide-title truncate">
<MiniTypeWriter text={title} />
</div>
</div>
<div className={`grid ${getGridCols(body.length)} gap-1`}>
{body.map((item, index) => (
<div
key={index}
className="bg-gray-50 shadow-md p-1 rounded-sm overflow-hidden slide-box"
>
<div className="h-12 w-full">
{updatedImages && updatedImages[index] && (
<img
src={updatedImages[index]}
alt={item.heading}
className="w-full h-full object-cover"
/>
)}
</div>
<div className="p-[2px]">
<div className=" text-[8px] font-medium line-clamp-2 leading-3 pb-1 slide-heading">
<MiniTypeWriter text={item.heading} />
</div>
<div className="text-gray-600 text-[5px] slide-description">
<MiniTypeWriter text={item.description} />
</div>
</div>
</div>
))}
</div>
</div>
);
};
export default Type4Mini;

View file

@ -1,42 +0,0 @@
import MiniCharts from "./MiniCharts";
import MiniTypeWriter from "./MiniTypeWriter";
interface Type5MiniProps {
title: string;
description: string;
chartData: any;
slideIndex: number;
isFullSizeGraph: boolean;
}
const Type5Mini = ({ title, description, chartData, slideIndex, isFullSizeGraph }: Type5MiniProps) => {
return (
<div className="slide-container w-full aspect-video bg-white p-2 flex flex-col justify-center rounded-lg text-[6px] border shadow-xl">
<div className="text-center mb-2">
<div className="font-semibold text-[10px] text-start slide-title truncate">
<MiniTypeWriter text={title} />
</div>
</div>
<div className={`flex gap-2 w-full items-center ${isFullSizeGraph ? ' flex-col ' : ''} `}>
<div className={` w-[80%]`}>
<MiniCharts chartData={chartData} />
</div>
{/* <div className="w-full h-full">
</div> */}
<div className="w-full text-gray-600 text-[8px] line-clamp-6 slide-description">
<MiniTypeWriter text={description} />
</div>
</div>
{/* <div className="grid grid-cols-2 gap-2">
<div className="text-gray-600 text-[8px] line-clamp-6 slide-description">{description}</div>
</div> */}
</div>
);
};
export default Type5Mini;

View file

@ -1,49 +0,0 @@
import { RootState } from "@/store/store";
import { useSelector } from "react-redux";
import MiniTypeWriter from "./MiniTypeWriter";
interface Type6MiniProps {
title: string;
description: string;
body: Array<{
heading: string;
description: string;
}>;
}
const Type6Mini = ({ title, description, body }: Type6MiniProps) => {
const { currentColors } = useSelector((state: RootState) => state.theme);
return (
<div className="slide-container w-full flex items-center aspect-video bg-white p-2 rounded-lg text-[6px] border shadow-xl">
<div className="grid grid-cols-2 gap-2 items-center h-full">
<div className="space-y-1">
<div className="font-semibold text-[10px] slide-title">
<MiniTypeWriter text={title} />
</div>
<div className="text-gray-600 text-[8px] line-clamp-3 slide-description">
<MiniTypeWriter text={description} />
</div>
</div>
<div className="space-y-[3px]">
{body.map((item, index) => (
<div key={index} className="bg-gray-50 rounded-sm p-[2px] slide-box">
<div
style={{
color: currentColors.iconBg
}}
className=" text-[5px] font-semibold">0{index + 1}</div>
<div className="truncate font-medium slide-heading">
<MiniTypeWriter text={item.heading} />
</div>
<div className="text-gray-600 text-[4px] slide-description">
<MiniTypeWriter text={item.description} />
</div>
</div>
))}
</div>
</div>
</div>
);
};
export default Type6Mini;

View file

@ -1,59 +0,0 @@
import { useSelector } from "react-redux";
import { PresentationGenerationApi } from "../../services/api/presentation-generation";
import { RootState } from "@/store/store";
import MiniTypeWriter from "./MiniTypeWriter";
import { getStaticFileUrl } from "../../utils/others";
interface Type7MiniProps {
title: string;
body: Array<{
heading: string;
description: string;
}>;
icons: string[];
}
const Type7Mini = ({ title, body, icons }: Type7MiniProps) => {
const { currentColors } = useSelector((state: RootState) => state.theme);
const isGridLayout = body.length === 4;
const updatedIcons = icons.map((icon) => {
return getStaticFileUrl(icon);
});
return (
<div className="slide-container w-full aspect-video bg-white p-2 flex flex-col justify-center items-center rounded-lg text-[6px] border shadow-xl">
<div className="text-center mb-1">
<div className="font-semibold text-[10px] slide-title truncate">
<MiniTypeWriter text={title} />
</div>
</div>
<div className={isGridLayout ? "grid grid-cols-2 gap-1" : "flex gap-1"}>
{body.map((item, index) => (
<div key={index} className="bg-gray-50 rounded-sm p-1 slide-box">
<div
className="w-2 h-2 mb-1 rounded-sm flex items-center justify-center"
style={{ backgroundColor: currentColors.iconBg }}
>
{updatedIcons && updatedIcons[index] && (
<img
src={updatedIcons[index]}
alt={item.heading}
className="w-1 h-1 object-contain"
/>
)}
</div>
<div className="truncate font-medium slide-heading">
<MiniTypeWriter text={item.heading} />
</div>
<div className="text-gray-600 text-[4px] slide-description">
<MiniTypeWriter text={item.description} />
</div>
</div>
))}
</div>
</div>
);
};
export default Type7Mini;

View file

@ -1,68 +0,0 @@
import { useSelector } from "react-redux";
import { RootState } from "@/store/store";
import { PresentationGenerationApi } from "../../services/api/presentation-generation";
import MiniTypeWriter from "./MiniTypeWriter";
import { getStaticFileUrl } from "../../utils/others";
interface Type8MiniProps {
title: string;
description: string;
body: Array<{
heading: string;
description: string;
}>;
icons: string[];
}
const Type8Mini = ({ title, description, body, icons }: Type8MiniProps) => {
const { currentColors } = useSelector((state: RootState) => state.theme);
const updatedIcons = icons.map((icon) => {
return getStaticFileUrl(icon);
});
return (
<div className="slide-container w-full aspect-video bg-white p-2 flex flex-col justify-center items-center rounded-lg text-[6px] border shadow-xl">
<div className="grid grid-cols-2 gap-2 items-center h-full">
<div className="space-y-1">
<div className="font-semibold text-[10px] slide-title">
<MiniTypeWriter text={title} />
</div>
<div className="text-gray-600 text-[8px] line-clamp-3 slide-description">
<MiniTypeWriter text={description} />
</div>
</div>
<div className="space-y-1">
{body.map((item, index) => (
<div
key={index}
className="flex gap-1 bg-gray-50 rounded-sm p-1 slide-box"
>
<div
className="w-2 h-2 rounded-sm flex items-center justify-center"
style={{ backgroundColor: currentColors.iconBg }}
>
{updatedIcons && updatedIcons[index] && (
<img
src={updatedIcons[index]}
alt={item.heading}
className="w-1 h-1 object-contain"
/>
)}
</div>
<div>
<div className="truncate font-medium slide-heading">
<MiniTypeWriter text={item.heading} />
</div>
<div className="text-gray-600 text-[4px] slide-description">
<MiniTypeWriter text={item.description} />
</div>
</div>
</div>
))}
</div>
</div>
</div>
);
};
export default Type8Mini;

View file

@ -1,49 +0,0 @@
import { useSelector } from "react-redux";
import MiniCharts from "./MiniCharts";
import { RootState } from "@/store/store";
import MiniTypeWriter from "./MiniTypeWriter";
interface Type9MiniProps {
title: string;
body: Array<{
heading: string;
description: string;
}>;
chartData: any;
slideIndex: number;
}
const Type9Mini = ({ title, body, chartData, slideIndex }: Type9MiniProps) => {
const { currentColors } = useSelector((state: RootState) => state.theme);
return (
<div className="slide-container w-full aspect-video bg-white p-2 overflow-hidden flex flex-col justify-center items-center rounded-lg text-[6px] border shadow-xl">
<div className="grid grid-cols-2 gap-2 h-full items-center">
<div className="space-y-1">
<div className="font-semibold text-[10px] slide-title">
<MiniTypeWriter text={title} />
</div>
<MiniCharts chartData={chartData} />
</div>
<div className="space-y-1">
{body && body.length > 0 && body.map((item, index) => (
<div key={index} className="bg-gray-50 rounded-sm p-1 slide-box">
<div
style={{
color: currentColors.iconBg
}}
className=" text-[5px] font-semibold">0{index + 1}</div>
<div className="truncate font-medium slide-heading">
<MiniTypeWriter text={item.heading} />
</div>
<div className="text-gray-600 line-clamp-1 text-[4px] slide-description">
<MiniTypeWriter text={item.description} />
</div>
</div>
))}
</div>
</div>
</div>
);
};
export default Type9Mini;

View file

@ -1,7 +1,5 @@
import { Slide } from "../types/slide";
import Type1Mini from "./mini-slides/Type1Mini";
import Type4Mini from "./mini-slides/Type4Mini";
import Type2Mini from "./mini-slides/Type2Mini";
import Type1Layout from "./slide_layouts/Type1Layout";
import Type2Layout from "./slide_layouts/Type2Layout";
import Type4Layout from "./slide_layouts/Type4Layout";
@ -10,11 +8,7 @@ import Type6Layout from "./slide_layouts/Type6Layout";
import Type7Layout from "./slide_layouts/Type7Layout";
import Type8Layout from "./slide_layouts/Type8Layout";
import Type9Layout from "./slide_layouts/Type9Layout";
import Type7Mini from "./mini-slides/Type7Mini";
import Type6Mini from "./mini-slides/Type6Mini";
import Type5Mini from "./mini-slides/Type5Mini";
import Type9Mini from "./mini-slides/Type9Mini";
import Type8Mini from "./mini-slides/Type8Mini";
import { Chart, ChartSettings } from "@/store/slices/presentationGeneration";
@ -154,91 +148,7 @@ export const renderSlideContent = (slide: Slide, language: string) => {
}
};
export const renderMiniSlideContent = (slide: Slide) => {
const { type, content } = slide;
switch (type) {
case 1:
return (
<Type1Mini
title={content.title}
description={
typeof slide.content.body === "string"
? slide.content.body
: slide.content.body[0]?.description || ""
}
image={slide.images?.[0] || ""}
/>
);
case 2:
return (
<Type2Mini
title={slide.content.title}
body={Array.isArray(slide.content.body) ? slide.content.body : []}
design_index={slide.design_index || 2}
/>
);
case 4:
return (
<Type4Mini
title={slide.content.title}
body={Array.isArray(slide.content.body) ? slide.content.body : []}
images={slide.images || []}
/>
);
case 5:
const isFullSizeGraph =
slide.content.graph?.data.categories.length > 4 &&
slide.content.graph.type !== "pie";
return (
<Type5Mini
title={slide.content.title}
isFullSizeGraph={isFullSizeGraph}
description={(slide.content.body as string) || ""}
chartData={slide.content.graph!}
slideIndex={slide.index}
/>
);
case 6:
return (
<Type6Mini
title={slide.content.title}
description={slide.content.description || ""}
body={Array.isArray(slide.content.body) ? slide.content.body : []}
/>
);
case 7:
return (
<Type7Mini
title={slide.content.title}
body={Array.isArray(slide.content.body) ? slide.content.body : []}
icons={slide.icons || []}
/>
);
case 8:
return (
<Type8Mini
title={slide.content.title}
description={slide.content.description || ""}
body={Array.isArray(slide.content.body) ? slide.content.body : []}
icons={slide.icons || []}
/>
);
case 9:
return (
<Type9Mini
title={slide.content.title}
// @ts-ignore
body={slide.content.body}
chartData={slide.content.graph!}
slideIndex={slide.index}
/>
);
default:
return null;
}
};
// CHART RENDERING
export const renderChart = (

View file

@ -1,18 +0,0 @@
import React from 'react'
import { Skeleton } from '@/components/ui/skeleton'
const loading = () => {
return (
<div>
<Skeleton className='h-24 w-full' />
<div className="flex flex-col max-w-7xl mx-auto gap-6 my-10 justify-center items-center ">
{
Array.from({ length: 10 }).map((_, index) => (
<Skeleton key={index} className="animate-pulse aspect-video w-full" />
))
}
</div>
</div>
)
}
export default loading

View file

@ -47,7 +47,7 @@ import Modal from "./Modal";
import Announcement from "@/components/Announcement";
import { getFontLink, getStaticFileUrl } from "../../utils/others";
import path from "path";
const Header = ({
presentation_id,
@ -132,7 +132,7 @@ const Header = ({
const metadata = await (await fetch('/api/slide-metadata', {
method: 'POST',
body: JSON.stringify({
url: 'http://localhost/presentation?id=' + presentation_id,
url: 'http://localhost/pdf-maker?id=' + presentation_id,
theme: currentTheme,
customColors: currentColors,
})

View file

@ -24,7 +24,7 @@ import * as htmlToImage from "html-to-image";
import { setPresentationData } from "@/store/slices/presentationGeneration";
import { SortableSlide } from "./SortableSlide";
import { SortableListItem } from "./SortableListItem";
import { renderMiniSlideContent } from "../../components/slide_config";
import { renderSlideContent } from "../../components/slide_config";
interface SidePanelProps {
selectedSlide: number;
@ -156,18 +156,6 @@ const SidePanel = ({
) {
return null;
// <div className="space-y-4 ">
// <div className="flex items-center gap-2">
// <div className="w-4 h-4 rounded-lg animate-pulse bg-gray-200" />
// <div className="w-full h-2 rounded-lg animate-pulse bg-gray-200" />
// </div>
// {Array.from({ length: 8 }).map((_, index) => (
// <div key={index} className="animate-pulse">
// <div className="w-full aspect-video bg-gray-200 rounded-lg" />
// </div>
// ))}
// </div>
}
return (
@ -206,10 +194,9 @@ const SidePanel = ({
fixed xl:relative h-full z-50 xl:z-auto
transition-all duration-300 ease-in-out
${isOpen ? "ml-0" : "-ml-[300px]"}
${
isMobilePanelOpen
? "translate-x-0"
: "-translate-x-full xl:translate-x-0"
${isMobilePanelOpen
? "translate-x-0"
: "-translate-x-full xl:translate-x-0"
}
`}
>
@ -230,34 +217,30 @@ const SidePanel = ({
<div className="flex items-center justify-start gap-4">
<ToolTip content="Image Preview">
<Button
className={`${
active === "grid"
? "bg-[#5141e5] hover:bg-[#4638c7]"
: "bg-white hover:bg-white"
}`}
className={`${active === "grid"
? "bg-[#5141e5] hover:bg-[#4638c7]"
: "bg-white hover:bg-white"
}`}
onClick={() => setActive("grid")}
>
<LayoutList
className={`${
active === "grid" ? "text-white" : "text-black"
}`}
className={`${active === "grid" ? "text-white" : "text-black"
}`}
size={20}
/>
</Button>
</ToolTip>
<ToolTip content="List Preview">
<Button
className={`${
active === "list"
? "bg-[#5141e5] hover:bg-[#4638c7]"
: "bg-white hover:bg-white"
}`}
className={`${active === "list"
? "bg-[#5141e5] hover:bg-[#4638c7]"
: "bg-white hover:bg-white"
}`}
onClick={() => setActive("list")}
>
<ListTree
className={`${
active === "list" ? "text-white" : "text-black"
}`}
className={`${active === "list" ? "text-white" : "text-black"
}`}
size={20}
/>
</Button>
@ -323,8 +306,18 @@ const SidePanel = ({
{isStreaming ? (
presentationData &&
presentationData?.slides.map((slide, index) => (
<div key={`${index}-${slide.type}-${slide.id}`}>
{renderMiniSlideContent(slide)}
<div
key={`${slide.id}-${index}`}
onClick={() => onSlideClick(index)}
className={` cursor-pointer ring-2 p-1 rounded-md transition-all duration-200 ${selectedSlide === index ? ' ring-[#5141e5]' : 'ring-gray-200'
}`}
>
<div className=" bg-white relative overflow-hidden aspect-video">
<div className="absolute bg-gray-100/5 z-40 top-0 left-0 w-full h-full" />
<div className="transform scale-[0.2] flex justify-center items-center origin-top-left w-[500%] h-[500%]">
{renderSlideContent(slide, 'English')}
</div>
</div>
</div>
))
) : (

View file

@ -116,7 +116,7 @@ const SlideContent = ({
<>
<div
id={`slide-${isStreaming ? index : slide.index}`}
className=" w-full max-w-[1280px] flex items-center max-md:mb-4 justify-center relative"
className=" w-full max-w-[1280px] main-slide flex items-center max-md:mb-4 justify-center relative"
>
{isStreaming && (
<Loader2 className="w-8 h-8 absolute right-2 top-2 z-30 text-blue-800 animate-spin" />

View file

@ -1,6 +1,6 @@
import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { renderMiniSlideContent } from '../../components/slide_config';
import { renderSlideContent } from '../../components/slide_config';
import { Slide } from '../../types/slide';
import { useState } from 'react';
@ -51,12 +51,15 @@ export function SortableSlide({ slide, index, selectedSlide, onSlideClick }: Sor
{...listeners}
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
className={`flex justify-center items-center cursor-pointer ${selectedSlide === index
? 'ring-2 ring-[#5141e5]'
: 'hover:ring-2 hover:ring-gray-200'
} rounded-lg`}
className={` cursor-pointer border-[3px] p-1 shadow-lg rounded-md transition-all duration-200 ${selectedSlide === index ? ' border-[#5141e5]' : 'border-color'
}`}
>
{renderMiniSlideContent(slide)}
<div className=" slide-box relative overflow-hidden aspect-video">
<div className="absolute bg-transparent z-40 top-0 left-0 w-full h-full" />
<div className="transform scale-[0.2] flex justify-center items-center origin-top-left w-[500%] h-[500%]">
{renderSlideContent(slide, 'English')}
</div>
</div>
</div>
);
}

View file

@ -19,9 +19,9 @@ const page = () => {
);
}
return (
<FooterProvider>
<PresentationPage presentation_id={queryId} />
</FooterProvider>
<PresentationPage presentation_id={queryId} />
);
};
export default page;

View file

@ -99,6 +99,9 @@
transition: background-color 0.3s ease, color 0.3s ease;
}
.slide-theme .border-color{
border-color: var(--slide-box);
}
@keyframes progress {

View file

@ -1,7 +1,7 @@
import React from "react";
import { Card } from "@/components/ui/card";
import { DashboardApi, PresentationResponse } from "../api/dashboard";
import { DashboardApi } from "../api/dashboard";
import { DotsVerticalIcon, TrashIcon } from "@radix-ui/react-icons";
import {
Popover,
@ -10,21 +10,25 @@ import {
} from "@/components/ui/popover";
import { useRouter } from "next/navigation";
import { toast } from "@/hooks/use-toast";
import { PresentationGenerationApi } from "@/app/(presentation-generator)/services/api/presentation-generation";
import { getStaticFileUrl } from "@/app/(presentation-generator)/utils/others";
import { renderSlideContent } from "@/app/(presentation-generator)/components/slide_config";
export const PresentationCard = ({
id,
title,
created_at,
thumbnail,
type,
}: PresentationResponse & { type: "video" | "slide" }) => {
theme,
}: {
id: string;
title: string;
created_at: string;
thumbnail: string;
theme: any;
}) => {
const router = useRouter();
const handlePreview = (e: React.MouseEvent) => {
e.preventDefault();
router.push(`/presentation?id=${id}`);
};
@ -38,7 +42,7 @@ export const PresentationCard = ({
variant: "default",
});
const response = await DashboardApi.deletePresentation(id);
console.log(response);
if (response) {
toast({
title: "Presentation deleted",
@ -55,10 +59,25 @@ export const PresentationCard = ({
window.location.reload();
};
const themeName = theme.name;
// Create CSS variables object
const cssVariables = {
'--slide-bg': theme.colors.slideBg,
'--slide-title': theme.colors.slideTitle,
'--slide-heading': theme.colors.slideHeading,
'--slide-description': theme.colors.slideDescription,
'--slide-box': theme.colors.slideBox,
'--icon-bg': theme.colors.iconBg,
'--background': theme.colors.background,
'--font-family': theme.colors.fontFamily,
} as React.CSSProperties;
return (
<Card
onClick={handlePreview}
className="bg-white rounded-[8px] cursor-pointer overflow-hidden p-4"
data-theme={themeName}
className="bg-white rounded-[8px] slide-theme cursor-pointer overflow-hidden p-4"
style={cssVariables}
>
<div className="space-y-4">
{/* Date */}
@ -83,7 +102,7 @@ export const PresentationCard = ({
</div>
{/* Thumbnail */}
<div className="relative border-2 border-gray-200 aspect-[16/9] rounded-[8px] overflow-hidden">
{/* <div className="relative border-2 border-gray-200 aspect-[16/9] rounded-[8px] overflow-hidden">
{thumbnail ? (
<img
src={getStaticFileUrl(thumbnail)}
@ -100,69 +119,48 @@ export const PresentationCard = ({
</p>
</div>
)}
</div> */}
<div className=" slide-box relative overflow-hidden border aspect-video"
style={{
}}
>
<div className="absolute bg-transparent z-40 top-0 left-0 w-full h-full" />
<div className="transform scale-[0.2] flex justify-center items-center origin-top-left w-[500%] h-[500%]">
{renderSlideContent({
id: 'mock-slide-1',
type: 1,
index: 0,
design_index: 1,
properties: null,
images: ['/static/user_data/ee7cb066-86d0-45fc-adc9-15bf565eab30/images/af54ed41-483e-4983-aef0-b254aac48408.jpg'],
icons: [],
graph_id: null,
presentation: id,
content: {
title: title || 'Sample Presentation',
body: "This is a sample slide description to demonstrate the layout and styling. The content here helps visualize how actual presentation content would appear.",
infographics: [],
image_prompts: ['Sample image showing business growth']
},
}, 'English')}
</div>
</div>
{/* Icon and Title */}
<div className="flex items-center gap-2 pb-1">
{type === "video" ? (
<svg
xmlns="http://www.w3.org/2000/svg"
className="w-full h-full max-w-[20px] max-h-[20px]"
viewBox="0 0 24 24"
fill="none"
>
<path
d="M22 15V9C22 4 20 2 15 2H9C4 2 2 4 2 9V15C2 20 4 22 9 22H15C20 22 22 20 22 15Z"
stroke="black"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M2.52002 7.10938H21.48"
stroke="black"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M8.52002 2.10938V6.96937"
stroke="black"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M15.48 2.10938V6.51937"
stroke="black"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M9.75 14.4501V13.2501C9.75 11.7101 10.84 11.0801 12.17 11.8501L13.21 12.4501L14.25 13.0501C15.58 13.8201 15.58 15.0801 14.25 15.8501L13.21 16.4501L12.17 17.0501C10.84 17.8201 9.75 17.1901 9.75 15.6501V14.4501V14.4501Z"
stroke="black"
strokeWidth="1.5"
strokeMiterlimit="10"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
) : (
<svg
xmlns="http://www.w3.org/2000/svg"
className="w-full h-full max-w-[20px] max-h-[20px]"
viewBox="0 0 24 24"
fill="none"
>
<path
d="M15.75 0.75V6C15.75 6.42 16.08 6.75 16.5 6.75H21.75M9.75 17.25H7.5C7.08 17.25 6.75 16.92 6.75 16.5V12C6.75 11.58 7.08 11.25 7.5 11.25H13.5C13.92 11.25 14.25 11.58 14.25 12V14.25M21.75 6.3V22.5C21.75 22.92 21.42 23.25 21 23.25H3C2.58 23.25 2.25 22.92 2.25 22.5V1.5C2.25 1.08 2.58 0.75 3 0.75H16.275C16.47 0.75 16.665 0.825 16.815 0.975L21.54 5.775C21.675 5.925 21.75 6.105 21.75 6.3ZM10.5 14.25H16.5C16.92 14.25 17.25 14.58 17.25 15V19.5C17.25 19.92 16.92 20.25 16.5 20.25H10.5C10.08 20.25 9.75 19.92 9.75 19.5V15C9.75 14.58 10.08 14.25 10.5 14.25Z"
stroke="black"
strokeWidth="1.5"
/>
</svg>
)}
<svg
xmlns="http://www.w3.org/2000/svg"
className="w-full h-full max-w-[20px] max-h-[20px]"
viewBox="0 0 24 24"
fill="none"
>
<path
d="M15.75 0.75V6C15.75 6.42 16.08 6.75 16.5 6.75H21.75M9.75 17.25H7.5C7.08 17.25 6.75 16.92 6.75 16.5V12C6.75 11.58 7.08 11.25 7.5 11.25H13.5C13.92 11.25 14.25 11.58 14.25 12V14.25M21.75 6.3V22.5C21.75 22.92 21.42 23.25 21 23.25H3C2.58 23.25 2.25 22.92 2.25 22.5V1.5C2.25 1.08 2.58 0.75 3 0.75H16.275C16.47 0.75 16.665 0.825 16.815 0.975L21.54 5.775C21.675 5.925 21.75 6.105 21.75 6.3ZM10.5 14.25H16.5C16.92 14.25 17.25 14.58 17.25 15V19.5C17.25 19.92 16.92 20.25 16.5 20.25H10.5C10.08 20.25 9.75 19.92 9.75 19.5V15C9.75 14.58 10.08 14.25 10.5 14.25Z"
stroke="black"
strokeWidth="1.5"
/>
</svg>
<p className="text-[#667085] text-sm ml-1 line-clamp-2 font-roboto">
{title}
</p>

View file

@ -102,7 +102,8 @@ export const PresentationGrid = ({
<PresentationCard
key={presentation.id}
{...presentation}
type={type}
theme={presentation.theme}
/>
))}
</div>

View file

@ -4,6 +4,7 @@ import { Fraunces, Montserrat, Inria_Serif, Roboto, Instrument_Sans } from "next
import "./globals.css";
import { Providers } from "./providers";
import { Toaster } from "@/components/ui/toaster";
import { FooterProvider } from "./(presentation-generator)/context/footerContext";
const fraunces = Fraunces({
subsets: ["latin"],
@ -100,7 +101,12 @@ export default function RootLayout({
<body
className={`$ ${inter.variable} ${fraunces.variable} ${montserrat.variable} ${inria_serif.variable} ${roboto.variable} ${instrument_sans.variable} antialiased`}
>
<Providers>{children}</Providers>
<Providers>
<FooterProvider>
{children}
</FooterProvider>
</Providers>
<Toaster />
</body>
</html>