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:
parent
31f47bffca
commit
a95e8c148f
22 changed files with 126 additions and 738 deletions
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 = (
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
))
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -19,9 +19,9 @@ const page = () => {
|
|||
);
|
||||
}
|
||||
return (
|
||||
<FooterProvider>
|
||||
<PresentationPage presentation_id={queryId} />
|
||||
</FooterProvider>
|
||||
|
||||
<PresentationPage presentation_id={queryId} />
|
||||
|
||||
);
|
||||
};
|
||||
export default page;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -102,7 +102,8 @@ export const PresentationGrid = ({
|
|||
<PresentationCard
|
||||
key={presentation.id}
|
||||
{...presentation}
|
||||
type={type}
|
||||
theme={presentation.theme}
|
||||
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue