feat(Nextjs): Custom Layout Compiling & used in generation

This commit is contained in:
shiva raj badu 2025-08-01 23:01:41 +05:45
parent 7449be7f20
commit 2b2c7757e2
No known key found for this signature in database
23 changed files with 4554 additions and 2782 deletions

View file

@ -1,64 +1,74 @@
import { CheckCircle } from 'lucide-react';
import React from 'react';
import { CheckCircle } from "lucide-react";
import React from "react";
import { LayoutGroup } from "../types/index";
import { useGroupLayoutLoader } from '@/app/layout-preview/hooks/useGroupLayoutLoader';
import { useGroupLayoutLoader } from "@/app/layout-preview/hooks/useGroupLayoutLoader";
interface GroupLayoutsProps {
group: LayoutGroup;
onSelectLayoutGroup: (group: LayoutGroup) => void;
selectedLayoutGroup: LayoutGroup | null;
group: LayoutGroup;
onSelectLayoutGroup: (group: LayoutGroup) => void;
selectedLayoutGroup: LayoutGroup | null;
}
const GroupLayouts: React.FC<GroupLayoutsProps> = ({ group, onSelectLayoutGroup, selectedLayoutGroup }) => {
const { layoutGroup } = useGroupLayoutLoader(group.id);
return (
<div
onClick={() => onSelectLayoutGroup(group)}
className={`relative p-4 rounded-lg border cursor-pointer transition-all duration-200 ${selectedLayoutGroup?.id === group.id
? 'border-blue-500 bg-blue-50 shadow-md'
: 'border-gray-200 bg-white hover:border-gray-300 hover:shadow-sm'
}`}
>
{selectedLayoutGroup?.id === group.id && (
<div className="absolute top-3 right-3">
<CheckCircle className="w-5 h-5 text-blue-500" />
</div>
)}
<div className="mb-3">
<h6 className="text-base capitalize font-medium text-gray-900 mb-1">
{group.name}
</h6>
<p className="text-sm text-gray-600">
{group.description}
</p>
</div>
{/* Layout previews */}
<div className="grid grid-cols-2 gap-2 mb-3">
{layoutGroup && layoutGroup?.layouts.slice(0, 4).map((layout: any, index: number) => {
const { component: LayoutComponent, sampleData, layoutId } = layout
return (
<div key={`${layoutGroup?.groupName}-${index}`} className=" relative cursor-pointer overflow-hidden aspect-video">
<div className="absolute cursor-pointer bg-transparent z-40 top-0 left-0 w-full h-full" />
<div className="transform scale-[0.2] flex justify-center items-center origin-top-left w-[500%] h-[500%]">
<LayoutComponent data={sampleData} />
</div>
</div>
)
})}
</div>
<div className="flex items-center justify-between text-sm text-gray-500">
<span>{layoutGroup?.layouts.length} layouts</span>
<span className={`px-2 py-1 rounded text-xs ${group.ordered
? 'bg-gray-100 text-gray-700'
: 'bg-blue-100 text-blue-700'
}`}>
{group.ordered ? 'Structured' : 'Flexible'}
</span>
</div>
const GroupLayouts: React.FC<GroupLayoutsProps> = ({
group,
onSelectLayoutGroup,
selectedLayoutGroup,
}) => {
const { layoutGroup } = useGroupLayoutLoader(group.id);
return (
<div
onClick={() => onSelectLayoutGroup(group)}
className={`relative p-4 rounded-lg border cursor-pointer transition-all duration-200 ${
selectedLayoutGroup?.id === group.id
? "border-blue-500 bg-blue-50 shadow-md"
: "border-gray-200 bg-white hover:border-gray-300 hover:shadow-sm"
}`}
>
{selectedLayoutGroup?.id === group.id && (
<div className="absolute top-3 right-3">
<CheckCircle className="w-5 h-5 text-blue-500" />
</div>
);
)}
<div className="mb-3">
<h6 className="text-base capitalize font-medium text-gray-900 mb-1">
{group.name}
</h6>
<p className="text-sm text-gray-600">{group.description}</p>
</div>
{/* Layout previews */}
<div className="grid grid-cols-2 gap-2 mb-3 min-h-[300px]">
{layoutGroup &&
layoutGroup?.layouts.slice(0, 4).map((layout: any, index: number) => {
const { component: LayoutComponent, sampleData, layoutId } = layout;
return (
<div
key={`${layoutGroup?.groupName}-${index}`}
className=" relative cursor-pointer overflow-hidden aspect-video"
>
<div className="absolute cursor-pointer bg-transparent z-40 top-0 left-0 w-full h-full" />
<div className="transform scale-[0.2] flex justify-center items-center origin-top-left w-[500%] h-[500%]">
<LayoutComponent data={sampleData} />
</div>
</div>
);
})}
</div>
<div className="flex items-center justify-between text-sm text-gray-500">
<span>{layoutGroup?.layouts.length} layouts</span>
<span
className={`px-2 py-1 rounded text-xs ${
group.ordered
? "bg-gray-100 text-gray-700"
: "bg-blue-100 text-blue-700"
}`}
>
{group.ordered ? "Structured" : "Flexible"}
</span>
</div>
</div>
);
};
export default GroupLayouts;

View file

@ -65,6 +65,7 @@ const CustomLayoutPage = () => {
try {
// Convert each slide HTML to React component
const reactComponents = [];
const presentationId = uuidv4();
for (let i = 0; i < slides.length - 3; i++) {
const slide = slides[i];
@ -91,7 +92,7 @@ const CustomLayoutPage = () => {
);
reactComponents.push({
presentation_id: uuidv4(),
presentation_id: presentationId,
layout_id: `${slide.slide_number}`,
layout_name: `Slide${slide.slide_number}`,
layout_code: data.react_component || data.component_code,

View file

@ -0,0 +1,335 @@
import React, { useEffect, useState } from "react";
import * as Babel from "@babel/standalone";
import * as z from "zod";
// Clean JSX code without imports - they'll be available in the execution context
const jsxCode = `
const ImageSchema = z.object({
__image_url__: z.url().meta({
description: "URL to image",
}),
__image_prompt__: z.string().meta({
description: "Prompt used to generate the image",
}).min(10).max(50),
})
const layoutId = 'title-slide-with-decorative-elements'
const layoutName = 'TitleSlideWithDecorativeElements'
const layoutDescription = 'A title slide layout with company name, main title, subtitle, author text, and decorative curved shapes with images.'
const titleSlideWithDecorativeElementsSchema = z.object({
companyName: z.string().min(5).max(30).default('AROWWAI INDUSTRIES').meta({
description: "Company or organization name",
}),
mainTitle: z.string().min(5).max(50).default('STRATEGY DECK').meta({
description: "Main title of the presentation (can include line breaks)",
}),
subtitle: z.string().min(10).max(80).default('STRATEGIES FOR GROWTH AND INNOVATION').meta({
description: "Subtitle describing the presentation topic",
}),
authorText: z.string().min(5).max(30).default('BY GROUP 1').meta({
description: "Author or presenter information",
}),
logo: ImageSchema.default({
__image_url__: 'https://images.pexels.com/photos/31995895/pexels-photo-31995895/free-photo-of-turkish-coffee-with-scenic-bursa-view.jpeg',
__image_prompt__: 'Company logo or brand icon'
}).meta({
description: "Company logo or brand icon",
}),
leftDecorativeImage: ImageSchema.default({
__image_url__: 'https://images.pexels.com/photos/31995895/pexels-photo-31995895/free-photo-of-turkish-coffee-with-scenic-bursa-view.jpeg',
__image_prompt__: 'Turkish coffee with scenic Bursa view'
}).meta({
description: "Left decorative curved shape background image",
}),
rightDecorativeImage: ImageSchema.default({
__image_url__: 'https://images.pexels.com/photos/31995895/pexels-photo-31995895/free-photo-of-turkish-coffee-with-scenic-bursa-view.jpeg',
__image_prompt__: 'Turkish coffee with scenic Bursa view'
}).meta({
description: "Right decorative curved shape background image",
})
})
const Schema = titleSlideWithDecorativeElementsSchema
const TitleSlideWithDecorativeElementsLayout = ({ data: slideData }) => {
// Split main title by newlines for proper rendering
const titleLines = (slideData?.mainTitle || 'STRATEGY DECK').split('\\n')
return (
<>
{/* Import Google Fonts */}
<link
href="https://fonts.googleapis.com/css2?family=League+Spartan:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css2?family=Futura:wght@400;500;600&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={{ backgroundColor: '#8d7b68' }}
>
{/* Bottom horizontal line */}
<div
className="absolute bottom-0 left-0 w-full h-0.5 bg-yellow-50"
style={{ backgroundColor: '#fdf7e4' }}
></div>
{/* Upper horizontal line */}
<div
className="absolute top-20 left-48 w-80 h-0.5 bg-yellow-50"
style={{ backgroundColor: '#fdf7e4' }}
></div>
{/* Right vertical line */}
<div
className="absolute top-0 right-8 w-0.5 h-full bg-yellow-50"
style={{ backgroundColor: '#fdf7e4' }}
></div>
{/* Upper left circular element */}
<div
className="absolute top-8 left-24 w-20 h-20 rounded-full"
style={{ backgroundColor: '#a4907c' }}
></div>
{/* Lower right circular element */}
<div
className="absolute bottom-20 right-28 w-48 h-48 rounded-full"
style={{ backgroundColor: '#a4907c' }}
></div>
{/* Left decorative curved shape */}
<div className="absolute top-20 left-0 w-64 h-80 overflow-hidden">
<svg viewBox="0 0 660 996" className="w-full h-full">
<path
d="M220.252 19.07C254 7.556 292.6 0 330.378 0C368.157 0 404.509 6.476 438.009 17.99C438.723 18.35 439.435 18.35 440.148 18.71C565.955 64.765 658.618 186.379 660.4 332.57L660.4 995.919L0 995.919L0 333.062C1.782 185.66 93.019 64.045 220.252 19.07Z"
fill="url(#leftImage)"
/>
<defs>
<pattern id="leftImage" patternUnits="objectBoundingBox" width="1" height="1">
<image
href={slideData?.leftDecorativeImage?.__image_url__ || 'https://images.pexels.com/photos/31995895/pexels-photo-31995895/free-photo-of-turkish-coffee-with-scenic-bursa-view.jpeg'}
x="0"
y="0"
width="1"
height="1"
preserveAspectRatio="xMidYMid slice"
/>
</pattern>
</defs>
</svg>
</div>
{/* Right decorative curved shape */}
<div className="absolute top-32 right-0 w-80 h-96 overflow-hidden">
<svg viewBox="0 0 660 996" className="w-full h-full">
<path
d="M220.252 19.07C254 7.556 292.6 0 330.378 0C368.157 0 404.509 6.476 438.009 17.99C438.723 18.35 439.435 18.35 440.148 18.71C565.955 64.765 658.618 186.379 660.4 332.57L660.4 995.919L0 995.919L0 333.062C1.782 185.66 93.019 64.045 220.252 19.07Z"
fill="url(#rightImage)"
/>
<defs>
<pattern id="rightImage" patternUnits="objectBoundingBox" width="1" height="1">
<image
href={slideData?.rightDecorativeImage?.__image_url__ || 'https://images.pexels.com/photos/31995895/pexels-photo-31995895/free-photo-of-turkish-coffee-with-scenic-bursa-view.jpeg'}
x="0"
y="0"
width="1"
height="1"
preserveAspectRatio="xMidYMid slice"
/>
</pattern>
</defs>
</svg>
</div>
{/* Small icon/logo near company name */}
<div className="absolute top-16 right-56 w-16 h-12 overflow-hidden">
<img
src={slideData?.logo?.__image_url__ || 'https://images.pexels.com/photos/31995895/pexels-photo-31995895/free-photo-of-turkish-coffee-with-scenic-bursa-view.jpeg'}
alt={slideData?.logo?.__image_prompt__ || 'Company logo'}
className="w-full h-full object-cover"
/>
</div>
{/* Company name */}
<div className="absolute top-14 right-8 text-right">
<h2
className="text-yellow-50 text-lg font-normal tracking-wider"
style={{ fontFamily: "'Futura', sans-serif", color: '#fdf7e4' }}
>
{slideData?.companyName || 'AROWWAI INDUSTRIES'}
</h2>
</div>
{/* Main title */}
<div className="absolute top-64 left-64 right-8 text-right">
<h1
className="text-yellow-50 text-8xl font-normal tracking-wider leading-tight"
style={{ fontFamily: "'League Spartan', sans-serif", color: '#fdf7e4' }}
>
{titleLines.map((line, index) => (
<React.Fragment key={index}>
{line}
{index < titleLines.length - 1 && <br />}
</React.Fragment>
))}
</h1>
</div>
{/* Subtitle */}
<div className="absolute bottom-24 left-64 right-8 text-right">
<h3
className="text-yellow-50 text-xl font-normal tracking-wide"
style={{ fontFamily: "'Futura', sans-serif", color: '#fdf7e4' }}
>
{slideData?.subtitle || 'STRATEGIES FOR GROWTH AND INNOVATION'}
</h3>
</div>
{/* Bottom left text */}
<div className="absolute bottom-4 left-8">
<p
className="text-yellow-50 text-2xl font-normal tracking-wide"
style={{ fontFamily: "'Futura', sans-serif", color: '#fdf7e4' }}
>
{slideData?.authorText || 'BY GROUP 1'}
</p>
</div>
</div>
</>
)
}
// Return the component
`;
interface Layout {
presentation_id: string;
layout_id: string;
layout_name: string;
layout_code: string;
}
const CustomLayouts = () => {
const [layouts, setLayouts] = useState<Layout[]>([]);
const [component, setComponent] = useState<React.ComponentType<{
data: any;
}> | null>(null);
useEffect(() => {
const fetchLayouts = async () => {
try {
const res = await fetch(
"/api/v1/ppt/layout-management/get-layouts/6038f1cb-80cb-448c-83cc-f6cb96081943"
);
const data = await res.json();
setLayouts(data.layouts);
} catch (error) {
console.error("Failed to fetch layouts:", error);
}
};
fetchLayouts();
}, []);
useEffect(() => {
if (layouts.length === 0) return;
try {
/* ---------- 1. compile JSX to plain script ------------------ */
const compiled = Babel.transform(jsxCode, {
presets: [
["react", { runtime: "classic" }],
["typescript", { isTSX: true, allExtensions: true }],
],
sourceType: "script",
}).code;
/* ---------- 2. wrap compiled code --------------------------- */
const factory = new Function(
"React",
"z",
`
${compiled}
/* everything declared in the string is in scope here */
return {
__esModule: true,
default: TitleSlideWithDecorativeElementsLayout,
layoutName,
layoutId,
layoutDescription,
Schema
};
`
);
/* ---------- 4. split result --------------------------------- */
const mod = factory(React, z);
console.log("generated", mod);
// default export (the component)
const DynamicComponent = mod.default;
// named exports (meta data)
console.log(mod.layoutName);
console.log(mod.layoutId);
console.log(mod.layoutDescription);
console.log(mod.Schema);
const jsonSchema = z.toJSONSchema(mod.Schema, {
override: (ctx) => {
delete ctx.jsonSchema.default;
},
});
console.log(jsonSchema);
// tell React to render the component only
setComponent(() => DynamicComponent);
} catch (err: any) {
console.error("Compilation error:", err);
setComponent(() => () => (
<div style={{ color: "red", padding: 20 }}>
<h3>Compilation Error</h3>
<pre>{err.message}</pre>
</div>
));
}
}, [layouts]);
z;
return (
<div>
<h1>Custom Layout Renderer</h1>
{component && (
<div style={{ marginBottom: "20px" }}>
<h2>Rendered Component:</h2>
{React.createElement(component, { data: {} })}
</div>
)}
{layouts.map((layout) => (
<div key={layout.layout_id} style={{ marginBottom: "20px" }}>
<h2>{layout.layout_name}</h2>
<details>
<summary>View Code</summary>
<pre
style={{
background: "#f5f5f5",
padding: "10px",
overflow: "auto",
}}
>
{layout.layout_code}
</pre>
</details>
</div>
))}
</div>
);
};
export default CustomLayouts;

View file

@ -1,91 +1,109 @@
'use client'
import React from 'react'
import { useRouter } from 'next/navigation'
import { useLayoutLoader } from './hooks/useLayoutLoader'
import LoadingStates from './components/LoadingStates'
import { Card } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { ExternalLink, Wifi, WifiOff, RefreshCw } from 'lucide-react'
"use client";
import React from "react";
import { useRouter } from "next/navigation";
import { useLayoutLoader } from "./hooks/useLayoutLoader";
import LoadingStates from "./components/LoadingStates";
import { Card } from "@/components/ui/card";
import { ExternalLink } from "lucide-react";
import CustomLayouts from "./CustomLayouts";
const LayoutPreview = () => {
const { layoutGroups, layouts, loading, error, retry } = useLayoutLoader()
const router = useRouter()
const { layoutGroups, layouts, loading, error, retry } = useLayoutLoader();
const router = useRouter();
// Handle loading state
if (loading) {
return <LoadingStates type="loading" />
}
// Handle error state
if (error) {
return <LoadingStates type="error" message={error} onRetry={retry} />
}
// Handle empty state
if (layoutGroups.length === 0 || layouts.length === 0) {
return <LoadingStates type="empty" onRetry={retry} />
}
return (
<div className="min-h-screen bg-gray-50">
<div className="bg-white shadow-sm border-b sticky top-0 z-30">
<div className="max-w-7xl mx-auto px-6 py-6">
<div className="text-center">
<h1 className="text-3xl font-bold text-gray-900">Layout Preview</h1>
<p className="text-gray-600 mt-2">
{layoutGroups.length} groups {layouts.length} layouts
</p>
</div>
</div>
{/* Group Navigation Cards */}
<div className="border-t bg-gray-50 min-h-screen flex justify-center items-center">
<div className="max-w-7xl mx-auto px-6 py-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{layoutGroups.map((group) => (
<Card
key={group.groupName}
className="cursor-pointer hover:shadow-md transition-all duration-200 group"
onClick={() => router.push(`/layout-preview/${group.groupName}`)}
>
<div className="p-6">
<div className="flex items-center justify-between mb-3">
<h3 className="text-lg font-semibold text-gray-900 capitalize group-hover:text-blue-600 transition-colors">
{group.groupName}
</h3>
<div className="flex items-center gap-2">
<span className="px-2 py-1 bg-blue-100 text-blue-800 rounded-full text-xs font-medium">
{group.layouts.length}
</span>
<ExternalLink className="w-4 h-4 text-gray-400 group-hover:text-blue-600 transition-colors" />
</div>
</div>
<p className="text-sm text-gray-600 mb-4">
{group.settings.description}
</p>
<div className="flex items-center justify-between">
<span className="text-xs text-gray-500">
{group.layouts.length} layout{group.layouts.length !== 1 ? 's' : ''}
</span>
{group.settings.default && (
<span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800">
Default
</span>
)}
</div>
</div>
</Card>
))}
</div>
</div>
</div>
</div>
// Handle loading state
if (loading) {
return <LoadingStates type="loading" />;
}
// Handle error state
if (error) {
return <LoadingStates type="error" message={error} onRetry={retry} />;
}
// Handle empty state
if (layoutGroups.length === 0 || layouts.length === 0) {
return <LoadingStates type="empty" onRetry={retry} />;
}
return (
<div className="min-h-screen bg-gray-50">
<div className="bg-white shadow-sm border-b sticky top-0 z-30">
<div className="max-w-7xl mx-auto px-6 py-6">
<div className="text-center">
<h1 className="text-3xl font-bold text-gray-900">Layout Preview</h1>
<p className="text-gray-600 mt-2">
{layoutGroups.length} groups {layouts.length} layouts
</p>
</div>
</div>
)
}
export default LayoutPreview
{/* Group Navigation Cards */}
<div className="border-t bg-gray-50 min-h-screen flex justify-center items-center">
<div className="max-w-7xl mx-auto px-6 py-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{layoutGroups.map((group) => (
<Card
key={group.groupName}
className="cursor-pointer hover:shadow-md transition-all duration-200 group"
onClick={() =>
router.push(`/layout-preview/${group.groupName}`)
}
>
<div className="p-6">
<div className="flex items-center justify-between mb-3">
<h3 className="text-lg font-semibold text-gray-900 capitalize group-hover:text-blue-600 transition-colors">
{group.groupName}
</h3>
<div className="flex items-center gap-2">
<span className="px-2 py-1 bg-blue-100 text-blue-800 rounded-full text-xs font-medium">
{group.layouts.length}
</span>
<ExternalLink className="w-4 h-4 text-gray-400 group-hover:text-blue-600 transition-colors" />
</div>
</div>
<p className="text-sm text-gray-600 mb-4">
{group.settings.description}
</p>
<div className="flex items-center justify-between">
<span className="text-xs text-gray-500">
{group.layouts.length} layout
{group.layouts.length !== 1 ? "s" : ""}
</span>
{group.settings.default && (
<span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800">
Default
</span>
)}
</div>
</div>
</Card>
))}
<Card
className="cursor-pointer hover:shadow-md transition-all border-blue-500 duration-200 group"
onClick={() => router.push(`/custom-layout`)}
>
<div className="p-6">
<div className="flex items-center justify-between mb-3">
<h3 className="text-lg font-semibold text-gray-900 capitalize group-hover:text-blue-600 transition-colors">
Custom Layouts
</h3>
<div className="flex items-center gap-2">
<ExternalLink className="w-4 h-4 text-gray-400 group-hover:text-blue-600 transition-colors" />
</div>
</div>
<p className="text-sm text-gray-600 mb-4">
Custom layouts for your presentations
</p>
</div>
</Card>
</div>
</div>
</div>
<CustomLayouts />
</div>
</div>
);
};
export default LayoutPreview;

View file

@ -8,9 +8,11 @@
"name": "presenton",
"version": "0.1.0",
"dependencies": {
"@babel/standalone": "^7.28.2",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@paciolan/remote-component": "^2.13.0",
"@radix-ui/react-accordion": "^1.2.1",
"@radix-ui/react-avatar": "^1.1.2",
"@radix-ui/react-dialog": "^1.1.6",
@ -50,6 +52,7 @@
"puppeteer": "^24.13.0",
"react": "^18",
"react-dom": "^18",
"react-jsx-parser": "^2.4.0",
"react-redux": "^9.1.2",
"react-sketch-canvas": "^6.2.0",
"recharts": "^2.15.4",
@ -64,6 +67,7 @@
},
"devDependencies": {
"@types/animejs": "^3.1.12",
"@types/babel__standalone": "^7.1.9",
"@types/node": "^20",
"@types/puppeteer": "^5.4.7",
"@types/react": "^18",
@ -71,6 +75,7 @@
"@types/uuid": "^10.0.0",
"@types/ws": "^8.5.13",
"cypress": "^14.3.3",
"esbuild": "0.25.8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
},
@ -126,6 +131,16 @@
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-string-parser": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
@ -135,6 +150,22 @@
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz",
"integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.28.0"
},
"bin": {
"parser": "bin/babel-parser.js"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@babel/runtime": {
"version": "7.27.6",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz",
@ -144,6 +175,29 @@
"node": ">=6.9.0"
}
},
"node_modules/@babel/standalone": {
"version": "7.28.2",
"resolved": "https://registry.npmjs.org/@babel/standalone/-/standalone-7.28.2.tgz",
"integrity": "sha512-1kjA8XzBRN68HoDDYKP38bucHtxYWCIX8XdYwe1drRNUOjOVNt8EMy9jiE6UwaGFfU7NOHCG+C8KgBc9CR08nA==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/types": {
"version": "7.28.2",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz",
"integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@braintree/sanitize-url": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.1.tgz",
@ -311,6 +365,448 @@
"tslib": "^2.4.0"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz",
"integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz",
"integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz",
"integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz",
"integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz",
"integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz",
"integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz",
"integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz",
"integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz",
"integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz",
"integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz",
"integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz",
"integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz",
"integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz",
"integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz",
"integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz",
"integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz",
"integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz",
"integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz",
"integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz",
"integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz",
"integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz",
"integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz",
"integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz",
"integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz",
"integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz",
"integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@floating-ui/core": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.2.tgz",
@ -1140,6 +1636,25 @@
"node": ">= 8"
}
},
"node_modules/@paciolan/remote-component": {
"version": "2.13.0",
"resolved": "https://registry.npmjs.org/@paciolan/remote-component/-/remote-component-2.13.0.tgz",
"integrity": "sha512-VESHSqjYf3kpDmqpc1Fx9e5Ccnyc1fbCVrBGSp5yRw1+6oRwzoFlDGc27u8Af3vDl1RJD/4aldaeMZ8f2Btojg==",
"license": "MIT",
"dependencies": {
"@paciolan/remote-module-loader": "^3.0.2"
},
"peerDependencies": {
"react": ">=16.9",
"react-dom": ">=16.9"
}
},
"node_modules/@paciolan/remote-module-loader": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@paciolan/remote-module-loader/-/remote-module-loader-3.0.3.tgz",
"integrity": "sha512-gwdJcP5QQbO7OUf00FWh+A5DkF3TnIv06JB3aMpm9pbbHcdouFrjo4nEW7HtbWeIs7z3gwGEVd82clAmzVzh3Q==",
"license": "MIT"
},
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@ -2803,6 +3318,66 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/babel__core": {
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
"integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.20.7",
"@babel/types": "^7.20.7",
"@types/babel__generator": "*",
"@types/babel__template": "*",
"@types/babel__traverse": "*"
}
},
"node_modules/@types/babel__generator": {
"version": "7.27.0",
"resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
"integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.0.0"
}
},
"node_modules/@types/babel__standalone": {
"version": "7.1.9",
"resolved": "https://registry.npmjs.org/@types/babel__standalone/-/babel__standalone-7.1.9.tgz",
"integrity": "sha512-IcCNPLqpevUD7UpV8QB0uwQPOyoOKACFf0YtYWRHcmxcakaje4Q7dbG2+jMqxw/I8Zk0NHvEps66WwS7z/UaaA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.25.6",
"@babel/types": "^7.25.6",
"@types/babel__core": "^7.20.5",
"@types/babel__generator": "^7.6.8",
"@types/babel__template": "^7.4.4",
"@types/babel__traverse": "^7.20.6"
}
},
"node_modules/@types/babel__template": {
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
"integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.1.0",
"@babel/types": "^7.0.0"
}
},
"node_modules/@types/babel__traverse": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
"integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.28.2"
}
},
"node_modules/@types/d3": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz",
@ -3228,6 +3803,15 @@
"acorn-walk": "^8.0.2"
}
},
"node_modules/acorn-jsx": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
"license": "MIT",
"peerDependencies": {
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/acorn-walk": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
@ -5221,6 +5805,48 @@
"node": ">= 0.4"
}
},
"node_modules/esbuild": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz",
"integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.25.8",
"@esbuild/android-arm": "0.25.8",
"@esbuild/android-arm64": "0.25.8",
"@esbuild/android-x64": "0.25.8",
"@esbuild/darwin-arm64": "0.25.8",
"@esbuild/darwin-x64": "0.25.8",
"@esbuild/freebsd-arm64": "0.25.8",
"@esbuild/freebsd-x64": "0.25.8",
"@esbuild/linux-arm": "0.25.8",
"@esbuild/linux-arm64": "0.25.8",
"@esbuild/linux-ia32": "0.25.8",
"@esbuild/linux-loong64": "0.25.8",
"@esbuild/linux-mips64el": "0.25.8",
"@esbuild/linux-ppc64": "0.25.8",
"@esbuild/linux-riscv64": "0.25.8",
"@esbuild/linux-s390x": "0.25.8",
"@esbuild/linux-x64": "0.25.8",
"@esbuild/netbsd-arm64": "0.25.8",
"@esbuild/netbsd-x64": "0.25.8",
"@esbuild/openbsd-arm64": "0.25.8",
"@esbuild/openbsd-x64": "0.25.8",
"@esbuild/openharmony-arm64": "0.25.8",
"@esbuild/sunos-x64": "0.25.8",
"@esbuild/win32-arm64": "0.25.8",
"@esbuild/win32-ia32": "0.25.8",
"@esbuild/win32-x64": "0.25.8"
}
},
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
@ -8168,6 +8794,27 @@
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
"license": "MIT"
},
"node_modules/react-jsx-parser": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/react-jsx-parser/-/react-jsx-parser-2.4.0.tgz",
"integrity": "sha512-ugh/99kuUTec2C/zrznDU/4X5avEYlkf0tOkicQfQq151ERTsga0mxM7uXhV7PEPV+6nfwKV1rzWIRhzsN1uLQ==",
"license": "MIT",
"dependencies": {
"acorn": "^8.12.1",
"acorn-jsx": "^5.3.2"
},
"engines": {
"bun": "^1.1.27"
},
"optionalDependencies": {
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1"
},
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18"
}
},
"node_modules/react-redux": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",

View file

@ -10,9 +10,11 @@
"lint": "next lint"
},
"dependencies": {
"@babel/standalone": "^7.28.2",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@paciolan/remote-component": "^2.13.0",
"@radix-ui/react-accordion": "^1.2.1",
"@radix-ui/react-avatar": "^1.1.2",
"@radix-ui/react-dialog": "^1.1.6",
@ -52,6 +54,7 @@
"puppeteer": "^24.13.0",
"react": "^18",
"react-dom": "^18",
"react-jsx-parser": "^2.4.0",
"react-redux": "^9.1.2",
"react-sketch-canvas": "^6.2.0",
"recharts": "^2.15.4",
@ -66,6 +69,7 @@
},
"devDependencies": {
"@types/animejs": "^3.1.12",
"@types/babel__standalone": "^7.1.9",
"@types/node": "^20",
"@types/puppeteer": "^5.4.7",
"@types/react": "^18",
@ -73,6 +77,7 @@
"@types/uuid": "^10.0.0",
"@types/ws": "^8.5.13",
"cypress": "^14.3.3",
"esbuild": "0.25.8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
},

View file

@ -1,95 +1,100 @@
import * as z from 'zod';
import { ImageSchema, IconSchema } from '@/presentation-layouts/defaultSchemes';
import * as z from "zod";
import { ImageSchema, IconSchema } from "@/presentation-layouts/defaultSchemes";
export const Schema = z.object({
title: z.string()
.min(5)
.max(50)
.default("Quarterly Business Review")
.meta({
description: "Main slide title",
}),
title: z.string().min(5).max(50).default("Quarterly Business Review").meta({
description: "Main slide title",
}),
subtitle: z.string()
.min(3)
.max(100)
.optional()
.default("Q1 2024 Performance Summary")
.meta({
description: "Optional subtitle",
}),
subtitle: z
.string()
.min(3)
.max(100)
.optional()
.default("Q1 2024 Performance Summary")
.meta({
description: "Optional subtitle",
}),
metrics: z.array(z.object({
metrics: z
.array(
z.object({
label: z.string().min(2).max(20),
value: z.string().min(1).max(10),
trend: z.enum(['up', 'down', 'stable'])
})).default([
{ label: "Revenue", value: "$2.4M", trend: "up" },
{ label: "Growth", value: "15%", trend: "up" }
]).meta({
description: "Key performance metrics",
trend: z.enum(["up", "down", "stable"]),
})
)
.default([
{ label: "Revenue", value: "$2.4M", trend: "up" },
{ label: "Growth", value: "15%", trend: "up" },
])
.meta({
description: "Key performance metrics",
}),
chartImage: ImageSchema.default({
__image_url__: "https://example.com/quarterly-chart.png",
__image_prompt__: "Quarterly performance chart showing upward trend"
}).meta({
description: "Main performance chart",
}),
chartImage: ImageSchema.default({
__image_url__: "https://example.com/quarterly-chart.png",
__image_prompt__: "Quarterly performance chart showing upward trend",
}).meta({
description: "Main performance chart",
}),
trendIcon: IconSchema.default({
__icon_url__: "/static/icons/placeholder.png",
__icon_query__: "upward trend arrow icon"
}).meta({
description: "Trend indicator icon",
}),
trendIcon: IconSchema.default({
__icon_url__: "/static/icons/placeholder.png",
__icon_query__: "upward trend arrow icon",
}).meta({
description: "Trend indicator icon",
}),
});
type SchemaType = z.infer<typeof Schema>;
export default function ExampleSlideLayout({ data }: { data: SchemaType }) {
const { title, subtitle, metrics, chartImage, trendIcon } = data;
return (
<div className="aspect-video max-w-[1280px] w-full bg-white">
<header className="slide-header">
{title && <h1 className="text-4xl font-bold text-gray-900">{title}</h1>}
{subtitle && <p className="text-xl text-gray-600 mt-2">{subtitle}</p>}
</header>
const { title, subtitle, metrics, chartImage, trendIcon } = data;
return (
<div className="aspect-video max-w-[1280px] w-full bg-white">
<header className="slide-header">
{title && <h1 className="text-4xl font-bold text-gray-900">{title}</h1>}
{subtitle && <p className="text-xl text-gray-600 mt-2">{subtitle}</p>}
</header>
<main className="slide-content flex-1 flex">
{chartImage?.__image_url__ && (
<div className="chart-section flex-1">
<img
src={chartImage.__image_url__}
alt={chartImage.__image_prompt__}
className="w-full h-auto max-h-96 object-contain"
/>
</div>
)}
<main className="slide-content flex-1 flex">
{chartImage?.__image_url__ && (
<div className="chart-section flex-1">
<img
src={chartImage.__image_url__}
alt={chartImage.__image_prompt__}
className="w-full h-auto max-h-96 object-contain"
/>
</div>
)}
{metrics && metrics.length > 0 && (
<div className="metrics-section w-1/3 ml-6">
<h2 className="text-2xl font-semibold mb-4">Key Metrics</h2>
{metrics.map((metric, index) => (
<div key={index} className="metric-item mb-4 p-3 bg-gray-50 rounded">
<div className="flex items-center justify-between">
<span className="font-medium">{metric.label}</span>
{trendIcon?.__icon_url__ && (
<img
src={trendIcon.__icon_url__}
alt={metric.trend}
className="w-6 h-6"
/>
)}
</div>
<span className="text-2xl font-bold text-blue-600">
{metric.value}
</span>
</div>
))}
</div>
)}
</main>
</div>
);
}
{metrics && metrics.length > 0 && (
<div className="metrics-section w-1/3 ml-6">
<h2 className="text-2xl font-semibold mb-4">Key Metrics</h2>
{metrics.map((metric, index) => (
<div
key={index}
className="metric-item mb-4 p-3 bg-gray-50 rounded"
>
<div className="flex items-center justify-between">
<span className="font-medium">{metric.label}</span>
{trendIcon?.__icon_url__ && (
<img
src={trendIcon.__icon_url__}
alt={metric.trend}
className="w-6 h-6"
/>
)}
</div>
<span className="text-2xl font-bold text-blue-600">
{metric.value}
</span>
</div>
))}
</div>
)}
</main>
</div>
);
}

View file

@ -2,153 +2,167 @@ import * as z from "zod";
import { ImageSchema, IconSchema } from "../defaultSchemes";
export const layoutName = "About Us Slide";
export const layoutId = "about-us-slide";
export const layoutDescription =
"A slide with an introduction to the organization";
// Schema definition
export const Schema = z.object({
sectionTitle: z.string().min(3).max(25).default("ABOUT US").meta({
description:
"Main section heading - can be used for any organizational introduction",
}),
sectionTitle: z.string()
.min(3)
.max(25)
.default("ABOUT US")
.meta({
description: "Main section heading - can be used for any organizational introduction",
}),
sectionSubtitle: z.string()
.min(5)
.max(40)
.default("GET TO KNOW US BETTER")
.meta({
description: "Supporting subtitle that invites audience engagement and builds connection",
}),
organizationDescription: z.string()
.min(50)
.max(300)
.default("We believe in the transformative power of innovation, strategic thinking, and cutting-edge solutions. Our mission is simple: to empower organizations with comprehensive strategies that not only elevate performance but also drive tangible growth and success.")
.meta({
description: "Primary description of the organization's mission, values, and approach",
}),
additionalContext: z.string()
.min(30)
.max(150)
.default("What sets us apart is not just our expertise but our commitment to understanding the unique needs of each client.")
.meta({
description: "Additional context or differentiating statement about the organization",
}),
featuredImage: ImageSchema.default({
__image_url__: "https://images.unsplash.com/photo-1454165804606-c3d57bc86b40?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
__image_prompt__: "Professional business team analyzing data and working collaboratively"
}).meta({
description: "Primary visual that represents the organization's work or environment",
sectionSubtitle: z
.string()
.min(5)
.max(40)
.default("GET TO KNOW US BETTER")
.meta({
description:
"Supporting subtitle that invites audience engagement and builds connection",
}),
showVisualAccents: z.boolean()
.default(true)
.meta({
description: "Whether to display decorative visual accent elements",
}),
organizationDescription: z
.string()
.min(50)
.max(300)
.default(
"We believe in the transformative power of innovation, strategic thinking, and cutting-edge solutions. Our mission is simple: to empower organizations with comprehensive strategies that not only elevate performance but also drive tangible growth and success."
)
.meta({
description:
"Primary description of the organization's mission, values, and approach",
}),
showColorBlocks: z.boolean()
.default(true)
.meta({
description: "Whether to show colored background blocks for visual hierarchy",
}),
additionalContext: z
.string()
.min(30)
.max(150)
.default(
"What sets us apart is not just our expertise but our commitment to understanding the unique needs of each client."
)
.meta({
description:
"Additional context or differentiating statement about the organization",
}),
showAccentSquare: z.boolean()
.default(true)
.meta({
description: "Whether to display the accent square decoration element",
}),
})
featuredImage: ImageSchema.default({
__image_url__:
"https://images.unsplash.com/photo-1454165804606-c3d57bc86b40?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
__image_prompt__:
"Professional business team analyzing data and working collaboratively",
}).meta({
description:
"Primary visual that represents the organization's work or environment",
}),
showVisualAccents: z.boolean().default(true).meta({
description: "Whether to display decorative visual accent elements",
}),
showColorBlocks: z.boolean().default(true).meta({
description:
"Whether to show colored background blocks for visual hierarchy",
}),
showAccentSquare: z.boolean().default(true).meta({
description: "Whether to display the accent square decoration element",
}),
});
// Type inference
type SchemaType = z.infer<typeof Schema>;
// Component definitionz
const AboutUsSlide = ({ data }: { data: Partial<SchemaType> }) => {
const {
sectionTitle,
sectionSubtitle,
organizationDescription,
additionalContext,
featuredImage,
showVisualAccents,
showColorBlocks,
showAccentSquare,
} = data;
const { sectionTitle, sectionSubtitle, organizationDescription, additionalContext, featuredImage, showVisualAccents, showColorBlocks, showAccentSquare } = data;
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Main Content Area */}
<div className="h-full flex">
{/* Left Side - Content */}
<div className="w-3/5 relative bg-white px-16 py-12 flex flex-col justify-center">
{/* Title Section */}
<div className="mb-0">
{sectionTitle && (
<h1 className="text-3xl lg:text-4xl font-black text-teal-700 leading-tight mb-4">
{sectionTitle}
</h1>
)}
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Main Content Area */}
<div className="h-full flex">
{/* Left Side - Content */}
<div className="w-3/5 relative bg-white px-16 py-12 flex flex-col justify-center">
{/* Title Section */}
<div className="mb-0">
{sectionTitle && (
<h1 className="text-3xl lg:text-4xl font-black text-teal-700 leading-tight mb-4">
{sectionTitle}
</h1>
)}
{sectionSubtitle && (
<p className="text-base font-semibold text-gray-800 tracking-wide mb-6">
{sectionSubtitle}
</p>
)}
{sectionSubtitle && (
<p className="text-base font-semibold text-gray-800 tracking-wide mb-6">
{sectionSubtitle}
</p>
)}
{/* Decorative gray line */}
<div className="w-1 h-20 bg-gray-400 mb-8"></div>
</div>
{/* Decorative gray line */}
<div className="w-1 h-20 bg-gray-400 mb-8"></div>
</div>
{/* Description Text */}
{organizationDescription && (
<p className="text-base leading-relaxed text-gray-700 max-w-xl">
{organizationDescription}
</p>
)}
{/* Description Text */}
{organizationDescription && (
<p className="text-base leading-relaxed text-gray-700 max-w-xl">
{organizationDescription}
</p>
)}
{/* Additional Text */}
{additionalContext && (
<div>
<p className="text-base leading-relaxed text-gray-700 max-w-xl">
{additionalContext}
</p>
</div>
)}
</div>
{/* Right Side - Image and Decorative Elements */}
<div className="w-2/5 relative">
{/* Yellow Square - Top Right */}
{showAccentSquare && (
<div className="absolute bottom-0 right-0 w-24 h-24 bg-yellow-300 z-10"></div>
)}
{/* Decorative Circle - On Yellow Square */}
{showVisualAccents && (
<div className="absolute top-6 right-6 w-6 h-6 border-2 border-teal-600 rounded-full z-20"></div>
)}
{/* Business Image - Left positioned */}
{featuredImage?.__image_url__ && (
<div className="absolute right-36 top-1/2 -translate-y-1/2 h-[500px] w-[350px] z-20 shadow-lg">
<img
src={featuredImage.__image_url__}
alt={featuredImage.__image_prompt__}
className="w-full h-full object-cover"
/>
</div>
)}
{/* Teal Accent Areas */}
{showColorBlocks && (
<>
{/* Vertical Teal Strip - Center */}
<div className="absolute top-0 right-0 bottom-16 h-full w-[300px] bg-teal-600 z-10"></div>
</>
)}
</div>
{/* Additional Text */}
{additionalContext && (
<div>
<p className="text-base leading-relaxed text-gray-700 max-w-xl">
{additionalContext}
</p>
</div>
)}
</div>
);
{/* Right Side - Image and Decorative Elements */}
<div className="w-2/5 relative">
{/* Yellow Square - Top Right */}
{showAccentSquare && (
<div className="absolute bottom-0 right-0 w-24 h-24 bg-yellow-300 z-10"></div>
)}
{/* Decorative Circle - On Yellow Square */}
{showVisualAccents && (
<div className="absolute top-6 right-6 w-6 h-6 border-2 border-teal-600 rounded-full z-20"></div>
)}
{/* Business Image - Left positioned */}
{featuredImage?.__image_url__ && (
<div className="absolute right-36 top-1/2 -translate-y-1/2 h-[500px] w-[350px] z-20 shadow-lg">
<img
src={featuredImage.__image_url__}
alt={featuredImage.__image_prompt__}
className="w-full h-full object-cover"
/>
</div>
)}
{/* Teal Accent Areas */}
{showColorBlocks && (
<>
{/* Vertical Teal Strip - Center */}
<div className="absolute top-0 right-0 bottom-16 h-full w-[300px] bg-teal-600 z-10"></div>
</>
)}
</div>
</div>
</div>
);
};
export default AboutUsSlide;
export default AboutUsSlide;

View file

@ -2,83 +2,103 @@ import React from "react";
import * as z from "zod";
import { ImageSchema, IconSchema } from "../defaultSchemes";
import { ChartContainer, ChartLegend, ChartLegendContent, ChartTooltip, ChartTooltipContent } from '@/components/ui/chart';
import {
ChartContainer,
ChartLegend,
ChartLegendContent,
ChartTooltip,
ChartTooltipContent,
} from "@/components/ui/chart";
import { BarChart, Bar, XAxis, YAxis, CartesianGrid } from "recharts";
export const layoutName = "Business Model Slide";
export const layoutId = "business-model-slide";
export const layoutDescription = "A slide with a business model";
// Schema definition
export const Schema = z.object({
sectionTitle: z.string().min(3).max(30).default("BUSINESS MODEL").meta({
description:
"Main section heading - adapt to presentation topic (e.g., 'Revenue Strategy', 'Funding Model', 'Implementation Plan', 'Solution Framework')",
}),
sectionTitle: z.string()
.min(3)
.max(30)
.default("BUSINESS MODEL")
.meta({
description: "Main section heading - adapt to presentation topic (e.g., 'Revenue Strategy', 'Funding Model', 'Implementation Plan', 'Solution Framework')",
}),
sectionSubtitle: z.string()
.min(10)
.max(60)
.default("SUSTAINABLE REVENUE AND VALUE CREATION")
.meta({
description: "Supporting subtitle that describes the approach - adapt to topic (e.g., 'Carbon Reduction Strategy', 'Healthcare Delivery Model', 'Educational Framework')",
}),
modelDescription: z.string()
.min(50)
.max(300)
.default("Our business model focuses on creating sustainable value through multiple revenue streams, strategic partnerships, and customer-centric solutions. We prioritize long-term relationships and scalable growth opportunities.")
.meta({
description: "IMPORTANT: Provide topic-specific description of the model/approach. For global warming: describe carbon reduction strategies, renewable energy adoption, sustainability metrics. For healthcare: treatment protocols, patient care models. For education: learning methodologies, curriculum design. Always provide concrete, relevant details for the presentation topic.",
}),
headerVisual: ImageSchema.default({
__image_url__: "https://images.unsplash.com/photo-1559136555-9303baea8ebd?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
__image_prompt__: "Business strategy meeting with charts, graphs and team collaboration"
}).meta({
description: "Header visual representing the topic area - ADAPT the image prompt to match presentation topic (e.g., 'Climate scientists analyzing global warming data', 'Medical team reviewing patient care protocols', 'Teachers planning educational curriculum')",
sectionSubtitle: z
.string()
.min(10)
.max(60)
.default("SUSTAINABLE REVENUE AND VALUE CREATION")
.meta({
description:
"Supporting subtitle that describes the approach - adapt to topic (e.g., 'Carbon Reduction Strategy', 'Healthcare Delivery Model', 'Educational Framework')",
}),
chartData: z.array(z.object({
modelDescription: z
.string()
.min(50)
.max(300)
.default(
"Our business model focuses on creating sustainable value through multiple revenue streams, strategic partnerships, and customer-centric solutions. We prioritize long-term relationships and scalable growth opportunities."
)
.meta({
description:
"IMPORTANT: Provide topic-specific description of the model/approach. For global warming: describe carbon reduction strategies, renewable energy adoption, sustainability metrics. For healthcare: treatment protocols, patient care models. For education: learning methodologies, curriculum design. Always provide concrete, relevant details for the presentation topic.",
}),
headerVisual: ImageSchema.default({
__image_url__:
"https://images.unsplash.com/photo-1559136555-9303baea8ebd?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
__image_prompt__:
"Business strategy meeting with charts, graphs and team collaboration",
}).meta({
description:
"Header visual representing the topic area - ADAPT the image prompt to match presentation topic (e.g., 'Climate scientists analyzing global warming data', 'Medical team reviewing patient care protocols', 'Teachers planning educational curriculum')",
}),
chartData: z
.array(
z.object({
category: z.string().min(3).max(25),
value: z.number().min(0).max(100),
color: z.string().min(3).max(20).optional()
})).min(2).max(6).default([
{ category: "Product Sales", value: 45, color: "#22C55E" },
{ category: "Services", value: 30, color: "#0891B2" },
{ category: "Partnerships", value: 15, color: "#FDE047" },
{ category: "Licensing", value: 10, color: "#F97316" }
]).meta({
description: "CRITICAL: Provide actual data relevant to the presentation topic. For global warming: CO2 emission sources (Transport 29%, Energy 25%, Industry 21%, Agriculture 24%), temperature rise by decade, renewable energy adoption rates. For healthcare: treatment success rates, patient demographics, cost breakdowns. For education: student performance metrics, learning outcomes, resource allocation. Always use REAL topic-specific data with appropriate categories and realistic values.",
color: z.string().min(3).max(20).optional(),
})
)
.min(2)
.max(6)
.default([
{ category: "Product Sales", value: 45, color: "#22C55E" },
{ category: "Services", value: 30, color: "#0891B2" },
{ category: "Partnerships", value: 15, color: "#FDE047" },
{ category: "Licensing", value: 10, color: "#F97316" },
])
.meta({
description:
"CRITICAL: Provide actual data relevant to the presentation topic. For global warming: CO2 emission sources (Transport 29%, Energy 25%, Industry 21%, Agriculture 24%), temperature rise by decade, renewable energy adoption rates. For healthcare: treatment success rates, patient demographics, cost breakdowns. For education: student performance metrics, learning outcomes, resource allocation. Always use REAL topic-specific data with appropriate categories and realistic values.",
}),
showChart: z.boolean()
.default(true)
.meta({
description: "Whether to display the data visualization - typically keep true for data-driven presentations",
}),
showChart: z.boolean().default(true).meta({
description:
"Whether to display the data visualization - typically keep true for data-driven presentations",
}),
showVisualAccents: z.boolean()
.default(true)
.meta({
description: "Whether to display decorative visual accent elements",
}),
})
showVisualAccents: z.boolean().default(true).meta({
description: "Whether to display decorative visual accent elements",
}),
});
// Chart configuration
const chartConfig = {
series1: {
label: "Series 1",
color: "#1D9A8A",
},
series2: {
label: "Series 2",
color: "#E8F4B8",
},
series3: {
label: "Series 3",
color: "#A8C97F",
},
series1: {
label: "Series 1",
color: "#1D9A8A",
},
series2: {
label: "Series 2",
color: "#E8F4B8",
},
series3: {
label: "Series 3",
color: "#A8C97F",
},
};
// Type inference
@ -86,103 +106,107 @@ type SchemaType = z.infer<typeof Schema>;
// Component definition
const BusinessModelSlide = ({ data }: { data: Partial<SchemaType> }) => {
const {
sectionTitle,
sectionSubtitle,
modelDescription,
headerVisual,
chartData,
showChart,
showVisualAccents,
} = data;
const { sectionTitle, sectionSubtitle, modelDescription, headerVisual, chartData, showChart, showVisualAccents } = data;
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Header Image Section */}
{headerVisual?.__image_url__ && (
<div className="h-32 w-full relative">
<img
src={headerVisual.__image_url__}
alt={headerVisual.__image_prompt__}
className="w-full h-full object-cover"
/>
<div className="absolute inset-0 bg-black bg-opacity-40"></div>
</div>
)}
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Header Image Section */}
{headerVisual?.__image_url__ && (
<div className="h-32 w-full relative">
<img
src={headerVisual.__image_url__}
alt={headerVisual.__image_prompt__}
className="w-full h-full object-cover"
/>
<div className="absolute inset-0 bg-black bg-opacity-40"></div>
</div>
{/* Main Content Area */}
<div className="flex h-[calc(100%-8rem)]">
{/* Left Side - Content */}
<div className="w-1/2 px-16 py-8 flex flex-col justify-start">
{/* Title Section */}
<div className="mb-6">
{sectionTitle && (
<h1 className="text-3xl lg:text-4xl font-black text-teal-700 leading-tight mb-4">
{sectionTitle}
</h1>
)}
{/* Main Content Area */}
<div className="flex h-[calc(100%-8rem)]">
{/* Left Side - Content */}
<div className="w-1/2 px-16 py-8 flex flex-col justify-start">
{/* Title Section */}
<div className="mb-6">
{sectionTitle && (
<h1 className="text-3xl lg:text-4xl font-black text-teal-700 leading-tight mb-4">
{sectionTitle}
</h1>
)}
{sectionSubtitle && (
<p className="text-base font-semibold text-gray-800 tracking-wide mb-6">
{sectionSubtitle}
</p>
)}
</div>
{sectionSubtitle && (
<p className="text-base font-semibold text-gray-800 tracking-wide mb-6">
{sectionSubtitle}
</p>
)}
</div>
{/* Model Description */}
{modelDescription && (
<div>
<p className="text-base leading-relaxed text-gray-700">
{modelDescription}
</p>
</div>
)}
{/* Visual Accents */}
{showVisualAccents && (
<>
<div className="absolute bottom-8 left-8 w-6 h-6 bg-yellow-300 rounded-full"></div>
<div className="absolute top-40 right-1/2 w-4 h-4 bg-teal-600 rounded-full"></div>
</>
)}
</div>
{/* Right Side - Chart */}
<div className="w-1/2 px-8 py-8 flex flex-col justify-center">
{showChart && chartData && chartData.length > 0 && (
<div className="h-80 w-full">
<ChartContainer
config={{
value: {
label: "Value",
color: "hsl(var(--chart-1))",
},
}}
className="h-full w-full"
>
<BarChart data={chartData} margin={{ top: 20, right: 20, bottom: 20, left: 20 }}>
<CartesianGrid strokeDasharray="3 3" stroke="#e5e7eb" />
<XAxis
dataKey="category"
stroke="#6b7280"
fontSize={12}
angle={-45}
textAnchor="end"
height={60}
/>
<YAxis stroke="#6b7280" fontSize={12} />
<ChartTooltip content={<ChartTooltipContent />} />
<Bar
dataKey="value"
fill="#0891b2"
radius={[4, 4, 0, 0]}
/>
</BarChart>
</ChartContainer>
</div>
)}
</div>
{/* Model Description */}
{modelDescription && (
<div>
<p className="text-base leading-relaxed text-gray-700">
{modelDescription}
</p>
</div>
)}
{/* Bottom accent strip */}
<div className="absolute bottom-0 left-0 right-0 h-2 bg-teal-600"></div>
{/* Visual Accents */}
{showVisualAccents && (
<>
<div className="absolute bottom-8 left-8 w-6 h-6 bg-yellow-300 rounded-full"></div>
<div className="absolute top-40 right-1/2 w-4 h-4 bg-teal-600 rounded-full"></div>
</>
)}
</div>
);
{/* Right Side - Chart */}
<div className="w-1/2 px-8 py-8 flex flex-col justify-center">
{showChart && chartData && chartData.length > 0 && (
<div className="h-80 w-full">
<ChartContainer
config={{
value: {
label: "Value",
color: "hsl(var(--chart-1))",
},
}}
className="h-full w-full"
>
<BarChart
data={chartData}
margin={{ top: 20, right: 20, bottom: 20, left: 20 }}
>
<CartesianGrid strokeDasharray="3 3" stroke="#e5e7eb" />
<XAxis
dataKey="category"
stroke="#6b7280"
fontSize={12}
angle={-45}
textAnchor="end"
height={60}
/>
<YAxis stroke="#6b7280" fontSize={12} />
<ChartTooltip content={<ChartTooltipContent />} />
<Bar dataKey="value" fill="#0891b2" radius={[4, 4, 0, 0]} />
</BarChart>
</ChartContainer>
</div>
)}
</div>
</div>
{/* Bottom accent strip */}
<div className="absolute bottom-0 left-0 right-0 h-2 bg-teal-600"></div>
</div>
);
};
export default BusinessModelSlide;
export default BusinessModelSlide;

View file

@ -2,155 +2,173 @@ import * as z from "zod";
import { ImageSchema, IconSchema } from "../defaultSchemes";
export const layoutName = "Market Size Slide";
export const layoutId = "market-size-slide";
export const layoutDescription = "A slide with a market size analysis";
// Schema definition
export const Schema = z.object({
sectionTitle: z.string().min(3).max(30).default("MARKET ANALYSIS").meta({
description:
"Main section heading - can be 'Market Size', 'Market Opportunity', 'Industry Overview', or similar",
}),
sectionTitle: z.string()
.min(3)
.max(30)
.default("MARKET ANALYSIS")
.meta({
description: "Main section heading - can be 'Market Size', 'Market Opportunity', 'Industry Overview', or similar",
}),
sectionSubtitle: z
.string()
.min(10)
.max(60)
.default("UNDERSTANDING THE OPPORTUNITY LANDSCAPE")
.meta({
description:
"Supporting subtitle that frames the market discussion and opportunity scope",
}),
sectionSubtitle: z.string()
.min(10)
.max(60)
.default("UNDERSTANDING THE OPPORTUNITY LANDSCAPE")
.meta({
description: "Supporting subtitle that frames the market discussion and opportunity scope",
}),
marketDefinitions: z.array(z.object({
marketDefinitions: z
.array(
z.object({
marketType: z.string().min(3).max(30),
marketDescription: z.string().min(20).max(150),
marketValue: z.string().min(3).max(25).optional()
})).min(2).max(3).default([
{
marketType: "Total Addressable Market (TAM)",
marketDescription: "The overall revenue opportunity available if we achieved 100% market share across all segments and geographies.",
marketValue: "$50B"
},
{
marketType: "Serviceable Addressable Market (SAM)",
marketDescription: "The portion of TAM targeted by our products and services within our geographic reach.",
marketValue: "$15B"
},
{
marketType: "Serviceable Obtainable Market (SOM)",
marketDescription: "The portion of SAM that we can realistically capture based on our resources and market conditions.",
marketValue: "$3B"
}
]).meta({
description: "List of market definitions and opportunities with descriptions and potential values",
marketValue: z.string().min(3).max(25).optional(),
})
)
.min(2)
.max(3)
.default([
{
marketType: "Total Addressable Market (TAM)",
marketDescription:
"The overall revenue opportunity available if we achieved 100% market share across all segments and geographies.",
marketValue: "$50B",
},
{
marketType: "Serviceable Addressable Market (SAM)",
marketDescription:
"The portion of TAM targeted by our products and services within our geographic reach.",
marketValue: "$15B",
},
{
marketType: "Serviceable Obtainable Market (SOM)",
marketDescription:
"The portion of SAM that we can realistically capture based on our resources and market conditions.",
marketValue: "$3B",
},
])
.meta({
description:
"List of market definitions and opportunities with descriptions and potential values",
}),
visualRepresentation: ImageSchema.default({
__image_url__: "https://images.unsplash.com/photo-1597149962419-0d900ac2b46c?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
__image_prompt__: "World map showing global market reach and geographic distribution"
}).meta({
description: "Visual that represents market scope - could be a world map, chart, or geographic visualization",
}),
visualRepresentation: ImageSchema.default({
__image_url__:
"https://images.unsplash.com/photo-1597149962419-0d900ac2b46c?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
__image_prompt__:
"World map showing global market reach and geographic distribution",
}).meta({
description:
"Visual that represents market scope - could be a world map, chart, or geographic visualization",
}),
showYellowUnderline: z.boolean()
.default(true)
.meta({
description: "Whether to display the decorative yellow underline accent",
}),
showYellowUnderline: z.boolean().default(true).meta({
description: "Whether to display the decorative yellow underline accent",
}),
showVisualAccents: z.boolean()
.default(true)
.meta({
description: "Whether to display decorative visual accent elements",
}),
})
showVisualAccents: z.boolean().default(true).meta({
description: "Whether to display decorative visual accent elements",
}),
});
// Type inference
type SchemaType = z.infer<typeof Schema>;
// Component definition
const MarketSizeSlide = ({ data }: { data: Partial<SchemaType> }) => {
const {
sectionTitle,
sectionSubtitle,
marketDefinitions,
visualRepresentation,
showYellowUnderline,
showVisualAccents,
} = data;
const { sectionTitle, sectionSubtitle, marketDefinitions, visualRepresentation, showYellowUnderline, showVisualAccents } = data;
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Main Content Area */}
<div className="h-full flex">
{/* Left Side - Content */}
<div className="w-3/5 relative bg-white px-16 py-12 flex flex-col justify-start">
{/* Title Section */}
<div className="mb-8">
{sectionTitle && (
<h1 className="text-3xl lg:text-4xl font-black leading-tight mb-4">
{sectionTitle}
</h1>
)}
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Main Content Area */}
<div className="h-full flex">
{/* Left Side - Content */}
<div className="w-3/5 relative bg-white px-16 py-12 flex flex-col justify-start">
{/* Title Section */}
<div className="mb-8">
{sectionTitle && (
<h1 className="text-3xl lg:text-4xl font-black leading-tight mb-4">
{sectionTitle}
</h1>
)}
{sectionSubtitle && (
<p className="text-base font-semibold tracking-wide mb-4">
{sectionSubtitle}
</p>
)}
{sectionSubtitle && (
<p className="text-base font-semibold tracking-wide mb-4">
{sectionSubtitle}
</p>
)}
{/* Yellow Decorative Underline */}
{showYellowUnderline && (
<div className="w-24 h-1 bg-yellow-300 mb-8"></div>
)}
</div>
{/* Yellow Decorative Underline */}
{showYellowUnderline && (
<div className="w-24 h-1 bg-yellow-300 mb-8"></div>
)}
</div>
{/* Market Definitions List */}
{marketDefinitions && marketDefinitions.length > 0 && (
<div className="space-y-6">
{marketDefinitions.map((market, index) => (
<div key={index} className="border-l-4 border-teal-600 pl-6">
<div className="flex items-center justify-between mb-2">
<h3 className="text-lg font-bold text-gray-900">
{market.marketType}
</h3>
{market.marketValue && (
<span className="text-xl font-bold text-teal-600">
{market.marketValue}
</span>
)}
</div>
<p className="text-base leading-relaxed text-gray-700">
{market.marketDescription}
</p>
</div>
))}
</div>
)}
</div>
{/* Right Side - Visual Representation */}
<div className="w-2/5 relative bg-gray-50">
{/* Visual Accents */}
{showVisualAccents && (
<>
{/* Decorative circles */}
<div className="absolute top-8 right-8 w-6 h-6 bg-teal-600 rounded-full opacity-60 z-20"></div>
<div className="absolute bottom-12 left-8 w-4 h-4 bg-yellow-300 rounded-full z-20"></div>
</>
)}
{/* Visual Representation */}
{visualRepresentation?.__image_url__ && (
<div className="absolute inset-8 shadow-lg">
<img
src={visualRepresentation.__image_url__}
alt={visualRepresentation.__image_prompt__}
className="w-full h-full object-cover rounded-lg"
/>
</div>
{/* Market Definitions List */}
{marketDefinitions && marketDefinitions.length > 0 && (
<div className="space-y-6">
{marketDefinitions.map((market, index) => (
<div key={index} className="border-l-4 border-teal-600 pl-6">
<div className="flex items-center justify-between mb-2">
<h3 className="text-lg font-bold text-gray-900">
{market.marketType}
</h3>
{market.marketValue && (
<span className="text-xl font-bold text-teal-600">
{market.marketValue}
</span>
)}
</div>
<p className="text-base leading-relaxed text-gray-700">
{market.marketDescription}
</p>
</div>
))}
</div>
{/* Bottom accent strip */}
<div className="absolute bottom-0 left-0 right-0 h-3 bg-teal-600"></div>
)}
</div>
);
{/* Right Side - Visual Representation */}
<div className="w-2/5 relative bg-gray-50">
{/* Visual Accents */}
{showVisualAccents && (
<>
{/* Decorative circles */}
<div className="absolute top-8 right-8 w-6 h-6 bg-teal-600 rounded-full opacity-60 z-20"></div>
<div className="absolute bottom-12 left-8 w-4 h-4 bg-yellow-300 rounded-full z-20"></div>
</>
)}
{/* Visual Representation */}
{visualRepresentation?.__image_url__ && (
<div className="absolute inset-8 shadow-lg">
<img
src={visualRepresentation.__image_url__}
alt={visualRepresentation.__image_prompt__}
className="w-full h-full object-cover rounded-lg"
/>
</div>
)}
</div>
</div>
{/* Bottom accent strip */}
<div className="absolute bottom-0 left-0 right-0 h-3 bg-teal-600"></div>
</div>
);
};
export default MarketSizeSlide;
export default MarketSizeSlide;

View file

@ -2,129 +2,146 @@ import * as z from "zod";
import { ImageSchema, IconSchema } from "../defaultSchemes";
export const layoutName = "Our Service Slide";
export const layoutId = "our-service-slide";
export const layoutDescription = "A slide with a list of services";
// Schema definition
export const Schema = z.object({
sectionTitle: z.string().min(3).max(30).default("OUR SERVICES").meta({
description:
"Main section heading - can be 'Our Services', 'What We Offer', 'Service Portfolio', or similar",
}),
sectionTitle: z.string()
.min(3)
.max(30)
.default("OUR SERVICES")
.meta({
description: "Main section heading - can be 'Our Services', 'What We Offer', 'Service Portfolio', or similar",
}),
sectionSubtitle: z.string()
.min(10)
.max(60)
.default("COMPREHENSIVE SOLUTIONS TAILORED FOR SUCCESS")
.meta({
description: "Supporting subtitle that describes the service approach or value proposition",
}),
bulletPoints: z.array(z.string().min(10).max(40)).min(4).max(6)
.default([
"Customized solutions for your business",
"Expert guidance and support",
"Innovative technology solutions",
"Comprehensive service portfolio"
]).meta({
description: "Bullet points to describe the services offered",
}),
serviceHighlight: ImageSchema.default({
__image_url__: "https://images.unsplash.com/photo-1556761175-b413da4baf72?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
__image_prompt__: "Professional service delivery or team working on client solutions"
}).meta({
description: "Visual that represents service delivery, expertise, or client collaboration",
sectionSubtitle: z
.string()
.min(10)
.max(60)
.default("COMPREHENSIVE SOLUTIONS TAILORED FOR SUCCESS")
.meta({
description:
"Supporting subtitle that describes the service approach or value proposition",
}),
showVisualAccents: z.boolean()
.default(true)
.meta({
description: "Whether to display decorative visual accent elements",
}),
bulletPoints: z
.array(z.string().min(10).max(40))
.min(4)
.max(6)
.default([
"Customized solutions for your business",
"Expert guidance and support",
"Innovative technology solutions",
"Comprehensive service portfolio",
])
.meta({
description: "Bullet points to describe the services offered",
}),
showColorBlocks: z.boolean()
.default(true)
.meta({
description: "Whether to show colored background sections for visual hierarchy",
}),
})
serviceHighlight: ImageSchema.default({
__image_url__:
"https://images.unsplash.com/photo-1556761175-b413da4baf72?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
__image_prompt__:
"Professional service delivery or team working on client solutions",
}).meta({
description:
"Visual that represents service delivery, expertise, or client collaboration",
}),
showVisualAccents: z.boolean().default(true).meta({
description: "Whether to display decorative visual accent elements",
}),
showColorBlocks: z.boolean().default(true).meta({
description:
"Whether to show colored background sections for visual hierarchy",
}),
});
// Type inference
type SchemaType = z.infer<typeof Schema>;
// Component definition
const OurServiceSlide = ({ data }: { data: Partial<SchemaType> }) => {
const {
sectionTitle,
sectionSubtitle,
serviceHighlight,
showVisualAccents,
showColorBlocks,
bulletPoints,
} = data;
const { sectionTitle, sectionSubtitle, serviceHighlight, showVisualAccents, showColorBlocks, bulletPoints } = data;
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Main Content Area */}
<div className="h-full flex">
{/* Left - Title */}
<div className="w-1/2 px-16 py-12 bg-gray-100">
{sectionTitle && (
<h1 className="text-3xl lg:text-4xl font-black text-teal-700 leading-tight mb-4">
{sectionTitle}
</h1>
)}
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Main Content Area */}
<div className="h-full flex">
{/* Left - Title */}
<div className="w-1/2 px-16 py-12 bg-gray-100">
{sectionTitle && (
<h1 className="text-3xl lg:text-4xl font-black text-teal-700 leading-tight mb-4">
{sectionTitle}
</h1>
)}
{sectionSubtitle && (
<p className="text-base font-semibold text-gray-800 tracking-wide">
{sectionSubtitle}
</p>
)}
{sectionSubtitle && (
<p className="text-base font-semibold text-gray-800 tracking-wide">
{sectionSubtitle}
</p>
)}
<div className="mt-16 space-y-4">
{bulletPoints && bulletPoints.map((point, index) => (
<div key={index} className="text-base font-semibold text-gray-800 tracking-wide">
<div className="flex gap-3 items-center">
<div className="min-w-8 min-h-8 bg-teal-700 rounded-full "></div>
<p className="text-lg font-medium text-gray-800 tracking-wide">{point}</p>
</div>
</div>
))}
</div>
{/* Visual Accents */}
{showVisualAccents && (
<>
{/* Decorative elements */}
<div className="absolute bottom-16 left-16 w-8 h-8 bg-yellow-300 rounded-full"></div>
<div className="absolute top-20 right-20 w-4 h-4 bg-teal-600 rounded-full"></div>
</>
)}
<div className="mt-16 space-y-4">
{bulletPoints &&
bulletPoints.map((point, index) => (
<div
key={index}
className="text-base font-semibold text-gray-800 tracking-wide"
>
<div className="flex gap-3 items-center">
<div className="min-w-8 min-h-8 bg-teal-700 rounded-full "></div>
<p className="text-lg font-medium text-gray-800 tracking-wide">
{point}
</p>
</div>
</div>
))}
</div>
{/* Right - Service Highlight */}
<div className="w-1/2 relative">
{/* Service Highlight Image */}
{serviceHighlight?.__image_url__ && (
<div className="h-full w-full">
<img
src={serviceHighlight.__image_url__}
alt={serviceHighlight.__image_prompt__}
className="w-full h-full object-cover"
/>
</div>
)}
{/* Color overlay if enabled */}
{showColorBlocks && (
<div className="absolute inset-0 bg-teal-600 bg-opacity-20"></div>
)}
</div>
</div>
{/* Bottom accent strip */}
{showColorBlocks && (
<div className="absolute bottom-0 left-0 right-0 h-3 bg-teal-600"></div>
)}
{/* Visual Accents */}
{showVisualAccents && (
<>
{/* Decorative elements */}
<div className="absolute bottom-16 left-16 w-8 h-8 bg-yellow-300 rounded-full"></div>
<div className="absolute top-20 right-20 w-4 h-4 bg-teal-600 rounded-full"></div>
</>
)}
</div>
);
{/* Right - Service Highlight */}
<div className="w-1/2 relative">
{/* Service Highlight Image */}
{serviceHighlight?.__image_url__ && (
<div className="h-full w-full">
<img
src={serviceHighlight.__image_url__}
alt={serviceHighlight.__image_prompt__}
className="w-full h-full object-cover"
/>
</div>
)}
{/* Color overlay if enabled */}
{showColorBlocks && (
<div className="absolute inset-0 bg-teal-600 bg-opacity-20"></div>
)}
</div>
</div>
{/* Bottom accent strip */}
{showColorBlocks && (
<div className="absolute bottom-0 left-0 right-0 h-3 bg-teal-600"></div>
)}
</div>
);
};
export default OurServiceSlide;
export default OurServiceSlide;

View file

@ -2,154 +2,171 @@ import * as z from "zod";
import { ImageSchema, IconSchema } from "../defaultSchemes";
export const layoutName = "Problems Slide";
export const layoutId = "problems-slide";
export const layoutDescription = "A slide with a list of problems";
// Schema definition
export const Schema = z.object({
sectionTitle: z.string().min(3).max(25).default("CHALLENGES").meta({
description:
"Main section heading - can be 'Problems', 'Challenges', 'Issues', or similar",
}),
sectionTitle: z.string()
.min(3)
.max(25)
.default("CHALLENGES")
.meta({
description: "Main section heading - can be 'Problems', 'Challenges', 'Issues', or similar",
}),
sectionSubtitle: z
.string()
.min(10)
.max(50)
.default("KEY CHALLENGES TO ADDRESS")
.meta({
description: "Supporting subtitle that frames the problem discussion",
}),
sectionSubtitle: z.string()
.min(10)
.max(50)
.default("KEY CHALLENGES TO ADDRESS")
.meta({
description: "Supporting subtitle that frames the problem discussion",
}),
challengeItems: z.array(z.object({
challengeItems: z
.array(
z.object({
itemNumber: z.string().min(1).max(3),
challengeTitle: z.string().min(5).max(40),
challengeDescription: z.string().min(20).max(200)
})).min(2).max(3).default([
{
itemNumber: "01",
challengeTitle: "Inefficient Processes",
challengeDescription: "Current workflows and systems lack optimization, leading to wasted resources and reduced productivity across all operational areas."
},
{
itemNumber: "02",
challengeTitle: "Limited Scalability",
challengeDescription: "Existing infrastructure and methodologies cannot accommodate growth, creating bottlenecks that hinder expansion and progress."
},
{
itemNumber: "03",
challengeTitle: "Resource Constraints",
challengeDescription: "Limited availability of key resources including time, budget, and skilled personnel creates barriers to achieving desired outcomes."
}
]).meta({
description: "List of key challenges or problems with numbered identification and detailed descriptions",
challengeDescription: z.string().min(20).max(200),
})
)
.min(2)
.max(3)
.default([
{
itemNumber: "01",
challengeTitle: "Inefficient Processes",
challengeDescription:
"Current workflows and systems lack optimization, leading to wasted resources and reduced productivity across all operational areas.",
},
{
itemNumber: "02",
challengeTitle: "Limited Scalability",
challengeDescription:
"Existing infrastructure and methodologies cannot accommodate growth, creating bottlenecks that hinder expansion and progress.",
},
{
itemNumber: "03",
challengeTitle: "Resource Constraints",
challengeDescription:
"Limited availability of key resources including time, budget, and skilled personnel creates barriers to achieving desired outcomes.",
},
])
.meta({
description:
"List of key challenges or problems with numbered identification and detailed descriptions",
}),
supportingVisual: ImageSchema.default({
__image_url__: "https://images.unsplash.com/photo-1542744173-8e7e53415bb0?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
__image_prompt__: "Professional workspace showing analysis and problem-solving activities"
}).meta({
description: "Visual that supports the problem discussion - could show analysis, challenges, or work environment",
}),
supportingVisual: ImageSchema.default({
__image_url__:
"https://images.unsplash.com/photo-1542744173-8e7e53415bb0?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
__image_prompt__:
"Professional workspace showing analysis and problem-solving activities",
}).meta({
description:
"Visual that supports the problem discussion - could show analysis, challenges, or work environment",
}),
showVisualAccents: z.boolean()
.default(true)
.meta({
description: "Whether to display decorative visual accent elements",
}),
showVisualAccents: z.boolean().default(true).meta({
description: "Whether to display decorative visual accent elements",
}),
showColorBlocks: z.boolean()
.default(true)
.meta({
description: "Whether to show colored accent blocks for visual hierarchy",
}),
})
showColorBlocks: z.boolean().default(true).meta({
description: "Whether to show colored accent blocks for visual hierarchy",
}),
});
// Type inference
type SchemaType = z.infer<typeof Schema>;
// Component definition
const ProblemsSlide = ({ data }: { data: Partial<SchemaType> }) => {
const {
sectionTitle,
sectionSubtitle,
challengeItems,
supportingVisual,
showVisualAccents,
showColorBlocks,
} = data;
const { sectionTitle, sectionSubtitle, challengeItems, supportingVisual, showVisualAccents, showColorBlocks } = data;
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Main Content Area */}
<div className="h-full flex">
{/* Left Side - Content */}
<div className="w-3/5 relative bg-white px-16 py-12 flex flex-col justify-start">
{/* Title Section */}
<div className="mb-12">
{sectionTitle && (
<h1 className="text-3xl lg:text-4xl font-black text-teal-700 leading-tight mb-4">
{sectionTitle}
</h1>
)}
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Main Content Area */}
<div className="h-full flex">
{/* Left Side - Content */}
<div className="w-3/5 relative bg-white px-16 py-12 flex flex-col justify-start">
{/* Title Section */}
<div className="mb-12">
{sectionTitle && (
<h1 className="text-3xl lg:text-4xl font-black text-teal-700 leading-tight mb-4">
{sectionTitle}
</h1>
)}
{sectionSubtitle && (
<p className="text-base font-semibold text-gray-800 tracking-wide">
{sectionSubtitle}
</p>
)}
</div>
{sectionSubtitle && (
<p className="text-base font-semibold text-gray-800 tracking-wide">
{sectionSubtitle}
</p>
)}
</div>
{/* Challenge Items List */}
{challengeItems && challengeItems.length > 0 && (
<div className="space-y-8">
{challengeItems.map((item, index) => (
<div key={index} className="flex items-start space-x-6">
{/* Number Circle */}
<div className="w-16 h-16 bg-yellow-200 rounded-full flex items-center justify-center flex-shrink-0">
<span className="text-teal-700 font-bold text-xl">
{item.itemNumber}
</span>
</div>
{/* Challenge Items List */}
{challengeItems && challengeItems.length > 0 && (
<div className="space-y-8">
{challengeItems.map((item, index) => (
<div key={index} className="flex items-start space-x-6">
{/* Number Circle */}
<div className="w-16 h-16 bg-yellow-200 rounded-full flex items-center justify-center flex-shrink-0">
<span className="text-teal-700 font-bold text-xl">
{item.itemNumber}
</span>
</div>
{/* Content */}
<div className="flex-1">
<h3 className="text-xl font-bold text-gray-900 mb-3">
{item.challengeTitle}
</h3>
<p className="text-base leading-relaxed text-gray-700">
{item.challengeDescription}
</p>
</div>
</div>
))}
</div>
)}
</div>
{/* Right Side - Image and Decorative Elements */}
<div className="w-2/5 relative">
{/* Decorative Circle */}
{showVisualAccents && (
<div className="absolute top-12 left-8 w-8 h-8 border-4 border-teal-600 rounded-full z-20"></div>
)}
{/* Supporting Visual */}
{supportingVisual?.__image_url__ && (
<div className="absolute top-8 right-8 bottom-20 left-4 shadow-lg">
<img
src={supportingVisual.__image_url__}
alt={supportingVisual.__image_prompt__}
className="w-full h-full object-cover rounded-lg"
/>
</div>
)}
{/* Teal Accent Block - Right Edge */}
{showColorBlocks && (
<div className="absolute top-0 right-0 bottom-0 w-16 bg-teal-600 z-10"></div>
)}
{/* Content */}
<div className="flex-1">
<h3 className="text-xl font-bold text-gray-900 mb-3">
{item.challengeTitle}
</h3>
<p className="text-base leading-relaxed text-gray-700">
{item.challengeDescription}
</p>
</div>
</div>
))}
</div>
{/* Bottom Teal Stripe */}
<div className="absolute bottom-0 left-0 right-0 h-4 bg-teal-600 z-5"></div>
)}
</div>
);
{/* Right Side - Image and Decorative Elements */}
<div className="w-2/5 relative">
{/* Decorative Circle */}
{showVisualAccents && (
<div className="absolute top-12 left-8 w-8 h-8 border-4 border-teal-600 rounded-full z-20"></div>
)}
{/* Supporting Visual */}
{supportingVisual?.__image_url__ && (
<div className="absolute top-8 right-8 bottom-20 left-4 shadow-lg">
<img
src={supportingVisual.__image_url__}
alt={supportingVisual.__image_prompt__}
className="w-full h-full object-cover rounded-lg"
/>
</div>
)}
{/* Teal Accent Block - Right Edge */}
{showColorBlocks && (
<div className="absolute top-0 right-0 bottom-0 w-16 bg-teal-600 z-10"></div>
)}
</div>
</div>
{/* Bottom Teal Stripe */}
<div className="absolute bottom-0 left-0 right-0 h-4 bg-teal-600 z-5"></div>
</div>
);
};
export default ProblemsSlide;
export default ProblemsSlide;

View file

@ -2,189 +2,207 @@ import * as z from "zod";
import { ImageSchema, IconSchema } from "../defaultSchemes";
export const layoutName = "Solutions Slide";
export const layoutId = "solutions-slide";
export const layoutDescription = "A slide with a list of solutions";
// Schema definition
export const Schema = z.object({
sectionTitle: z.string().min(3).max(25).default("OUR SOLUTIONS").meta({
description:
"Main section heading - can be 'Solutions', 'Our Approach', 'How We Help', or similar",
}),
sectionTitle: z.string()
.min(3)
.max(25)
.default("OUR SOLUTIONS")
.meta({
description: "Main section heading - can be 'Solutions', 'Our Approach', 'How We Help', or similar",
}),
sectionSubtitle: z
.string()
.min(10)
.max(50)
.default("COMPREHENSIVE SOLUTIONS FOR YOUR NEEDS")
.meta({
description: "Supporting subtitle that frames the solution discussion",
}),
sectionSubtitle: z.string()
.min(10)
.max(50)
.default("COMPREHENSIVE SOLUTIONS FOR YOUR NEEDS")
.meta({
description: "Supporting subtitle that frames the solution discussion",
}),
solutionItems: z.array(z.object({
solutionItems: z
.array(
z.object({
itemNumber: z.string().min(1).max(3),
solutionTitle: z.string().min(5).max(40),
solutionDescription: z.string().min(20).max(200)
})).min(2).max(3).default([
{
itemNumber: "01",
solutionTitle: "Process Optimization",
solutionDescription: "Streamline workflows and implement efficient systems that reduce waste, improve productivity, and maximize resource utilization across all operational areas."
},
{
itemNumber: "02",
solutionTitle: "Scalable Infrastructure",
solutionDescription: "Build robust, flexible systems and methodologies that can grow with your organization, eliminating bottlenecks and supporting expansion efforts."
},
{
itemNumber: "03",
solutionTitle: "Resource Management",
solutionDescription: "Strategic allocation and optimization of available resources including time, budget, and personnel to achieve maximum impact and desired outcomes."
}
]).meta({
description: "List of key solutions or approaches with numbered identification and detailed descriptions",
solutionDescription: z.string().min(20).max(200),
})
)
.min(2)
.max(3)
.default([
{
itemNumber: "01",
solutionTitle: "Process Optimization",
solutionDescription:
"Streamline workflows and implement efficient systems that reduce waste, improve productivity, and maximize resource utilization across all operational areas.",
},
{
itemNumber: "02",
solutionTitle: "Scalable Infrastructure",
solutionDescription:
"Build robust, flexible systems and methodologies that can grow with your organization, eliminating bottlenecks and supporting expansion efforts.",
},
{
itemNumber: "03",
solutionTitle: "Resource Management",
solutionDescription:
"Strategic allocation and optimization of available resources including time, budget, and personnel to achieve maximum impact and desired outcomes.",
},
])
.meta({
description:
"List of key solutions or approaches with numbered identification and detailed descriptions",
}),
primaryVisual: ImageSchema.default({
__image_url__: "https://images.unsplash.com/photo-1560472354-b33ff0c44a43?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
__image_prompt__: "Modern workspace with team collaboration and strategic planning"
}).meta({
description: "Primary visual representing teamwork, strategy, or solution implementation",
}),
primaryVisual: ImageSchema.default({
__image_url__:
"https://images.unsplash.com/photo-1560472354-b33ff0c44a43?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
__image_prompt__:
"Modern workspace with team collaboration and strategic planning",
}).meta({
description:
"Primary visual representing teamwork, strategy, or solution implementation",
}),
brandingVisual: ImageSchema.default({
__image_url__: "https://via.placeholder.com/150x80/22C55E/FFFFFF?text=LOGO",
__image_prompt__: "Organization logo or brand mark"
}).meta({
description: "Logo or branding element to maintain visual identity",
}),
brandingVisual: ImageSchema.default({
__image_url__: "https://via.placeholder.com/150x80/22C55E/FFFFFF?text=LOGO",
__image_prompt__: "Organization logo or brand mark",
}).meta({
description: "Logo or branding element to maintain visual identity",
}),
showYellowUnderline: z.boolean()
.default(true)
.meta({
description: "Whether to display the decorative yellow underline accent",
}),
showYellowUnderline: z.boolean().default(true).meta({
description: "Whether to display the decorative yellow underline accent",
}),
showVisualAccents: z.boolean()
.default(true)
.meta({
description: "Whether to display decorative visual accent elements",
}),
showVisualAccents: z.boolean().default(true).meta({
description: "Whether to display decorative visual accent elements",
}),
showColorBlocks: z.boolean()
.default(true)
.meta({
description: "Whether to show colored background blocks for visual hierarchy",
}),
})
showColorBlocks: z.boolean().default(true).meta({
description:
"Whether to show colored background blocks for visual hierarchy",
}),
});
// Type inference
type SchemaType = z.infer<typeof Schema>;
// Component definition
const SolutionsSlide = ({ data }: { data: Partial<SchemaType> }) => {
const {
sectionTitle,
sectionSubtitle,
solutionItems,
primaryVisual,
brandingVisual,
showYellowUnderline,
showVisualAccents,
showColorBlocks,
} = data;
const { sectionTitle, sectionSubtitle, solutionItems, primaryVisual, brandingVisual, showYellowUnderline, showVisualAccents, showColorBlocks } = data;
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Main Content Area */}
<div className="h-full flex">
{/* Left Side - Images and Branding */}
<div className="w-2/5 relative bg-gray-100 flex flex-col">
{/* Top Image Area */}
{primaryVisual?.__image_url__ && (
<div className="flex-1 relative">
<img
src={primaryVisual.__image_url__}
alt={primaryVisual.__image_prompt__}
className="w-full h-full object-cover"
/>
</div>
)}
{/* Bottom Branding Area */}
<div className="h-24 bg-white flex items-center justify-center px-8">
{brandingVisual?.__image_url__ && (
<img
src={brandingVisual.__image_url__}
alt={brandingVisual.__image_prompt__}
className="h-12 object-contain"
/>
)}
</div>
</div>
{/* Right Side - Content */}
<div className="w-3/5 relative bg-teal-600 px-16 py-12 flex flex-col justify-start">
{/* Title Section */}
<div className="mb-8">
{sectionTitle && (
<h1 className="text-3xl lg:text-4xl font-black text-white leading-tight mb-4">
{sectionTitle}
</h1>
)}
{sectionSubtitle && (
<p className="text-base font-semibold text-gray-100 tracking-wide mb-4">
{sectionSubtitle}
</p>
)}
{/* Yellow Decorative Underline */}
{showYellowUnderline && (
<div className="w-24 h-1 bg-yellow-300"></div>
)}
</div>
{/* Solution Items List */}
{solutionItems && solutionItems.length > 0 && (
<div className="space-y-8">
{solutionItems.map((item, index) => (
<div key={index} className="flex items-start space-x-6">
{/* Number Circle */}
<div className="w-16 h-16 bg-yellow-300 rounded-full flex items-center justify-center flex-shrink-0">
<span className="text-teal-700 font-bold text-xl">
{item.itemNumber}
</span>
</div>
{/* Content */}
<div className="flex-1">
<h3 className="text-xl font-bold text-white mb-3">
{item.solutionTitle}
</h3>
<p className="text-base leading-relaxed text-gray-100">
{item.solutionDescription}
</p>
</div>
</div>
))}
</div>
)}
{/* Visual Accents */}
{showVisualAccents && (
<>
{/* Decorative circles */}
<div className="absolute top-8 right-8 w-4 h-4 bg-yellow-300 rounded-full"></div>
<div className="absolute bottom-16 right-12 w-3 h-3 bg-yellow-200 rounded-full"></div>
</>
)}
</div>
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Main Content Area */}
<div className="h-full flex">
{/* Left Side - Images and Branding */}
<div className="w-2/5 relative bg-gray-100 flex flex-col">
{/* Top Image Area */}
{primaryVisual?.__image_url__ && (
<div className="flex-1 relative">
<img
src={primaryVisual.__image_url__}
alt={primaryVisual.__image_prompt__}
className="w-full h-full object-cover"
/>
</div>
)}
{/* Color blocks for visual hierarchy */}
{showColorBlocks && (
<>
{/* Bottom accent strip */}
<div className="absolute bottom-0 left-0 right-0 h-3 bg-yellow-300"></div>
{/* Side accent */}
<div className="absolute top-0 left-0 bottom-0 w-1 bg-yellow-400"></div>
</>
{/* Bottom Branding Area */}
<div className="h-24 bg-white flex items-center justify-center px-8">
{brandingVisual?.__image_url__ && (
<img
src={brandingVisual.__image_url__}
alt={brandingVisual.__image_prompt__}
className="h-12 object-contain"
/>
)}
</div>
</div>
);
{/* Right Side - Content */}
<div className="w-3/5 relative bg-teal-600 px-16 py-12 flex flex-col justify-start">
{/* Title Section */}
<div className="mb-8">
{sectionTitle && (
<h1 className="text-3xl lg:text-4xl font-black text-white leading-tight mb-4">
{sectionTitle}
</h1>
)}
{sectionSubtitle && (
<p className="text-base font-semibold text-gray-100 tracking-wide mb-4">
{sectionSubtitle}
</p>
)}
{/* Yellow Decorative Underline */}
{showYellowUnderline && (
<div className="w-24 h-1 bg-yellow-300"></div>
)}
</div>
{/* Solution Items List */}
{solutionItems && solutionItems.length > 0 && (
<div className="space-y-8">
{solutionItems.map((item, index) => (
<div key={index} className="flex items-start space-x-6">
{/* Number Circle */}
<div className="w-16 h-16 bg-yellow-300 rounded-full flex items-center justify-center flex-shrink-0">
<span className="text-teal-700 font-bold text-xl">
{item.itemNumber}
</span>
</div>
{/* Content */}
<div className="flex-1">
<h3 className="text-xl font-bold text-white mb-3">
{item.solutionTitle}
</h3>
<p className="text-base leading-relaxed text-gray-100">
{item.solutionDescription}
</p>
</div>
</div>
))}
</div>
)}
{/* Visual Accents */}
{showVisualAccents && (
<>
{/* Decorative circles */}
<div className="absolute top-8 right-8 w-4 h-4 bg-yellow-300 rounded-full"></div>
<div className="absolute bottom-16 right-12 w-3 h-3 bg-yellow-200 rounded-full"></div>
</>
)}
</div>
</div>
{/* Color blocks for visual hierarchy */}
{showColorBlocks && (
<>
{/* Bottom accent strip */}
<div className="absolute bottom-0 left-0 right-0 h-3 bg-yellow-300"></div>
{/* Side accent */}
<div className="absolute top-0 left-0 bottom-0 w-1 bg-yellow-400"></div>
</>
)}
</div>
);
};
export default SolutionsSlide;
export default SolutionsSlide;

View file

@ -1,236 +1,268 @@
import * as z from "zod";
import { ImageSchema, IconSchema } from "../defaultSchemes";
export const layoutName = "Statistic Circular Slide";
export const layoutId = "statistic-circular-slide";
export const layoutDescription = "A slide with a circular statistic";
// Schema definition
export const Schema = z.object({
sectionTitle: z.string().min(3).max(20).default("CLIENT SATISFACTION").meta({
description:
"Main section heading - adapt to presentation topic (e.g., 'Climate Progress', 'Treatment Success', 'Learning Achievement', 'Project Completion')",
}),
sectionTitle: z.string()
.min(3)
.max(20)
.default("CLIENT SATISFACTION")
.meta({
description: "Main section heading - adapt to presentation topic (e.g., 'Climate Progress', 'Treatment Success', 'Learning Achievement', 'Project Completion')",
}),
sectionSubtitle: z.string()
.min(10)
.max(35)
.default("MEASURING OUR IMPACT AND SUCCESS")
.meta({
description: "Supporting subtitle that provides context - adapt to topic (e.g., 'Tracking Climate Action Progress', 'Monitoring Patient Recovery Rates', 'Assessing Educational Outcomes')",
}),
description: z.string()
.min(2)
.max(230)
.default("At the heart of our success lies the unwavering satisfaction of our clients. We take pride in fostering lasting partnerships, consistently exceeding expectations, and delivering results that not only meet but surpass the unique objectives of each client we serve.")
.meta({
description: "Name of the organization or entity being measured",
}),
brandLogo: ImageSchema.default({
__image_url__: "https://via.placeholder.com/40x40/22C55E/FFFFFF?text=L",
__image_prompt__: "Professional organization logo - clean and modern design"
}).meta({
description: "Logo or brand mark representing the organization",
sectionSubtitle: z
.string()
.min(10)
.max(35)
.default("MEASURING OUR IMPACT AND SUCCESS")
.meta({
description:
"Supporting subtitle that provides context - adapt to topic (e.g., 'Tracking Climate Action Progress', 'Monitoring Patient Recovery Rates', 'Assessing Educational Outcomes')",
}),
satisfactionRate: z.object({
value: z.number().min(0).max(100),
label: z.string().min(5).max(30),
percentage: z.string().min(2).max(5)
}).default({
value: 90,
label: "CLIENT'S REPEAT ORDER",
percentage: "90%"
}).meta({
description: "CRITICAL: Provide topic-specific circular progress metric. For global warming: {value: 33, label: 'CO2 REDUCTION ACHIEVED', percentage: '33%'} or {value: 78, label: 'RENEWABLE ENERGY ADOPTION', percentage: '78%'}. For healthcare: {value: 95, label: 'PATIENT RECOVERY RATE', percentage: '95%'} or {value: 87, label: 'TREATMENT SUCCESS RATE', percentage: '87%'}. For education: {value: 92, label: 'GRADUATION SUCCESS RATE', percentage: '92%'}. Use realistic percentages and meaningful labels.",
description: z
.string()
.min(2)
.max(230)
.default(
"At the heart of our success lies the unwavering satisfaction of our clients. We take pride in fostering lasting partnerships, consistently exceeding expectations, and delivering results that not only meet but surpass the unique objectives of each client we serve."
)
.meta({
description: "Name of the organization or entity being measured",
}),
statisticBlocks: z.array(z.object({
brandLogo: ImageSchema.default({
__image_url__: "https://via.placeholder.com/40x40/22C55E/FFFFFF?text=L",
__image_prompt__:
"Professional organization logo - clean and modern design",
}).meta({
description: "Logo or brand mark representing the organization",
}),
satisfactionRate: z
.object({
value: z.number().min(0).max(100),
label: z.string().min(5).max(30),
percentage: z.string().min(2).max(5),
})
.default({
value: 90,
label: "CLIENT'S REPEAT ORDER",
percentage: "90%",
})
.meta({
description:
"CRITICAL: Provide topic-specific circular progress metric. For global warming: {value: 33, label: 'CO2 REDUCTION ACHIEVED', percentage: '33%'} or {value: 78, label: 'RENEWABLE ENERGY ADOPTION', percentage: '78%'}. For healthcare: {value: 95, label: 'PATIENT RECOVERY RATE', percentage: '95%'} or {value: 87, label: 'TREATMENT SUCCESS RATE', percentage: '87%'}. For education: {value: 92, label: 'GRADUATION SUCCESS RATE', percentage: '92%'}. Use realistic percentages and meaningful labels.",
}),
statisticBlocks: z
.array(
z.object({
percentage: z.string().min(2).max(5),
description: z.string().min(20).max(150),
backgroundColor: z.enum(["teal", "beige"])
})).min(2).max(2).default([
{
percentage: "90%",
description: "Our client loyalty speaks volumes as evidenced by a robust repeat order rate",
backgroundColor: "teal"
},
{
percentage: "99%",
description: "Our paramount focus on client satisfaction is the bedrock of our agency's success.",
backgroundColor: "beige"
}
]).meta({
description: "ESSENTIAL: Provide two topic-relevant supporting statistics. For global warming: [{percentage: '1.1°C', description: 'Global temperature increase since pre-industrial times represents urgent need for climate action', backgroundColor: 'teal'}, {percentage: '410ppm', description: 'Current atmospheric CO2 levels are the highest in human history requiring immediate intervention', backgroundColor: 'beige'}]. For healthcare: [{percentage: '85%', description: 'Early detection rates have improved significantly with advanced screening technologies', backgroundColor: 'teal'}, {percentage: '72h', description: 'Average patient response time demonstrates our commitment to rapid care delivery', backgroundColor: 'beige'}]. Always provide factual, impactful statistics.",
backgroundColor: z.enum(["teal", "beige"]),
})
)
.min(2)
.max(2)
.default([
{
percentage: "90%",
description:
"Our client loyalty speaks volumes as evidenced by a robust repeat order rate",
backgroundColor: "teal",
},
{
percentage: "99%",
description:
"Our paramount focus on client satisfaction is the bedrock of our agency's success.",
backgroundColor: "beige",
},
])
.meta({
description:
"ESSENTIAL: Provide two topic-relevant supporting statistics. For global warming: [{percentage: '1.1°C', description: 'Global temperature increase since pre-industrial times represents urgent need for climate action', backgroundColor: 'teal'}, {percentage: '410ppm', description: 'Current atmospheric CO2 levels are the highest in human history requiring immediate intervention', backgroundColor: 'beige'}]. For healthcare: [{percentage: '85%', description: 'Early detection rates have improved significantly with advanced screening technologies', backgroundColor: 'teal'}, {percentage: '72h', description: 'Average patient response time demonstrates our commitment to rapid care delivery', backgroundColor: 'beige'}]. Always provide factual, impactful statistics.",
}),
companyLogo: ImageSchema.default({
__image_url__: "https://via.placeholder.com/40x40/FFFFFF/1D9A8A?text=C",
__image_prompt__: "Clean modern company logo icon in white"
}).meta({
description: "Company logo icon",
}),
companyLogo: ImageSchema.default({
__image_url__: "https://via.placeholder.com/40x40/FFFFFF/1D9A8A?text=C",
__image_prompt__: "Clean modern company logo icon in white",
}).meta({
description: "Company logo icon",
}),
companyName: z.string()
.min(2)
.max(25)
.default("Deskpro")
.meta({
description: "Company name for branding",
}),
})
companyName: z.string().min(2).max(25).default("Deskpro").meta({
description: "Company name for branding",
}),
});
// Type inference
type SchemaType = z.infer<typeof Schema>;
// Component definition
const StatisticCircularSlide = ({ data }: { data: Partial<SchemaType> }) => {
const {
sectionTitle,
sectionSubtitle,
description,
brandLogo,
satisfactionRate,
statisticBlocks,
companyLogo,
companyName,
} = data;
const { sectionTitle, sectionSubtitle, description, brandLogo, satisfactionRate, statisticBlocks, companyLogo, companyName } = data;
const getBackgroundClass = (bg: string) => {
switch (bg) {
case "teal":
return "bg-teal-600 text-white";
case "beige":
return "bg-yellow-200 text-gray-900";
default:
return "bg-gray-200 text-gray-900";
}
};
const getBackgroundClass = (bg: string) => {
switch (bg) {
case "teal": return "bg-teal-600 text-white";
case "beige": return "bg-yellow-200 text-gray-900";
default: return "bg-gray-200 text-gray-900";
}
};
// Calculate stroke dash array for circular progress
const radius = 150;
const circumference = 2 * Math.PI * radius;
const strokeDasharray = circumference;
const strokeDashoffset =
circumference - (circumference * (satisfactionRate?.value || 90)) / 100;
// Calculate stroke dash array for circular progress
const radius = 150;
const circumference = 2 * Math.PI * radius;
const strokeDasharray = circumference;
const strokeDashoffset = circumference - (circumference * (satisfactionRate?.value || 90) / 100);
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Main Content Area */}
<div className="h-full flex flex-col">
{/* Header Section */}
<div className="flex h-32">
{/* Left - Title */}
<div className="w-1/2 px-16 py-8 bg-gray-100 flex flex-col justify-center">
{sectionTitle && (
<h1 className="text-4xl lg:text-5xl font-black text-teal-700 leading-tight mb-2">
{sectionTitle}
</h1>
)}
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Main Content Area */}
<div className="h-full flex flex-col">
{/* Header Section */}
<div className="flex h-32">
{/* Left - Title */}
<div className="w-1/2 px-16 py-8 bg-gray-100 flex flex-col justify-center">
{sectionTitle && (
<h1 className="text-4xl lg:text-5xl font-black text-teal-700 leading-tight mb-2">
{sectionTitle}
</h1>
)}
{sectionSubtitle && (
<p className="text-base font-semibold text-gray-800 tracking-wide">
{sectionSubtitle}
</p>
)}
</div>
{sectionSubtitle && (
<p className="text-base font-semibold text-gray-800 tracking-wide">
{sectionSubtitle}
</p>
)}
</div>
{/* Right - Company Branding */}
<div className="w-1/2 bg-teal-600 px-16 py-8 flex items-center justify-end">
<div className="flex items-center space-x-3">
{companyLogo?.__image_url__ && (
<div className="w-10 h-10">
<img
src={companyLogo.__image_url__}
alt={companyLogo.__image_prompt__}
className="w-full h-full object-contain"
/>
</div>
)}
{companyName && (
<span className="text-xl font-bold text-white">
{companyName}
</span>
)}
</div>
</div>
</div>
{/* Content Section */}
<div className="flex-1 flex">
{/* Left Side - Circular Chart */}
<div className="w-1/2 px-8 py-8 bg-gray-100 flex items-center justify-center">
<div className="relative">
{/* Circular Progress SVG */}
<svg width="340" height="340" className="transform -rotate-90">
{/* Background circle */}
<circle
cx="170"
cy="170"
r={radius}
stroke="#E5E7EB"
strokeWidth="25"
fill="transparent"
/>
{/* Progress circle */}
<circle
cx="170"
cy="170"
r={radius}
stroke="#1D9A8A"
strokeWidth="25"
fill="transparent"
strokeDasharray={strokeDasharray}
strokeDashoffset={strokeDashoffset}
strokeLinecap="round"
className="transition-all duration-1000 ease-out"
/>
</svg>
{/* Center Content */}
<div className="absolute inset-0 flex flex-col items-center justify-center">
{satisfactionRate?.label && (
<p className="text-sm font-semibold text-gray-800 mb-4 text-center max-w-32 leading-tight">
{satisfactionRate.label}
</p>
)}
{satisfactionRate?.percentage && (
<span className="text-7xl font-black text-teal-700">
{satisfactionRate.percentage}
</span>
)}
</div>
</div>
</div>
{/* Right Side - Content and Statistics */}
<div className="w-1/2 bg-white flex flex-col">
{/* Description */}
<div className="px-16 py-8 flex-1 flex items-start justify-center flex-col">
{description && (
<p className="text-base leading-relaxed text-gray-700">
{description}
</p>
)}
</div>
{/* Statistics Blocks */}
{statisticBlocks && statisticBlocks.length > 0 && (
<div className="px-16 pb-8 space-y-4">
{statisticBlocks.map((block, index) => (
<div key={index} className="flex items-stretch">
{/* Percentage Block */}
<div className={`w-24 h-20 ${getBackgroundClass(block.backgroundColor)} flex items-center justify-center flex-shrink-0`}>
<span className="text-2xl font-black">
{block.percentage}
</span>
</div>
{/* Description Block */}
<div className="flex-1 border-2 border-gray-900 p-4 h-20 flex items-center">
<p className="text-sm leading-relaxed text-gray-900">
{block.description}
</p>
</div>
</div>
))}
</div>
)}
</div>
{/* Right - Company Branding */}
<div className="w-1/2 bg-teal-600 px-16 py-8 flex items-center justify-end">
<div className="flex items-center space-x-3">
{companyLogo?.__image_url__ && (
<div className="w-10 h-10">
<img
src={companyLogo.__image_url__}
alt={companyLogo.__image_prompt__}
className="w-full h-full object-contain"
/>
</div>
)}
{companyName && (
<span className="text-xl font-bold text-white">
{companyName}
</span>
)}
</div>
</div>
</div>
);
{/* Content Section */}
<div className="flex-1 flex">
{/* Left Side - Circular Chart */}
<div className="w-1/2 px-8 py-8 bg-gray-100 flex items-center justify-center">
<div className="relative">
{/* Circular Progress SVG */}
<svg width="340" height="340" className="transform -rotate-90">
{/* Background circle */}
<circle
cx="170"
cy="170"
r={radius}
stroke="#E5E7EB"
strokeWidth="25"
fill="transparent"
/>
{/* Progress circle */}
<circle
cx="170"
cy="170"
r={radius}
stroke="#1D9A8A"
strokeWidth="25"
fill="transparent"
strokeDasharray={strokeDasharray}
strokeDashoffset={strokeDashoffset}
strokeLinecap="round"
className="transition-all duration-1000 ease-out"
/>
</svg>
{/* Center Content */}
<div className="absolute inset-0 flex flex-col items-center justify-center">
{satisfactionRate?.label && (
<p className="text-sm font-semibold text-gray-800 mb-4 text-center max-w-32 leading-tight">
{satisfactionRate.label}
</p>
)}
{satisfactionRate?.percentage && (
<span className="text-7xl font-black text-teal-700">
{satisfactionRate.percentage}
</span>
)}
</div>
</div>
</div>
{/* Right Side - Content and Statistics */}
<div className="w-1/2 bg-white flex flex-col">
{/* Description */}
<div className="px-16 py-8 flex-1 flex items-start justify-center flex-col">
{description && (
<p className="text-base leading-relaxed text-gray-700">
{description}
</p>
)}
</div>
{/* Statistics Blocks */}
{statisticBlocks && statisticBlocks.length > 0 && (
<div className="px-16 pb-8 space-y-4">
{statisticBlocks.map((block, index) => (
<div key={index} className="flex items-stretch">
{/* Percentage Block */}
<div
className={`w-24 h-20 ${getBackgroundClass(
block.backgroundColor
)} flex items-center justify-center flex-shrink-0`}
>
<span className="text-2xl font-black">
{block.percentage}
</span>
</div>
{/* Description Block */}
<div className="flex-1 border-2 border-gray-900 p-4 h-20 flex items-center">
<p className="text-sm leading-relaxed text-gray-900">
{block.description}
</p>
</div>
</div>
))}
</div>
)}
</div>
</div>
</div>
</div>
);
};
export default StatisticCircularSlide;
export default StatisticCircularSlide;

View file

@ -1,112 +1,149 @@
import React from "react";
import * as z from "zod";
import { ImageSchema, IconSchema } from "../defaultSchemes";
import { ChartContainer, ChartTooltip, ChartTooltipContent } from '@/components/ui/chart';
import { BarChart, Bar, AreaChart, Area, XAxis, YAxis, CartesianGrid } from "recharts";
import {
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from "@/components/ui/chart";
import {
BarChart,
Bar,
AreaChart,
Area,
XAxis,
YAxis,
CartesianGrid,
} from "recharts";
export const layoutName = "Statistic Dual Chart Slide";
export const layoutId = "statistic-dual-chart-slide";
export const layoutDescription = "A slide with a statistic and a chart";
// Schema definition
export const Schema = z.object({
sectionTitle: z.string().min(3).max(30).default("PERFORMANCE METRICS").meta({
description:
"Main section heading - adapt to presentation topic (e.g., 'Climate Analysis', 'Health Outcomes', 'Research Data', 'Impact Assessment')",
}),
sectionTitle: z.string()
.min(3)
.max(30)
.default("PERFORMANCE METRICS")
.meta({
description: "Main section heading - adapt to presentation topic (e.g., 'Climate Analysis', 'Health Outcomes', 'Research Data', 'Impact Assessment')",
}),
organizationName: z.string()
.min(2)
.max(30)
.default("Your Organization")
.meta({
description: "Name of the organization or entity presenting the data",
}),
brandLogo: ImageSchema.default({
__image_url__: "https://via.placeholder.com/40x40/22C55E/FFFFFF?text=L",
__image_prompt__: "Professional organization logo - clean and modern design"
}).meta({
description: "Logo or brand mark representing the organization",
organizationName: z
.string()
.min(2)
.max(30)
.default("Your Organization")
.meta({
description: "Name of the organization or entity presenting the data",
}),
barChartData: z.array(z.object({
brandLogo: ImageSchema.default({
__image_url__: "https://via.placeholder.com/40x40/22C55E/FFFFFF?text=L",
__image_prompt__:
"Professional organization logo - clean and modern design",
}).meta({
description: "Logo or brand mark representing the organization",
}),
barChartData: z
.array(
z.object({
name: z.string(),
series1: z.number(),
series2: z.number(),
series3: z.number()
})).min(5).max(5).default([
{ name: "Item 1", series1: 5, series2: 5, series3: 8 },
{ name: "Item 2", series1: 8, series2: 8, series3: 15 },
{ name: "Item 3", series1: 15, series2: 10, series3: 18 },
{ name: "Item 4", series1: 18, series2: 14, series3: 22 },
{ name: "Item 5", series1: 22, series2: 20, series3: 8 }
]).meta({
description: "CRITICAL: Provide topic-specific data for the left bar chart. For global warming: 5 years of data (2020-2024) with CO2 emissions by sector (Transport, Industry, Energy) with actual values. For healthcare: Patient outcomes across 5 categories (Prevention, Treatment, Recovery) with real percentages. For education: Student performance across 5 metrics (Reading, Math, Science) with grade levels. Use realistic data patterns and values.",
series3: z.number(),
})
)
.min(5)
.max(5)
.default([
{ name: "Item 1", series1: 5, series2: 5, series3: 8 },
{ name: "Item 2", series1: 8, series2: 8, series3: 15 },
{ name: "Item 3", series1: 15, series2: 10, series3: 18 },
{ name: "Item 4", series1: 18, series2: 14, series3: 22 },
{ name: "Item 5", series1: 22, series2: 20, series3: 8 },
])
.meta({
description:
"CRITICAL: Provide topic-specific data for the left bar chart. For global warming: 5 years of data (2020-2024) with CO2 emissions by sector (Transport, Industry, Energy) with actual values. For healthcare: Patient outcomes across 5 categories (Prevention, Treatment, Recovery) with real percentages. For education: Student performance across 5 metrics (Reading, Math, Science) with grade levels. Use realistic data patterns and values.",
}),
areaChartData: z.array(z.object({
areaChartData: z
.array(
z.object({
name: z.string(),
series1: z.number(),
series2: z.number(),
series3: z.number()
})).min(5).max(5).default([
{ name: "Item 1", series1: 20, series2: 30, series3: 15 },
{ name: "Item 2", series1: 40, series2: 45, series3: 35 },
{ name: "Item 3", series1: 45, series2: 50, series3: 80 },
{ name: "Item 4", series1: 50, series2: 45, series3: 85 },
{ name: "Item 5", series1: 80, series2: 75, series3: 120 }
]).meta({
description: "CRITICAL: Provide topic-specific data for the right area chart. For global warming: Cumulative data over 5 time periods showing renewable energy adoption, carbon reduction efforts, and policy implementations with realistic growth trends. For healthcare: Cumulative patient care metrics showing improvement over time. For education: Progressive learning outcomes showing student advancement. Ensure data shows meaningful trends relevant to the topic.",
series3: z.number(),
})
)
.min(5)
.max(5)
.default([
{ name: "Item 1", series1: 20, series2: 30, series3: 15 },
{ name: "Item 2", series1: 40, series2: 45, series3: 35 },
{ name: "Item 3", series1: 45, series2: 50, series3: 80 },
{ name: "Item 4", series1: 50, series2: 45, series3: 85 },
{ name: "Item 5", series1: 80, series2: 75, series3: 120 },
])
.meta({
description:
"CRITICAL: Provide topic-specific data for the right area chart. For global warming: Cumulative data over 5 time periods showing renewable energy adoption, carbon reduction efforts, and policy implementations with realistic growth trends. For healthcare: Cumulative patient care metrics showing improvement over time. For education: Progressive learning outcomes showing student advancement. Ensure data shows meaningful trends relevant to the topic.",
}),
leftChartTitle: z.string()
.min(5)
.max(40)
.default("Our Customer's Satisfaction")
.meta({
description: "IMPORTANT: Provide topic-specific title for left chart. For global warming: 'Global CO2 Emissions by Sector', 'Temperature Rise by Region', 'Renewable Energy Adoption'. For healthcare: 'Patient Treatment Outcomes', 'Healthcare Quality Metrics', 'Recovery Success Rates'. For education: 'Student Performance by Subject', 'Learning Progress Assessment', 'Academic Achievement Trends'.",
}),
leftChartTitle: z
.string()
.min(5)
.max(40)
.default("Our Customer's Satisfaction")
.meta({
description:
"IMPORTANT: Provide topic-specific title for left chart. For global warming: 'Global CO2 Emissions by Sector', 'Temperature Rise by Region', 'Renewable Energy Adoption'. For healthcare: 'Patient Treatment Outcomes', 'Healthcare Quality Metrics', 'Recovery Success Rates'. For education: 'Student Performance by Subject', 'Learning Progress Assessment', 'Academic Achievement Trends'.",
}),
leftChartDescription: z.string()
.min(20)
.max(200)
.default("An impressive client satisfaction rate underscores our unwavering commitment to delivering exceptional service and exceeding expectations.")
.meta({
description: "ESSENTIAL: Provide topic-relevant description explaining the left chart data. For global warming: Explain emission sources, trends, and implications. For healthcare: Describe treatment effectiveness and patient outcomes. For education: Explain performance metrics and learning indicators. Make it informative and specific to the data shown.",
}),
leftChartDescription: z
.string()
.min(20)
.max(200)
.default(
"An impressive client satisfaction rate underscores our unwavering commitment to delivering exceptional service and exceeding expectations."
)
.meta({
description:
"ESSENTIAL: Provide topic-relevant description explaining the left chart data. For global warming: Explain emission sources, trends, and implications. For healthcare: Describe treatment effectiveness and patient outcomes. For education: Explain performance metrics and learning indicators. Make it informative and specific to the data shown.",
}),
rightChartTitle: z.string()
.min(5)
.max(40)
.default("Repeat Order Rate")
.meta({
description: "IMPORTANT: Provide topic-specific title for right chart. For global warming: 'Climate Action Progress', 'Carbon Reduction Timeline', 'Sustainability Milestones'. For healthcare: 'Patient Recovery Timeline', 'Treatment Progress Tracking', 'Health Improvement Trajectory'. For education: 'Learning Progress Over Time', 'Student Development Path', 'Academic Growth Timeline'.",
}),
rightChartTitle: z.string().min(5).max(40).default("Repeat Order Rate").meta({
description:
"IMPORTANT: Provide topic-specific title for right chart. For global warming: 'Climate Action Progress', 'Carbon Reduction Timeline', 'Sustainability Milestones'. For healthcare: 'Patient Recovery Timeline', 'Treatment Progress Tracking', 'Health Improvement Trajectory'. For education: 'Learning Progress Over Time', 'Student Development Path', 'Academic Growth Timeline'.",
}),
rightChartDescription: z.string()
.min(20)
.max(200)
.default("Our remarkable client repeat order rate of 123 times are testament to the quality of our products/services and the trust our clients place in our ability.")
.meta({
description: "ESSENTIAL: Provide topic-relevant description explaining the right chart's cumulative/timeline data. For global warming: Describe progress in climate action, policy impact, or environmental improvements. For healthcare: Explain patient journey and recovery progression. For education: Describe learning advancement and skill development over time. Make it specific and data-driven.",
}),
})
rightChartDescription: z
.string()
.min(20)
.max(200)
.default(
"Our remarkable client repeat order rate of 123 times are testament to the quality of our products/services and the trust our clients place in our ability."
)
.meta({
description:
"ESSENTIAL: Provide topic-relevant description explaining the right chart's cumulative/timeline data. For global warming: Describe progress in climate action, policy impact, or environmental improvements. For healthcare: Explain patient journey and recovery progression. For education: Describe learning advancement and skill development over time. Make it specific and data-driven.",
}),
});
// Chart configuration
const chartConfig = {
series1: {
label: "Series 1",
color: "#1D9A8A",
},
series2: {
label: "Series 2",
color: "#A8C97F",
},
series3: {
label: "Series 3",
color: "#E8F4B8",
},
series1: {
label: "Series 1",
color: "#1D9A8A",
},
series2: {
label: "Series 2",
color: "#A8C97F",
},
series3: {
label: "Series 3",
color: "#E8F4B8",
},
};
// Type inference
@ -114,208 +151,211 @@ type SchemaType = z.infer<typeof Schema>;
// Component definition
const StatisticDualChartSlide = ({ data }: { data: Partial<SchemaType> }) => {
const {
sectionTitle,
organizationName,
brandLogo,
barChartData,
areaChartData,
leftChartTitle,
leftChartDescription,
rightChartTitle,
rightChartDescription,
} = data;
const { sectionTitle, organizationName, brandLogo, barChartData, areaChartData, leftChartTitle, leftChartDescription, rightChartTitle, rightChartDescription } = data;
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Header Section */}
<div className="h-20 bg-teal-600 px-16 py-4 flex justify-between items-center">
{/* Title */}
{sectionTitle && (
<h1 className="text-4xl font-black text-white">{sectionTitle}</h1>
)}
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Header Section */}
<div className="h-20 bg-teal-600 px-16 py-4 flex justify-between items-center">
{/* Title */}
{sectionTitle && (
<h1 className="text-4xl font-black text-white">
{sectionTitle}
</h1>
)}
{/* Company Branding */}
<div className="flex items-center space-x-3">
{brandLogo?.__image_url__ && (
<div className="w-8 h-8">
<img
src={brandLogo.__image_url__}
alt={brandLogo.__image_prompt__}
className="w-full h-full object-contain"
/>
</div>
)}
{organizationName && (
<span className="text-lg font-bold text-white">
{organizationName}
</span>
)}
</div>
</div>
{/* Content Section */}
<div className="flex-1 h-[calc(100%-80px)] flex">
{/* Left Chart Section */}
<div className="w-1/2 p-8 bg-gray-50 flex flex-col">
{/* Chart Legend */}
<div className="flex items-center justify-start mb-4 space-x-4">
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-teal-600 rounded-full"></div>
<span className="text-sm text-gray-600">Series 1</span>
</div>
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-green-400 rounded-full"></div>
<span className="text-sm text-gray-600">Series 2</span>
</div>
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-yellow-200 rounded-full"></div>
<span className="text-sm text-gray-600">Series 3</span>
</div>
</div>
{/* Bar Chart */}
{barChartData && barChartData.length > 0 && (
<div className="flex-1 mb-6">
<ChartContainer config={chartConfig} className="h-full w-full">
<BarChart
data={barChartData}
margin={{ top: 10, right: 20, left: 0, bottom: 30 }}
barCategoryGap="20%"
>
<CartesianGrid strokeDasharray="3 3" stroke="#f0f0f0" />
<XAxis
dataKey="name"
axisLine={false}
tickLine={false}
tick={{ fontSize: 12, fill: '#666' }}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fontSize: 12, fill: '#666' }}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<Bar
dataKey="series1"
fill="#1D9A8A"
radius={[2, 2, 0, 0]}
barSize={15}
/>
<Bar
dataKey="series2"
fill="#A8C97F"
radius={[2, 2, 0, 0]}
barSize={15}
/>
<Bar
dataKey="series3"
fill="#E8F4B8"
radius={[2, 2, 0, 0]}
barSize={15}
/>
</BarChart>
</ChartContainer>
</div>
)}
{/* Chart Description */}
<div className="space-y-3">
{leftChartTitle && (
<h3 className="text-xl font-bold text-gray-900">
{leftChartTitle}
</h3>
)}
{leftChartDescription && (
<p className="text-base leading-relaxed text-gray-700">
{leftChartDescription}
</p>
)}
</div>
</div>
{/* Right Chart Section */}
<div className="w-1/2 p-8 bg-white flex flex-col">
{/* Chart Legend */}
<div className="flex items-center justify-end mb-4 space-x-4">
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-yellow-200 rounded-full"></div>
<span className="text-sm text-gray-600">Series 1</span>
</div>
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-green-400 rounded-full"></div>
<span className="text-sm text-gray-600">Series 2</span>
</div>
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-teal-600 rounded-full"></div>
<span className="text-sm text-gray-600">Series 3</span>
</div>
</div>
{/* Area Chart */}
{areaChartData && areaChartData.length > 0 && (
<div className="flex-1 mb-6">
<ChartContainer config={chartConfig} className="h-full w-full">
<AreaChart
data={areaChartData}
margin={{ top: 10, right: 20, left: 0, bottom: 30 }}
>
<CartesianGrid strokeDasharray="3 3" stroke="#f0f0f0" />
<XAxis
dataKey="name"
axisLine={false}
tickLine={false}
tick={{ fontSize: 12, fill: '#666' }}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fontSize: 12, fill: '#666' }}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<Area
type="monotone"
dataKey="series3"
stackId="1"
stroke="#1D9A8A"
fill="#1D9A8A"
fillOpacity={0.8}
/>
<Area
type="monotone"
dataKey="series2"
stackId="1"
stroke="#A8C97F"
fill="#A8C97F"
fillOpacity={0.8}
/>
<Area
type="monotone"
dataKey="series1"
stackId="1"
stroke="#E8F4B8"
fill="#E8F4B8"
fillOpacity={0.8}
/>
</AreaChart>
</ChartContainer>
</div>
)}
{/* Chart Description */}
<div className="space-y-3">
{rightChartTitle && (
<h3 className="text-xl font-bold text-gray-900">
{rightChartTitle}
</h3>
)}
{rightChartDescription && (
<p className="text-base leading-relaxed text-gray-700">
{rightChartDescription}
</p>
)}
</div>
</div>
{/* Company Branding */}
<div className="flex items-center space-x-3">
{brandLogo?.__image_url__ && (
<div className="w-8 h-8">
<img
src={brandLogo.__image_url__}
alt={brandLogo.__image_prompt__}
className="w-full h-full object-contain"
/>
</div>
)}
{organizationName && (
<span className="text-lg font-bold text-white">
{organizationName}
</span>
)}
</div>
);
</div>
{/* Content Section */}
<div className="flex-1 h-[calc(100%-80px)] flex">
{/* Left Chart Section */}
<div className="w-1/2 p-8 bg-gray-50 flex flex-col">
{/* Chart Legend */}
<div className="flex items-center justify-start mb-4 space-x-4">
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-teal-600 rounded-full"></div>
<span className="text-sm text-gray-600">Series 1</span>
</div>
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-green-400 rounded-full"></div>
<span className="text-sm text-gray-600">Series 2</span>
</div>
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-yellow-200 rounded-full"></div>
<span className="text-sm text-gray-600">Series 3</span>
</div>
</div>
{/* Bar Chart */}
{barChartData && barChartData.length > 0 && (
<div className="flex-1 mb-6">
<ChartContainer config={chartConfig} className="h-full w-full">
<BarChart
data={barChartData}
margin={{ top: 10, right: 20, left: 0, bottom: 30 }}
barCategoryGap="20%"
>
<CartesianGrid strokeDasharray="3 3" stroke="#f0f0f0" />
<XAxis
dataKey="name"
axisLine={false}
tickLine={false}
tick={{ fontSize: 12, fill: "#666" }}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fontSize: 12, fill: "#666" }}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<Bar
dataKey="series1"
fill="#1D9A8A"
radius={[2, 2, 0, 0]}
barSize={15}
/>
<Bar
dataKey="series2"
fill="#A8C97F"
radius={[2, 2, 0, 0]}
barSize={15}
/>
<Bar
dataKey="series3"
fill="#E8F4B8"
radius={[2, 2, 0, 0]}
barSize={15}
/>
</BarChart>
</ChartContainer>
</div>
)}
{/* Chart Description */}
<div className="space-y-3">
{leftChartTitle && (
<h3 className="text-xl font-bold text-gray-900">
{leftChartTitle}
</h3>
)}
{leftChartDescription && (
<p className="text-base leading-relaxed text-gray-700">
{leftChartDescription}
</p>
)}
</div>
</div>
{/* Right Chart Section */}
<div className="w-1/2 p-8 bg-white flex flex-col">
{/* Chart Legend */}
<div className="flex items-center justify-end mb-4 space-x-4">
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-yellow-200 rounded-full"></div>
<span className="text-sm text-gray-600">Series 1</span>
</div>
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-green-400 rounded-full"></div>
<span className="text-sm text-gray-600">Series 2</span>
</div>
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-teal-600 rounded-full"></div>
<span className="text-sm text-gray-600">Series 3</span>
</div>
</div>
{/* Area Chart */}
{areaChartData && areaChartData.length > 0 && (
<div className="flex-1 mb-6">
<ChartContainer config={chartConfig} className="h-full w-full">
<AreaChart
data={areaChartData}
margin={{ top: 10, right: 20, left: 0, bottom: 30 }}
>
<CartesianGrid strokeDasharray="3 3" stroke="#f0f0f0" />
<XAxis
dataKey="name"
axisLine={false}
tickLine={false}
tick={{ fontSize: 12, fill: "#666" }}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fontSize: 12, fill: "#666" }}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<Area
type="monotone"
dataKey="series3"
stackId="1"
stroke="#1D9A8A"
fill="#1D9A8A"
fillOpacity={0.8}
/>
<Area
type="monotone"
dataKey="series2"
stackId="1"
stroke="#A8C97F"
fill="#A8C97F"
fillOpacity={0.8}
/>
<Area
type="monotone"
dataKey="series1"
stackId="1"
stroke="#E8F4B8"
fill="#E8F4B8"
fillOpacity={0.8}
/>
</AreaChart>
</ChartContainer>
</div>
)}
{/* Chart Description */}
<div className="space-y-3">
{rightChartTitle && (
<h3 className="text-xl font-bold text-gray-900">
{rightChartTitle}
</h3>
)}
{rightChartDescription && (
<p className="text-base leading-relaxed text-gray-700">
{rightChartDescription}
</p>
)}
</div>
</div>
</div>
</div>
);
};
export default StatisticDualChartSlide;
export default StatisticDualChartSlide;

