Merge pull request #116 from presenton/fix/schema-validation
fix/schema validation
This commit is contained in:
commit
f92a760158
11 changed files with 1017 additions and 4 deletions
|
|
@ -50,8 +50,6 @@ You are an expert presentation creator. Generate structured presentations based
|
|||
- Generate **exactly** the number of slides requested
|
||||
- Distribute content **evenly** across slides
|
||||
- Create **balanced information flow**
|
||||
|
||||
|
||||
"""
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ from utils.llm_provider import (
|
|||
get_google_llm_client,
|
||||
get_llm_client,
|
||||
get_nano_model,
|
||||
get_small_model,
|
||||
is_google_selected,
|
||||
)
|
||||
from utils.schema_utils import remove_fields_from_schema
|
||||
|
|
@ -20,10 +19,12 @@ system_prompt = """
|
|||
2. Generate structured slide based on the outline and title.
|
||||
|
||||
# Notes
|
||||
- **Strictly follow the max and min character limit for each property in the slide.**
|
||||
- Slide body should not use words like "This slide", "This presentation".
|
||||
- Rephrase the slide body to make it flow naturally.
|
||||
- Provide prompt to generate image on "__image_prompt__" property.
|
||||
- Provide query to search icon on "__icon_query__" property.
|
||||
- Do not use markdown formatting in slide body.
|
||||
- **Strictly follow the max and min character limit for every property in the slide.**
|
||||
"""
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,117 @@
|
|||
import React from 'react'
|
||||
import * as z from "zod";
|
||||
import { ImageSchema } from '@/presentation-layouts/defaultSchemes';
|
||||
|
||||
export const layoutId = 'general-intro-slide'
|
||||
export const layoutName = 'Intro Slide'
|
||||
export const layoutDescription = 'A clean slide layout with title, description text, presenter info, and a supporting image.'
|
||||
|
||||
const introSlideSchema = z.object({
|
||||
title: z.string().min(3).max(50).default('Product Overview').meta({
|
||||
description: "Main title of the slide",
|
||||
}),
|
||||
description: z.string().min(10).max(200).default('Our product offers customizable dashboards for real-time reporting and data-driven decisions. It integrates with third-party tools to enhance operations and scales with business growth for improved efficiency.').meta({
|
||||
description: "Main description text content",
|
||||
}),
|
||||
presenterName: z.string().min(2).max(50).default('John Doe').meta({
|
||||
description: "Name of the presenter",
|
||||
}),
|
||||
presentationDate: z.string().min(2).max(50).default('December 2024').meta({
|
||||
description: "Date of the presentation",
|
||||
}),
|
||||
image: ImageSchema.default({
|
||||
__image_url__: 'https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
|
||||
__image_prompt__: 'Business team in meeting room discussing product features and solutions'
|
||||
}).meta({
|
||||
description: "Supporting image for the slide",
|
||||
})
|
||||
})
|
||||
|
||||
export const Schema = introSlideSchema
|
||||
|
||||
export type IntroSlideData = z.infer<typeof introSlideSchema>
|
||||
|
||||
interface IntroSlideLayoutProps {
|
||||
data?: Partial<IntroSlideData>
|
||||
}
|
||||
|
||||
const IntroSlideLayout: React.FC<IntroSlideLayoutProps> = ({ data: slideData }) => {
|
||||
// Generate initials from presenter name
|
||||
const getInitials = (name: string) => {
|
||||
return name.split(' ').map(word => word.charAt(0).toUpperCase()).join('');
|
||||
};
|
||||
|
||||
const presenterInitials = getInitials(slideData?.presenterName || 'John Doe');
|
||||
return (
|
||||
<>
|
||||
{/* Import Google Fonts */}
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<div
|
||||
className="w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video bg-white relative z-20 mx-auto overflow-hidden"
|
||||
style={{
|
||||
fontFamily: 'Poppins, sans-serif'
|
||||
}}
|
||||
>
|
||||
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="relative z-10 flex h-full px-8 sm:px-12 lg:px-20 pb-8">
|
||||
{/* Left Section - Image */}
|
||||
<div className="flex-1 flex items-center justify-center pr-8">
|
||||
<div className="w-full max-w-lg h-80 rounded-2xl overflow-hidden shadow-lg">
|
||||
<img
|
||||
src={slideData?.image?.__image_url__ || ''}
|
||||
alt={slideData?.image?.__image_prompt__ || slideData?.title || ''}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Section - Content */}
|
||||
<div className="flex-1 flex flex-col justify-center pl-8 space-y-6">
|
||||
{/* Title */}
|
||||
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-bold text-gray-900 leading-tight">
|
||||
{slideData?.title || 'Product Overview'}
|
||||
</h1>
|
||||
|
||||
{/* Purple accent line */}
|
||||
<div className="w-20 h-1 bg-purple-600"></div>
|
||||
|
||||
{/* Description */}
|
||||
<p className="text-base sm:text-lg text-gray-700 leading-relaxed">
|
||||
{slideData?.description || 'Our product offers customizable dashboards for real-time reporting and data-driven decisions. It integrates with third-party tools to enhance operations and scales with business growth for improved efficiency.'}
|
||||
</p>
|
||||
|
||||
{/* Presenter Section */}
|
||||
<div className="bg-white/50 backdrop-blur-sm rounded-lg p-4 lg:p-6 border border-gray-200 shadow-sm">
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Custom Initials Icon */}
|
||||
<div className="w-10 h-10 lg:w-12 lg:h-12 bg-purple-600 rounded-full flex items-center justify-center">
|
||||
<span className="text-white font-bold text-sm lg:text-base">
|
||||
{presenterInitials}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Presenter Info */}
|
||||
<div className="flex flex-col">
|
||||
<span className="text-lg lg:text-xl font-bold text-gray-900">
|
||||
{slideData?.presenterName || 'John Doe'}
|
||||
</span>
|
||||
<span className="text-sm lg:text-base text-gray-600 font-medium">
|
||||
{slideData?.presentationDate || 'December 2024'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default IntroSlideLayout
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
import React from 'react'
|
||||
import * as z from "zod";
|
||||
import { ImageSchema } from '@/presentation-layouts/defaultSchemes';
|
||||
|
||||
export const layoutId = 'basic-info-slide'
|
||||
export const layoutName = 'Basic Info'
|
||||
export const layoutDescription = 'A clean slide layout with title, description text, and a supporting image.'
|
||||
|
||||
const basicInfoSlideSchema = z.object({
|
||||
title: z.string().min(3).max(50).default('Product Overview').meta({
|
||||
description: "Main title of the slide",
|
||||
}),
|
||||
description: z.string().min(10).max(200).default('Our product offers customizable dashboards for real-time reporting and data-driven decisions. It integrates with third-party tools to enhance operations and scales with business growth for improved efficiency.').meta({
|
||||
description: "Main description text content",
|
||||
}),
|
||||
image: ImageSchema.default({
|
||||
__image_url__: 'https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
|
||||
__image_prompt__: 'Business team in meeting room discussing product features and solutions'
|
||||
}).meta({
|
||||
description: "Supporting image for the slide",
|
||||
})
|
||||
})
|
||||
|
||||
export const Schema = basicInfoSlideSchema
|
||||
|
||||
export type BasicInfoSlideData = z.infer<typeof basicInfoSlideSchema>
|
||||
|
||||
interface BasicInfoSlideLayoutProps {
|
||||
data?: Partial<BasicInfoSlideData>
|
||||
}
|
||||
|
||||
const BasicInfoSlideLayout: React.FC<BasicInfoSlideLayoutProps> = ({ data: slideData }) => {
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Import Google Fonts */}
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<div
|
||||
className="w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video bg-white relative z-20 mx-auto overflow-hidden"
|
||||
style={{
|
||||
fontFamily: 'Poppins, sans-serif'
|
||||
}}
|
||||
>
|
||||
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="relative z-10 flex h-full px-8 sm:px-12 lg:px-20 pb-8">
|
||||
{/* Left Section - Image */}
|
||||
<div className="flex-1 flex items-center justify-center pr-8">
|
||||
<div className="w-full max-w-lg h-80 rounded-2xl overflow-hidden shadow-lg">
|
||||
<img
|
||||
src={slideData?.image?.__image_url__ || ''}
|
||||
alt={slideData?.image?.__image_prompt__ || slideData?.title || ''}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Section - Content */}
|
||||
<div className="flex-1 flex flex-col justify-center pl-8 space-y-6">
|
||||
{/* Title */}
|
||||
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-bold text-gray-900 leading-tight">
|
||||
{slideData?.title || 'Product Overview'}
|
||||
</h1>
|
||||
|
||||
{/* Purple accent line */}
|
||||
<div className="w-20 h-1 bg-purple-600"></div>
|
||||
|
||||
{/* Description */}
|
||||
<p className="text-base sm:text-lg text-gray-700 leading-relaxed">
|
||||
{slideData?.description || 'Our product offers customizable dashboards for real-time reporting and data-driven decisions. It integrates with third-party tools to enhance operations and scales with business growth for improved efficiency.'}
|
||||
</p>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default BasicInfoSlideLayout
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
import React from 'react'
|
||||
import * as z from "zod";
|
||||
import { ImageSchema, IconSchema } from '@/presentation-layouts/defaultSchemes';
|
||||
|
||||
export const layoutId = 'bullet-icons-only-slide'
|
||||
export const layoutName = 'Bullet Icons Only'
|
||||
export const layoutDescription = 'A slide layout with title, grid of bullet points with icons (no descriptions), and a supporting image.'
|
||||
|
||||
const bulletIconsOnlySlideSchema = z.object({
|
||||
title: z.string().min(3).max(50).default('Solutions').meta({
|
||||
description: "Main title of the slide",
|
||||
}),
|
||||
image: ImageSchema.default({
|
||||
__image_url__: 'https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
|
||||
__image_prompt__: 'Business professionals collaborating and discussing solutions'
|
||||
}).meta({
|
||||
description: "Supporting image for the slide",
|
||||
}),
|
||||
bulletPoints: z.array(z.object({
|
||||
title: z.string().min(2).max(80).meta({
|
||||
description: "Bullet point title",
|
||||
}),
|
||||
subtitle: z.string().min(5).max(150).optional().meta({
|
||||
description: "Optional short subtitle or brief explanation",
|
||||
}),
|
||||
icon: IconSchema,
|
||||
})).min(2).max(4).default([
|
||||
{
|
||||
title: 'Custom Software',
|
||||
subtitle: 'We create tailored software to optimize processes and boost efficiency.',
|
||||
icon: {
|
||||
__icon_url__: 'https://cdn.jsdelivr.net/npm/lucide@latest/dist/esm/icons/code.js',
|
||||
__icon_query__: 'code software development'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Digital Consulting',
|
||||
subtitle: 'Our consultants guide organizations in leveraging the latest technologies.',
|
||||
icon: {
|
||||
__icon_url__: 'https://cdn.jsdelivr.net/npm/lucide@latest/dist/esm/icons/users.js',
|
||||
__icon_query__: 'users consulting team'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Support Services',
|
||||
subtitle: 'We provide ongoing support to help businesses adapt and maintain performance.',
|
||||
icon: {
|
||||
__icon_url__: 'https://cdn.jsdelivr.net/npm/lucide@latest/dist/esm/icons/headphones.js',
|
||||
__icon_query__: 'headphones support service'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Scalable Marketing',
|
||||
subtitle: 'Our data-driven strategies help businesses expand their reach and engagement.',
|
||||
icon: {
|
||||
__icon_url__: 'https://cdn.jsdelivr.net/npm/lucide@latest/dist/esm/icons/trending-up.js',
|
||||
__icon_query__: 'trending up marketing growth'
|
||||
}
|
||||
}
|
||||
]).meta({
|
||||
description: "List of bullet points with icons and optional subtitles",
|
||||
})
|
||||
})
|
||||
|
||||
export const Schema = bulletIconsOnlySlideSchema
|
||||
|
||||
export type BulletIconsOnlySlideData = z.infer<typeof bulletIconsOnlySlideSchema>
|
||||
|
||||
interface BulletIconsOnlySlideLayoutProps {
|
||||
data?: Partial<BulletIconsOnlySlideData>
|
||||
}
|
||||
|
||||
const BulletIconsOnlySlideLayout: React.FC<BulletIconsOnlySlideLayoutProps> = ({ data: slideData }) => {
|
||||
const bulletPoints = slideData?.bulletPoints || []
|
||||
|
||||
// Function to determine grid classes based on number of bullets
|
||||
const getGridClasses = (count: number) => {
|
||||
if (count <= 2) {
|
||||
return 'grid-cols-1 gap-6'
|
||||
} else if (count <= 4) {
|
||||
return 'grid-cols-2 gap-6'
|
||||
} else {
|
||||
return 'grid-cols-2 lg:grid-cols-3 gap-6'
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Import Google Fonts */}
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<div
|
||||
className="w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video bg-white relative z-20 mx-auto overflow-hidden"
|
||||
style={{
|
||||
fontFamily: 'Poppins, sans-serif'
|
||||
}}
|
||||
>
|
||||
{/* Decorative Wave Patterns */}
|
||||
<div className="absolute top-0 left-0 w-32 h-full opacity-10 overflow-hidden">
|
||||
<svg className="w-full h-full" viewBox="0 0 100 400" fill="none">
|
||||
<path d="M0 100C25 150 50 50 75 100C87.5 125 100 100 100 100V0H0V100Z" fill="#8b5cf6" opacity="0.4" />
|
||||
<path d="M0 200C37.5 250 62.5 150 100 200V150C75 175 50 150 25 175L0 200Z" fill="#8b5cf6" opacity="0.3" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div className="absolute bottom-0 left-0 w-48 h-32 opacity-10 overflow-hidden">
|
||||
<svg className="w-full h-full" viewBox="0 0 200 100" fill="none">
|
||||
<path d="M0 50C50 25 100 75 150 50C175 37.5 200 50 200 50V100H0V50Z" fill="#8b5cf6" opacity="0.2" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="relative z-10 flex h-full px-8 sm:px-12 lg:px-20 pt-8 pb-8">
|
||||
{/* Left Section - Title and Bullet Points */}
|
||||
<div className="flex-1 flex flex-col pr-8">
|
||||
{/* Title */}
|
||||
<h1 className="text-5xl sm:text-6xl lg:text-7xl font-bold text-gray-900 mb-8">
|
||||
{slideData?.title || 'Solutions'}
|
||||
</h1>
|
||||
|
||||
{/* Bullet Points Grid */}
|
||||
<div className={`grid ${getGridClasses(bulletPoints.length)} flex-1 content-center`}>
|
||||
{bulletPoints.map((bullet, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`flex items-start space-x-4 p-4 rounded-lg transition-all duration-200 hover:bg-gray-50`}
|
||||
>
|
||||
{/* Icon */}
|
||||
<div className="flex-shrink-0 w-12 h-12 bg-purple-600 rounded-full flex items-center justify-center">
|
||||
<img
|
||||
src={bullet.icon.__icon_url__}
|
||||
alt={bullet.icon.__icon_query__}
|
||||
className="w-6 h-6 object-contain brightness-0 invert"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="flex-1">
|
||||
<h3 className="text-lg sm:text-xl font-semibold text-gray-900 mb-1">
|
||||
{bullet.title}
|
||||
</h3>
|
||||
{bullet.subtitle && (
|
||||
<p className="text-sm text-gray-700 leading-relaxed">
|
||||
{bullet.subtitle}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Section - Image */}
|
||||
<div className="flex-shrink-0 w-96 flex items-center justify-center relative">
|
||||
{/* Decorative Elements */}
|
||||
<div className="absolute top-8 right-8 text-purple-600 opacity-60">
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="currentColor">
|
||||
<path d="M16 0l4.12 8.38L28 12l-7.88 3.62L16 24l-4.12-8.38L4 12l7.88-3.62L16 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div className="absolute top-16 left-8 opacity-20">
|
||||
<svg width="80" height="20" viewBox="0 0 80 20" className="text-purple-600">
|
||||
<path
|
||||
d="M0 10 Q20 0 40 10 T80 10"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
fill="none"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{/* Main Image */}
|
||||
<div className="w-full h-80 rounded-2xl overflow-hidden shadow-lg">
|
||||
<img
|
||||
src={slideData?.image?.__image_url__ || ''}
|
||||
alt={slideData?.image?.__image_prompt__ || slideData?.title || ''}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default BulletIconsOnlySlideLayout
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
import React from 'react'
|
||||
import * as z from "zod";
|
||||
import { ImageSchema, IconSchema } from '@/presentation-layouts/defaultSchemes';
|
||||
|
||||
export const layoutId = 'bullet-with-icons-slide'
|
||||
export const layoutName = 'Bullet with Icons'
|
||||
export const layoutDescription = 'A bullets style slide with main content, supporting image, and bullet points with icons and descriptions.'
|
||||
|
||||
const bulletWithIconsSlideSchema = z.object({
|
||||
title: z.string().min(3).max(50).default('Problem').meta({
|
||||
description: "Main title of the slide",
|
||||
}),
|
||||
description: z.string().max(150).default('Businesses face challenges with outdated technology and rising costs, limiting efficiency and growth in competitive markets.').meta({
|
||||
description: "Main description text explaining the problem or topic",
|
||||
}),
|
||||
image: ImageSchema.default({
|
||||
__image_url__: 'https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
|
||||
__image_prompt__: 'Business people analyzing documents and charts in office'
|
||||
}).meta({
|
||||
description: "Supporting image for the slide",
|
||||
}),
|
||||
bulletPoints: z.array(z.object({
|
||||
title: z.string().min(2).max(80).meta({
|
||||
description: "Bullet point title",
|
||||
}),
|
||||
description: z.string().min(10).max(150).meta({
|
||||
description: "Bullet point description",
|
||||
}),
|
||||
icon: IconSchema,
|
||||
})).min(1).max(3).default([
|
||||
{
|
||||
title: 'Inefficiency',
|
||||
description: 'Businesses struggle to find digital tools that meet their needs, causing operational slowdowns.',
|
||||
icon: {
|
||||
__icon_url__: 'https://cdn.jsdelivr.net/npm/lucide@latest/dist/esm/icons/alert-triangle.js',
|
||||
__icon_query__: 'warning alert inefficiency'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'High Costs',
|
||||
description: 'Outdated systems increase expenses, while small businesses struggle to expand their market reach.',
|
||||
icon: {
|
||||
__icon_url__: 'https://cdn.jsdelivr.net/npm/lucide@latest/dist/esm/icons/trending-up.js',
|
||||
__icon_query__: 'trending up costs chart'
|
||||
}
|
||||
}
|
||||
]).meta({
|
||||
description: "List of bullet points with icons and descriptions",
|
||||
})
|
||||
})
|
||||
|
||||
export const Schema = bulletWithIconsSlideSchema
|
||||
|
||||
export type BulletWithIconsSlideData = z.infer<typeof bulletWithIconsSlideSchema>
|
||||
|
||||
interface BulletWithIconsSlideLayoutProps {
|
||||
data?: Partial<BulletWithIconsSlideData>
|
||||
}
|
||||
|
||||
const BulletWithIconsSlideLayout: React.FC<BulletWithIconsSlideLayoutProps> = ({ data: slideData }) => {
|
||||
const bulletPoints = slideData?.bulletPoints || []
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Import Google Fonts */}
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<div
|
||||
className="w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video bg-gradient-to-br from-gray-50 to-white relative z-20 mx-auto overflow-hidden"
|
||||
style={{
|
||||
fontFamily: 'Poppins, sans-serif'
|
||||
}}
|
||||
>
|
||||
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="flex flex-col h-full px-8 sm:px-12 lg:px-20 pt-8 pb-8">
|
||||
{/* Title Section - Full Width */}
|
||||
<div className="mb-8">
|
||||
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-bold text-gray-900">
|
||||
{slideData?.title || 'Problem'}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
{/* Content Container */}
|
||||
<div className="flex flex-1">
|
||||
{/* Left Section - Image with Grid Pattern */}
|
||||
<div className="flex-1 relative">
|
||||
{/* Grid Pattern Background */}
|
||||
<div className="absolute top-0 left-0 w-full h-full">
|
||||
<svg className="w-full h-full opacity-30" viewBox="0 0 200 200">
|
||||
<defs>
|
||||
<pattern id="grid" width="20" height="20" patternUnits="userSpaceOnUse">
|
||||
<path d="M 20 0 L 0 0 0 20" fill="none" stroke="#8b5cf6" strokeWidth="0.5"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="url(#grid)" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{/* Image Container */}
|
||||
<div className="relative z-10 h-full flex items-center justify-center p-4">
|
||||
<div className="w-full max-w-md h-80 rounded-2xl overflow-hidden shadow-lg">
|
||||
<img
|
||||
src={slideData?.image?.__image_url__ || ''}
|
||||
alt={slideData?.image?.__image_prompt__ || slideData?.title || ''}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Decorative Sparkle */}
|
||||
<div className="absolute top-20 right-8 text-purple-600">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 0l3.09 6.26L22 9l-6.91 2.74L12 18l-3.09-6.26L2 9l6.91-2.74L12 0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Section - Content */}
|
||||
<div className="flex-1 flex flex-col justify-center pl-8 lg:pl-16">
|
||||
{/* Description */}
|
||||
<p className="text-lg text-gray-700 leading-relaxed mb-8">
|
||||
{slideData?.description || 'Businesses face challenges with outdated technology and rising costs, limiting efficiency and growth in competitive markets.'}
|
||||
</p>
|
||||
|
||||
{/* Bullet Points */}
|
||||
<div className="space-y-6">
|
||||
{bulletPoints.map((bullet, index) => (
|
||||
<div key={index} className="flex items-start space-x-4">
|
||||
{/* Icon */}
|
||||
<div className="flex-shrink-0 w-12 h-12 bg-white rounded-lg shadow-md flex items-center justify-center">
|
||||
<img
|
||||
src={bullet.icon.__icon_url__}
|
||||
alt={bullet.icon.__icon_query__}
|
||||
className="w-6 h-6 object-contain text-gray-700"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="flex-1">
|
||||
<h3 className="text-xl font-semibold text-gray-900 mb-2">
|
||||
{bullet.title}
|
||||
</h3>
|
||||
<div className="w-12 h-0.5 bg-purple-600 mb-3"></div>
|
||||
<p className="text-base text-gray-700 leading-relaxed">
|
||||
{bullet.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default BulletWithIconsSlideLayout
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
import React from 'react'
|
||||
import * as z from "zod";
|
||||
import { ImageSchema } from '@/presentation-layouts/defaultSchemes';
|
||||
|
||||
export const layoutId = 'basic-info-slide'
|
||||
export const layoutName = 'Basic Info'
|
||||
export const layoutDescription = 'A clean slide layout with title, description text, and a supporting image.'
|
||||
|
||||
const basicInfoSlideSchema = z.object({
|
||||
title: z.string().min(3).max(50).default('Product Overview').meta({
|
||||
description: "Main title of the slide",
|
||||
}),
|
||||
description: z.string().min(10).max(200).default('Our product offers customizable dashboards for real-time reporting and data-driven decisions. It integrates with third-party tools to enhance operations and scales with business growth for improved efficiency.').meta({
|
||||
description: "Main description text content",
|
||||
}),
|
||||
image: ImageSchema.default({
|
||||
__image_url__: 'https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
|
||||
__image_prompt__: 'Business team in meeting room discussing product features and solutions'
|
||||
}).meta({
|
||||
description: "Supporting image for the slide",
|
||||
})
|
||||
})
|
||||
|
||||
export const Schema = basicInfoSlideSchema
|
||||
|
||||
export type BasicInfoSlideData = z.infer<typeof basicInfoSlideSchema>
|
||||
|
||||
interface BasicInfoSlideLayoutProps {
|
||||
data?: Partial<BasicInfoSlideData>
|
||||
}
|
||||
|
||||
const BasicInfoSlideLayout: React.FC<BasicInfoSlideLayoutProps> = ({ data: slideData }) => {
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Import Google Fonts */}
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<div
|
||||
className="w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video bg-white relative z-20 mx-auto overflow-hidden"
|
||||
style={{
|
||||
fontFamily: 'Poppins, sans-serif'
|
||||
}}
|
||||
>
|
||||
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="relative z-10 flex h-full px-8 sm:px-12 lg:px-20 pb-8">
|
||||
{/* Left Section - Image */}
|
||||
<div className="flex-1 flex items-center justify-center pr-8">
|
||||
<div className="w-full max-w-lg h-80 rounded-2xl overflow-hidden shadow-lg">
|
||||
<img
|
||||
src={slideData?.image?.__image_url__ || ''}
|
||||
alt={slideData?.image?.__image_prompt__ || slideData?.title || ''}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Section - Content */}
|
||||
<div className="flex-1 flex flex-col justify-center pl-8 space-y-6">
|
||||
{/* Title */}
|
||||
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-bold text-gray-900 leading-tight">
|
||||
{slideData?.title || 'Product Overview'}
|
||||
</h1>
|
||||
|
||||
{/* Purple accent line */}
|
||||
<div className="w-20 h-1 bg-purple-600"></div>
|
||||
|
||||
{/* Description */}
|
||||
<p className="text-base sm:text-lg text-gray-700 leading-relaxed">
|
||||
{slideData?.description || 'Our product offers customizable dashboards for real-time reporting and data-driven decisions. It integrates with third-party tools to enhance operations and scales with business growth for improved efficiency.'}
|
||||
</p>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default BasicInfoSlideLayout
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
import React from 'react'
|
||||
import * as z from "zod";
|
||||
import { ImageSchema } from '@/presentation-layouts/defaultSchemes';
|
||||
|
||||
export const layoutId = 'numbered-bullets-slide'
|
||||
export const layoutName = 'Numbered Bullets'
|
||||
export const layoutDescription = 'A slide layout with large title, supporting image, and numbered bullet points with descriptions.'
|
||||
|
||||
const numberedBulletsSlideSchema = z.object({
|
||||
title: z.string().min(3).max(50).default('Market Validation').meta({
|
||||
description: "Main title of the slide",
|
||||
}),
|
||||
image: ImageSchema.default({
|
||||
__image_url__: 'https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
|
||||
__image_prompt__: 'Business people analyzing charts and data on wall'
|
||||
}).meta({
|
||||
description: "Supporting image for the slide",
|
||||
}),
|
||||
bulletPoints: z.array(z.object({
|
||||
title: z.string().min(2).max(80).meta({
|
||||
description: "Bullet point title",
|
||||
}),
|
||||
description: z.string().min(10).max(150).meta({
|
||||
description: "Bullet point description",
|
||||
}),
|
||||
})).min(1).max(4).default([
|
||||
{
|
||||
title: 'Customer Insights',
|
||||
description: 'Surveys reveal that 78% of businesses are planning to invest in digital solutions, with 85% preferring customized approaches.'
|
||||
},
|
||||
{
|
||||
title: 'Pilot Program Success',
|
||||
description: 'The survey revealed that 78% of businesses plan to invest in digital solutions, and 85% prefer a tailored approach.'
|
||||
},
|
||||
{
|
||||
title: 'Pilot Program Success',
|
||||
description: 'The survey revealed that 78% of businesses plan to invest in digital solutions, and 85% prefer a tailored approach.'
|
||||
},
|
||||
{
|
||||
title: 'Pilot Program Success',
|
||||
description: 'The survey revealed that 78% of businesses plan to invest in digital solutions, and 85% prefer a tailored approach.'
|
||||
}
|
||||
]).meta({
|
||||
description: "List of numbered bullet points with descriptions",
|
||||
})
|
||||
})
|
||||
|
||||
export const Schema = numberedBulletsSlideSchema
|
||||
|
||||
export type NumberedBulletsSlideData = z.infer<typeof numberedBulletsSlideSchema>
|
||||
|
||||
interface NumberedBulletsSlideLayoutProps {
|
||||
data?: Partial<NumberedBulletsSlideData>
|
||||
}
|
||||
|
||||
const NumberedBulletsSlideLayout: React.FC<NumberedBulletsSlideLayoutProps> = ({ data: slideData }) => {
|
||||
const bulletPoints = slideData?.bulletPoints || []
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Import Google Fonts */}
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<div
|
||||
className="w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video bg-white relative z-20 mx-auto overflow-hidden"
|
||||
style={{
|
||||
fontFamily: 'Poppins, sans-serif'
|
||||
}}
|
||||
>
|
||||
|
||||
{/* Main Content Container */}
|
||||
<div className="px-8 sm:px-12 lg:px-20 pt-8 pb-8 h-full">
|
||||
{/* Top Section - Title and Image */}
|
||||
<div className="flex items-start justify-between mb-8">
|
||||
{/* Title Section */}
|
||||
<div className="flex-1 pr-8">
|
||||
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-bold text-gray-900 leading-tight mb-4">
|
||||
{slideData?.title || 'Market Validation'}
|
||||
</h1>
|
||||
{/* Purple accent line */}
|
||||
<div className="w-24 h-1 bg-purple-600 mb-6"></div>
|
||||
</div>
|
||||
|
||||
{/* Image Section */}
|
||||
<div className="flex-shrink-0 w-80 h-48">
|
||||
<img
|
||||
src={slideData?.image?.__image_url__ || ''}
|
||||
alt={slideData?.image?.__image_prompt__ || slideData?.title || ''}
|
||||
className="w-full h-full object-cover rounded-lg shadow-md"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Numbered Bullet Points */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 mb-8">
|
||||
{bulletPoints.map((bullet, index) => (
|
||||
<div key={index} className="flex items-start space-x-4">
|
||||
{/* Number */}
|
||||
<div className="flex-shrink-0">
|
||||
<div className="text-4xl sm:text-5xl font-bold text-gray-900">
|
||||
{String(index + 1).padStart(2, '0')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="flex-1 pt-2">
|
||||
<h3 className="text-xl sm:text-2xl font-bold text-gray-900 mb-3">
|
||||
{bullet.title}
|
||||
</h3>
|
||||
<p className="text-base text-gray-700 leading-relaxed">
|
||||
{bullet.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Decorative Wave Pattern at Bottom */}
|
||||
<div className="absolute bottom-0 left-0 right-0 h-20 overflow-hidden">
|
||||
<svg
|
||||
className="w-full h-full opacity-20"
|
||||
viewBox="0 0 1200 200"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M0 100C300 150 600 50 900 100C1050 125 1125 100 1200 100V200H0V100Z"
|
||||
fill="url(#wave-gradient)"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient id="wave-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" stopColor="#8b5cf6" />
|
||||
<stop offset="50%" stopColor="#a855f7" />
|
||||
<stop offset="100%" stopColor="#c084fc" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default NumberedBulletsSlideLayout
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
import React from 'react'
|
||||
import * as z from "zod";
|
||||
import { ImageSchema } from '@/presentation-layouts/defaultSchemes';
|
||||
|
||||
export const layoutId = 'metrics-with-image-slide'
|
||||
export const layoutName = 'Metrics with Image'
|
||||
export const layoutDescription = 'A slide layout with supporting image on the left and title, description, and metrics grid on the right. Can be used alternatively with MetricSlide.'
|
||||
|
||||
const metricsWithImageSlideSchema = z.object({
|
||||
title: z.string().min(3).max(50).default('Competitive Advantage').meta({
|
||||
description: "Main title of the slide",
|
||||
}),
|
||||
description: z.string().min(10).max(150).default('Ginyard International Co. stands out by offering custom digital solutions tailored to client needs, alongside long-term support to ensure lasting relationships and continuous adaptation.').meta({
|
||||
description: "Description text below the title",
|
||||
}),
|
||||
image: ImageSchema.default({
|
||||
__image_url__: 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
|
||||
__image_prompt__: 'Person holding tablet with analytics dashboard and charts'
|
||||
}).meta({
|
||||
description: "Supporting image for the slide",
|
||||
}),
|
||||
metrics: z.array(z.object({
|
||||
label: z.string().min(2).max(100).meta({
|
||||
description: "Metric label/title"
|
||||
}),
|
||||
value: z.string().min(1).max(20).meta({
|
||||
description: "Metric value (e.g., 200+, 95%, 50%)"
|
||||
}),
|
||||
})).min(1).max(4).default([
|
||||
{
|
||||
label: 'Satisfied Clients',
|
||||
value: '200+'
|
||||
},
|
||||
{
|
||||
label: 'Client Retention Rate',
|
||||
value: '95%'
|
||||
},
|
||||
|
||||
]).meta({
|
||||
description: "List of key business metrics to display",
|
||||
})
|
||||
})
|
||||
|
||||
export const Schema = metricsWithImageSlideSchema
|
||||
|
||||
export type MetricsWithImageSlideData = z.infer<typeof metricsWithImageSlideSchema>
|
||||
|
||||
interface MetricsWithImageSlideLayoutProps {
|
||||
data?: Partial<MetricsWithImageSlideData>
|
||||
}
|
||||
|
||||
const MetricsWithImageSlideLayout: React.FC<MetricsWithImageSlideLayoutProps> = ({ data: slideData }) => {
|
||||
const metrics = slideData?.metrics || []
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Import Google Fonts */}
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<div
|
||||
className="w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video bg-white relative z-20 mx-auto overflow-hidden"
|
||||
style={{
|
||||
fontFamily: 'Poppins, sans-serif'
|
||||
}}
|
||||
>
|
||||
{/* Decorative Wave Patterns */}
|
||||
<div className="absolute bottom-0 left-0 w-48 h-48 opacity-10 overflow-hidden">
|
||||
<svg className="w-full h-full" viewBox="0 0 200 200" fill="none">
|
||||
<path d="M0 100C50 75 100 125 150 100C175 87.5 200 100 200 100V200H0V100Z" fill="#8b5cf6" opacity="0.4"/>
|
||||
<path d="M0 150C75 175 125 125 200 150V175C150 162.5 100 175 50 162.5L0 150Z" fill="#8b5cf6" opacity="0.3"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div className="absolute top-0 right-0 w-64 h-64 opacity-10 overflow-hidden">
|
||||
<svg className="w-full h-full" viewBox="0 0 200 200" fill="none">
|
||||
<path d="M100 0C150 50 200 0 200 50C200 100 150 150 100 150C50 150 0 100 0 50C0 0 50 50 100 0Z" fill="#8b5cf6" opacity="0.2"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="relative z-10 flex h-full px-8 sm:px-12 lg:px-20 pb-8">
|
||||
{/* Left Section - Image */}
|
||||
<div className="flex-1 flex items-center justify-center pr-8">
|
||||
<div className="w-full max-w-lg h-96 rounded-2xl overflow-hidden shadow-lg">
|
||||
<img
|
||||
src={slideData?.image?.__image_url__ || ''}
|
||||
alt={slideData?.image?.__image_prompt__ || slideData?.title || ''}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Section - Content and Metrics */}
|
||||
<div className="flex-1 flex flex-col justify-center pl-8 space-y-6">
|
||||
{/* Title */}
|
||||
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-bold text-gray-900 leading-tight">
|
||||
{slideData?.title || 'Competitive Advantage'}
|
||||
</h1>
|
||||
|
||||
{/* Description */}
|
||||
<p className="text-base sm:text-lg text-gray-700 leading-relaxed">
|
||||
{slideData?.description || 'Ginyard International Co. stands out by offering custom digital solutions tailored to client needs, alongside long-term support to ensure lasting relationships and continuous adaptation.'}
|
||||
</p>
|
||||
|
||||
{/* Metrics Grid */}
|
||||
<div className="grid grid-cols-2 gap-6">
|
||||
{metrics.map((metric, index) => (
|
||||
<div key={index} className="text-center space-y-2">
|
||||
<div className="text-sm text-gray-600 font-medium">
|
||||
{metric.label}
|
||||
</div>
|
||||
<div className="text-3xl sm:text-4xl lg:text-5xl font-bold text-purple-600">
|
||||
{metric.value}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default MetricsWithImageSlideLayout
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
import React from 'react'
|
||||
import * as z from "zod";
|
||||
import { ImageSchema } from '@/presentation-layouts/defaultSchemes';
|
||||
|
||||
export const layoutId = 'basic-info-slide'
|
||||
export const layoutName = 'Basic Info'
|
||||
export const layoutDescription = 'A clean slide layout with title, description text, and a supporting image.'
|
||||
|
||||
const basicInfoSlideSchema = z.object({
|
||||
title: z.string().min(3).max(50).default('Product Overview').meta({
|
||||
description: "Main title of the slide",
|
||||
}),
|
||||
description: z.string().min(10).max(150).default('Our product offers customizable dashboards for real-time reporting and data-driven decisions. It integrates with third-party tools to enhance operations and scales with business growth for improved efficiency.').meta({
|
||||
description: "Main description text content",
|
||||
}),
|
||||
image: ImageSchema.default({
|
||||
__image_url__: 'https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
|
||||
__image_prompt__: 'Business team in meeting room discussing product features and solutions'
|
||||
}).meta({
|
||||
description: "Supporting image for the slide",
|
||||
})
|
||||
})
|
||||
|
||||
export const Schema = basicInfoSlideSchema
|
||||
|
||||
export type BasicInfoSlideData = z.infer<typeof basicInfoSlideSchema>
|
||||
|
||||
interface BasicInfoSlideLayoutProps {
|
||||
data?: Partial<BasicInfoSlideData>
|
||||
}
|
||||
|
||||
const BasicInfoSlideLayout: React.FC<BasicInfoSlideLayoutProps> = ({ data: slideData }) => {
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Import Google Fonts */}
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<div
|
||||
className="w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video bg-white relative z-20 mx-auto overflow-hidden"
|
||||
style={{
|
||||
fontFamily: 'Poppins, sans-serif'
|
||||
}}
|
||||
>
|
||||
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="relative z-10 flex h-full px-8 sm:px-12 lg:px-20 pb-8">
|
||||
{/* Left Section - Image */}
|
||||
<div className="flex-1 flex items-center justify-center pr-8">
|
||||
<div className="w-full max-w-lg h-80 rounded-2xl overflow-hidden shadow-lg">
|
||||
<img
|
||||
src={slideData?.image?.__image_url__ || ''}
|
||||
alt={slideData?.image?.__image_prompt__ || slideData?.title || ''}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Section - Content */}
|
||||
<div className="flex-1 flex flex-col justify-center pl-8 space-y-6">
|
||||
{/* Title */}
|
||||
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-bold text-gray-900 leading-tight">
|
||||
{slideData?.title || 'Product Overview'}
|
||||
</h1>
|
||||
|
||||
{/* Purple accent line */}
|
||||
<div className="w-20 h-1 bg-purple-600"></div>
|
||||
|
||||
{/* Description */}
|
||||
<p className="text-base sm:text-lg text-gray-700 leading-relaxed">
|
||||
{slideData?.description || 'Our product offers customizable dashboards for real-time reporting and data-driven decisions. It integrates with third-party tools to enhance operations and scales with business growth for improved efficiency.'}
|
||||
</p>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default BasicInfoSlideLayout
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"description": "Ordered layouts for presentations",
|
||||
"ordered": true,
|
||||
"isDefault": false
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue