From 13cf2eb5c7636ed1cc21707f58b5c7df801e20c4 Mon Sep 17 00:00:00 2001 From: shiva raj badu Date: Thu, 17 Jul 2025 00:37:37 +0545 Subject: [PATCH] chore(Nextjs) --- .../components/PresentationPage.tsx | 6 +- .../presentation/components/SidePanel.tsx | 157 ++++++---- .../presentation/components/SlideContent.tsx | 49 +-- .../components/SortableListItem.tsx | 4 +- .../presentation/components/SortableSlide.tsx | 4 +- .../store/themeSlice.ts | 4 +- .../layouts/BulletPointSlideLayout.tsx | 137 +++----- .../components/layouts/CardSlideLayout.tsx | 141 +++++++++ .../layouts/ComparisonSlideLayout.tsx | 173 +++++++++++ .../layouts/ConclusionSlideLayout.tsx | 250 --------------- .../components/layouts/ContentSlideLayout.tsx | 109 +++---- .../components/layouts/FirstSlideLayout.tsx | 120 +++---- .../components/layouts/IconSlideLayout.tsx | 109 +++++++ .../components/layouts/ImageSlideLayout.tsx | 115 +++++++ .../layouts/NumberBoxSlideLayout.tsx | 114 +++++++ .../components/layouts/ProcessSlideLayout.tsx | 237 ++++++-------- .../components/layouts/QuoteSlideLayout.tsx | 181 ++++------- .../layouts/StatisticsSlideLayout.tsx | 97 +++--- .../components/layouts/TeamSlideLayout.tsx | 216 ++++++------- .../layouts/TimelineSlideLayout.tsx | 292 +++++++----------- .../components/layouts/Type1SlideLayout.tsx | 65 ++++ .../layouts/Type2NumberedSlideLayout.tsx | 124 ++++++++ .../components/layouts/Type2SlideLayout.tsx | 111 +++++++ .../layouts/Type2TimelineSlideLayout.tsx | 102 ++++++ .../components/layouts/Type3SlideLayout.tsx | 119 +++++++ .../components/layouts/Type4SlideLayout.tsx | 71 +++++ .../components/layouts/Type5SlideLayout.tsx | 102 ++++++ .../components/layouts/Type6SlideLayout.tsx | 150 +++++++++ .../components/layouts/Type7SlideLayout.tsx | 173 +++++++++++ .../components/layouts/Type8SlideLayout.tsx | 167 ++++++++++ .../components/layouts/defaultSchemes.ts | 2 +- .../store/slices/presentationGeneration.ts | 6 +- 32 files changed, 2518 insertions(+), 1189 deletions(-) create mode 100644 servers/nextjs/components/layouts/CardSlideLayout.tsx create mode 100644 servers/nextjs/components/layouts/ComparisonSlideLayout.tsx delete mode 100644 servers/nextjs/components/layouts/ConclusionSlideLayout.tsx create mode 100644 servers/nextjs/components/layouts/IconSlideLayout.tsx create mode 100644 servers/nextjs/components/layouts/ImageSlideLayout.tsx create mode 100644 servers/nextjs/components/layouts/NumberBoxSlideLayout.tsx create mode 100644 servers/nextjs/components/layouts/Type1SlideLayout.tsx create mode 100644 servers/nextjs/components/layouts/Type2NumberedSlideLayout.tsx create mode 100644 servers/nextjs/components/layouts/Type2SlideLayout.tsx create mode 100644 servers/nextjs/components/layouts/Type2TimelineSlideLayout.tsx create mode 100644 servers/nextjs/components/layouts/Type3SlideLayout.tsx create mode 100644 servers/nextjs/components/layouts/Type4SlideLayout.tsx create mode 100644 servers/nextjs/components/layouts/Type5SlideLayout.tsx create mode 100644 servers/nextjs/components/layouts/Type6SlideLayout.tsx create mode 100644 servers/nextjs/components/layouts/Type7SlideLayout.tsx create mode 100644 servers/nextjs/components/layouts/Type8SlideLayout.tsx diff --git a/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationPage.tsx b/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationPage.tsx index dde9eebc..d398fdd7 100644 --- a/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationPage.tsx +++ b/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationPage.tsx @@ -107,7 +107,7 @@ const PresentationPage = ({ presentation_id }: { presentation_id: string }) => { !isInitialLoad.current && presentationData.slides && presentationData.slides.some( - (slide) => slide.images && slide.images.length > 0 + (slide: any) => slide.images && slide.images.length > 0 ) ) { @@ -245,7 +245,7 @@ const PresentationPage = ({ presentation_id }: { presentation_id: string }) => { }; }, []); // Function to scroll to specific slide - const handleSlideClick = (index: number) => { + const handleSlideClick = (index: any) => { const slideElement = document.getElementById(`slide-${index}`); if (slideElement) { slideElement.scrollIntoView({ @@ -416,7 +416,7 @@ const PresentationPage = ({ presentation_id }: { presentation_id: string }) => { {presentationData && presentationData.slides && presentationData.slides.length > 0 && - presentationData.slides.map((slide, index) => ( + presentationData.slides.map((slide: any, index: number) => ( state.theme ); + console.log('presentationData', presentationData) const dispatch = useDispatch(); const { getLayout } = useLayoutCache(); @@ -90,10 +91,10 @@ const SidePanel = ({ if (active.id !== over.id) { // Find the indices of the dragged and target items const oldIndex = presentationData?.slides.findIndex( - (item) => item.id === active.id + (item: any) => item.id === active.id ); const newIndex = presentationData?.slides.findIndex( - (item) => item.id === over.id + (item: any) => item.id === over.id ); // Reorder the array @@ -104,7 +105,7 @@ const SidePanel = ({ ); // Update indices of all slides - const updatedArray = reorderedArray.map((slide, index) => ({ + const updatedArray = reorderedArray.map((slide: any, index: number) => ({ ...slide, index: index, })); @@ -128,66 +129,96 @@ const SidePanel = ({ return ( <> - {/* Mobile Toggle Button */} -
- -
+ {/* Desktop Toggle Button - Always visible when panel is closed */} + {!isOpen && ( +
+ + + +
+ )} - {/* Backdrop for mobile */} - {isMobilePanelOpen && ( -
setIsMobilePanelOpen(false)} - /> + {/* Mobile Toggle Button */} + {!isMobilePanelOpen && ( +
+ + + +
)}
-
- {/* Header */} -
-

Slides

-
- - - - - - - + + + + +
+ - - + className="text-[#6c7081] cursor-pointer hover:text-gray-600" + size={20} + />
@@ -201,7 +232,7 @@ const SidePanel = ({
{isStreaming ? ( presentationData && - presentationData?.slides.map((slide, index) => ( + presentationData?.slides.map((slide: any, index: number) => (
slide.id!) || [] + presentationData?.slides.map((slide: any) => slide.id!) || [] } strategy={verticalListSortingStrategy} >
{presentationData && - presentationData?.slides.map((slide, index) => ( + presentationData?.slides.map((slide: any, index: number) => ( {isStreaming ? ( presentationData && - presentationData?.slides.map((slide, index) => ( + presentationData?.slides.map((slide: any, index: number) => (
onSlideClick(index)} @@ -261,12 +292,12 @@ const SidePanel = ({ ) : ( slide.id!) || [] + presentationData?.slides.map((slide: any) => slide.id!) || [] } strategy={verticalListSortingStrategy} > {presentationData && - presentationData?.slides.map((slide, index) => ( + presentationData?.slides.map((slide: any, index: number) => ( { + const newSlideElement = document.getElementById(`slide-${newSlide.id}`); + if (newSlideElement) { + newSlideElement.scrollIntoView({ + behavior: "smooth", + block: "center", + }); + } + }, 100); }; - // Scroll to the new slide when the presentationData is updated - // useEffect(() => { - // if ( - // presentationData && - // presentationData?.slides && - // presentationData.slides.length > 1 && - // isStreaming - // ) { - // const slideElement = document.getElementById(`slide-${index}`); - // if (slideElement) { - // slideElement.scrollIntoView({ - // behavior: "smooth", - // block: "center", - // }); - // } - // } - // }, [presentationData?.slides, isStreaming]); + // Scroll to the new slide when streaming and new slides are being generated + useEffect(() => { + if ( + presentationData && + presentationData?.slides && + presentationData.slides.length > 1 && + isStreaming + ) { + // Scroll to the last slide (newly generated during streaming) + const lastSlideIndex = presentationData.slides.length - 1; + const slideElement = document.getElementById(`slide-${presentationData.slides[lastSlideIndex].id}`); + if (slideElement) { + slideElement.scrollIntoView({ + behavior: "smooth", + block: "center", + }); + } + } + }, [presentationData?.slides?.length, isStreaming]); // Memoized slide content rendering to prevent unnecessary re-renders const slideContent = useMemo(() => { @@ -127,7 +140,7 @@ const SlideContent = ({ return ( <>
{isStreaming && ( diff --git a/servers/nextjs/app/(presentation-generator)/presentation/components/SortableListItem.tsx b/servers/nextjs/app/(presentation-generator)/presentation/components/SortableListItem.tsx index 63bc73f9..30e88ac6 100644 --- a/servers/nextjs/app/(presentation-generator)/presentation/components/SortableListItem.tsx +++ b/servers/nextjs/app/(presentation-generator)/presentation/components/SortableListItem.tsx @@ -7,7 +7,7 @@ interface SortableListItemProps { slide: Slide; index: number; selectedSlide: number; - onSlideClick: (index: number) => void; + onSlideClick: (index: any) => void; } export function SortableListItem({ slide, index, selectedSlide, onSlideClick }: SortableListItemProps) { @@ -38,7 +38,7 @@ export function SortableListItem({ slide, index, selectedSlide, onSlideClick }: // If the mouse was down for less than 200ms, consider it a click if (timeDiff < 200 && !isDragging) { - onSlideClick(slide.index); + onSlideClick(slide.id); } }; diff --git a/servers/nextjs/app/(presentation-generator)/presentation/components/SortableSlide.tsx b/servers/nextjs/app/(presentation-generator)/presentation/components/SortableSlide.tsx index 77cbe88e..de527cc6 100644 --- a/servers/nextjs/app/(presentation-generator)/presentation/components/SortableSlide.tsx +++ b/servers/nextjs/app/(presentation-generator)/presentation/components/SortableSlide.tsx @@ -7,7 +7,7 @@ interface SortableSlideProps { slide: Slide; index: number; selectedSlide: number; - onSlideClick: (index: number) => void; + onSlideClick: (index: any) => void; renderSlideContent: (slide: any) => React.ReactElement; } @@ -39,7 +39,7 @@ export function SortableSlide({ slide, index, selectedSlide, onSlideClick, rende // If the mouse was down for less than 200ms, consider it a click if (timeDiff < 200 && !isDragging) { - onSlideClick(slide.index); + onSlideClick(slide.id); } }; diff --git a/servers/nextjs/app/(presentation-generator)/store/themeSlice.ts b/servers/nextjs/app/(presentation-generator)/store/themeSlice.ts index 855c1b36..d848cf05 100644 --- a/servers/nextjs/app/(presentation-generator)/store/themeSlice.ts +++ b/servers/nextjs/app/(presentation-generator)/store/themeSlice.ts @@ -121,8 +121,8 @@ interface ThemeState { } const initialState: ThemeState = { - currentTheme: ThemeType.Dark, - currentColors: defaultColors.dark, + currentTheme: ThemeType.Light, + currentColors: defaultColors.light, isLoading: false, }; diff --git a/servers/nextjs/components/layouts/BulletPointSlideLayout.tsx b/servers/nextjs/components/layouts/BulletPointSlideLayout.tsx index b44e039b..f61ae195 100644 --- a/servers/nextjs/components/layouts/BulletPointSlideLayout.tsx +++ b/servers/nextjs/components/layouts/BulletPointSlideLayout.tsx @@ -1,6 +1,6 @@ import React from 'react' import * as z from "zod"; -import { imageSchema } from './defaultSchemes'; +import { ImageSchema } from './defaultSchemes'; export const layoutId = 'bullet-point-slide' export const layoutName = 'Bullet Point Slide' @@ -26,7 +26,7 @@ const bulletPointSlideSchema = z.object({ ]).meta({ description: "List of bullet points (2-8 items)", }), - backgroundImage: imageSchema.optional().meta({ + backgroundImage: ImageSchema.optional().meta({ description: "Background image for the slide", }), }) @@ -37,36 +37,11 @@ export type BulletPointSlideData = z.infer interface BulletPointSlideLayoutProps { data?: Partial - accentColor?: 'blue' | 'green' | 'purple' | 'orange' | 'red' } -const BulletPointSlideLayout: React.FC = ({ data: slideData, accentColor = 'blue' }) => { +const BulletPointSlideLayout: React.FC = ({ data: slideData }) => { - const accentColors = { - blue: 'from-blue-600 to-blue-800', - green: 'from-emerald-600 to-emerald-800', - purple: 'from-violet-600 to-violet-800', - orange: 'from-orange-600 to-orange-800', - red: 'from-red-600 to-red-800' - } - - const bulletColors = { - blue: 'bg-blue-600', - green: 'bg-emerald-600', - purple: 'bg-violet-600', - orange: 'bg-orange-600', - red: 'bg-red-600' - } - - const accentSolids = { - blue: 'bg-blue-600', - green: 'bg-emerald-600', - purple: 'bg-violet-600', - orange: 'bg-orange-600', - red: 'bg-red-600' - } - return (
= ({ data: s backgroundPosition: 'center' } : {}} > - {/* Enhanced geometric background decoration */} -
-
-
-
-
- {/* Professional Header */} -
-

- - {slideData?.title} + + + {/* Content */} +
+ {/* Header */} +
+

+ {slideData?.title?.split(' ').slice(0, -1).join(' ')}{' '} + + {slideData?.title?.split(' ').slice(-1)[0]}

+ {/* Subtitle */} {slideData?.subtitle && ( -

- {slideData?.subtitle} -

+
+
+
+
+
+

+ {slideData.subtitle} +

+
)} -
-
-
+ {/* Corner accent */} +
+
+ + {/* Bullet Points */} +
+
+ {slideData?.bulletPoints?.map((point, index) => ( +
+
+
+
+
+

+ {point} +

+
+ ))}
-

- - {/* Enhanced Bullet Points */} -
-
- {/* Content background accent */} -
- -
    - {slideData?.bulletPoints?.map((point, index) => ( -
  • - {/* Enhanced bullet point icon */} -
    -
    -
    -
    - - {/* Enhanced bullet text */} - - {point} - -
  • - ))} -
-
-
+
- {/* Enhanced decorative accent */} -
-
-
+ {/* Bottom accent */} +
+
+ {/* Glow effect */} +
+
+ {/* Corner accents */} +
- - {/* Professional corner accents */} -
) } diff --git a/servers/nextjs/components/layouts/CardSlideLayout.tsx b/servers/nextjs/components/layouts/CardSlideLayout.tsx new file mode 100644 index 00000000..5a9714d1 --- /dev/null +++ b/servers/nextjs/components/layouts/CardSlideLayout.tsx @@ -0,0 +1,141 @@ +import React from 'react' +import * as z from "zod"; +import { ImageSchema, IconSchema } from './defaultSchemes'; + +export const layoutId = 'card-slide' +export const layoutName = 'Card Slide' +export const layoutDescription = 'A professional slide featuring feature cards with icons, titles, and descriptions.' + +const cardSlideSchema = z.object({ + title: z.string().min(3).max(100).default('Our Features').meta({ + description: "Main title of the slide", + }), + subtitle: z.string().min(10).max(200).optional().meta({ + description: "Optional subtitle or description", + }), + cards: z.array(z.object({ + icon: IconSchema.default({ + url: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg', + prompt: 'Default card icon' + }).meta({ + description: "Icon for the card", + }), + title: z.string().min(2).max(50).meta({ + description: "Title for the card", + }), + description: z.string().min(10).max(150).meta({ + description: "Description of the feature", + }) + })).min(2).max(6).default([ + { + icon: { + url: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg', + prompt: 'Lightning fast icon' + }, + title: 'Lightning Fast', + description: 'Optimized performance for quick results and seamless user experience' + }, + { + icon: { + url: 'https://cdn.pixabay.com/photo/2016/02/19/11/19/office-1209640_1280.jpg', + prompt: 'Secure and safe icon' + }, + title: 'Secure & Safe', + description: 'Enterprise-grade security with advanced encryption and protection' + }, + { + icon: { + url: 'https://cdn.pixabay.com/photo/2017/08/10/08/47/laptop-2619235_1280.jpg', + prompt: 'Precise targeting icon' + }, + title: 'Precise Targeting', + description: 'Advanced analytics to reach your exact audience with precision' + } + ]).meta({ + description: "List of feature cards (2-6 items)", + }), + backgroundImage: ImageSchema.optional().meta({ + description: "Background image for the slide", + }) +}) + +export const Schema = cardSlideSchema + +export type CardSlideData = z.infer + +interface CardSlideLayoutProps { + data?: Partial +} + +const CardSlideLayout: React.FC = ({ data: slideData }) => { + + return ( +
+ + + {/* Header section */} +
+

+ {slideData?.title || 'Our Features'} +

+ {slideData?.subtitle && ( +

+ {slideData.subtitle} +

+ )} +
+
+ + {/* Cards section */} +
+
+ {slideData?.cards?.map((card, index) => ( +
+ {/* Card accent */} +
+ + {/* Icon */} +
+
+ {card.icon?.prompt +
+
+ + {/* Title */} +

+ {card.title} +

+ + {/* Description */} +

+ {card.description} +

+ + {/* Background decoration */} +
+
+ ))} +
+
+ + {/* Bottom decorative accent */} +
+
+ ) +} + +export default CardSlideLayout \ No newline at end of file diff --git a/servers/nextjs/components/layouts/ComparisonSlideLayout.tsx b/servers/nextjs/components/layouts/ComparisonSlideLayout.tsx new file mode 100644 index 00000000..72e438cf --- /dev/null +++ b/servers/nextjs/components/layouts/ComparisonSlideLayout.tsx @@ -0,0 +1,173 @@ +import React from 'react' +import * as z from "zod"; +import { ImageSchema } from './defaultSchemes'; + +export const layoutId = 'comparison-slide' +export const layoutName = 'Comparison Slide' +export const layoutDescription = 'A professional slide for comparing features, options, or before/after scenarios.' + +const comparisonSlideSchema = z.object({ + title: z.string().min(3).max(100).default('Feature Comparison').meta({ + description: "Main title of the slide", + }), + subtitle: z.string().min(10).max(200).optional().meta({ + description: "Optional subtitle or description", + }), + leftColumn: z.object({ + title: z.string().min(2).max(50).meta({ + description: "Title for left column", + }), + icon: z.string().default('📊').meta({ + description: "Icon for left column", + }), + items: z.array(z.string().min(5).max(100)).min(2).max(8).meta({ + description: "List of items for left column", + }) + }).default({ + title: 'Before', + icon: '📊', + items: [ + 'Manual processes and workflows', + 'Limited scalability options', + 'Disconnected systems', + 'Time-consuming operations' + ] + }).meta({ + description: "Left comparison column", + }), + rightColumn: z.object({ + title: z.string().min(2).max(50).meta({ + description: "Title for right column", + }), + icon: z.string().default('🚀').meta({ + description: "Icon for right column", + }), + items: z.array(z.string().min(5).max(100)).min(2).max(8).meta({ + description: "List of items for right column", + }) + }).default({ + title: 'After', + icon: '🚀', + items: [ + 'Automated intelligent workflows', + 'Unlimited scaling capabilities', + 'Seamlessly integrated ecosystem', + 'Lightning-fast performance' + ] + }).meta({ + description: "Right comparison column", + }), + backgroundImage: ImageSchema.optional().meta({ + description: "Background image for the slide", + }) +}) + +export const Schema = comparisonSlideSchema + +export type ComparisonSlideData = z.infer + +interface ComparisonSlideLayoutProps { + data?: Partial +} + +const ComparisonSlideLayout: React.FC = ({ data: slideData }) => { + + return ( +
+ + {/* Header section */} +
+

+ {slideData?.title || 'Feature Comparison'} +

+ {slideData?.subtitle && ( +

+ {slideData.subtitle} +

+ )} +
+
+ + {/* Comparison section */} +
+
+ {/* Left Column */} +
+ {/* Header */} +
+
+ {slideData?.leftColumn?.icon || '📊'} +
+

+ {slideData?.leftColumn?.title || 'Before'} +

+
+ + {/* Items */} +
+ {slideData?.leftColumn?.items?.map((item, index) => ( +
+
+

+ {item} +

+
+ ))} +
+ + {/* Accent */} +
+
+ + {/* VS Divider */} +
+
+ VS +
+
+
+ + {/* Right Column */} +
+ {/* Header */} +
+
+ {slideData?.rightColumn?.icon || '🚀'} +
+

+ {slideData?.rightColumn?.title || 'After'} +

+
+ + {/* Items */} +
+ {slideData?.rightColumn?.items?.map((item, index) => ( +
+
+

+ {item} +

+
+ ))} +
+ + {/* Accent */} +
+
+
+
+ + {/* Bottom decorative accent */} +
+
+ ) +} + +export default ComparisonSlideLayout \ No newline at end of file diff --git a/servers/nextjs/components/layouts/ConclusionSlideLayout.tsx b/servers/nextjs/components/layouts/ConclusionSlideLayout.tsx deleted file mode 100644 index 9d7e4a3e..00000000 --- a/servers/nextjs/components/layouts/ConclusionSlideLayout.tsx +++ /dev/null @@ -1,250 +0,0 @@ -import React from 'react' -import * as z from "zod"; -import { imageSchema } from './defaultSchemes'; - -export const layoutId = 'conclusion-slide' -export const layoutName = 'Conclusion Slide' -export const layoutDescription = 'A slide with a title, subtitle, key takeaways, call to action, and contact information' - -const conclusionSlideSchema = z.object({ - title: z.string().min(3).max(100).default('Conclusion').meta({ - description: "Title of the slide", - }), - subtitle: z.string().min(3).max(150).optional().meta({ - description: "Optional subtitle or description", - }), - keyTakeaways: z.array(z.string().min(5).max(200)).min(2).max(6).default([ - 'Successfully achieved our primary objectives', - 'Demonstrated significant value and impact', - 'Established clear next steps for continued success', - 'Built strong foundation for future growth' - ]).meta({ - description: "Key takeaways or summary points (2-6 items)", - }), - callToAction: z.string().min(5).max(150).optional().meta({ - description: "Optional call to action or next steps", - }), - contactInfo: z.object({ - email: z.email().optional().meta({ - description: "Contact email", - }), - phone: z.string().min(5).max(50).optional().meta({ - description: "Contact phone number", - }), - website: z.string().url().optional().meta({ - description: "Website URL", - }) - }).optional().meta({ - description: "Optional contact information", - }), - backgroundImage: imageSchema.optional().meta({ - description: "Background image for the slide", - }) -}) - -export const Schema = conclusionSlideSchema - -export type ConclusionSlideData = z.infer - -interface ConclusionSlideLayoutProps { - data?: Partial - accentColor?: 'blue' | 'green' | 'purple' | 'orange' | 'red' -} - -const ConclusionSlideLayout: React.FC = ({ data: slideData, accentColor = 'blue' }) => { - - const accentColors = { - blue: 'from-blue-600 to-blue-800', - green: 'from-emerald-600 to-emerald-800', - purple: 'from-violet-600 to-violet-800', - orange: 'from-orange-600 to-orange-800', - red: 'from-red-600 to-red-800' - } - - const bulletColors = { - blue: 'bg-blue-600', - green: 'bg-emerald-600', - purple: 'bg-violet-600', - orange: 'bg-orange-600', - red: 'bg-red-600' - } - - const accentSolids = { - blue: 'bg-blue-600', - green: 'bg-emerald-600', - purple: 'bg-violet-600', - orange: 'bg-orange-600', - red: 'bg-red-600' - } - - return ( -
- {/* Enhanced geometric background decoration */} -
-
-
-
- -
- {/* Professional Header */} -
-

- - {slideData?.title} - -

- - {slideData?.subtitle && ( -

- {slideData?.subtitle} -

- )} - -
-
-
-
-
- - {/* Enhanced Content Layout */} -
- {/* Key Takeaways - Takes up 2/3 of space */} -
-
- {/* Content accent */} -
- -

Key Takeaways

- -
    - {slideData?.keyTakeaways?.map((takeaway, index) => ( -
  • - {/* Enhanced bullet point */} -
    -
    -
    -
    - - - {takeaway} - -
  • - ))} -
- - {/* Background decoration */} -
-
-
- - {/* Call to Action & Contact Info - Takes up 1/3 of space */} -
- {/* Call to Action */} - {slideData?.callToAction && ( -
- {/* CTA accent */} -
- -
- - - -
-
- -

Next Steps

-

- {slideData?.callToAction} -

-
- )} - - {/* Contact Information */} - {slideData?.contactInfo && Object.values(slideData?.contactInfo).some(Boolean) && ( -
- {/* Contact accent */} -
- -

Get in Touch

- -
- {slideData?.contactInfo?.email && ( - -
- - - - -
- {slideData?.contactInfo?.email} -
- )} - - {slideData?.contactInfo?.phone && ( - -
- - - -
- {slideData.contactInfo.phone} -
- )} - - {slideData?.contactInfo?.website && ( - -
- - - -
- {slideData.contactInfo.website.replace(/^https?:\/\//, '')} -
- )} -
- - {/* Background decoration */} -
-
- )} -
-
-
- - {/* Enhanced decorative accent */} -
-
-
-
- - {/* Professional corner accents */} -
-
- ) -} - -export default ConclusionSlideLayout \ No newline at end of file diff --git a/servers/nextjs/components/layouts/ContentSlideLayout.tsx b/servers/nextjs/components/layouts/ContentSlideLayout.tsx index 5ad058d8..fbeabcec 100644 --- a/servers/nextjs/components/layouts/ContentSlideLayout.tsx +++ b/servers/nextjs/components/layouts/ContentSlideLayout.tsx @@ -1,6 +1,6 @@ import React from 'react' import * as z from "zod"; -import { imageSchema } from './defaultSchemes'; +import { ImageSchema } from './defaultSchemes'; export const layoutId = 'content-slide' @@ -17,7 +17,7 @@ const contentSlideSchema = z.object({ content: z.string().min(10).max(1000).default('Your slide content goes here. This is where you can add detailed information, explanations, or any other text content that supports your presentation.').meta({ description: "Main content text", }), - backgroundImage: imageSchema.optional().meta({ + backgroundImage: ImageSchema.optional().meta({ description: "Background image for the slide", }) }) @@ -28,26 +28,9 @@ export type ContentSlideData = z.infer interface ContentSlideLayoutProps { data?: Partial - accentColor?: 'blue' | 'green' | 'purple' | 'orange' | 'red' } -const ContentSlideLayout: React.FC = ({ data: slideData, accentColor = 'blue' }) => { - - const accentColors = { - blue: 'from-blue-600 to-blue-800', - green: 'from-emerald-600 to-emerald-800', - purple: 'from-violet-600 to-violet-800', - orange: 'from-orange-600 to-orange-800', - red: 'from-red-600 to-red-800' - } - - const accentSolids = { - blue: 'bg-blue-600', - green: 'bg-emerald-600', - purple: 'bg-violet-600', - orange: 'bg-orange-600', - red: 'bg-red-600' - } +const ContentSlideLayout: React.FC = ({ data: slideData }) => { return (
= ({ data: slideData backgroundPosition: 'center' } : {}} > - {/* Enhanced geometric background decoration */} -
-
-
-
+ {/* Grid overlay for professional look */}
= ({ data: slideData backgroundSize: '40px 40px' }} /> -
- {/* Professional Header */} -
-

- - {slideData?.title} + {/* Content */} +
+ {/* Header */} +
+

+ {slideData?.title?.split(' ').slice(0, -1).join(' ')}{' '} + + {slideData?.title?.split(' ').slice(-1)[0]}

+ {/* Subtitle */} {slideData?.subtitle && ( -

- {slideData?.subtitle} -

+
+
+
+
+
+

+ {slideData.subtitle} +

+
)} -
-
-
-
-

+ {/* Corner accent */} +
+
- {/* Main Content with Enhanced Styling */} -
-
- {/* Content background accent */} -
- -
+ {/* Content */} +
+
+
{slideData?.content?.split('\n').map((paragraph, index) => ( - paragraph.trim() && ( -

- {paragraph} -

- ) +

+ {paragraph} +

))}
-
+
- {/* Enhanced decorative accent */} -
-
-
+ {/* Bottom accent */} +
+
+ {/* Glow effect */} +
+
+ {/* Corner accents */} +
- - {/* Professional corner accents */} -
) } diff --git a/servers/nextjs/components/layouts/FirstSlideLayout.tsx b/servers/nextjs/components/layouts/FirstSlideLayout.tsx index a65f8d8e..928f6302 100644 --- a/servers/nextjs/components/layouts/FirstSlideLayout.tsx +++ b/servers/nextjs/components/layouts/FirstSlideLayout.tsx @@ -1,6 +1,6 @@ import React from 'react' import * as z from "zod"; -import { imageSchema } from './defaultSchemes'; +import { ImageSchema } from './defaultSchemes'; export const layoutId = 'first-slide' @@ -23,7 +23,7 @@ const firstSlideSchema = z.object({ company: z.string().max(100).default('Company Name').optional().meta({ description: "Company or organization name", }), - backgroundImage: imageSchema.optional().meta({ + backgroundImage: ImageSchema.optional().meta({ description: "Background image for the slide", }) }) @@ -34,28 +34,11 @@ export type FirstSlideData = z.infer interface FirstSlideLayoutProps { data?: Partial - accentColor?: 'blue' | 'green' | 'purple' | 'orange' | 'red' } -const FirstSlideLayout: React.FC = ({ data: slideData, accentColor = 'blue' }) => { +const FirstSlideLayout: React.FC = ({ data: slideData }) => { - const accentColors = { - blue: 'from-blue-600 to-blue-800', - green: 'from-emerald-600 to-emerald-800', - purple: 'from-violet-600 to-violet-800', - orange: 'from-orange-600 to-orange-800', - red: 'from-red-600 to-red-800' - } - - const accentSolids = { - blue: 'bg-blue-600', - green: 'bg-emerald-600', - purple: 'bg-violet-600', - orange: 'bg-orange-600', - red: 'bg-red-600' - } - return (
= ({ data: slideData, ac backgroundPosition: 'center' } : {}} > - {/* Enhanced geometric background decoration */} -
-
-
-
-
- {/* Grid overlay for professional look */} -
-
- {/* Main Content */} -
+ {/* Content */} +
+ {/* Header */} +
{/* Title */} -

- - {slideData?.title} +

+ {slideData?.title?.split(' ').slice(0, -1).join(' ')}{' '} + + {slideData?.title?.split(' ').slice(-1)[0]}

{/* Subtitle */} {slideData?.subtitle && ( -

- {slideData?.subtitle} -

+
+
+
+
+
+

+ {slideData.subtitle} +

+
)} - {/* Enhanced Accent Line */} -
-
-
-
+ {/* Date */} + {slideData?.date && ( +

+ {slideData.date} +

+ )} +
- {/* Professional Metadata Container */} -
-
+ {/* Footer with Author and Company */} +
+
+
{slideData?.author && ( -

- {slideData?.author} -

+

{slideData.author}

)} - {slideData?.company && ( -

- {slideData?.company} -

- )} - - {slideData?.date && ( -

- {slideData?.date} -

+

{slideData.company}

)}
-

+
- {/* Enhanced decorative accent with glow effect */} -
-
-
+ {/* Bottom accent */} +
+
+ {/* Glow effect */} +
+
+ {/* Corner accents */} +
+
- - {/* Professional corner accents */} -
-
) } diff --git a/servers/nextjs/components/layouts/IconSlideLayout.tsx b/servers/nextjs/components/layouts/IconSlideLayout.tsx new file mode 100644 index 00000000..228accf7 --- /dev/null +++ b/servers/nextjs/components/layouts/IconSlideLayout.tsx @@ -0,0 +1,109 @@ +import React from 'react' +import * as z from "zod"; +import { IconSchema } from './defaultSchemes'; + +export const layoutId = 'icon-slide' +export const layoutName = 'Icon Slide' +export const layoutDescription = 'A professional slide featuring a prominent icon with title, description, and key points.' + + + +const iconSlideSchema = z.object({ + title: z.string().min(3).max(100).default('Key Features').meta({ + description: "Main title of the slide", + }), + subtitle: z.string().min(10).max(200).optional().meta({ + description: "Optional subtitle or description", + }), + icon: IconSchema.default({ + url: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg', + prompt: 'A beautiful road in the mountains' + }).meta({ + description: "Main slide icon", + }), + features: z.array(z.string().min(10).max(150)).min(2).max(6).default([ + 'Advanced analytics and reporting capabilities', + 'Seamless integration with existing systems', + 'Real-time collaboration and communication', + 'Enhanced security and data protection' + ]).meta({ + description: "List of key features (2-6 items)", + }) +}) + +export const Schema = iconSlideSchema + +export type IconSlideData = z.infer + +interface IconSlideLayoutProps { + data?: Partial +} + +const IconSlideLayout: React.FC = ({ data: slideData }) => { + + + return ( +
+ {/* Subtle background pattern for print compatibility */} +
+
+
+ + {/* Left side - Icon and title */} +
+ {/* Icon container */} +
+
+ {slideData?.icon?.prompt +
+
+ + {/* Title */} +

+ {slideData?.title || 'Key Features'} +

+ + {/* Subtitle */} + {slideData?.subtitle && ( +

+ {slideData.subtitle} +

+ )} + + {/* Decorative element */} +
+
+ + {/* Right side - Content */} +
+ {/* Features */} +
+ {slideData?.features?.map((feature, index) => ( +
+
+ + {index + 1} + +
+

+ {feature} +

+
+ ))} +
+ + {/* Bottom accent line */} +
+
+ + {/* Top decorative border */} +
+
+ ) +} + +export default IconSlideLayout \ No newline at end of file diff --git a/servers/nextjs/components/layouts/ImageSlideLayout.tsx b/servers/nextjs/components/layouts/ImageSlideLayout.tsx new file mode 100644 index 00000000..21289c8a --- /dev/null +++ b/servers/nextjs/components/layouts/ImageSlideLayout.tsx @@ -0,0 +1,115 @@ +import React from 'react' +import * as z from "zod"; +import { ImageSchema } from './defaultSchemes'; + +export const layoutId = 'image-slide' +export const layoutName = 'Image Slide' +export const layoutDescription = 'A professional slide featuring a prominent image with overlaid text content and call-to-action elements.' + + + +const imageSlideSchema = z.object({ + title: z.string().min(3).max(100).default('Our Vision').meta({ + description: "Main title of the slide", + }), + subtitle: z.string().min(10).max(200).optional().meta({ + description: "Optional subtitle or description", + }), + description: z.string().min(20).max(500).default('Transform your ideas into reality with innovative solutions that drive success and growth.').meta({ + description: "Main description text", + }), + image: ImageSchema.default({ + url: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg', + prompt: 'A beautiful road in the mountains' + }).meta({ + description: "Main slide image", + }), + buttonText: z.string().max(50).default('Learn More').optional().meta({ + description: "Optional button text", + }), + buttonUrl: z.string().url().optional().meta({ + description: "Optional button URL", + }) +}) + +export const Schema = imageSlideSchema + +export type ImageSlideData = z.infer + +interface ImageSlideLayoutProps { + data?: Partial +} + +const ImageSlideLayout: React.FC = ({ data: slideData }) => { + + return ( +
+ {/* Left panel - Image */} +
+ {slideData?.image?.prompt + {/* Overlay gradient */} +
+ + {/* Content overlay */} +
+ {/* Title */} +

+ {slideData?.title || 'Our Vision'} +

+ + {/* Subtitle */} + {slideData?.subtitle && ( +

+ {slideData.subtitle} +

+ )} + + {/* Description */} +

+ {slideData?.description || 'Transform your ideas into reality with innovative solutions that drive success and growth.'} +

+ + {/* Button */} + {slideData?.buttonText && ( +
+ +
+ )} + + {/* Decorative line */} +
+
+
+ + {/* Right panel for additional context */} +
+ {/* Feature card */} +
+
+
+ +
+
+

Professional Quality

+

Excellence in every detail

+
+
+
+ + {/* Bottom accent line */} +
+
+ + {/* Top decorative border */} +
+
+ ) +} + +export default ImageSlideLayout \ No newline at end of file diff --git a/servers/nextjs/components/layouts/NumberBoxSlideLayout.tsx b/servers/nextjs/components/layouts/NumberBoxSlideLayout.tsx new file mode 100644 index 00000000..3458408b --- /dev/null +++ b/servers/nextjs/components/layouts/NumberBoxSlideLayout.tsx @@ -0,0 +1,114 @@ +import React from 'react' +import * as z from "zod"; + +export const layoutId = 'number-box-slide' +export const layoutName = 'Number Box Slide' +export const layoutDescription = 'A professional slide featuring numbered content boxes with titles, descriptions, and clean typography.' + + + +const numberBoxSlideSchema = z.object({ + title: z.string().min(3).max(100).default('Key Metrics').meta({ + description: "Main title of the slide", + }), + subtitle: z.string().min(10).max(200).optional().meta({ + description: "Optional subtitle or description", + }), + numberBoxes: z.array(z.object({ + number: z.string().min(1).max(20).meta({ + description: "Number or statistic to display", + }), + title: z.string().min(2).max(50).meta({ + description: "Title for the number", + }), + description: z.string().min(10).max(100).meta({ + description: "Brief description of the metric", + }) + })).min(2).max(6).default([ + { + number: '150+', + title: 'Projects Completed', + description: 'Successfully delivered across various industries' + }, + { + number: '98%', + title: 'Client Satisfaction', + description: 'Consistently exceeding expectations' + }, + { + number: '24/7', + title: 'Support Available', + description: 'Round-the-clock assistance for all clients' + } + ]).meta({ + description: "List of number boxes (2-6 items)", + }) +}) + +export const Schema = numberBoxSlideSchema + +export type NumberBoxSlideData = z.infer + +interface NumberBoxSlideLayoutProps { + data?: Partial +} + +const NumberBoxSlideLayout: React.FC = ({ data: slideData }) => { + // Parse and validate data with defaults + // const data = numberBoxSlideSchema.parse(slideData || {}) + + return ( +
+ {/* Subtle background pattern */} +
+
+
+ + {/* Header section */} +
+

+ {slideData?.title || 'Key Metrics'} +

+ {slideData?.subtitle && ( +

+ {slideData.subtitle} +

+ )} +
+
+ + {/* Content section */} +
+
+ {slideData?.numberBoxes?.map((box, index) => ( +
+ {/* Number display */} +
+
+ {box.number} +
+

+ {box.title} +

+

+ {box.description} +

+
+ + {/* Decorative corner accent */} +
+
+ ))} +
+
+ + {/* Bottom decorative accent */} +
+
+ ) +} + +export default NumberBoxSlideLayout \ No newline at end of file diff --git a/servers/nextjs/components/layouts/ProcessSlideLayout.tsx b/servers/nextjs/components/layouts/ProcessSlideLayout.tsx index 2bb03eae..6698922a 100644 --- a/servers/nextjs/components/layouts/ProcessSlideLayout.tsx +++ b/servers/nextjs/components/layouts/ProcessSlideLayout.tsx @@ -1,52 +1,60 @@ import React from 'react' import * as z from "zod"; -import { imageSchema } from './defaultSchemes'; - +import { ImageSchema, IconSchema } from './defaultSchemes'; export const layoutId = 'process-slide' export const layoutName = 'Process Slide' -export const layoutDescription = 'A slide with a title, subtitle, and process steps' +export const layoutDescription = 'A professional slide featuring step-by-step processes with icons, titles, and descriptions.' const processSlideSchema = z.object({ title: z.string().min(3).max(100).default('Our Process').meta({ - description: "Title of the slide", + description: "Main title of the slide", }), - subtitle: z.string().min(3).max(150).optional().meta({ + subtitle: z.string().min(10).max(200).optional().meta({ description: "Optional subtitle or description", }), - processSteps: z.array(z.object({ - step: z.number().min(1).max(10).meta({ - description: "Step number", + steps: z.array(z.object({ + icon: IconSchema.default({ + url: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg', + prompt: 'Default step icon' + }).meta({ + description: "Icon for the step", }), - title: z.string().min(3).max(100).meta({ - description: "Step title", + title: z.string().min(2).max(50).meta({ + description: "Title for the step", }), - description: z.string().min(10).max(200).meta({ - description: "Step description", + description: z.string().min(10).max(150).meta({ + description: "Description of the step", }) })).min(2).max(6).default([ { - step: 1, - title: 'Discovery', - description: 'Understanding requirements and gathering initial insights' + icon: { + url: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg', + prompt: 'Plan and strategy icon' + }, + title: 'Plan & Strategy', + description: 'Define objectives, analyze requirements, and create a comprehensive roadmap' }, { - step: 2, - title: 'Planning', - description: 'Strategic planning and roadmap development' + icon: { + url: 'https://cdn.pixabay.com/photo/2016/02/19/11/19/office-1209640_1280.jpg', + prompt: 'Execute and build icon' + }, + title: 'Execute & Build', + description: 'Implement solutions with precision using cutting-edge technology and best practices' }, { - step: 3, - title: 'Implementation', - description: 'Executing the plan with precision and quality' - }, - { - step: 4, - title: 'Delivery', - description: 'Final delivery and ongoing support' + icon: { + url: 'https://cdn.pixabay.com/photo/2017/08/10/08/47/laptop-2619235_1280.jpg', + prompt: 'Launch and optimize icon' + }, + title: 'Launch & Optimize', + description: 'Deploy the solution and continuously improve based on performance metrics' } - ]).describe('Process steps (2-6 items)'), - backgroundImage: imageSchema.optional().meta({ + ]).meta({ + description: "List of process steps (2-6 items)", + }), + backgroundImage: ImageSchema.optional().meta({ description: "Background image for the slide", }) }) @@ -57,140 +65,85 @@ export type ProcessSlideData = z.infer interface ProcessSlideLayoutProps { data?: Partial - accentColor?: 'blue' | 'green' | 'purple' | 'orange' | 'red' } -const ProcessSlideLayout: React.FC = ({ data: slideData, accentColor = 'blue' }) => { - - const accentColors = { - blue: 'from-blue-600 to-blue-800', - green: 'from-emerald-600 to-emerald-800', - purple: 'from-violet-600 to-violet-800', - orange: 'from-orange-600 to-orange-800', - red: 'from-red-600 to-red-800' - } - - const stepColors = { - blue: 'bg-blue-600 text-white border-blue-600', - green: 'bg-emerald-600 text-white border-emerald-600', - purple: 'bg-violet-600 text-white border-violet-600', - orange: 'bg-orange-600 text-white border-orange-600', - red: 'bg-red-600 text-white border-red-600' - } - - const accentSolids = { - blue: 'bg-blue-600', - green: 'bg-emerald-600', - purple: 'bg-violet-600', - orange: 'bg-orange-600', - red: 'bg-red-600' - } +const ProcessSlideLayout: React.FC = ({ data: slideData }) => { return (
- {/* Enhanced geometric background decoration */} -
-
-
+ + + {/* Header section */} +
+

+ {slideData?.title || 'Our Process'} +

+ {slideData?.subtitle && ( +

+ {slideData.subtitle} +

+ )} +
-
- {/* Professional Header */} -
-

- - {slideData?.title} - -

- - {slideData?.subtitle && ( -

- {slideData?.subtitle} -

- )} - -
-
-
-
-
- - {/* Enhanced Process Steps */} -
-
-
- {slideData?.processSteps?.map((step, index) => ( - - {/* Process Step */} -
- {/* Step Number Circle */} -
- {step.step} -
+ {/* Process steps section */} +
+
+
+ {slideData?.steps?.map((step, index) => ( + + {/* Process Step */} +
+ {/* Step Number and Icon */} +
+
+ + {index + 1} +
- - {/* Step Content Card */} -
- {/* Card accent */} -
- - {/* Step Title */} -

- {step.title} -

- - {/* Step Description */} -

- {step.description} -

- - {/* Background decoration */} -
+
+ {step.icon?.prompt
- {/* Arrow Between Steps */} - {index < (slideData?.processSteps?.length || 0) - 1 && ( -
-
-
-
+ {/* Step Title */} +

+ {step.title} +

+ + {/* Step Description */} +

+ {step.description} +

+
+ + {/* Arrow between steps */} + {index < (slideData?.steps?.length || 0) - 1 && ( +
+
+
- )} - - ))} -
+
+ )} + + ))}
-
+
- {/* Enhanced decorative accent */} -
-
-
-
- - {/* Professional corner accents */} -
+ {/* Bottom decorative accent */} +
) } diff --git a/servers/nextjs/components/layouts/QuoteSlideLayout.tsx b/servers/nextjs/components/layouts/QuoteSlideLayout.tsx index 0f1f5715..711bd38c 100644 --- a/servers/nextjs/components/layouts/QuoteSlideLayout.tsx +++ b/servers/nextjs/components/layouts/QuoteSlideLayout.tsx @@ -1,6 +1,6 @@ import React from 'react' import * as z from "zod"; -import { imageSchema } from './defaultSchemes'; +import { ImageSchema } from './defaultSchemes'; export const layoutId = 'quote-slide' @@ -29,7 +29,7 @@ const quoteSlideSchema = z.object({ authorImage: z.string().optional().meta({ description: "URL to author photo", }), - backgroundImage: imageSchema.optional().meta({ + backgroundImage: ImageSchema.optional().meta({ description: "Background image for the slide", }) }) @@ -40,26 +40,9 @@ export type QuoteSlideData = z.infer interface QuoteSlideLayoutProps { data?: Partial - accentColor?: 'blue' | 'green' | 'purple' | 'orange' | 'red' } -const QuoteSlideLayout: React.FC = ({ data: slideData, accentColor = 'blue' }) => { - - const accentColors = { - blue: 'from-blue-600 to-blue-800', - green: 'from-emerald-600 to-emerald-800', - purple: 'from-violet-600 to-violet-800', - orange: 'from-orange-600 to-orange-800', - red: 'from-red-600 to-red-800' - } - - const accentSolids = { - blue: 'bg-blue-600', - green: 'bg-emerald-600', - purple: 'bg-violet-600', - orange: 'bg-orange-600', - red: 'bg-red-600' - } +const QuoteSlideLayout: React.FC = ({ data: slideData }) => { return (
= ({ data: slideData, ac backgroundPosition: 'center' } : {}} > - {/* Enhanced geometric background decoration */} -
-
-
-
-
- {/* Professional Header */} -
-

- - {slideData?.title} - -

- {slideData?.subtitle && ( -

- {slideData?.subtitle} -

- )} + {/* Content */} +
+ {/* Title */} +

+ {slideData?.title?.split(' ').slice(0, -1).join(' ')}{' '} + + {slideData?.title?.split(' ').slice(-1)[0]} + +

-
-
-
+ {/* Quote */} +
+ {/* Decorative line */} +
+
+
-
- {/* Enhanced Quote Content */} -
-
- {/* Enhanced Background Quote Decoration */} -
- " -
-
- " + {/* Quote Text */} +
+ "{slideData?.quote}" +
+ + {/* Author Attribution */} +
+ {/* Author Avatar */} +
+ {slideData?.authorImage ? ( + {slideData?.author} + ) : ( +
+ {slideData?.author?.split(' ').map(n => n[0]).join('')} +
+ )}
- {/* Quote Text */} -
- "{slideData?.quote}" -
+ {/* Author Details */} +
+

+ {slideData?.author} +

- {/* Professional Author Attribution */} -
- {/* Author Avatar */} -
- {slideData?.authorImage ? ( - {slideData?.author} - ) : ( -
- {slideData?.author?.split(' ').map(n => n[0]).join('')} -
- )} -
- - {/* Author Details */} -
-

- {slideData?.author} + {slideData?.authorTitle && ( +

+ {slideData?.authorTitle}

+ )} - {slideData?.authorTitle && ( -

- {slideData?.authorTitle} -

- )} - - {slideData?.company && ( -

- {slideData?.company} -

- )} -
+ {slideData?.company && ( +

+ {slideData?.company} +

+ )}
- - {/* Enhanced Quote Accent Line */} -
-
-
-
-
-
- - {/* Background decoration */} -
-
+ + {/* Quote Accent Line */} +
+
+
+
+
+
+ + {/* Background decoration */} +
+
- {/* Enhanced decorative accent */} -
+ {/* Decorative accent */} +
-
+
{/* Professional corner accents */} -
+
) } diff --git a/servers/nextjs/components/layouts/StatisticsSlideLayout.tsx b/servers/nextjs/components/layouts/StatisticsSlideLayout.tsx index d9eea523..e2eaae59 100644 --- a/servers/nextjs/components/layouts/StatisticsSlideLayout.tsx +++ b/servers/nextjs/components/layouts/StatisticsSlideLayout.tsx @@ -1,6 +1,6 @@ import React from 'react' import * as z from "zod"; -import { imageSchema } from './defaultSchemes'; +import { ImageSchema } from './defaultSchemes'; export const layoutId = 'statistics-slide' @@ -53,7 +53,7 @@ const statisticsSlideSchema = z.object({ context: 'Customer service' } ]).describe('List of statistics (2-6 items)'), - backgroundImage: imageSchema.optional().meta({ + backgroundImage: ImageSchema.optional().meta({ description: "Background image for the slide", }) }) @@ -64,41 +64,12 @@ export type StatisticsSlideData = z.infer interface StatisticsSlideLayoutProps { data?: Partial - accentColor?: 'blue' | 'green' | 'purple' | 'orange' | 'red' } -const StatisticsSlideLayout: React.FC = ({ data: slideData, accentColor = 'blue' }) => { +const StatisticsSlideLayout: React.FC = ({ data: slideData }) => { const statsCount = slideData?.statistics?.length || 0 - const accentColors = { - blue: 'from-blue-600 to-blue-800', - green: 'from-emerald-600 to-emerald-800', - purple: 'from-violet-600 to-violet-800', - orange: 'from-orange-600 to-orange-800', - red: 'from-red-600 to-red-800' - } - - const accentSolids = { - blue: 'bg-blue-600', - green: 'bg-emerald-600', - purple: 'bg-violet-600', - orange: 'bg-orange-600', - red: 'bg-red-600' - } - - const trendColors = { - up: { - blue: 'text-emerald-600 bg-emerald-50 border-emerald-200', - green: 'text-emerald-600 bg-emerald-50 border-emerald-200', - purple: 'text-emerald-600 bg-emerald-50 border-emerald-200', - orange: 'text-emerald-600 bg-emerald-50 border-emerald-200', - red: 'text-emerald-600 bg-emerald-50 border-emerald-200' - }, - down: 'text-red-600 bg-red-50 border-red-200', - neutral: 'text-slate-600 bg-slate-50 border-slate-200' - } - const getTrendIcon = (trend?: string) => { switch (trend) { case 'up': @@ -121,17 +92,18 @@ const StatisticsSlideLayout: React.FC = ({ data: sli return (
{/* Enhanced geometric background decoration */}
-
-
+
+
@@ -141,7 +113,7 @@ const StatisticsSlideLayout: React.FC = ({ data: sli ? 'text-white drop-shadow-lg' : 'text-slate-900' }`}> - + {slideData?.title} @@ -156,8 +128,8 @@ const StatisticsSlideLayout: React.FC = ({ data: sli )}
-
-
+
+
@@ -167,38 +139,39 @@ const StatisticsSlideLayout: React.FC = ({ data: sli {slideData?.statistics?.map((stat, index) => (
{/* Card accent */} -
+
{/* Statistic Value */} -
+
{stat?.value}
{/* Statistic Label */} -

+

{stat?.label}

- {/* Trend Indicator */} - {stat?.trend && ( -
- {getTrendIcon(stat.trend)} - {stat.trend.toUpperCase()} -
- )} - - {/* Context */} - {stat.context && ( -

- {stat.context} + {/* Description */} + {stat?.context && ( +

+ {stat?.context}

)} + {/* Trend Indicator */} + {stat?.trend && ( +
+ {getTrendIcon(stat?.trend)} + {stat?.trend === 'up' ? 'Trending Up' : + stat?.trend === 'down' ? 'Trending Down' : 'Stable'} +
+ )} + {/* Background decoration */} -
+
))}
@@ -206,13 +179,13 @@ const StatisticsSlideLayout: React.FC = ({ data: sli
{/* Enhanced decorative accent */} -
+
-
+
{/* Professional corner accents */} -
+
) } diff --git a/servers/nextjs/components/layouts/TeamSlideLayout.tsx b/servers/nextjs/components/layouts/TeamSlideLayout.tsx index dbf018eb..cda0b2c2 100644 --- a/servers/nextjs/components/layouts/TeamSlideLayout.tsx +++ b/servers/nextjs/components/layouts/TeamSlideLayout.tsx @@ -1,6 +1,6 @@ import React from 'react' import * as z from "zod"; -import { imageSchema } from './defaultSchemes'; +import { ImageSchema } from './defaultSchemes'; export const layoutId = 'team-slide' export const layoutName = 'Team Slide' @@ -26,10 +26,10 @@ const teamSlideSchema = z.object({ bio: z.string().min(10).max(300).optional().meta({ description: "Brief biography or description", }), - email: z.string().email().optional().meta({ + email: z.email().optional().meta({ description: "Contact email", }), - linkedin: z.string().url().optional().meta({ + linkedin: z.string().optional().meta({ description: "LinkedIn profile URL", }) })).min(1).max(6).default([ @@ -58,7 +58,7 @@ const teamSlideSchema = z.object({ linkedin: 'https://linkedin.com/in/emmarodriguez' } ]).describe('Team members (1-6 people)'), - backgroundImage: imageSchema.optional().meta({ + backgroundImage: ImageSchema.optional().meta({ description: "Background image for the slide", }), }) @@ -69,50 +69,33 @@ export type TeamSlideData = z.infer interface TeamSlideLayoutProps { data?: Partial - accentColor?: 'blue' | 'green' | 'purple' | 'orange' | 'red' } -const TeamSlideLayout: React.FC = ({ data: slideData, accentColor = 'blue' }) => { - - const accentColors = { - blue: 'from-blue-600 to-blue-800', - green: 'from-emerald-600 to-emerald-800', - purple: 'from-violet-600 to-violet-800', - orange: 'from-orange-600 to-orange-800', - red: 'from-red-600 to-red-800' - } - - const accentSolids = { - blue: 'bg-blue-600', - green: 'bg-emerald-600', - purple: 'bg-violet-600', - orange: 'bg-orange-600', - red: 'bg-red-600' - } - - const iconColors = { - blue: 'text-blue-600 hover:text-blue-700', - green: 'text-emerald-600 hover:text-emerald-700', - purple: 'text-violet-600 hover:text-violet-700', - orange: 'text-orange-600 hover:text-orange-700', - red: 'text-red-600 hover:text-red-700' - } +const TeamSlideLayout: React.FC = ({ data: slideData }) => { return (
{/* Enhanced geometric background decoration */}
-
-
+
+
+ {/* Grid overlay for professional look */} +
+
{/* Professional Header */}
@@ -120,7 +103,7 @@ const TeamSlideLayout: React.FC = ({ data: slideData, acce ? 'text-white drop-shadow-lg' : 'text-slate-900' }`}> - + {slideData?.title} @@ -135,103 +118,108 @@ const TeamSlideLayout: React.FC = ({ data: slideData, acce )}
-
-
+
+
{/* Enhanced Team Grid */}
-
- {slideData?.teamMembers?.map((member, index) => ( -
- {/* Card accent */} -
+
+ {/* Team grid with responsive layout */} +
+ {slideData?.teamMembers?.map((member, index) => ( +
+ {/* Card accent */} +
- {/* Professional Avatar */} -
-
- {member.image ? ( - {member.name} - ) : ( -
- {member.name.split(' ').map(n => n[0]).join('')} -
- )} -
+ {/* Professional Avatar */} +
+
+ {member.image ? ( + {member.name} + ) : ( +
+ {member.name.split(' ').map(n => n[0]).join('')} +
+ )} +
+
-
- {/* Member Info */} -
-

- {member.name} -

-

- {member.title} -

- - {member.bio && ( -

- {member.bio} + {/* Member Details */} +

+

+ {member.name} +

+

+ {member.title}

- )} + + {member.bio && ( +

+ {member.bio} +

+ )} + + {/* Contact Icons */} +
+ {member.email && ( + + + + + + + )} + + {member.linkedin && ( + + + + + + )} +
+
+ + {/* Background decoration */} +
- - {/* Professional Contact Links */} -
- {member.email && ( - - - - - - - )} - - {member.linkedin && ( - - - - - - )} -
- - {/* Background decoration */} -
-
- ))} + ))} +
{/* Enhanced decorative accent */} -
+
-
+
{/* Professional corner accents */} -
+
) } diff --git a/servers/nextjs/components/layouts/TimelineSlideLayout.tsx b/servers/nextjs/components/layouts/TimelineSlideLayout.tsx index aca68146..81a4281b 100644 --- a/servers/nextjs/components/layouts/TimelineSlideLayout.tsx +++ b/servers/nextjs/components/layouts/TimelineSlideLayout.tsx @@ -1,61 +1,75 @@ import React from 'react' import * as z from "zod"; -import { imageSchema } from './defaultSchemes'; - +import { ImageSchema, IconSchema } from './defaultSchemes'; export const layoutId = 'timeline-slide' export const layoutName = 'Timeline Slide' -export const layoutDescription = 'A slide with a title, subtitle, and timeline items' +export const layoutDescription = 'A professional slide featuring a chronological timeline with dates, events, and descriptions.' const timelineSlideSchema = z.object({ - title: z.string().min(3).max(100).default('Project Timeline').meta({ - description: "Title of the slide", + title: z.string().min(3).max(100).default('Our Journey').meta({ + description: "Main title of the slide", }), - subtitle: z.string().min(3).max(150).optional().meta({ + subtitle: z.string().min(10).max(200).optional().meta({ description: "Optional subtitle or description", }), - timelineItems: z.array(z.object({ - date: z.string().min(2).max(50).meta({ + events: z.array(z.object({ + date: z.string().min(2).max(20).meta({ description: "Date or time period", }), - title: z.string().min(3).max(100).meta({ - description: "Event or milestone title", + title: z.string().min(2).max(50).meta({ + description: "Event title", }), - description: z.string().min(10).max(300).meta({ + description: z.string().min(10).max(150).meta({ description: "Event description", }), - status: z.enum(['completed', 'current', 'upcoming']).default('upcoming').meta({ - description: "Timeline item status", + icon: IconSchema.default({ + url: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg', + prompt: 'Default event icon' + }).meta({ + description: "Icon for the event", }) })).min(2).max(6).default([ { - date: 'Q1 2024', - title: 'Project Initiation', - description: 'Project planning, team assembly, and initial requirements gathering', - status: 'completed' + date: '2020', + title: 'Foundation', + description: 'Company founded with a vision to transform digital experiences', + icon: { + url: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg', + prompt: 'Foundation icon' + } }, { - date: 'Q2 2024', - title: 'Development Phase', - description: 'Core development work, prototype creation, and testing implementation', - status: 'current' + date: '2021', + title: 'First Success', + description: 'Launched first product and gained initial market traction', + icon: { + url: 'https://cdn.pixabay.com/photo/2016/02/19/11/19/office-1209640_1280.jpg', + prompt: 'First success icon' + } }, { - date: 'Q3 2024', - title: 'Testing & QA', - description: 'Comprehensive testing, quality assurance, and user acceptance testing', - status: 'upcoming' + date: '2022', + title: 'Expansion', + description: 'Expanded team and entered new markets with innovative solutions', + icon: { + url: 'https://cdn.pixabay.com/photo/2017/08/10/08/47/laptop-2619235_1280.jpg', + prompt: 'Expansion icon' + } }, { - date: 'Q4 2024', - title: 'Launch & Deployment', - description: 'Final deployment, go-live activities, and post-launch monitoring', - status: 'upcoming' + date: '2023', + title: 'Innovation', + description: 'Introduced breakthrough technology and achieved industry recognition', + icon: { + url: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg', + prompt: 'Innovation icon' + } } ]).meta({ - description: "Timeline events (2-6 items)", + description: "List of timeline events (2-6 items)", }), - backgroundImage: imageSchema.optional().meta({ + backgroundImage: ImageSchema.optional().meta({ description: "Background image for the slide", }) }) @@ -66,179 +80,93 @@ export type TimelineSlideData = z.infer interface TimelineSlideLayoutProps { data?: Partial - accentColor?: 'blue' | 'green' | 'purple' | 'orange' | 'red' } -const TimelineSlideLayout: React.FC = ({ data: slideData, accentColor = 'blue' }) => { - - const accentColors = { - blue: 'from-blue-600 to-blue-800', - green: 'from-emerald-600 to-emerald-800', - purple: 'from-violet-600 to-violet-800', - orange: 'from-orange-600 to-orange-800', - red: 'from-red-600 to-red-800' - } - - const accentSolids = { - blue: 'bg-blue-600', - green: 'bg-emerald-600', - purple: 'bg-violet-600', - orange: 'bg-orange-600', - red: 'bg-red-600' - } - - const statusColors = { - completed: { - blue: 'bg-emerald-600 border-emerald-600 shadow-emerald-200', - green: 'bg-emerald-600 border-emerald-600 shadow-emerald-200', - purple: 'bg-emerald-600 border-emerald-600 shadow-emerald-200', - orange: 'bg-emerald-600 border-emerald-600 shadow-emerald-200', - red: 'bg-emerald-600 border-emerald-600 shadow-emerald-200' - }, - current: { - blue: 'bg-blue-600 border-blue-600 ring-4 ring-blue-200 shadow-blue-300', - green: 'bg-emerald-600 border-emerald-600 ring-4 ring-emerald-200 shadow-emerald-300', - purple: 'bg-violet-600 border-violet-600 ring-4 ring-violet-200 shadow-violet-300', - orange: 'bg-orange-600 border-orange-600 ring-4 ring-orange-200 shadow-orange-300', - red: 'bg-red-600 border-red-600 ring-4 ring-red-200 shadow-red-300' - }, - upcoming: { - blue: 'bg-slate-300 border-slate-400 shadow-slate-200', - green: 'bg-slate-300 border-slate-400 shadow-slate-200', - purple: 'bg-slate-300 border-slate-400 shadow-slate-200', - orange: 'bg-slate-300 border-slate-400 shadow-slate-200', - red: 'bg-slate-300 border-slate-400 shadow-slate-200' - } - } - - const lineColors = { - blue: 'bg-blue-200', - green: 'bg-emerald-200', - purple: 'bg-violet-200', - orange: 'bg-orange-200', - red: 'bg-red-200' - } +const TimelineSlideLayout: React.FC = ({ data: slideData }) => { return (
- {/* Enhanced geometric background decoration */} -
-
-
+ {/* Background Effects */} +
+
+
-
- {/* Professional Header */} -
-

- - {slideData?.title} - -

+ {/* Header section */} +
+

+ {slideData?.title || 'Our Journey'} +

+ {slideData?.subtitle && ( +

+ {slideData.subtitle} +

+ )} +
+
- {slideData?.subtitle && ( -

- {slideData?.subtitle} -

- )} + {/* Timeline section */} +
+
+ {/* Timeline line */} +
-
-
-
-
-
- - {/* Enhanced Timeline */} -
-
-
- {/* Timeline line */} -
-
- - {slideData?.timelineItems?.map((item, index) => ( -
- {/* Timeline node */} -
- {item.status === 'completed' && ( -
- - - -
- )} - {item.status === 'current' && ( -
-
-
- )} -
- - {/* Timeline content card */} -
- {/* Card accent */} -
- -
- {/* Date */} -
- {item.date} -
- - {/* Title */} -

- {item.title} -

- - {/* Description */} -

- {item.description} -

+ {/* Timeline events */} +
+ {slideData?.events?.map((event, index) => ( +
+ {/* Event content */} +
+
+ {/* Date */} +
+ {event.date}
- {/* Status indicator */} -
- - {item.status === 'completed' && '✓ Completed'} - {item.status === 'current' && '● In Progress'} - {item.status === 'upcoming' && '○ Upcoming'} - -
+ {/* Title with icon */} +

+
+ {event.icon?.prompt +
+ {event.title} +

- {/* Background decoration */} -
+ {/* Description */} +

+ {event.description} +

+ + {/* Accent */} +
- ))} -
+ + {/* Timeline dot */} +
+
+
+
+ ))}
-
+
- {/* Enhanced decorative accent */} -
-
-
-
- - {/* Professional corner accents */} -
+ {/* Bottom decorative accent */} +
) } diff --git a/servers/nextjs/components/layouts/Type1SlideLayout.tsx b/servers/nextjs/components/layouts/Type1SlideLayout.tsx new file mode 100644 index 00000000..30c73952 --- /dev/null +++ b/servers/nextjs/components/layouts/Type1SlideLayout.tsx @@ -0,0 +1,65 @@ +import React from 'react' +import * as z from "zod"; +import { ImageSchema } from './defaultSchemes'; + +export const layoutId = 'type1-slide' +export const layoutName = 'Type1 Slide' +export const layoutDescription = 'A clean two-column layout with title and description on the left and a featured image on the right.' + +const type1SlideSchema = z.object({ + title: z.string().min(3).max(100).default('Sample Title').meta({ + description: "Main title of the slide", + }), + description: z.string().min(10).max(500).default('Your description content goes here. This layout provides a clean and professional way to present content with supporting imagery.').meta({ + description: "Main description text", + }), + image: ImageSchema.default({ + url: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg', + prompt: 'A beautiful road in the mountains' + }).meta({ + description: "Main slide image", + }) +}) + +export const Schema = type1SlideSchema + +export type Type1SlideData = z.infer + +interface Type1SlideLayoutProps { + data?: Partial +} + +const Type1SlideLayout: React.FC = ({ data: slideData }) => { + + return ( +
+
+
+ {/* Title */} +

+ {slideData?.title || 'Sample Title'} +

+ + {/* Description */} +

+ {slideData?.description || 'Your description content goes here. This layout provides a clean and professional way to present content with supporting imagery.'} +

+
+ + {/* Image */} +
+ {slideData?.image?.prompt +
+
+
+ ) +} + +export default Type1SlideLayout \ No newline at end of file diff --git a/servers/nextjs/components/layouts/Type2NumberedSlideLayout.tsx b/servers/nextjs/components/layouts/Type2NumberedSlideLayout.tsx new file mode 100644 index 00000000..74d6b10f --- /dev/null +++ b/servers/nextjs/components/layouts/Type2NumberedSlideLayout.tsx @@ -0,0 +1,124 @@ +import React from 'react' +import * as z from "zod"; + +export const layoutId = 'type2-numbered-slide' +export const layoutName = 'Type2 Numbered Slide' +export const layoutDescription = 'A content layout with title and numbered content items with large numerals and shadow boxes.' + +const type2NumberedSlideSchema = z.object({ + title: z.string().min(3).max(100).default('Main Title').meta({ + description: "Main title of the slide", + }), + items: z.array(z.object({ + heading: z.string().min(2).max(100).meta({ + description: "Item heading", + }), + description: z.string().min(10).max(300).meta({ + description: "Item description", + }) + })).min(2).max(4).default([ + { + heading: 'First Point', + description: 'Description for the first key point that explains important details' + }, + { + heading: 'Second Point', + description: 'Description for the second key point with relevant information' + }, + { + heading: 'Third Point', + description: 'Description for the third key point highlighting crucial aspects' + } + ]).meta({ + description: "List of content items (2-4 items)", + }) +}) + +export const Schema = type2NumberedSlideSchema + +export type Type2NumberedSlideData = z.infer + +interface Type2NumberedSlideLayoutProps { + data?: Partial +} + +const Type2NumberedSlideLayout: React.FC = ({ data: slideData }) => { + const items = slideData?.items || [] + const isGridLayout = items.length >= 4 + const numberTranslations: string[] = ['01', '02', '03', '04', '05', '06'] + + const renderGridContent = () => { + return ( +
+ {items.map((item, index) => ( +
+
+
+ {numberTranslations[index] || `0${index + 1}`} +
+
+

+ {item.heading} +

+

+ {item.description} +

+
+
+
+ ))} +
+ ) + } + + const renderHorizontalContent = () => { + return ( +
+ {items.map((item, index) => ( +
+
+ {numberTranslations[index] || `0${index + 1}`} +
+
+

+ {item.heading} +

+

+ {item.description} +

+
+
+ ))} +
+ ) + } + + return ( +
+
+

+ {slideData?.title || 'Main Title'} +

+
+ + {isGridLayout ? renderGridContent() : renderHorizontalContent()} +
+ ) +} + +export default Type2NumberedSlideLayout \ No newline at end of file diff --git a/servers/nextjs/components/layouts/Type2SlideLayout.tsx b/servers/nextjs/components/layouts/Type2SlideLayout.tsx new file mode 100644 index 00000000..49f744ed --- /dev/null +++ b/servers/nextjs/components/layouts/Type2SlideLayout.tsx @@ -0,0 +1,111 @@ +import React from 'react' +import * as z from "zod"; + +export const layoutId = 'type2-slide' +export const layoutName = 'Type2 Slide' +export const layoutDescription = 'A flexible content layout with title and multiple content items in default presentation style.' + +const type2SlideSchema = z.object({ + title: z.string().min(3).max(100).default('Main Title').meta({ + description: "Main title of the slide", + }), + items: z.array(z.object({ + heading: z.string().min(2).max(100).meta({ + description: "Item heading", + }), + description: z.string().min(10).max(300).meta({ + description: "Item description", + }) + })).min(2).max(4).default([ + { + heading: 'First Point', + description: 'Description for the first key point that explains important details' + }, + { + heading: 'Second Point', + description: 'Description for the second key point with relevant information' + }, + { + heading: 'Third Point', + description: 'Description for the third key point highlighting crucial aspects' + } + ]).meta({ + description: "List of content items (2-4 items)", + }) +}) + +export const Schema = type2SlideSchema + +export type Type2SlideData = z.infer + +interface Type2SlideLayoutProps { + data?: Partial +} + +const Type2SlideLayout: React.FC = ({ data: slideData }) => { + const items = slideData?.items || [] + const isGridLayout = items.length >= 4 + + const renderGridContent = () => { + return ( +
+ {items.map((item, index) => ( +
+
+

+ {item.heading} +

+

+ {item.description} +

+
+
+ ))} +
+ ) + } + + const renderHorizontalContent = () => { + return ( +
+ {items.map((item, index) => ( +
+
+

+ {item.heading} +

+

+ {item.description} +

+
+
+ ))} +
+ ) + } + + return ( +
+
+

+ {slideData?.title || 'Main Title'} +

+
+ + {isGridLayout ? renderGridContent() : renderHorizontalContent()} +
+ ) +} + +export default Type2SlideLayout \ No newline at end of file diff --git a/servers/nextjs/components/layouts/Type2TimelineSlideLayout.tsx b/servers/nextjs/components/layouts/Type2TimelineSlideLayout.tsx new file mode 100644 index 00000000..580627d1 --- /dev/null +++ b/servers/nextjs/components/layouts/Type2TimelineSlideLayout.tsx @@ -0,0 +1,102 @@ +import React from 'react' +import * as z from "zod"; + +export const layoutId = 'type2-timeline-slide' +export const layoutName = 'Type2 Timeline Slide' +export const layoutDescription = 'A timeline layout with title and content items arranged horizontally with numbered circles and connecting line.' + +const type2TimelineSlideSchema = z.object({ + title: z.string().min(3).max(100).default('Main Title').meta({ + description: "Main title of the slide", + }), + items: z.array(z.object({ + heading: z.string().min(2).max(100).meta({ + description: "Item heading", + }), + description: z.string().min(10).max(300).meta({ + description: "Item description", + }) + })).min(2).max(4).default([ + { + heading: 'First Point', + description: 'Description for the first key point that explains important details' + }, + { + heading: 'Second Point', + description: 'Description for the second key point with relevant information' + }, + { + heading: 'Third Point', + description: 'Description for the third key point highlighting crucial aspects' + } + ]).meta({ + description: "List of content items (2-4 items)", + }) +}) + +export const Schema = type2TimelineSlideSchema + +export type Type2TimelineSlideData = z.infer + +interface Type2TimelineSlideLayoutProps { + data?: Partial +} + +const Type2TimelineSlideLayout: React.FC = ({ data: slideData }) => { + const items = slideData?.items || [] + const numberTranslations: string[] = ['01', '02', '03', '04', '05', '06'] + + const renderTimelineContent = () => { + return ( +
+ {/* Timeline Header with Numbers and Line */} +
+ {/* Horizontal Line */} +
+ + {/* Timeline Numbers */} + {items.map((_, index) => ( +
+ {numberTranslations[index] || `0${index + 1}`} +
+ ))} +
+ + {/* Timeline Content */} +
+ {items.map((item, index) => ( +
+
+

+ {item.heading} +

+

+ {item.description} +

+
+
+ ))} +
+
+ ) + } + + return ( +
+
+

+ {slideData?.title || 'Main Title'} +

+
+ + {renderTimelineContent()} +
+ ) +} + +export default Type2TimelineSlideLayout \ No newline at end of file diff --git a/servers/nextjs/components/layouts/Type3SlideLayout.tsx b/servers/nextjs/components/layouts/Type3SlideLayout.tsx new file mode 100644 index 00000000..21385dba --- /dev/null +++ b/servers/nextjs/components/layouts/Type3SlideLayout.tsx @@ -0,0 +1,119 @@ +import React from 'react' +import * as z from "zod"; +import { ImageSchema } from './defaultSchemes'; + +export const layoutId = 'type3-slide' +export const layoutName = 'Type3 Slide' +export const layoutDescription = 'A centered title with a grid of image cards, each containing a heading and description.' + +const type3SlideSchema = z.object({ + title: z.string().min(3).max(100).default('Featured Content').meta({ + description: "Main title of the slide", + }), + items: z.array(z.object({ + heading: z.string().min(2).max(100).meta({ + description: "Item heading", + }), + description: z.string().min(10).max(300).meta({ + description: "Item description", + }), + image: ImageSchema.meta({ + description: "Item image", + }) + })).min(2).max(4).default([ + { + heading: 'First Feature', + description: 'Description for the first featured item with detailed information', + image: { + url: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg', + prompt: 'A beautiful road in the mountains' + } + }, + { + heading: 'Second Feature', + description: 'Description for the second featured item with relevant details', + image: { + url: 'https://cdn.pixabay.com/photo/2016/02/19/11/19/office-1209640_1280.jpg', + prompt: 'Modern office workspace' + } + }, + { + heading: 'Third Feature', + description: 'Description for the third featured item with important points', + image: { + url: 'https://cdn.pixabay.com/photo/2017/08/10/08/47/laptop-2619235_1280.jpg', + prompt: 'Laptop with code on screen' + } + } + ]).meta({ + description: "List of featured items (2-4 items)", + }) +}) + +export const Schema = type3SlideSchema + +export type Type3SlideData = z.infer + +interface Type3SlideLayoutProps { + data?: Partial +} + +const Type3SlideLayout: React.FC = ({ data: slideData }) => { + const items = slideData?.items || [] + + const getGridCols = (length: number) => { + switch (length) { + case 1: return 'lg:grid-cols-1'; + case 2: return 'lg:grid-cols-2'; + case 3: return 'lg:grid-cols-3'; + case 4: return 'lg:grid-cols-4'; + default: return 'lg:grid-cols-1'; + } + } + + return ( +
+
+

+ {slideData?.title || 'Featured Content'} +

+
+ +
+ {items.map((item, index) => ( +
+ {/* Image */} +
+ {item.image?.prompt +
+ + {/* Content */} +
+

+ {item.heading} +

+

+ {item.description} +

+
+
+ ))} +
+
+ ) +} + +export default Type3SlideLayout \ No newline at end of file diff --git a/servers/nextjs/components/layouts/Type4SlideLayout.tsx b/servers/nextjs/components/layouts/Type4SlideLayout.tsx new file mode 100644 index 00000000..1fbb236f --- /dev/null +++ b/servers/nextjs/components/layouts/Type4SlideLayout.tsx @@ -0,0 +1,71 @@ +import React from 'react' +import * as z from "zod"; + +export const layoutId = 'type4-slide' +export const layoutName = 'Type4 Slide' +export const layoutDescription = 'A chart-focused layout with title, chart visualization, and description text.' + +const type4SlideSchema = z.object({ + title: z.string().min(3).max(100).default('Chart Analysis').meta({ + description: "Main title of the slide", + }), + description: z.string().min(10).max(500).default('This chart shows important data trends and insights that help understand the current situation and make informed decisions.').meta({ + description: "Description text for the chart", + }), + chartData: z.any().optional().meta({ + description: "Chart data object", + }), + isFullSizeChart: z.boolean().default(false).meta({ + description: "Whether to display chart in full size mode", + }) +}) + +export const Schema = type4SlideSchema + +export type Type4SlideData = z.infer + +interface Type4SlideLayoutProps { + data?: Partial +} + +const Type4SlideLayout: React.FC = ({ data: slideData }) => { + const isFullSizeGraph = slideData?.isFullSizeChart || false + + // Simple placeholder chart component + const ChartPlaceholder = () => ( +
+
+
📊
+

Chart Component

+

Data visualization will appear here

+
+
+ ) + + return ( +
+

+ {slideData?.title || 'Chart Analysis'} +

+ +
+
+ +
+
+

+ {slideData?.description || 'This chart shows important data trends and insights that help understand the current situation and make informed decisions.'} +

+
+
+
+ ) +} + +export default Type4SlideLayout \ No newline at end of file diff --git a/servers/nextjs/components/layouts/Type5SlideLayout.tsx b/servers/nextjs/components/layouts/Type5SlideLayout.tsx new file mode 100644 index 00000000..91765142 --- /dev/null +++ b/servers/nextjs/components/layouts/Type5SlideLayout.tsx @@ -0,0 +1,102 @@ +import React from 'react' +import * as z from "zod"; + +export const layoutId = 'type5-slide' +export const layoutName = 'Type5 Slide' +export const layoutDescription = 'A two-column layout with title and description on the left, and numbered items with large numerals on the right.' + +const type5SlideSchema = z.object({ + title: z.string().min(3).max(100).default('Key Points').meta({ + description: "Main title of the slide", + }), + description: z.string().min(10).max(500).default('Here is the main description that provides context and introduction to the numbered points on the right side.').meta({ + description: "Main description text", + }), + items: z.array(z.object({ + heading: z.string().min(2).max(100).meta({ + description: "Item heading", + }), + description: z.string().min(10).max(300).meta({ + description: "Item description", + }) + })).min(2).max(3).default([ + { + heading: 'First Key Point', + description: 'Detailed explanation of the first important point that supports the main topic' + }, + { + heading: 'Second Key Point', + description: 'Detailed explanation of the second important point with relevant information' + }, + { + heading: 'Third Key Point', + description: 'Detailed explanation of the third important point that concludes the discussion' + } + ]).meta({ + description: "List of numbered items (2-3 items)", + }) +}) + +export const Schema = type5SlideSchema + +export type Type5SlideData = z.infer + +interface Type5SlideLayoutProps { + data?: Partial +} + +const Type5SlideLayout: React.FC = ({ data: slideData }) => { + const items = slideData?.items || [] + const numberTranslations: string[] = ['01', '02', '03', '04', '05', '06'] + + return ( +
+
+ {/* Left section - Title and Description */} +
+

+ {slideData?.title || 'Key Points'} +

+ +

+ {slideData?.description || 'Here is the main description that provides context and introduction to the numbered points on the right side.'} +

+
+ + {/* Right section - Numbered items */} +
+
+ {items.map((item, index) => ( +
+
+
+ {numberTranslations[index] || `0${index + 1}`} +
+
+

+ {item.heading} +

+

+ {item.description} +

+
+
+
+ ))} +
+
+
+
+ ) +} + +export default Type5SlideLayout \ No newline at end of file diff --git a/servers/nextjs/components/layouts/Type6SlideLayout.tsx b/servers/nextjs/components/layouts/Type6SlideLayout.tsx new file mode 100644 index 00000000..c199e288 --- /dev/null +++ b/servers/nextjs/components/layouts/Type6SlideLayout.tsx @@ -0,0 +1,150 @@ +import React from 'react' +import * as z from "zod"; + +export const layoutId = 'type6-slide' +export const layoutName = 'Type6 Slide' +export const layoutDescription = 'A centered title with a flexible grid of icon-based content items, adapting layout based on item count.' + +const type6SlideSchema = z.object({ + title: z.string().min(3).max(100).default('Our Services').meta({ + description: "Main title of the slide", + }), + items: z.array(z.object({ + heading: z.string().min(2).max(100).meta({ + description: "Item heading", + }), + description: z.string().min(10).max(300).meta({ + description: "Item description", + }), + icon: z.string().default('⭐').meta({ + description: "Icon emoji for the item", + }) + })).min(2).max(6).default([ + { + heading: 'Professional Service', + description: 'High-quality professional services tailored to your specific needs and requirements', + icon: '🎯' + }, + { + heading: 'Expert Consultation', + description: 'Expert advice and consultation from experienced professionals in the field', + icon: '💡' + }, + { + heading: 'Quality Assurance', + description: 'Comprehensive quality assurance processes to ensure excellent results', + icon: '✅' + }, + { + heading: 'Customer Support', + description: 'Dedicated customer support available to assist you throughout the process', + icon: '🤝' + } + ]).meta({ + description: "List of service items (2-6 items)", + }) +}) + +export const Schema = type6SlideSchema + +export type Type6SlideData = z.infer + +interface Type6SlideLayoutProps { + data?: Partial +} + +const Type6SlideLayout: React.FC = ({ data: slideData }) => { + const items = slideData?.items || [] + const isGridLayout = items.length >= 4 + + const getGridCols = (length: number) => { + switch (length) { + case 1: return 'lg:grid-cols-1'; + case 2: return 'lg:grid-cols-2'; + case 3: return 'lg:grid-cols-3'; + case 4: return 'lg:grid-cols-4'; + case 5: return 'lg:grid-cols-5'; + case 6: return 'lg:grid-cols-6'; + default: return 'lg:grid-cols-1'; + } + } + + const renderGridContent = () => { + return ( +
4 ? 'md:grid-cols-3' : 'md:grid-cols-2'} gap-4 sm:gap-6 lg:gap-8 mt-4 lg:mt-12 w-full`}> + {items.map((item, index) => ( +
+
+
+
+ {item.icon} +
+
+
+

+ {item.heading} +

+

+ {item.description} +

+
+
+
+ ))} +
+ ) + } + + const renderHorizontalContent = () => { + return ( +
+ {items.map((item, index) => ( +
+
+
+ {item.icon} +
+
+
+

+ {item.heading} +

+

+ {item.description} +

+
+
+ ))} +
+ ) + } + + return ( +
+
+

+ {slideData?.title || 'Our Services'} +

+
+ + {isGridLayout ? renderGridContent() : renderHorizontalContent()} +
+ ) +} + +export default Type6SlideLayout \ No newline at end of file diff --git a/servers/nextjs/components/layouts/Type7SlideLayout.tsx b/servers/nextjs/components/layouts/Type7SlideLayout.tsx new file mode 100644 index 00000000..b717e433 --- /dev/null +++ b/servers/nextjs/components/layouts/Type7SlideLayout.tsx @@ -0,0 +1,173 @@ +import React from 'react' +import * as z from "zod"; +import { IconSchema } from './defaultSchemes'; + +export const layoutId = 'type7-slide' +export const layoutName = 'Type7 Slide' +export const layoutDescription = 'A centered title with a flexible grid of icon-based content items, adapting layout based on item count.' + +const type7SlideSchema = z.object({ + title: z.string().min(3).max(100).default('Our Services').meta({ + description: "Main title of the slide", + }), + items: z.array(z.object({ + heading: z.string().min(2).max(100).meta({ + description: "Item heading", + }), + description: z.string().min(10).max(300).meta({ + description: "Item description", + }), + icon: IconSchema.default({ + url: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg', + prompt: 'Default icon' + }).meta({ + description: "Icon for the item", + }) + })).min(2).max(6).default([ + { + heading: 'Professional Service', + description: 'High-quality professional services tailored to your specific needs and requirements', + icon: { + url: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg', + prompt: 'Professional service icon' + } + }, + { + heading: 'Expert Consultation', + description: 'Expert advice and consultation from experienced professionals in the field', + icon: { + url: 'https://cdn.pixabay.com/photo/2016/02/19/11/19/office-1209640_1280.jpg', + prompt: 'Expert consultation icon' + } + }, + { + heading: 'Quality Assurance', + description: 'Comprehensive quality assurance processes to ensure excellent results', + icon: { + url: 'https://cdn.pixabay.com/photo/2017/08/10/08/47/laptop-2619235_1280.jpg', + prompt: 'Quality assurance icon' + } + }, + { + heading: 'Customer Support', + description: 'Dedicated customer support available to assist you throughout the process', + icon: { + url: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg', + prompt: 'Customer support icon' + } + } + ]).meta({ + description: "List of service items (2-6 items)", + }) +}) + +export const Schema = type7SlideSchema + +export type Type7SlideData = z.infer + +interface Type7SlideLayoutProps { + data?: Partial +} + +const Type7SlideLayout: React.FC = ({ data: slideData }) => { + const items = slideData?.items || [] + const isGridLayout = items.length >= 4 + + const getGridCols = (length: number) => { + switch (length) { + case 1: return 'lg:grid-cols-1'; + case 2: return 'lg:grid-cols-2'; + case 3: return 'lg:grid-cols-3'; + case 4: return 'lg:grid-cols-4'; + case 5: return 'lg:grid-cols-5'; + case 6: return 'lg:grid-cols-6'; + default: return 'lg:grid-cols-1'; + } + } + + const renderGridContent = () => { + return ( +
4 ? 'md:grid-cols-3' : 'md:grid-cols-2'} gap-4 sm:gap-6 lg:gap-8 mt-4 lg:mt-12 w-full`}> + {items.map((item, index) => ( +
+
+
+
+ {item.icon?.prompt +
+
+
+

+ {item.heading} +

+

+ {item.description} +

+
+
+
+ ))} +
+ ) + } + + const renderHorizontalContent = () => { + return ( +
+ {items.map((item, index) => ( +
+
+
+ {item.icon?.prompt +
+
+
+

+ {item.heading} +

+

+ {item.description} +

+
+
+ ))} +
+ ) + } + + return ( +
+
+

+ {slideData?.title || 'Our Services'} +

+
+ + {isGridLayout ? renderGridContent() : renderHorizontalContent()} +
+ ) +} + +export default Type7SlideLayout \ No newline at end of file diff --git a/servers/nextjs/components/layouts/Type8SlideLayout.tsx b/servers/nextjs/components/layouts/Type8SlideLayout.tsx new file mode 100644 index 00000000..a87b18d5 --- /dev/null +++ b/servers/nextjs/components/layouts/Type8SlideLayout.tsx @@ -0,0 +1,167 @@ +import React from 'react' +import * as z from "zod"; +import { IconSchema } from './defaultSchemes'; + +export const layoutId = 'type8-slide' +export const layoutName = 'Type8 Slide' +export const layoutDescription = 'A two-column layout with title and description on the left, and icon-based items on the right.' + +const type8SlideSchema = z.object({ + title: z.string().min(3).max(100).default('Key Features').meta({ + description: "Main title of the slide", + }), + description: z.string().min(10).max(500).default('Here is the main description that provides context and introduces the key features outlined on the right side.').meta({ + description: "Main description text", + }), + items: z.array(z.object({ + heading: z.string().min(2).max(100).meta({ + description: "Item heading", + }), + description: z.string().min(10).max(300).meta({ + description: "Item description", + }), + icon: IconSchema.default({ + url: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg', + prompt: 'Default icon' + }).meta({ + description: "Icon for the item", + }) + })).min(2).max(3).default([ + { + heading: 'Advanced Features', + description: 'Cutting-edge functionality designed to enhance productivity and user experience', + icon: { + url: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg', + prompt: 'Advanced features icon' + } + }, + { + heading: 'Reliable Performance', + description: 'Consistent and dependable performance across all platforms and devices', + icon: { + url: 'https://cdn.pixabay.com/photo/2016/02/19/11/19/office-1209640_1280.jpg', + prompt: 'Reliable performance icon' + } + }, + { + heading: 'Secure Environment', + description: 'Enterprise-grade security measures to protect your data and privacy', + icon: { + url: 'https://cdn.pixabay.com/photo/2017/08/10/08/47/laptop-2619235_1280.jpg', + prompt: 'Secure environment icon' + } + } + ]).meta({ + description: "List of featured items (2-3 items)", + }) +}) + +export const Schema = type8SlideSchema + +export type Type8SlideData = z.infer + +interface Type8SlideLayoutProps { + data?: Partial +} + +const Type8SlideLayout: React.FC = ({ data: slideData }) => { + const items = slideData?.items || [] + + const renderItems = () => { + if (items.length === 2) { + // Vertical stacked layout for 2 items + return ( +
+ {items.map((item, index) => ( +
+
+
+ {item.icon?.prompt +
+
+
+

+ {item.heading} +

+

+ {item.description} +

+
+
+ ))} +
+ ) + } else { + // Horizontal layout with side icons for 3+ items + return ( +
+ {items.map((item, index) => ( +
+
+
+
+ {item.icon?.prompt +
+
+
+

+ {item.heading} +

+

+ {item.description} +

+
+
+
+ ))} +
+ ) + } + } + + return ( +
+
+ {/* Left section - Title and Description */} +
+

+ {slideData?.title || 'Key Features'} +

+ +

+ {slideData?.description || 'Here is the main description that provides context and introduces the key features outlined on the right side.'} +

+
+ + {/* Right section - Items */} +
+ {renderItems()} +
+
+
+ ) +} + +export default Type8SlideLayout \ No newline at end of file diff --git a/servers/nextjs/components/layouts/defaultSchemes.ts b/servers/nextjs/components/layouts/defaultSchemes.ts index c6ea594b..90e668c5 100644 --- a/servers/nextjs/components/layouts/defaultSchemes.ts +++ b/servers/nextjs/components/layouts/defaultSchemes.ts @@ -1,6 +1,6 @@ import * as z from "zod"; -export const imageSchema = z.object({ +export const ImageSchema = z.object({ url: z.url().meta({ description: "URL to image", }), diff --git a/servers/nextjs/store/slices/presentationGeneration.ts b/servers/nextjs/store/slices/presentationGeneration.ts index 04f0b683..8cebde83 100644 --- a/servers/nextjs/store/slices/presentationGeneration.ts +++ b/servers/nextjs/store/slices/presentationGeneration.ts @@ -52,7 +52,7 @@ export interface PresentationData { thumbnail: string | null; language: string; } | null; - slides: Slide[]; + slides: any; } interface PresentationGenerationState { @@ -143,7 +143,7 @@ const presentationGenerationSlice = createSlice({ // Update indices for all slides to ensure they remain sequential state.presentationData.slides = state.presentationData.slides.map( - (slide, idx) => ({ + (slide: any, idx: number) => ({ ...slide, index: idx, }) @@ -154,7 +154,7 @@ const presentationGenerationSlice = createSlice({ if (state.presentationData) { state.presentationData.slides.splice(action.payload, 1); state.presentationData.slides = state.presentationData.slides.map( - (slide, idx) => ({ + (slide: any, idx: number) => ({ ...slide, index: idx, })