View file

@ -1,102 +1,120 @@
import React from "react";
import * as z from "zod";
import { ImageSchema, IconSchema } from "../defaultSchemes";
import { ChartContainer, ChartTooltip, ChartTooltipContent } from '@/components/ui/chart';
import {
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from "@/components/ui/chart";
import { LineChart, Line, XAxis, YAxis, CartesianGrid } from "recharts";
export const layoutName = "Statistic Slide";
export const layoutId = "statistic-slide";
export const layoutDescription = "A slide with a statistic";
// Schema definition
export const Schema = z.object({
sectionTitle: z.string().min(3).max(30).default("KEY STATISTICS").meta({
description:
"Main section heading - adapt to presentation topic (e.g., 'Climate Data', 'Health Metrics', 'Performance Stats', 'Research Findings')",
}),
sectionTitle: z.string()
.min(3)
.max(30)
.default("KEY STATISTICS")
.meta({
description: "Main section heading - adapt to presentation topic (e.g., 'Climate Data', 'Health Metrics', 'Performance Stats', 'Research Findings')",
}),
sectionSubtitle: z.string()
.min(10)
.max(60)
.default("DATA-DRIVEN INSIGHTS AND PERFORMANCE")
.meta({
description: "Supporting subtitle that frames the data - adapt to topic (e.g., 'Global Temperature Trends and Impact', 'Patient Outcomes and Recovery Rates', 'Student Achievement and Progress')",
}),
statisticValue: z.string()
.min(1)
.max(15)
.default("85%")
.meta({
description: "CRITICAL: Provide the most important statistic for the topic. For global warming: '1.1°C', '+2.1°C', '410ppm', '33%'. For healthcare: '95%', '72 hours', '89%'. For education: '78%', '3.2 GPA', '92%'. Use real, impactful numbers relevant to the presentation topic.",
}),
statisticLabel: z.string()
.min(5)
.max(40)
.default("Client Satisfaction Rate")
.meta({
description: "IMPORTANT: Provide topic-specific label for the main statistic. For global warming: 'Global Temperature Rise Since 1880', 'CO2 Concentration Increase', 'Arctic Ice Loss Rate'. For healthcare: 'Patient Recovery Rate', 'Treatment Success Rate', 'Early Detection Rate'. For education: 'Graduation Success Rate', 'Student Engagement Level', 'Learning Improvement Rate'.",
}),
supportingVisual: ImageSchema.default({
__image_url__: "https://images.unsplash.com/photo-1460925895917-afdab827c52f?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
__image_prompt__: "Business analytics dashboard with charts and data visualization"
}).meta({
description: "ADAPT the image prompt to match the presentation topic: For global warming: 'Climate monitoring station with temperature sensors and weather equipment', 'Scientists analyzing ice core data in Arctic research facility'. For healthcare: 'Medical monitoring equipment displaying patient vital signs', 'Healthcare analytics dashboard showing treatment outcomes'. For education: 'Educational assessment data on computer screens', 'Students using digital learning platforms'.",
sectionSubtitle: z
.string()
.min(10)
.max(60)
.default("DATA-DRIVEN INSIGHTS AND PERFORMANCE")
.meta({
description:
"Supporting subtitle that frames the data - adapt to topic (e.g., 'Global Temperature Trends and Impact', 'Patient Outcomes and Recovery Rates', 'Student Achievement and Progress')",
}),
bulletPoints: z.array(z.string().min(10).max(100)).min(2).max(5).default([
"Consistent performance improvement over 12 months",
"High customer retention and satisfaction scores",
"Measurable ROI across all key performance indicators",
"Data-driven decision making and strategic optimization"
]).meta({
description: "ESSENTIAL: Provide topic-relevant supporting facts and insights. For global warming: 'Global average temperature has risen 1.1°C since pre-industrial times', 'Arctic sea ice is declining at 13% per decade', 'CO2 levels are highest in 3 million years', 'Renewable energy adoption increased 85% in last decade'. For healthcare: 'Early detection improves survival rates by 85%', 'Telemedicine reduced patient wait times by 60%', 'Preventive care decreased hospital readmissions by 40%'. Always provide factual, verifiable statements related to the presentation topic.",
statisticValue: z.string().min(1).max(15).default("85%").meta({
description:
"CRITICAL: Provide the most important statistic for the topic. For global warming: '1.1°C', '+2.1°C', '410ppm', '33%'. For healthcare: '95%', '72 hours', '89%'. For education: '78%', '3.2 GPA', '92%'. Use real, impactful numbers relevant to the presentation topic.",
}),
statisticLabel: z
.string()
.min(5)
.max(40)
.default("Client Satisfaction Rate")
.meta({
description:
"IMPORTANT: Provide topic-specific label for the main statistic. For global warming: 'Global Temperature Rise Since 1880', 'CO2 Concentration Increase', 'Arctic Ice Loss Rate'. For healthcare: 'Patient Recovery Rate', 'Treatment Success Rate', 'Early Detection Rate'. For education: 'Graduation Success Rate', 'Student Engagement Level', 'Learning Improvement Rate'.",
}),
chartData: z.array(z.object({
supportingVisual: ImageSchema.default({
__image_url__:
"https://images.unsplash.com/photo-1460925895917-afdab827c52f?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
__image_prompt__:
"Business analytics dashboard with charts and data visualization",
}).meta({
description:
"ADAPT the image prompt to match the presentation topic: For global warming: 'Climate monitoring station with temperature sensors and weather equipment', 'Scientists analyzing ice core data in Arctic research facility'. For healthcare: 'Medical monitoring equipment displaying patient vital signs', 'Healthcare analytics dashboard showing treatment outcomes'. For education: 'Educational assessment data on computer screens', 'Students using digital learning platforms'.",
}),
bulletPoints: z
.array(z.string().min(10).max(100))
.min(2)
.max(5)
.default([
"Consistent performance improvement over 12 months",
"High customer retention and satisfaction scores",
"Measurable ROI across all key performance indicators",
"Data-driven decision making and strategic optimization",
])
.meta({
description:
"ESSENTIAL: Provide topic-relevant supporting facts and insights. For global warming: 'Global average temperature has risen 1.1°C since pre-industrial times', 'Arctic sea ice is declining at 13% per decade', 'CO2 levels are highest in 3 million years', 'Renewable energy adoption increased 85% in last decade'. For healthcare: 'Early detection improves survival rates by 85%', 'Telemedicine reduced patient wait times by 60%', 'Preventive care decreased hospital readmissions by 40%'. Always provide factual, verifiable statements related to the presentation topic.",
}),
chartData: z
.array(
z.object({
name: z.string(),
series1: z.number(),
series2: z.number(),
series3: z.number()
})).min(5).max(5).default([
{ name: "Jan", series1: 18, series2: 0, series3: 0 },
{ name: "Feb", series1: 30, series2: 12, series3: 8 },
{ name: "Mar", series1: 26, series2: 38, series3: 20 },
{ name: "Apr", series1: 40, series2: 30, series3: 35 },
{ name: "May", series1: 42, series2: 45, series3: 32 }
]).meta({
description: "CRITICAL: Provide topic-specific time-series data for line chart. For global warming: Monthly temperature anomalies, CO2 levels, ice coverage data with realistic values. For healthcare: Patient recovery rates, treatment success metrics, diagnostic accuracy over time. For education: Student performance trends, learning progress, engagement metrics. Use realistic data patterns showing meaningful trends.",
series3: z.number(),
})
)
.min(5)
.max(5)
.default([
{ name: "Jan", series1: 18, series2: 0, series3: 0 },
{ name: "Feb", series1: 30, series2: 12, series3: 8 },
{ name: "Mar", series1: 26, series2: 38, series3: 20 },
{ name: "Apr", series1: 40, series2: 30, series3: 35 },
{ name: "May", series1: 42, series2: 45, series3: 32 },
])
.meta({
description:
"CRITICAL: Provide topic-specific time-series data for line chart. For global warming: Monthly temperature anomalies, CO2 levels, ice coverage data with realistic values. For healthcare: Patient recovery rates, treatment success metrics, diagnostic accuracy over time. For education: Student performance trends, learning progress, engagement metrics. Use realistic data patterns showing meaningful trends.",
}),
showYellowUnderline: z.boolean()
.default(true)
.meta({
description: "Whether to display the decorative yellow underline accent",
}),
showYellowUnderline: z.boolean().default(true).meta({
description: "Whether to display the decorative yellow underline accent",
}),
showVisualAccents: z.boolean()
.default(true)
.meta({
description: "Whether to display decorative visual accent elements",
}),
})
showVisualAccents: z.boolean().default(true).meta({
description: "Whether to display decorative visual accent elements",
}),
});
// Chart configuration
const chartConfig = {
series1: {
label: "Series 1",
color: "#1D9A8A",
},
series2: {
label: "Series 2",
color: "#A8C97F",
},
series3: {
label: "Series 3",
color: "#E8F4B8",
},
series1: {
label: "Series 1",
color: "#1D9A8A",
},
series2: {
label: "Series 2",
color: "#A8C97F",
},
series3: {
label: "Series 3",
color: "#E8F4B8",
},
};
// Type inference
@ -104,169 +122,180 @@ type SchemaType = z.infer<typeof Schema>;
// Component definition
const StatisticSlide = ({ data }: { data: Partial<SchemaType> }) => {
const {
sectionTitle,
sectionSubtitle,
statisticValue,
statisticLabel,
supportingVisual,
bulletPoints,
chartData,
showYellowUnderline,
showVisualAccents,
} = data;
const { sectionTitle, sectionSubtitle, statisticValue, statisticLabel, supportingVisual, bulletPoints, chartData, showYellowUnderline, showVisualAccents } = data;
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Main Content Area */}
<div className="h-full flex">
{/* Left Side - Teal Background */}
<div className="w-1/2 relative bg-teal-600 px-16 py-12 flex flex-col text-white">
{/* Title Section */}
<div className="mb-8">
{sectionTitle && (
<h1 className="text-3xl lg:text-4xl font-black leading-tight mb-4">
{sectionTitle}
</h1>
)}
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Main Content Area */}
<div className="h-full flex">
{/* Left Side - Teal Background */}
<div className="w-1/2 relative bg-teal-600 px-16 py-12 flex flex-col text-white">
{/* Title Section */}
<div className="mb-8">
{sectionTitle && (
<h1 className="text-3xl lg:text-4xl font-black leading-tight mb-4">
{sectionTitle}
</h1>
)}
{sectionSubtitle && (
<p className="text-base font-semibold tracking-wide mb-4">
{sectionSubtitle}
</p>
)}
{sectionSubtitle && (
<p className="text-base font-semibold tracking-wide mb-4">
{sectionSubtitle}
</p>
)}
{/* Yellow Decorative Underline */}
{showYellowUnderline && (
<div className="w-24 h-1 bg-yellow-300 mb-8"></div>
)}
</div>
{/* Yellow Decorative Underline */}
{showYellowUnderline && (
<div className="w-24 h-1 bg-yellow-300 mb-8"></div>
)}
</div>
{/* Large Statistic Display */}
<div className="mb-8">
{statisticValue && (
<div className="text-8xl font-black text-yellow-300 mb-4">
{statisticValue}
</div>
)}
{statisticLabel && (
<h2 className="text-2xl font-bold">{statisticLabel}</h2>
)}
</div>
{/* Large Statistic Display */}
<div className="mb-8">
{statisticValue && (
<div className="text-8xl font-black text-yellow-300 mb-4">
{statisticValue}
</div>
)}
{statisticLabel && (
<h2 className="text-2xl font-bold">
{statisticLabel}
</h2>
)}
</div>
{/* Business Image */}
{supportingVisual?.__image_url__ && (
<div className="flex-1 flex items-end">
<div className="w-full h-48">
<img
src={supportingVisual.__image_url__}
alt={supportingVisual.__image_prompt__}
className="w-full h-full object-cover rounded-lg"
/>
</div>
</div>
)}
{/* Business Image */}
{supportingVisual?.__image_url__ && (
<div className="flex-1 flex items-end">
<div className="w-full h-48">
<img
src={supportingVisual.__image_url__}
alt={supportingVisual.__image_prompt__}
className="w-full h-full object-cover rounded-lg"
/>
</div>
</div>
)}
{/* Visual Accents */}
{showVisualAccents && (
<>
<div className="absolute top-8 right-8 w-6 h-6 bg-yellow-300 rounded-full"></div>
<div className="absolute bottom-12 left-8 w-4 h-4 bg-yellow-200 rounded-full"></div>
</>
)}
</div>
{/* Visual Accents */}
{showVisualAccents && (
<>
<div className="absolute top-8 right-8 w-6 h-6 bg-yellow-300 rounded-full"></div>
<div className="absolute bottom-12 left-8 w-4 h-4 bg-yellow-200 rounded-full"></div>
</>
)}
</div>
{/* Right Side - White Background with Chart and Bullet Points */}
<div className="w-1/2 relative bg-white px-16 py-12">
{/* Chart Section */}
<div className="flex-1 px-8 pt-8">
{/* Chart Legend */}
<div className="flex items-center justify-end mb-4 space-x-4">
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-yellow-300 rounded-full"></div>
<span className="text-sm text-gray-600">Series 1</span>
</div>
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-gray-300 rounded-full"></div>
<span className="text-sm text-gray-600">Series 2</span>
</div>
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-teal-600 rounded-full"></div>
<span className="text-sm text-gray-600">Series 3</span>
</div>
</div>
{/* Chart Container */}
{chartData && chartData.length > 0 && (
<div className="h-64">
<ChartContainer config={chartConfig} className="h-full w-full">
<LineChart
data={chartData}
margin={{ top: 10, right: 20, left: 0, bottom: 30 }}
>
<CartesianGrid strokeDasharray="3 3" stroke="#f0f0f0" />
<XAxis
dataKey="name"
axisLine={false}
tickLine={false}
tick={{ fontSize: 12, fill: '#666' }}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fontSize: 12, fill: '#666' }}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<Line
type="monotone"
dataKey="series1"
stroke="#E8F4B8"
strokeWidth={3}
dot={{ fill: "#E8F4B8", strokeWidth: 2, r: 4 }}
/>
<Line
type="monotone"
dataKey="series2"
stroke="#A8C97F"
strokeWidth={3}
dot={{ fill: "#A8C97F", strokeWidth: 2, r: 4 }}
/>
<Line
type="monotone"
dataKey="series3"
stroke="#1D9A8A"
strokeWidth={3}
dot={{ fill: "#1D9A8A", strokeWidth: 2, r: 4 }}
/>
</LineChart>
</ChartContainer>
</div>
)}
</div>
{/* Bullet Points Section */}
<div className="px-8 pb-6 space-y-4 mt-10">
{bulletPoints && bulletPoints.length > 0 && (
<>
{bulletPoints.map((point, index) => {
// Rotate colors for visual variety
const colors = ['bg-teal-600', 'bg-yellow-300', 'bg-gray-400'];
const dotColor = colors[index % colors.length];
return (
<div key={index} className="flex items-start space-x-4">
<div className={`w-6 h-6 ${dotColor} rounded-full flex-shrink-0 mt-1`}></div>
<p className="text-base leading-relaxed text-gray-700">
{point}
</p>
</div>
);
})}
</>
)}
</div>
</div>
{/* Right Side - White Background with Chart and Bullet Points */}
<div className="w-1/2 relative bg-white px-16 py-12">
{/* Chart Section */}
<div className="flex-1 px-8 pt-8">
{/* Chart Legend */}
<div className="flex items-center justify-end mb-4 space-x-4">
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-yellow-300 rounded-full"></div>
<span className="text-sm text-gray-600">Series 1</span>
</div>
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-gray-300 rounded-full"></div>
<span className="text-sm text-gray-600">Series 2</span>
</div>
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-teal-600 rounded-full"></div>
<span className="text-sm text-gray-600">Series 3</span>
</div>
</div>
{/* Bottom accent strip */}
<div className="absolute bottom-0 left-0 right-0 h-3 bg-yellow-300"></div>
{/* Chart Container */}
{chartData && chartData.length > 0 && (
<div className="h-64">
<ChartContainer config={chartConfig} className="h-full w-full">
<LineChart
data={chartData}
margin={{ top: 10, right: 20, left: 0, bottom: 30 }}
>
<CartesianGrid strokeDasharray="3 3" stroke="#f0f0f0" />
<XAxis
dataKey="name"
axisLine={false}
tickLine={false}
tick={{ fontSize: 12, fill: "#666" }}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fontSize: 12, fill: "#666" }}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<Line
type="monotone"
dataKey="series1"
stroke="#E8F4B8"
strokeWidth={3}
dot={{ fill: "#E8F4B8", strokeWidth: 2, r: 4 }}
/>
<Line
type="monotone"
dataKey="series2"
stroke="#A8C97F"
strokeWidth={3}
dot={{ fill: "#A8C97F", strokeWidth: 2, r: 4 }}
/>
<Line
type="monotone"
dataKey="series3"
stroke="#1D9A8A"
strokeWidth={3}
dot={{ fill: "#1D9A8A", strokeWidth: 2, r: 4 }}
/>
</LineChart>
</ChartContainer>
</div>
)}
</div>
{/* Bullet Points Section */}
<div className="px-8 pb-6 space-y-4 mt-10">
{bulletPoints && bulletPoints.length > 0 && (
<>
{bulletPoints.map((point, index) => {
// Rotate colors for visual variety
const colors = [
"bg-teal-600",
"bg-yellow-300",
"bg-gray-400",
];
const dotColor = colors[index % colors.length];
return (
<div key={index} className="flex items-start space-x-4">
<div
className={`w-6 h-6 ${dotColor} rounded-full flex-shrink-0 mt-1`}
></div>
<p className="text-base leading-relaxed text-gray-700">
{point}
</p>
</div>
);
})}
</>
)}
</div>
</div>
);
</div>
{/* Bottom accent strip */}
<div className="absolute bottom-0 left-0 right-0 h-3 bg-yellow-300"></div>
</div>
);
};
export default StatisticSlide;
export default StatisticSlide;

View file

@ -2,176 +2,196 @@ import * as z from "zod";
import { ImageSchema, IconSchema } from "../defaultSchemes";
export const layoutName = "Table of Contents Slide";
export const layoutId = "table-of-contents-slide";
export const layoutDescription = "A slide with a table of contents";
// Schema definition
export const Schema = z.object({
sectionTitle: z.string().min(3).max(30).default("TABLE OF CONTENTS").meta({
description:
"Main heading for the content overview - can be 'Agenda', 'Overview', 'Contents', or similar",
}),
sectionTitle: z.string()
.min(3)
.max(30)
.default("TABLE OF CONTENTS")
.meta({
description: "Main heading for the content overview - can be 'Agenda', 'Overview', 'Contents', or similar",
}),
sectionSubtitle: z
.string()
.min(10)
.max(60)
.default("PRESENTATION OVERVIEW AND AGENDA")
.meta({
description:
"Supporting subtitle that explains what the audience will learn or see",
}),
sectionSubtitle: z.string()
.min(10)
.max(60)
.default("PRESENTATION OVERVIEW AND AGENDA")
.meta({
description: "Supporting subtitle that explains what the audience will learn or see",
}),
contentItems: z.array(z.object({
contentItems: z
.array(
z.object({
itemNumber: z.string().min(1).max(3),
contentTitle: z.string().min(3).max(40),
contentDescription: z.string().min(10).max(100).optional()
})).min(3).max(8).default([
{
itemNumber: "01",
contentTitle: "Introduction & Welcome",
contentDescription: "Brief overview and objectives"
},
{
itemNumber: "02",
contentTitle: "About Our Organization",
contentDescription: "Background and mission"
},
{
itemNumber: "03",
contentTitle: "Key Challenges",
contentDescription: "Current issues and opportunities"
},
{
itemNumber: "04",
contentTitle: "Our Solutions",
contentDescription: "Proposed approaches and methods"
},
{
itemNumber: "05",
contentTitle: "Implementation Plan",
contentDescription: "Timeline and next steps"
},
{
itemNumber: "06",
contentTitle: "Questions & Discussion",
contentDescription: "Interactive engagement"
}
]).meta({
description: "List of presentation sections with numbered sequence and brief descriptions",
contentDescription: z.string().min(10).max(100).optional(),
})
)
.min(3)
.max(8)
.default([
{
itemNumber: "01",
contentTitle: "Introduction & Welcome",
contentDescription: "Brief overview and objectives",
},
{
itemNumber: "02",
contentTitle: "About Our Organization",
contentDescription: "Background and mission",
},
{
itemNumber: "03",
contentTitle: "Key Challenges",
contentDescription: "Current issues and opportunities",
},
{
itemNumber: "04",
contentTitle: "Our Solutions",
contentDescription: "Proposed approaches and methods",
},
{
itemNumber: "05",
contentTitle: "Implementation Plan",
contentDescription: "Timeline and next steps",
},
{
itemNumber: "06",
contentTitle: "Questions & Discussion",
contentDescription: "Interactive engagement",
},
])
.meta({
description:
"List of presentation sections with numbered sequence and brief descriptions",
}),
brandingVisual: ImageSchema.default({
__image_url__: "https://via.placeholder.com/200x100/22C55E/FFFFFF?text=BRAND",
__image_prompt__: "Organization logo or brand visual element"
}).meta({
description: "Logo or branding element displayed prominently for visual identity",
}),
brandingVisual: ImageSchema.default({
__image_url__:
"https://via.placeholder.com/200x100/22C55E/FFFFFF?text=BRAND",
__image_prompt__: "Organization logo or brand visual element",
}).meta({
description:
"Logo or branding element displayed prominently for visual identity",
}),
showDecorations: z.boolean()
.default(true)
.meta({
description: "Whether to display decorative visual elements like underlines and accents",
}),
showDecorations: z.boolean().default(true).meta({
description:
"Whether to display decorative visual elements like underlines and accents",
}),
useColumnLayout: z.boolean()
.default(true)
.meta({
description: "Whether to arrange content items in two columns for better space utilization",
}),
})
useColumnLayout: z.boolean().default(true).meta({
description:
"Whether to arrange content items in two columns for better space utilization",
}),
});
// Type inference
type SchemaType = z.infer<typeof Schema>;
// Component definition
const TableOfContentsSlide = ({ data }: { data: Partial<SchemaType> }) => {
const {
sectionTitle,
sectionSubtitle,
contentItems,
brandingVisual,
showDecorations,
useColumnLayout,
} = data;
const { sectionTitle, sectionSubtitle, contentItems, brandingVisual, showDecorations, useColumnLayout } = data;
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Main Content Area */}
<div className="h-full flex">
{/* Left Side - Content */}
<div className="w-3/5 px-16 py-12 flex flex-col justify-start">
{/* Title Section */}
<div className="mb-12">
{sectionTitle && (
<h1 className="text-3xl lg:text-4xl font-black text-teal-700 leading-tight mb-4">
{sectionTitle}
</h1>
)}
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Main Content Area */}
<div className="h-full flex">
{/* Left Side - Content */}
<div className="w-3/5 px-16 py-12 flex flex-col justify-start">
{/* Title Section */}
<div className="mb-12">
{sectionTitle && (
<h1 className="text-3xl lg:text-4xl font-black text-teal-700 leading-tight mb-4">
{sectionTitle}
</h1>
)}
{/* Decorative underline */}
{showDecorations && (
<div className="w-32 h-1 bg-yellow-300 mb-6"></div>
)}
{/* Decorative underline */}
{showDecorations && (
<div className="w-32 h-1 bg-yellow-300 mb-6"></div>
)}
{sectionSubtitle && (
<p className="text-base font-medium text-gray-700 tracking-wide">
{sectionSubtitle}
</p>
)}
</div>
{sectionSubtitle && (
<p className="text-base font-medium text-gray-700 tracking-wide">
{sectionSubtitle}
</p>
)}
</div>
{/* Content Items */}
{contentItems && contentItems.length > 0 && (
<div
className={`grid ${
useColumnLayout ? "grid-cols-2" : "grid-cols-1"
} gap-x-16 gap-y-8`}
>
{contentItems.map((item, index) => (
<div key={index} className="flex items-start space-x-4">
{/* Number Circle */}
<div className="w-10 h-10 bg-yellow-200 rounded-full flex items-center justify-center flex-shrink-0">
<span className="text-teal-700 font-bold text-sm">
{item.itemNumber}
</span>
</div>
{/* Content Items */}
{contentItems && contentItems.length > 0 && (
<div className={`grid ${useColumnLayout ? 'grid-cols-2' : 'grid-cols-1'} gap-x-16 gap-y-8`}>
{contentItems.map((item, index) => (
<div key={index} className="flex items-start space-x-4">
{/* Number Circle */}
<div className="w-10 h-10 bg-yellow-200 rounded-full flex items-center justify-center flex-shrink-0">
<span className="text-teal-700 font-bold text-sm">
{item.itemNumber}
</span>
</div>
{/* Content */}
<div className="flex-1">
<h3 className="text-lg font-bold text-gray-900 mb-1">
{item.contentTitle}
</h3>
{item.contentDescription && (
<p className="text-sm text-gray-600 leading-relaxed">
{item.contentDescription}
</p>
)}
</div>
</div>
))}
</div>
)}
</div>
{/* Right Side - Branding and Visual Elements */}
<div className="w-2/5 relative bg-gray-50 flex items-center justify-center">
{/* Branding Visual */}
{brandingVisual?.__image_url__ && (
<div className="text-center">
<img
src={brandingVisual.__image_url__}
alt={brandingVisual.__image_prompt__}
className="max-w-64 max-h-32 object-contain mx-auto"
/>
</div>
)}
{/* Decorative Elements */}
{showDecorations && (
<>
{/* Decorative circles */}
<div className="absolute top-8 right-8 w-6 h-6 bg-teal-600 rounded-full opacity-60"></div>
<div className="absolute bottom-12 left-8 w-4 h-4 bg-yellow-300 rounded-full"></div>
<div className="absolute top-32 left-12 w-3 h-3 bg-teal-400 rounded-full"></div>
</>
{/* Content */}
<div className="flex-1">
<h3 className="text-lg font-bold text-gray-900 mb-1">
{item.contentTitle}
</h3>
{item.contentDescription && (
<p className="text-sm text-gray-600 leading-relaxed">
{item.contentDescription}
</p>
)}
</div>
</div>
))}
</div>
{/* Bottom accent strip */}
<div className="absolute bottom-0 left-0 right-0 h-2 bg-teal-600"></div>
)}
</div>
);
{/* Right Side - Branding and Visual Elements */}
<div className="w-2/5 relative bg-gray-50 flex items-center justify-center">
{/* Branding Visual */}
{brandingVisual?.__image_url__ && (
<div className="text-center">
<img
src={brandingVisual.__image_url__}
alt={brandingVisual.__image_prompt__}
className="max-w-64 max-h-32 object-contain mx-auto"
/>
</div>
)}
{/* Decorative Elements */}
{showDecorations && (
<>
{/* Decorative circles */}
<div className="absolute top-8 right-8 w-6 h-6 bg-teal-600 rounded-full opacity-60"></div>
<div className="absolute bottom-12 left-8 w-4 h-4 bg-yellow-300 rounded-full"></div>
<div className="absolute top-32 left-12 w-3 h-3 bg-teal-400 rounded-full"></div>
</>
)}
</div>
</div>
{/* Bottom accent strip */}
<div className="absolute bottom-0 left-0 right-0 h-2 bg-teal-600"></div>
</div>
);
};
export default TableOfContentsSlide;
export default TableOfContentsSlide;

View file

@ -2,199 +2,220 @@ import * as z from "zod";
import { ImageSchema, IconSchema } from "../defaultSchemes";
export const layoutName = "Testimonial Slide";
export const layoutId = "testimonial-slide";
export const layoutDescription = "A slide with a testimonial";
// Schema definition
export const Schema = z.object({
sectionTitle: z.string().min(3).max(30).default("CLIENT TESTIMONIALS").meta({
description:
"Main section heading - can be 'Testimonials', 'Client Feedback', 'Reviews', or similar",
}),
sectionTitle: z.string()
.min(3)
.max(30)
.default("CLIENT TESTIMONIALS")
.meta({
description: "Main section heading - can be 'Testimonials', 'Client Feedback', 'Reviews', or similar",
}),
organizationName: z.string()
.min(2)
.max(30)
.default("Your Organization")
.meta({
description: "Name of the organization or entity being featured",
}),
brandLogo: ImageSchema.default({
__image_url__: "https://via.placeholder.com/40x40/22C55E/FFFFFF?text=L",
__image_prompt__: "Professional organization logo - clean and modern design"
}).meta({
description: "Logo or brand mark representing the organization",
organizationName: z
.string()
.min(2)
.max(30)
.default("Your Organization")
.meta({
description: "Name of the organization or entity being featured",
}),
testimonialItems: z.array(z.object({
brandLogo: ImageSchema.default({
__image_url__: "https://via.placeholder.com/40x40/22C55E/FFFFFF?text=L",
__image_prompt__:
"Professional organization logo - clean and modern design",
}).meta({
description: "Logo or brand mark representing the organization",
}),
testimonialItems: z
.array(
z.object({
clientName: z.string().min(2).max(40),
clientTitle: z.string().min(5).max(60),
clientCompany: z.string().min(2).max(40),
testimonialText: z.string().min(50).max(300),
rating: z.number().min(1).max(5),
clientPhoto: ImageSchema
})).min(2).max(3).default([
{
clientName: "Sarah Johnson",
clientTitle: "Chief Executive Officer",
clientCompany: "TechCorp Solutions",
testimonialText: "Working with this team has been transformative for our business. Their expertise, dedication, and innovative approach exceeded our expectations and delivered remarkable results.",
rating: 5,
clientPhoto: {
__image_url__: "https://images.unsplash.com/photo-1494790108755-2616b612b830?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80",
__image_prompt__: "Professional businesswoman headshot"
}
clientPhoto: ImageSchema,
})
)
.min(2)
.max(3)
.default([
{
clientName: "Sarah Johnson",
clientTitle: "Chief Executive Officer",
clientCompany: "TechCorp Solutions",
testimonialText:
"Working with this team has been transformative for our business. Their expertise, dedication, and innovative approach exceeded our expectations and delivered remarkable results.",
rating: 5,
clientPhoto: {
__image_url__:
"https://images.unsplash.com/photo-1494790108755-2616b612b830?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80",
__image_prompt__: "Professional businesswoman headshot",
},
{
clientName: "Michael Chen",
clientTitle: "Director of Operations",
clientCompany: "Global Innovations Inc",
testimonialText: "The level of professionalism and quality of service provided was outstanding. They understood our needs perfectly and delivered solutions that truly made a difference.",
rating: 5,
clientPhoto: {
__image_url__: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80",
__image_prompt__: "Professional businessman headshot"
}
},
{
clientName: "Michael Chen",
clientTitle: "Director of Operations",
clientCompany: "Global Innovations Inc",
testimonialText:
"The level of professionalism and quality of service provided was outstanding. They understood our needs perfectly and delivered solutions that truly made a difference.",
rating: 5,
clientPhoto: {
__image_url__:
"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80",
__image_prompt__: "Professional businessman headshot",
},
{
clientName: "Emily Rodriguez",
clientTitle: "Marketing Manager",
clientCompany: "Creative Dynamics",
testimonialText: "Exceptional service and results that spoke for themselves. The team's attention to detail and commitment to excellence made our collaboration highly successful.",
rating: 5,
clientPhoto: {
__image_url__: "https://images.unsplash.com/photo-1580489944761-15a19d654956?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80",
__image_prompt__: "Professional woman headshot"
}
}
]).meta({
description: "List of client testimonials with ratings, photos, and detailed feedback",
},
{
clientName: "Emily Rodriguez",
clientTitle: "Marketing Manager",
clientCompany: "Creative Dynamics",
testimonialText:
"Exceptional service and results that spoke for themselves. The team's attention to detail and commitment to excellence made our collaboration highly successful.",
rating: 5,
clientPhoto: {
__image_url__:
"https://images.unsplash.com/photo-1580489944761-15a19d654956?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80",
__image_prompt__: "Professional woman headshot",
},
},
])
.meta({
description:
"List of client testimonials with ratings, photos, and detailed feedback",
}),
showRatings: z.boolean()
.default(true)
.meta({
description: "Whether to display star ratings for each testimonial",
}),
showRatings: z.boolean().default(true).meta({
description: "Whether to display star ratings for each testimonial",
}),
showClientPhotos: z.boolean()
.default(true)
.meta({
description: "Whether to show client photos alongside testimonials",
}),
})
showClientPhotos: z.boolean().default(true).meta({
description: "Whether to show client photos alongside testimonials",
}),
});
// Type inference
type SchemaType = z.infer<typeof Schema>;
// Component definition
const TestimonialSlide = ({ data }: { data: Partial<SchemaType> }) => {
const {
sectionTitle,
organizationName,
brandLogo,
testimonialItems,
showRatings,
showClientPhotos,
} = data;
const { sectionTitle, organizationName, brandLogo, testimonialItems, showRatings, showClientPhotos } = data;
// Helper function to render stars
const renderStars = (rating: number) => {
return Array.from({ length: 5 }, (_, i) => (
<svg
key={i}
className={`w-4 h-4 ${
i < rating ? "text-yellow-400" : "text-gray-300"
}`}
fill="currentColor"
viewBox="0 0 20 20"
>
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
</svg>
));
};
// Helper function to render stars
const renderStars = (rating: number) => {
return Array.from({ length: 5 }, (_, i) => (
<svg
key={i}
className={`w-4 h-4 ${i < rating ? 'text-yellow-400' : 'text-gray-300'}`}
fill="currentColor"
viewBox="0 0 20 20"
>
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
</svg>
));
};
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Header Section */}
<div className="h-20 bg-teal-600 px-16 py-4 flex justify-between items-center">
{/* Title */}
{sectionTitle && (
<h1 className="text-2xl font-black text-white">{sectionTitle}</h1>
)}
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Header Section */}
<div className="h-20 bg-teal-600 px-16 py-4 flex justify-between items-center">
{/* Title */}
{sectionTitle && (
<h1 className="text-2xl font-black text-white">
{sectionTitle}
</h1>
)}
{/* Company Branding */}
<div className="flex items-center space-x-3">
{brandLogo?.__image_url__ && (
<div className="w-8 h-8">
<img
src={brandLogo.__image_url__}
alt={brandLogo.__image_prompt__}
className="w-full h-full object-contain"
/>
</div>
)}
{organizationName && (
<span className="text-base font-bold text-white">
{organizationName}
</span>
)}
</div>
</div>
{/* Testimonials Content */}
<div className="flex-1 px-16 py-12 bg-gray-50">
{testimonialItems && testimonialItems.length > 0 && (
<div className="grid grid-cols-3 gap-8 h-full">
{testimonialItems.slice(0, 3).map((item, index) => {
// Rotate background colors for visual variety
const bgColors = ['bg-yellow-100', 'bg-teal-100', 'bg-gray-100'];
const bgColor = bgColors[index % bgColors.length];
return (
<div key={index} className={`${bgColor} rounded-lg p-6 flex flex-col`}>
{/* Stars Rating */}
{showRatings && (
<div className="flex space-x-1 mb-4">
{renderStars(item.rating)}
</div>
)}
{/* Testimonial Text */}
<p className="text-base leading-relaxed text-gray-800 mb-6 flex-1">
"{item.testimonialText}"
</p>
{/* Client Info */}
<div className="flex items-center space-x-4">
{/* Client Photo */}
{showClientPhotos && item.clientPhoto?.__image_url__ && (
<div className="w-12 h-12 rounded-full overflow-hidden flex-shrink-0">
<img
src={item.clientPhoto.__image_url__}
alt={item.clientPhoto.__image_prompt__}
className="w-full h-full object-cover"
/>
</div>
)}
{/* Client Details */}
<div className="flex-1">
<h4 className="text-lg font-bold text-gray-900">
{item.clientName}
</h4>
<p className="text-sm text-gray-600">
{item.clientTitle}
</p>
<p className="text-sm font-semibold text-teal-600">
{item.clientCompany}
</p>
</div>
</div>
</div>
);
})}
</div>
)}
{/* Company Branding */}
<div className="flex items-center space-x-3">
{brandLogo?.__image_url__ && (
<div className="w-8 h-8">
<img
src={brandLogo.__image_url__}
alt={brandLogo.__image_prompt__}
className="w-full h-full object-contain"
/>
</div>
)}
{organizationName && (
<span className="text-base font-bold text-white">
{organizationName}
</span>
)}
</div>
);
</div>
{/* Testimonials Content */}
<div className="flex-1 px-16 py-12 bg-gray-50">
{testimonialItems && testimonialItems.length > 0 && (
<div className="grid grid-cols-3 gap-8 h-full">
{testimonialItems.slice(0, 3).map((item, index) => {
// Rotate background colors for visual variety
const bgColors = ["bg-yellow-100", "bg-teal-100", "bg-gray-100"];
const bgColor = bgColors[index % bgColors.length];
return (
<div
key={index}
className={`${bgColor} rounded-lg p-6 flex flex-col`}
>
{/* Stars Rating */}
{showRatings && (
<div className="flex space-x-1 mb-4">
{renderStars(item.rating)}
</div>
)}
{/* Testimonial Text */}
<p className="text-base leading-relaxed text-gray-800 mb-6 flex-1">
"{item.testimonialText}"
</p>
{/* Client Info */}
<div className="flex items-center space-x-4">
{/* Client Photo */}
{showClientPhotos && item.clientPhoto?.__image_url__ && (
<div className="w-12 h-12 rounded-full overflow-hidden flex-shrink-0">
<img
src={item.clientPhoto.__image_url__}
alt={item.clientPhoto.__image_prompt__}
className="w-full h-full object-cover"
/>
</div>
)}
{/* Client Details */}
<div className="flex-1">
<h4 className="text-lg font-bold text-gray-900">
{item.clientName}
</h4>
<p className="text-sm text-gray-600">
{item.clientTitle}
</p>
<p className="text-sm font-semibold text-teal-600">
{item.clientCompany}
</p>
</div>
</div>
</div>
);
})}
</div>
)}
</div>
</div>
);
};
export default TestimonialSlide;
export default TestimonialSlide;

View file

@ -2,189 +2,228 @@ import * as z from "zod";
import { ImageSchema, IconSchema } from "../defaultSchemes";
export const layoutName = "Thank You Slide";
export const layoutId = "thank-you-slide";
export const layoutDescription = "A slide with a thank you message";
// Schema definition
export const Schema = z.object({
organizationName: z
.string()
.min(2)
.max(30)
.default("Your Organization")
.meta({
description: "Name of the organization, company, or entity presenting",
}),
organizationName: z.string()
.min(2)
.max(30)
.default("Your Organization")
.meta({
description: "Name of the organization, company, or entity presenting",
}),
primaryMessage: z.string().min(3).max(25).default("THANK YOU").meta({
description:
"Main closing message - can be 'Thank You', 'Questions?', 'Let's Connect', or similar",
}),
primaryMessage: z.string()
.min(3)
.max(25)
.default("THANK YOU")
.meta({
description: "Main closing message - can be 'Thank You', 'Questions?', 'Let's Connect', or similar",
}),
secondaryMessage: z
.string()
.min(5)
.max(60)
.default("FOR YOUR TIME AND ATTENTION")
.meta({
description:
"Supporting message that completes the primary message or adds context",
}),
secondaryMessage: z.string()
.min(5)
brandLogo: ImageSchema.default({
__image_url__: "https://via.placeholder.com/40x40/22C55E/FFFFFF?text=L",
__image_prompt__:
"Professional organization logo - clean and modern design",
}).meta({
description: "Logo or brand mark representing the presenting organization",
}),
contactDetails: z
.object({
phoneNumber: z.string().min(10).max(20).default("+1-234-567-8900"),
physicalAddress: z
.string()
.min(10)
.max(60)
.default("FOR YOUR TIME AND ATTENTION")
.meta({
description: "Supporting message that completes the primary message or adds context",
}),
brandLogo: ImageSchema.default({
__image_url__: "https://via.placeholder.com/40x40/22C55E/FFFFFF?text=L",
__image_prompt__: "Professional organization logo - clean and modern design"
}).meta({
description: "Logo or brand mark representing the presenting organization",
.default("123 Business Ave, City, State 12345"),
websiteUrl: z
.string()
.min(10)
.max(40)
.default("www.yourorganization.com"),
})
.default({
phoneNumber: "+1-234-567-8900",
physicalAddress: "123 Business Ave, City, State 12345",
websiteUrl: "www.yourorganization.com",
})
.meta({
description:
"Contact information for follow-up communication and connection",
}),
contactDetails: z.object({
phoneNumber: z.string().min(10).max(20).default("+1-234-567-8900"),
physicalAddress: z.string().min(10).max(60).default("123 Business Ave, City, State 12345"),
websiteUrl: z.string().min(10).max(40).default("www.yourorganization.com")
}).default({
phoneNumber: "+1-234-567-8900",
physicalAddress: "123 Business Ave, City, State 12345",
websiteUrl: "www.yourorganization.com"
}).meta({
description: "Contact information for follow-up communication and connection",
presentationDate: z
.string()
.min(3)
.max(20)
.default("Current Month Year")
.meta({
description:
"Date when the presentation was given or document was created",
}),
presentationDate: z.string()
.min(3)
.max(20)
.default("Current Month Year")
.meta({
description: "Date when the presentation was given or document was created",
}),
showDecorations: z.boolean().default(true).meta({
description:
"Whether to display decorative visual elements like background shapes",
}),
showDecorations: z.boolean()
.default(true)
.meta({
description: "Whether to display decorative visual elements like background shapes",
}),
showNavigationArrow: z.boolean().default(true).meta({
description:
"Whether to show a navigation arrow for interactive presentations",
}),
showNavigationArrow: z.boolean()
.default(true)
.meta({
description: "Whether to show a navigation arrow for interactive presentations",
}),
showContactInfo: z.boolean()
.default(true)
.meta({
description: "Whether to display contact information in the footer",
}),
})
showContactInfo: z.boolean().default(true).meta({
description: "Whether to display contact information in the footer",
}),
});
// Type inference
type SchemaType = z.infer<typeof Schema>;
// Component definition
const ThankYouSlide = ({ data }: { data: Partial<SchemaType> }) => {
const {
organizationName,
primaryMessage,
secondaryMessage,
brandLogo,
contactDetails,
presentationDate,
showDecorations,
showNavigationArrow,
showContactInfo,
} = data;
const { organizationName, primaryMessage, secondaryMessage, brandLogo, contactDetails, presentationDate, showDecorations, showNavigationArrow, showContactInfo } = data;
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Header with Logo and Arrow */}
<div className="absolute top-0 left-0 right-0 px-16 py-8 flex justify-between items-center z-20">
{/* Company Logo and Name */}
<div className="flex items-center space-x-3">
{brandLogo?.__image_url__ && (
<div className="w-10 h-10">
<img
src={brandLogo.__image_url__}
alt={brandLogo.__image_prompt__}
className="w-full h-full object-contain"
/>
</div>
)}
{organizationName && (
<span className="text-2xl font-bold text-gray-900">
{organizationName}
</span>
)}
</div>
{/* Arrow Button */}
{showNavigationArrow && (
<div className="w-12 h-12 bg-teal-600 rounded-full flex items-center justify-center">
<svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</div>
)}
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Header with Logo and Arrow */}
<div className="absolute top-0 left-0 right-0 px-16 py-8 flex justify-between items-center z-20">
{/* Company Logo and Name */}
<div className="flex items-center space-x-3">
{brandLogo?.__image_url__ && (
<div className="w-10 h-10">
<img
src={brandLogo.__image_url__}
alt={brandLogo.__image_prompt__}
className="w-full h-full object-contain"
/>
</div>
{/* Decorative Circle */}
{showDecorations && (
<div className="absolute top-20 right-16 w-96 h-96 bg-yellow-100 rounded-full opacity-60 z-10"></div>
)}
{/* Main Content */}
<div className="relative z-15 h-full flex flex-col justify-center px-16">
<div className="max-w-4xl">
{/* Main Title */}
{primaryMessage && (
<h1 className="text-8xl lg:text-9xl font-black text-teal-700 leading-none tracking-tight mb-4">
{primaryMessage}
</h1>
)}
{/* Subtitle with Circle Bullet */}
{secondaryMessage && (
<div className="flex items-center space-x-4 mb-12">
<div className="w-4 h-4 bg-teal-600 rounded-full"></div>
<h2 className="text-2xl font-bold text-gray-800 tracking-wide">
{secondaryMessage}
</h2>
</div>
)}
</div>
</div>
{/* Footer with Contact Info */}
{showContactInfo && (
<div className="absolute bottom-0 left-0 right-0 px-16 py-8 border-t-2 border-gray-300">
<div className="flex justify-between items-center text-gray-700">
<div className="flex space-x-16 text-sm">
{/* Telephone */}
{contactDetails?.phoneNumber && (
<div>
<div className="font-semibold text-gray-900 mb-1">Telephone</div>
<div>{contactDetails.phoneNumber}</div>
</div>
)}
{/* Address */}
{contactDetails?.physicalAddress && (
<div>
<div className="font-semibold text-gray-900 mb-1">Address</div>
<div>{contactDetails.physicalAddress}</div>
</div>
)}
{/* Website */}
{contactDetails?.websiteUrl && (
<div>
<div className="font-semibold text-gray-900 mb-1">Website</div>
<div>{contactDetails.websiteUrl}</div>
</div>
)}
</div>
{/* Presentation Date */}
{presentationDate && (
<div className="text-right">
<div className="text-lg font-bold text-gray-900">
{presentationDate}
</div>
</div>
)}
</div>
</div>
)}
)}
{organizationName && (
<span className="text-2xl font-bold text-gray-900">
{organizationName}
</span>
)}
</div>
);
{/* Arrow Button */}
{showNavigationArrow && (
<div className="w-12 h-12 bg-teal-600 rounded-full flex items-center justify-center">
<svg
className="w-6 h-6 text-white"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M9 5l7 7-7 7"
/>
</svg>
</div>
)}
</div>
{/* Decorative Circle */}
{showDecorations && (
<div className="absolute top-20 right-16 w-96 h-96 bg-yellow-100 rounded-full opacity-60 z-10"></div>
)}
{/* Main Content */}
<div className="relative z-15 h-full flex flex-col justify-center px-16">
<div className="max-w-4xl">
{/* Main Title */}
{primaryMessage && (
<h1 className="text-8xl lg:text-9xl font-black text-teal-700 leading-none tracking-tight mb-4">
{primaryMessage}
</h1>
)}
{/* Subtitle with Circle Bullet */}
{secondaryMessage && (
<div className="flex items-center space-x-4 mb-12">
<div className="w-4 h-4 bg-teal-600 rounded-full"></div>
<h2 className="text-2xl font-bold text-gray-800 tracking-wide">
{secondaryMessage}
</h2>
</div>
)}
</div>
</div>
{/* Footer with Contact Info */}
{showContactInfo && (
<div className="absolute bottom-0 left-0 right-0 px-16 py-8 border-t-2 border-gray-300">
<div className="flex justify-between items-center text-gray-700">
<div className="flex space-x-16 text-sm">
{/* Telephone */}
{contactDetails?.phoneNumber && (
<div>
<div className="font-semibold text-gray-900 mb-1">
Telephone
</div>
<div>{contactDetails.phoneNumber}</div>
</div>
)}
{/* Address */}
{contactDetails?.physicalAddress && (
<div>
<div className="font-semibold text-gray-900 mb-1">
Address
</div>
<div>{contactDetails.physicalAddress}</div>
</div>
)}
{/* Website */}
{contactDetails?.websiteUrl && (
<div>
<div className="font-semibold text-gray-900 mb-1">
Website
</div>
<div>{contactDetails.websiteUrl}</div>
</div>
)}
</div>
{/* Presentation Date */}
{presentationDate && (
<div className="text-right">
<div className="text-lg font-bold text-gray-900">
{presentationDate}
</div>
</div>
)}
</div>
</div>
)}
</div>
);
};
export default ThankYouSlide;
export default ThankYouSlide;

View file

@ -2,182 +2,216 @@ import * as z from "zod";
import { ImageSchema, IconSchema } from "../defaultSchemes";
export const layoutName = "Title Slide";
export const layoutId = "title-slide";
export const layoutDescription = "A slide with a title and subtitle";
// Schema definition
export const Schema = z.object({
organizationName: z.string()
.min(2)
.max(25)
.default("Your Organization")
.meta({
description: "Name of the organization, company, or entity presenting",
}),
primaryTitle: z.string()
.min(3)
.max(30)
.default("PRESENTATION TITLE")
.meta({
description: "Main headline or title for the presentation - should be impactful and attention-grabbing",
}),
secondaryTitle: z.string()
.min(5)
.max(50)
.default("PROFESSIONAL PRESENTATION")
.meta({
description: "Subtitle that provides context about the presentation type or purpose",
}),
brandLogo: ImageSchema.default({
__image_url__: "https://via.placeholder.com/40x40/22C55E/FFFFFF?text=L",
__image_prompt__: "Professional organization logo - clean and modern design"
}).meta({
description: "Logo or brand mark representing the presenting organization",
organizationName: z
.string()
.min(2)
.max(25)
.default("Your Organization")
.meta({
description: "Name of the organization, company, or entity presenting",
}),
contactDetails: z.object({
phoneNumber: z.string().min(10).max(20).default("+1-234-567-8900"),
physicalAddress: z.string().min(10).max(60).default("123 Business Ave, City, State 12345"),
websiteUrl: z.string().min(10).max(40).default("www.yourorganization.com")
}).default({
phoneNumber: "+1-234-567-8900",
physicalAddress: "123 Business Ave, City, State 12345",
websiteUrl: "www.yourorganization.com"
}).meta({
description: "Contact information including phone, address, and website for follow-up communication",
primaryTitle: z.string().min(3).max(30).default("PRESENTATION TITLE").meta({
description:
"Main headline or title for the presentation - should be impactful and attention-grabbing",
}),
secondaryTitle: z
.string()
.min(5)
.max(50)
.default("PROFESSIONAL PRESENTATION")
.meta({
description:
"Subtitle that provides context about the presentation type or purpose",
}),
presentationDate: z.string()
.min(3)
.max(20)
.default("Current Month Year")
.meta({
description: "Date when the presentation is being given or was created",
}),
brandLogo: ImageSchema.default({
__image_url__: "https://via.placeholder.com/40x40/22C55E/FFFFFF?text=L",
__image_prompt__:
"Professional organization logo - clean and modern design",
}).meta({
description: "Logo or brand mark representing the presenting organization",
}),
showDecorations: z.boolean()
.default(true)
.meta({
description: "Whether to display decorative visual elements like background shapes",
}),
contactDetails: z
.object({
phoneNumber: z.string().min(10).max(20).default("+1-234-567-8900"),
physicalAddress: z
.string()
.min(10)
.max(60)
.default("123 Business Ave, City, State 12345"),
websiteUrl: z
.string()
.min(10)
.max(40)
.default("www.yourorganization.com"),
})
.default({
phoneNumber: "+1-234-567-8900",
physicalAddress: "123 Business Ave, City, State 12345",
websiteUrl: "www.yourorganization.com",
})
.meta({
description:
"Contact information including phone, address, and website for follow-up communication",
}),
showNavigationArrow: z.boolean()
.default(true)
.meta({
description: "Whether to show a navigation arrow button for presentation flow",
}),
})
presentationDate: z
.string()
.min(3)
.max(20)
.default("Current Month Year")
.meta({
description: "Date when the presentation is being given or was created",
}),
showDecorations: z.boolean().default(true).meta({
description:
"Whether to display decorative visual elements like background shapes",
}),
showNavigationArrow: z.boolean().default(true).meta({
description:
"Whether to show a navigation arrow button for presentation flow",
}),
});
// Type inference
type SchemaType = z.infer<typeof Schema>;
// Component definition
const ThynkTitleSlide = ({ data }: { data: Partial<SchemaType> }) => {
const {
organizationName,
primaryTitle,
secondaryTitle,
brandLogo,
contactDetails,
presentationDate,
showDecorations,
showNavigationArrow,
} = data;
const { organizationName, primaryTitle, secondaryTitle, brandLogo, contactDetails, presentationDate, showDecorations, showNavigationArrow } = data;
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Header with Logo and Arrow */}
<div className="absolute top-0 left-0 right-0 px-16 py-8 flex justify-between items-center z-20">
{/* Company Logo and Name */}
<div className="flex items-center space-x-3">
{brandLogo?.__image_url__ && (
<div className="w-10 h-10">
<img
src={brandLogo.__image_url__}
alt={brandLogo.__image_prompt__}
className="w-full h-full object-contain"
/>
</div>
)}
{organizationName && (
<span className="text-2xl font-bold text-gray-900">
{organizationName}
</span>
)}
</div>
{/* Arrow Button */}
{showNavigationArrow && (
<div className="w-12 h-12 bg-teal-600 rounded-full flex items-center justify-center">
<svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</div>
)}
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Header with Logo and Arrow */}
<div className="absolute top-0 left-0 right-0 px-16 py-8 flex justify-between items-center z-20">
{/* Company Logo and Name */}
<div className="flex items-center space-x-3">
{brandLogo?.__image_url__ && (
<div className="w-10 h-10">
<img
src={brandLogo.__image_url__}
alt={brandLogo.__image_prompt__}
className="w-full h-full object-contain"
/>
</div>
)}
{organizationName && (
<span className="text-2xl font-bold text-gray-900">
{organizationName}
</span>
)}
</div>
{/* Decorative Circle */}
{showDecorations && (
<div className="absolute top-20 right-16 w-96 h-96 bg-yellow-100 rounded-full opacity-60 z-10"></div>
{/* Arrow Button */}
{showNavigationArrow && (
<div className="w-12 h-12 bg-teal-600 rounded-full flex items-center justify-center">
<svg
className="w-6 h-6 text-white"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M9 5l7 7-7 7"
/>
</svg>
</div>
)}
</div>
{/* Decorative Circle */}
{showDecorations && (
<div className="absolute top-20 right-16 w-96 h-96 bg-yellow-100 rounded-full opacity-60 z-10"></div>
)}
{/* Main Content */}
<div className="relative h-full flex flex-col justify-center px-16">
<div className="">
{/* Main Title */}
{primaryTitle && (
<h1 className="text-4xl lg:text-5xl font-black text-teal-700 leading-none tracking-tight mb-4">
{primaryTitle}
</h1>
)}
{/* Subtitle with Circle Bullet */}
{secondaryTitle && (
<div className="flex items-center space-x-4 mb-12">
<div className="w-4 h-4 bg-teal-600 rounded-full"></div>
<h2 className="text-xl font-bold text-gray-800 tracking-wide">
{secondaryTitle}
</h2>
</div>
)}
</div>
</div>
{/* Footer with Contact Info */}
<div className="absolute bottom-0 left-0 right-0 px-16 py-8 border-t-2 border-gray-300">
<div className="flex justify-between items-center text-gray-700">
<div className="flex space-x-16 text-sm">
{/* Telephone */}
{contactDetails?.phoneNumber && (
<div>
<div className="font-semibold text-gray-900 mb-1">
Telephone
</div>
<div>{contactDetails.phoneNumber}</div>
</div>
)}
{/* Main Content */}
<div className="relative h-full flex flex-col justify-center px-16">
<div className="">
{/* Main Title */}
{primaryTitle && (
<h1 className="text-4xl lg:text-5xl font-black text-teal-700 leading-none tracking-tight mb-4">
{primaryTitle}
</h1>
)}
{/* Address */}
{contactDetails?.physicalAddress && (
<div>
<div className="font-semibold text-gray-900 mb-1">Address</div>
<div>{contactDetails.physicalAddress}</div>
</div>
)}
{/* Subtitle with Circle Bullet */}
{secondaryTitle && (
<div className="flex items-center space-x-4 mb-12">
<div className="w-4 h-4 bg-teal-600 rounded-full"></div>
<h2 className="text-xl font-bold text-gray-800 tracking-wide">
{secondaryTitle}
</h2>
</div>
)}
</div>
</div>
{/* Footer with Contact Info */}
<div className="absolute bottom-0 left-0 right-0 px-16 py-8 border-t-2 border-gray-300">
<div className="flex justify-between items-center text-gray-700">
<div className="flex space-x-16 text-sm">
{/* Telephone */}
{contactDetails?.phoneNumber && (
<div>
<div className="font-semibold text-gray-900 mb-1">Telephone</div>
<div>{contactDetails.phoneNumber}</div>
</div>
)}
{/* Address */}
{contactDetails?.physicalAddress && (
<div>
<div className="font-semibold text-gray-900 mb-1">Address</div>
<div>{contactDetails.physicalAddress}</div>
</div>
)}
{/* Website */}
{contactDetails?.websiteUrl && (
<div>
<div className="font-semibold text-gray-900 mb-1">Website</div>
<div>{contactDetails.websiteUrl}</div>
</div>
)}
</div>
{/* Presentation Date */}
{presentationDate && (
<div className="text-right">
<div className="text-lg font-bold text-gray-900">
{presentationDate}
</div>
</div>
)}
</div>
{/* Website */}
{contactDetails?.websiteUrl && (
<div>
<div className="font-semibold text-gray-900 mb-1">Website</div>
<div>{contactDetails.websiteUrl}</div>
</div>
)}
</div>
{/* Presentation Date */}
{presentationDate && (
<div className="text-right">
<div className="text-lg font-bold text-gray-900">
{presentationDate}
</div>
</div>
)}
</div>
);
</div>
</div>
);
};
export default ThynkTitleSlide;
export default ThynkTitleSlide;

View file

@ -2,153 +2,178 @@ import * as z from "zod";
import { ImageSchema, IconSchema } from "../defaultSchemes";
export const layoutName = "What We Believe Slide";
export const layoutId = "what-we-believe-slide";
export const layoutDescription =
"A slide that describes the organization's vision and mission";
// Schema definition
export const Schema = z.object({
sectionTitle: z.string().min(3).max(30).default("OUR VISION & MISSION").meta({
description:
"Main section heading - can be 'Our Values', 'What We Believe', 'Our Philosophy', or similar",
}),
sectionTitle: z.string()
.min(3)
.max(30)
.default("OUR VISION & MISSION")
.meta({
description: "Main section heading - can be 'Our Values', 'What We Believe', 'Our Philosophy', or similar",
}),
sectionSubtitle: z.string()
.min(10)
.max(60)
.default("GUIDING PRINCIPLES AND CORE BELIEFS")
.meta({
description: "Supporting subtitle that introduces the organization's foundational concepts",
}),
visionStatement: z.string()
.min(30)
.max(200)
.default("We envision a future where innovative solutions transform challenges into opportunities, creating sustainable value for all stakeholders.")
.meta({
description: "Vision statement describing the organization's aspirational future goals and impact",
}),
missionContent: z.object({
missionTitle: z.string().min(3).max(30).default("Our Mission"),
missionDescription: z.string().min(50).max(300).default("To deliver exceptional value through strategic innovation, collaborative partnerships, and unwavering commitment to excellence. We believe in empowering organizations with the tools, insights, and support needed to achieve sustainable growth and meaningful impact in their communities.")
}).default({
missionTitle: "Our Mission",
missionDescription: "To deliver exceptional value through strategic innovation, collaborative partnerships, and unwavering commitment to excellence. We believe in empowering organizations with the tools, insights, and support needed to achieve sustainable growth and meaningful impact in their communities."
}).meta({
description: "Mission section with title and detailed description of organizational purpose and approach",
sectionSubtitle: z
.string()
.min(10)
.max(60)
.default("GUIDING PRINCIPLES AND CORE BELIEFS")
.meta({
description:
"Supporting subtitle that introduces the organization's foundational concepts",
}),
supportingVisual: ImageSchema.default({
__image_url__: "https://images.unsplash.com/photo-1522202176988-66273c2fd55f?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
__image_prompt__: "Diverse team collaborating and planning together in modern workspace"
}).meta({
description: "Visual that represents collaboration, vision, or organizational culture",
visionStatement: z
.string()
.min(30)
.max(200)
.default(
"We envision a future where innovative solutions transform challenges into opportunities, creating sustainable value for all stakeholders."
)
.meta({
description:
"Vision statement describing the organization's aspirational future goals and impact",
}),
showVisualAccents: z.boolean()
.default(true)
.meta({
description: "Whether to display decorative visual accent elements",
}),
missionContent: z
.object({
missionTitle: z.string().min(3).max(30).default("Our Mission"),
missionDescription: z
.string()
.min(50)
.max(300)
.default(
"To deliver exceptional value through strategic innovation, collaborative partnerships, and unwavering commitment to excellence. We believe in empowering organizations with the tools, insights, and support needed to achieve sustainable growth and meaningful impact in their communities."
),
})
.default({
missionTitle: "Our Mission",
missionDescription:
"To deliver exceptional value through strategic innovation, collaborative partnerships, and unwavering commitment to excellence. We believe in empowering organizations with the tools, insights, and support needed to achieve sustainable growth and meaningful impact in their communities.",
})
.meta({
description:
"Mission section with title and detailed description of organizational purpose and approach",
}),
showColorBlocks: z.boolean()
.default(true)
.meta({
description: "Whether to show colored background sections for visual hierarchy",
}),
})
supportingVisual: ImageSchema.default({
__image_url__:
"https://images.unsplash.com/photo-1522202176988-66273c2fd55f?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
__image_prompt__:
"Diverse team collaborating and planning together in modern workspace",
}).meta({
description:
"Visual that represents collaboration, vision, or organizational culture",
}),
showVisualAccents: z.boolean().default(true).meta({
description: "Whether to display decorative visual accent elements",
}),
showColorBlocks: z.boolean().default(true).meta({
description:
"Whether to show colored background sections for visual hierarchy",
}),
});
// Type inference
type SchemaType = z.infer<typeof Schema>;
// Component definition
const WhatWeBelieveSlide = ({ data }: { data: Partial<SchemaType> }) => {
const {
sectionTitle,
sectionSubtitle,
visionStatement,
missionContent,
supportingVisual,
showVisualAccents,
showColorBlocks,
} = data;
const { sectionTitle, sectionSubtitle, visionStatement, missionContent, supportingVisual, showVisualAccents, showColorBlocks } = data;
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Main Content Area */}
<div className="h-full flex">
{/* Left Side - Image */}
<div className="w-2/5 relative">
{supportingVisual?.__image_url__ && (
<div className="absolute inset-8 shadow-lg">
<img
src={supportingVisual.__image_url__}
alt={supportingVisual.__image_prompt__}
className="w-full h-full object-cover rounded-lg"
/>
</div>
)}
{/* Visual Accents */}
{showVisualAccents && (
<>
{/* Decorative circles */}
<div className="absolute top-4 right-4 w-6 h-6 bg-teal-600 rounded-full opacity-70 z-20"></div>
<div className="absolute bottom-8 left-8 w-4 h-4 bg-yellow-300 rounded-full z-20"></div>
</>
)}
</div>
{/* Right Side - Content */}
<div className="w-3/5 relative bg-white px-16 py-12 flex flex-col justify-start">
{/* Title Section */}
<div className="mb-8">
{sectionTitle && (
<h1 className="text-3xl lg:text-4xl font-black text-teal-700 leading-tight mb-4">
{sectionTitle}
</h1>
)}
{sectionSubtitle && (
<p className="text-base font-semibold text-gray-800 tracking-wide mb-8">
{sectionSubtitle}
</p>
)}
</div>
{/* Vision Section */}
{visionStatement && (
<div className="mb-8">
<h2 className="text-xl font-bold text-gray-900 mb-4">Vision</h2>
<p className="text-base leading-relaxed text-gray-700">
{visionStatement}
</p>
</div>
)}
{/* Mission Section with Teal Background */}
{missionContent && (
<div className="bg-teal-600 px-8 py-6 rounded-lg">
{missionContent.missionTitle && (
<h2 className="text-xl font-bold text-white mb-4">
{missionContent.missionTitle}
</h2>
)}
{missionContent.missionDescription && (
<p className="text-base leading-relaxed text-gray-100">
{missionContent.missionDescription}
</p>
)}
</div>
)}
</div>
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Main Content Area */}
<div className="h-full flex">
{/* Left Side - Image */}
<div className="w-2/5 relative">
{supportingVisual?.__image_url__ && (
<div className="absolute inset-8 shadow-lg">
<img
src={supportingVisual.__image_url__}
alt={supportingVisual.__image_prompt__}
className="w-full h-full object-cover rounded-lg"
/>
</div>
)}
{/* Color blocks for visual hierarchy */}
{showColorBlocks && (
<>
{/* Bottom accent strip */}
<div className="absolute bottom-0 left-0 right-0 h-2 bg-teal-600"></div>
{/* Left accent */}
<div className="absolute top-0 left-0 bottom-0 w-2 bg-yellow-300"></div>
</>
)}
{/* Visual Accents */}
{showVisualAccents && (
<>
{/* Decorative circles */}
<div className="absolute top-4 right-4 w-6 h-6 bg-teal-600 rounded-full opacity-70 z-20"></div>
<div className="absolute bottom-8 left-8 w-4 h-4 bg-yellow-300 rounded-full z-20"></div>
</>
)}
</div>
);
{/* Right Side - Content */}
<div className="w-3/5 relative bg-white px-16 py-12 flex flex-col justify-start">
{/* Title Section */}
<div className="mb-8">
{sectionTitle && (
<h1 className="text-3xl lg:text-4xl font-black text-teal-700 leading-tight mb-4">
{sectionTitle}
</h1>
)}
{sectionSubtitle && (
<p className="text-base font-semibold text-gray-800 tracking-wide mb-8">
{sectionSubtitle}
</p>
)}
</div>
{/* Vision Section */}
{visionStatement && (
<div className="mb-8">
<h2 className="text-xl font-bold text-gray-900 mb-4">Vision</h2>
<p className="text-base leading-relaxed text-gray-700">
{visionStatement}
</p>
</div>
)}
{/* Mission Section with Teal Background */}
{missionContent && (
<div className="bg-teal-600 px-8 py-6 rounded-lg">
{missionContent.missionTitle && (
<h2 className="text-xl font-bold text-white mb-4">
{missionContent.missionTitle}
</h2>
)}
{missionContent.missionDescription && (
<p className="text-base leading-relaxed text-gray-100">
{missionContent.missionDescription}
</p>
)}
</div>
)}
</div>
</div>
{/* Color blocks for visual hierarchy */}
{showColorBlocks && (
<>
{/* Bottom accent strip */}
<div className="absolute bottom-0 left-0 right-0 h-2 bg-teal-600"></div>
{/* Left accent */}
<div className="absolute top-0 left-0 bottom-0 w-2 bg-yellow-300"></div>
</>
)}
</div>
);
};
export default WhatWeBelieveSlide;
export default WhatWeBelieveSlide;