diff --git a/servers/fastapi/chroma/chroma.sqlite3 b/servers/fastapi/chroma/chroma.sqlite3 index b39add05..8e0ae9f0 100644 Binary files a/servers/fastapi/chroma/chroma.sqlite3 and b/servers/fastapi/chroma/chroma.sqlite3 differ diff --git a/servers/nextjs/app/(presentation-generator)/context/LayoutContext.tsx b/servers/nextjs/app/(presentation-generator)/context/LayoutContext.tsx index 6d40b623..520844d6 100644 --- a/servers/nextjs/app/(presentation-generator)/context/LayoutContext.tsx +++ b/servers/nextjs/app/(presentation-generator)/context/LayoutContext.tsx @@ -1,315 +1,718 @@ "use client"; -import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react'; -import dynamic from 'next/dynamic'; +import React, { + createContext, + useContext, + useEffect, + useState, + ReactNode, +} from "react"; +import dynamic from "next/dynamic"; import { toast } from "sonner"; -import * as z from 'zod'; -import { useDispatch } from 'react-redux'; -import { setLayoutLoading } from '@/store/slices/presentationGeneration'; +import * as z from "zod"; +import { useDispatch } from "react-redux"; +import { setLayoutLoading } from "@/store/slices/presentationGeneration"; +import * as Babel from "@babel/standalone"; export interface LayoutInfo { - id: string; - name?: string; - description?: string; - json_schema: any; - groupName: string; + id: string; + name?: string; + description?: string; + json_schema: any; + groupName: string; } export interface GroupSetting { - description: string; - ordered: boolean; - default?: boolean; + description: string; + ordered: boolean; + default?: boolean; } export interface GroupedLayoutsResponse { - groupName: string; - files: string[]; - settings: GroupSetting | null; + groupName: string; + files: string[]; + settings: GroupSetting | null; } export interface LayoutData { - layoutsById: Map; - layoutsByGroup: Map>; - groupSettings: Map; - fileMap: Map; - groupedLayouts: Map; - layoutSchema: LayoutInfo[]; + layoutsById: Map; + layoutsByGroup: Map>; + groupSettings: Map; + fileMap: Map; + groupedLayouts: Map; + layoutSchema: LayoutInfo[]; } export interface LayoutContextType { - getLayoutById: (layoutId: string) => LayoutInfo | null; - getLayoutByIdAndGroup: (layoutId: string, groupName: string) => LayoutInfo | null; - getLayoutsByGroup: (groupName: string) => LayoutInfo[]; - getGroupSetting: (groupName: string) => GroupSetting | null; - getAllGroups: () => string[]; - getAllLayouts: () => LayoutInfo[]; + getLayoutById: (layoutId: string) => LayoutInfo | null; + getLayoutByIdAndGroup: ( + layoutId: string, + groupName: string + ) => LayoutInfo | null; + getLayoutsByGroup: (groupName: string) => LayoutInfo[]; + getGroupSetting: (groupName: string) => GroupSetting | null; + getAllGroups: () => string[]; + getAllLayouts: () => LayoutInfo[]; - loading: boolean; - error: string | null; - getLayout: (layoutId: string) => React.ComponentType<{ data: any }> | null; - isPreloading: boolean; - cacheSize: number; - refetch: () => Promise; + loading: boolean; + error: string | null; + getLayout: (layoutId: string) => React.ComponentType<{ data: any }> | null; + isPreloading: boolean; + cacheSize: number; + refetch: () => Promise; } const LayoutContext = createContext(undefined); const layoutCache = new Map>(); -const createCacheKey = (groupName: string, fileName: string): string => `${groupName}/${fileName}`; +const createCacheKey = (groupName: string, fileName: string): string => + `${groupName}/${fileName}`; -export const LayoutProvider: React.FC<{ children: ReactNode }> = ({ children }) => { - const [layoutData, setLayoutData] = useState(null); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(null); - const [isPreloading, setIsPreloading] = useState(false); - const dispatch = useDispatch(); +// Extract Babel compilation logic into a utility function +const compileCustomLayout = (layoutCode: string, React: any, z: any) => { + 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 buildData = async (groupedLayoutsData: GroupedLayoutsResponse[]) => { - const layouts: LayoutInfo[] = []; +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 layoutsById = new Map(); - const layoutsByGroup = new Map>(); - const groupSettingsMap = new Map(); - const fileMap = new Map(); - const groupedLayouts = new Map(); +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", + }) +}) - // Start preloading process - setIsPreloading(true); +const Schema = titleSlideWithDecorativeElementsSchema - try { - for (const groupData of groupedLayoutsData) { - - // Initialize group - if (!layoutsByGroup.has(groupData.groupName)) { - layoutsByGroup.set(groupData.groupName, new Set()); - } - - // group settings or default settings - const settings = groupData.settings || { - description: `${groupData.groupName} presentation layouts`, - ordered: false, - default: false - }; - - groupSettingsMap.set(groupData.groupName, settings); - const groupLayouts: LayoutInfo[] = []; - - for (const fileName of groupData.files) { - try { - const file = fileName.replace('.tsx', '').replace('.ts', ''); - - const module = await import(`@/presentation-layouts/${groupData.groupName}/${file}`); - - if (!module.default) { - toast.error(`${file} has no default export`, { - description: 'Please ensure the layout file exports a default component', - }); - console.warn(`❌ ${file} has no default export`); - continue; - } - - if (!module.Schema) { - toast.error(`${file} has no Schema export`, { - description: 'Please ensure the layout file exports a Schema', - }); - console.warn(`❌ ${file} has no Schema export`); - continue; - } - - // Cache the layout component immediately after import - const cacheKey = createCacheKey(groupData.groupName, fileName); - if (!layoutCache.has(cacheKey)) { - layoutCache.set(cacheKey, module.default); - } - - const originalLayoutId = module.layoutId || file.toLowerCase().replace(/layout$/, ''); - const uniqueKey = `${groupData.groupName}:${originalLayoutId}`; - const layoutName = module.layoutName || file.replace(/([A-Z])/g, ' $1').trim(); - const layoutDescription = module.layoutDescription || `${layoutName} layout for presentations`; - - const jsonSchema = z.toJSONSchema(module.Schema, { - override: (ctx) => { - delete ctx.jsonSchema.default; - }, - }); - - const layout: LayoutInfo = { - id: uniqueKey, - name: layoutName, - description: layoutDescription, - json_schema: jsonSchema, - groupName: groupData.groupName, - }; - - layoutsById.set(uniqueKey, layout); - layoutsByGroup.get(groupData.groupName)!.add(uniqueKey); - fileMap.set(uniqueKey, { fileName, groupName: groupData.groupName }); - groupLayouts.push(layout); - layouts.push(layout); - - } catch (error) { - console.error(`💥 Error extracting schema for ${fileName} from ${groupData.groupName}:`, error); - } - } - - // Cache grouped layouts - groupedLayouts.set(groupData.groupName, groupLayouts); - } - } finally { - setIsPreloading(false); - } - - return { - layoutsById, - layoutsByGroup, - groupSettings: groupSettingsMap, - fileMap, - groupedLayouts, - layoutSchema: layouts - }; - }; - - - const loadLayouts = async () => { - try { - setLoading(true); - setError(null); - dispatch(setLayoutLoading(true)); - - - const layoutResponse = await fetch('/api/layouts'); - - if (!layoutResponse.ok) { - throw new Error(`Failed to fetch layouts: ${layoutResponse.statusText}`); - } - - const groupedLayoutsData: GroupedLayoutsResponse[] = await layoutResponse.json(); - - - if (!groupedLayoutsData || groupedLayoutsData.length === 0) { - console.warn('⚠️ API returned empty data'); - setError('No layout groups found'); - return; - } - - const data = await buildData(groupedLayoutsData); - setLayoutData(data); - - // The preloading is now handled within buildData - } catch (err: unknown) { - const errorMessage = err instanceof Error ? err.message : 'Failed to load layouts'; - setError(errorMessage); - console.error('💥 Error loading layouts:', err); - } finally { - dispatch(setLayoutLoading(false)); - setLoading(false); - } - }; - - const getLayout = (layoutId: string): React.ComponentType<{ data: any }> | null => { - if (!layoutData) return null; - - let fileInfo: { fileName: string; groupName: string } | undefined; - - // Search through all fileMap entries to find the layout - for (const [key, info] of Array.from(layoutData.fileMap.entries())) { - if (key === layoutId) { - fileInfo = info; - break; - } - } - - if (!fileInfo) { - console.warn(`No file info found for layout: ${layoutId}`); - return null; - } - - const cacheKey = createCacheKey(fileInfo.groupName, fileInfo.fileName); - - // Return cached layout if available - if (layoutCache.has(cacheKey)) { - return layoutCache.get(cacheKey)!; - } - // Create and cache layout if not available - const file = fileInfo.fileName.replace('.tsx', '').replace('.ts', ''); - const Layout = dynamic( - () => import(`@/presentation-layouts/${fileInfo.groupName}/${file}`), - { - loading: () =>
, - ssr: false, - } - ) as React.ComponentType<{ data: any }>; - - layoutCache.set(cacheKey, Layout); - return Layout; - }; - - // Updated accessor methods to handle group-specific lookups - const getLayoutById = (layoutId: string): LayoutInfo | null => { - if (!layoutData) return null; - - // Search through all entries to find the layout (since we don't know the group) - for (const [key, layout] of Array.from(layoutData.layoutsById.entries())) { - if (key === layoutId) { - return layout; - } - } - return null; - }; - - const getLayoutByIdAndGroup = (layoutId: string, groupName: string): LayoutInfo | null => { - if (!layoutData) return null; - return layoutData.layoutsById.get(layoutId) || null; - }; - - const getLayoutsByGroup = (groupName: string): LayoutInfo[] => { - return layoutData?.groupedLayouts.get(groupName) || []; - }; - - const getGroupSetting = (groupName: string): GroupSetting | null => { - return layoutData?.groupSettings.get(groupName) || null; - }; - - const getAllGroups = (): string[] => { - return layoutData ? Array.from(layoutData.groupSettings.keys()) : []; - }; - - const getAllLayouts = (): LayoutInfo[] => { - return layoutData?.layoutSchema || []; - }; - - // Load layouts on mount - useEffect(() => { - loadLayouts(); - }, []); - - const contextValue: LayoutContextType = { - - getLayoutById, - getLayoutByIdAndGroup, - getLayoutsByGroup, - getGroupSetting, - getAllGroups, - getAllLayouts, - - loading, - error, - getLayout, - isPreloading, - cacheSize: layoutCache.size, - refetch: loadLayouts, - }; +const TitleSlideWithDecorativeElementsLayout = ({ data: slideData }) => { + // Split main title by newlines for proper rendering + const titleLines = (slideData?.mainTitle || 'STRATEGY DECK').split('\\n') return ( - - {children} - + <> + {/* Import Google Fonts */} + + + +
+ {/* Bottom horizontal line */} +
+ + {/* Upper horizontal line */} +
+ + {/* Right vertical line */} +
+ + {/* Upper left circular element */} +
+ + {/* Lower right circular element */} +
+ + {/* Left decorative curved shape */} +
+ + + + + + + + +
+ + {/* Right decorative curved shape */} +
+ + + + + + + + +
+ + {/* Small icon/logo near company name */} +
+ {slideData?.logo?.__image_prompt__ +
+ + {/* Company name */} +
+

+ {slideData?.companyName || 'AROWWAI INDUSTRIES'} +

+
+ + {/* Main title */} +
+

+ {titleLines.map((line, index) => ( + + {line} + {index < titleLines.length - 1 &&
} +
+ ))} +

+
+ + {/* Subtitle */} +
+

+ {slideData?.subtitle || 'STRATEGIES FOR GROWTH AND INNOVATION'} +

+
+ + {/* Bottom left text */} +
+

+ {slideData?.authorText || 'BY GROUP 1'} +

+
+
+ + ) +} + +// Return the component + +`; + const compiled = Babel.transform(jsxCode, { + presets: [ + ["react", { runtime: "classic" }], + ["typescript", { isTSX: true, allExtensions: true }], + ], + sourceType: "script", + }).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 + }; + ` + ); + + return factory(React, z); +}; + +export const LayoutProvider: React.FC<{ + children: ReactNode; + presentationId?: string; +}> = ({ + children, + presentationId = "6038f1cb-80cb-448c-83cc-f6cb96081943", // default value +}) => { + const [layoutData, setLayoutData] = useState(null); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [isPreloading, setIsPreloading] = useState(false); + const dispatch = useDispatch(); + + console.log("🔍 layoutData", layoutData); + + const buildData = async (groupedLayoutsData: GroupedLayoutsResponse[]) => { + const layouts: LayoutInfo[] = []; + + const layoutsById = new Map(); + const layoutsByGroup = new Map>(); + const groupSettingsMap = new Map(); + const fileMap = new Map(); + const groupedLayouts = new Map(); + + // Start preloading process + setIsPreloading(true); + + try { + for (const groupData of groupedLayoutsData) { + // Initialize group + if (!layoutsByGroup.has(groupData.groupName)) { + layoutsByGroup.set(groupData.groupName, new Set()); + } + + // group settings or default settings + const settings = groupData.settings || { + description: `${groupData.groupName} presentation layouts`, + ordered: false, + default: false, + }; + + groupSettingsMap.set(groupData.groupName, settings); + const groupLayouts: LayoutInfo[] = []; + + for (const fileName of groupData.files) { + try { + const file = fileName.replace(".tsx", "").replace(".ts", ""); + + const module = await import( + `@/presentation-layouts/${groupData.groupName}/${file}` + ); + console.log("🔍 module", module); + + if (!module.default) { + toast.error(`${file} has no default export`, { + description: + "Please ensure the layout file exports a default component", + }); + console.warn(`❌ ${file} has no default export`); + continue; + } + + if (!module.Schema) { + toast.error(`${file} has no Schema export`, { + description: "Please ensure the layout file exports a Schema", + }); + console.warn(`❌ ${file} has no Schema export`); + continue; + } + + // Cache the layout component immediately after import + const cacheKey = createCacheKey(groupData.groupName, fileName); + if (!layoutCache.has(cacheKey)) { + layoutCache.set(cacheKey, module.default); + } + + const originalLayoutId = + module.layoutId || file.toLowerCase().replace(/layout$/, ""); + const uniqueKey = `${groupData.groupName}:${originalLayoutId}`; + const layoutName = + module.layoutName || file.replace(/([A-Z])/g, " $1").trim(); + const layoutDescription = + module.layoutDescription || + `${layoutName} layout for presentations`; + + const jsonSchema = z.toJSONSchema(module.Schema, { + override: (ctx) => { + delete ctx.jsonSchema.default; + }, + }); + + const layout: LayoutInfo = { + id: uniqueKey, + name: layoutName, + description: layoutDescription, + json_schema: jsonSchema, + groupName: groupData.groupName, + }; + + layoutsById.set(uniqueKey, layout); + layoutsByGroup.get(groupData.groupName)!.add(uniqueKey); + fileMap.set(uniqueKey, { + fileName, + groupName: groupData.groupName, + }); + groupLayouts.push(layout); + layouts.push(layout); + } catch (error) { + console.error( + `💥 Error extracting schema for ${fileName} from ${groupData.groupName}:`, + error + ); + } + } + + // Cache grouped layouts + groupedLayouts.set(groupData.groupName, groupLayouts); + } + } finally { + setIsPreloading(false); + } + + return { + layoutsById, + layoutsByGroup, + groupSettings: groupSettingsMap, + fileMap, + groupedLayouts, + layoutSchema: layouts, + }; + }; + + const loadLayouts = async () => { + try { + setLoading(true); + setError(null); + dispatch(setLayoutLoading(true)); + + const layoutResponse = await fetch("/api/layouts"); + + if (!layoutResponse.ok) { + throw new Error( + `Failed to fetch layouts: ${layoutResponse.statusText}` + ); + } + + const groupedLayoutsData: GroupedLayoutsResponse[] = + await layoutResponse.json(); + + if (!groupedLayoutsData || groupedLayoutsData.length === 0) { + console.warn("⚠️ API returned empty data"); + setError("No layout groups found"); + return; + } + + const data = await buildData(groupedLayoutsData); + const customLayouts = await LoadCustomLayouts(presentationId); + const combinedData = { + layoutsById: mergeMaps(data.layoutsById, customLayouts.layoutsById), + layoutsByGroup: mergeMaps( + data.layoutsByGroup, + customLayouts.layoutsByGroup + ), + groupSettings: mergeMaps( + data.groupSettings, + customLayouts.groupSettings + ), + fileMap: mergeMaps(data.fileMap, customLayouts.fileMap), + groupedLayouts: mergeMaps( + data.groupedLayouts, + customLayouts.groupedLayouts + ), + layoutSchema: [...data.layoutSchema, ...customLayouts.layoutSchema], + }; + + setLayoutData(combinedData); + + // The preloading is now handled within buildData + } catch (err: unknown) { + const errorMessage = + err instanceof Error ? err.message : "Failed to load layouts"; + setError(errorMessage); + console.error("💥 Error loading layouts:", err); + } finally { + dispatch(setLayoutLoading(false)); + setLoading(false); + } + }; + + function mergeMaps(map1: Map, map2: Map): Map { + const merged = new Map(map1); + map2.forEach((value, key) => { + merged.set(key, value); + }); + return merged; + } + + const LoadCustomLayouts = async (presentationId: string) => { + const layouts: LayoutInfo[] = []; + + const layoutsById = new Map(); + const layoutsByGroup = new Map>(); + const groupSettingsMap = new Map(); + const fileMap = new Map(); + const groupedLayouts = new Map(); + + const customLayoutResponse = await fetch( + `/api/v1/ppt/layout-management/get-layouts/${presentationId}` ); + const customLayoutsData = await customLayoutResponse.json(); + const allLayout = customLayoutsData.layouts; + + const settings = { + description: `Custom presentation layouts`, + ordered: false, + default: false, + }; + + groupSettingsMap.set(`custom-${presentationId}`, settings); + const groupLayouts: LayoutInfo[] = []; + const groupName = `custom-${presentationId}`; + if (!layoutsByGroup.has(groupName)) { + layoutsByGroup.set(groupName, new Set()); + } + for (const i of allLayout) { + try { + /* ---------- 1. compile JSX to plain script ------------------ */ + const module = compileCustomLayout(i.layout_code, React, z); + + if (!module.default) { + toast.error(`Custom Layout has no default export`, { + description: + "Please ensure the layout file exports a default component", + }); + console.warn(`❌ Custom Layout has no default export`); + continue; + } + + if (!module.Schema) { + toast.error(`Custom Layout has no Schema export`, { + description: "Please ensure the layout file exports a Schema", + }); + console.warn(`❌ Custom Layout has no Schema export`); + continue; + } + const cacheKey = createCacheKey( + `custom-${presentationId}`, + i.layout_name + ); + if (!layoutCache.has(cacheKey)) { + layoutCache.set(cacheKey, module.default); + } + + const originalLayoutId = + module.layoutId || i.layout_name.toLowerCase().replace(/layout$/, ""); + const uniqueKey = `${`custom-${presentationId}`}:${originalLayoutId}`; + const layoutName = + module.layoutName || i.layout_name.replace(/([A-Z])/g, " $1").trim(); + const layoutDescription = + module.layoutDescription || `${layoutName} layout for presentations`; + + const jsonSchema = z.toJSONSchema(module.Schema, { + override: (ctx) => { + delete ctx.jsonSchema.default; + }, + }); + + const layout: LayoutInfo = { + id: uniqueKey, + name: layoutName, + description: layoutDescription, + json_schema: jsonSchema, + groupName: groupName, + }; + + layoutsById.set(uniqueKey, layout); + layoutsByGroup.get(groupName)!.add(uniqueKey); + fileMap.set(uniqueKey, { + fileName: i.layout_name, + groupName: groupName, + }); + groupLayouts.push(layout); + layouts.push(layout); + } catch (err: any) { + console.error("Compilation error:", err); + } + } + // Cache grouped layouts + groupedLayouts.set(groupName, groupLayouts); + + return { + layoutsById, + layoutsByGroup, + groupSettings: groupSettingsMap, + fileMap, + groupedLayouts, + layoutSchema: layouts, + }; + }; + + const getLayout = ( + layoutId: string + ): React.ComponentType<{ data: any }> | null => { + if (!layoutData) return null; + + let fileInfo: { fileName: string; groupName: string } | undefined; + + // Search through all fileMap entries to find the layout + for (const [key, info] of Array.from(layoutData.fileMap.entries())) { + if (key === layoutId) { + fileInfo = info; + break; + } + } + + if (!fileInfo) { + console.warn(`No file info found for layout: ${layoutId}`); + return null; + } + + const cacheKey = createCacheKey(fileInfo.groupName, fileInfo.fileName); + + // Return cached layout if available + if (layoutCache.has(cacheKey)) { + return layoutCache.get(cacheKey)!; + } + // Create and cache layout if not available + const file = fileInfo.fileName.replace(".tsx", "").replace(".ts", ""); + const Layout = dynamic( + () => import(`@/presentation-layouts/${fileInfo.groupName}/${file}`), + { + loading: () => ( +
+ ), + ssr: false, + } + ) as React.ComponentType<{ data: any }>; + + layoutCache.set(cacheKey, Layout); + return Layout; + }; + + // Updated accessor methods to handle group-specific lookups + const getLayoutById = (layoutId: string): LayoutInfo | null => { + if (!layoutData) return null; + + // Search through all entries to find the layout (since we don't know the group) + for (const [key, layout] of Array.from(layoutData.layoutsById.entries())) { + if (key === layoutId) { + return layout; + } + } + return null; + }; + + const getLayoutByIdAndGroup = ( + layoutId: string, + groupName: string + ): LayoutInfo | null => { + if (!layoutData) return null; + return layoutData.layoutsById.get(layoutId) || null; + }; + + const getLayoutsByGroup = (groupName: string): LayoutInfo[] => { + return layoutData?.groupedLayouts.get(groupName) || []; + }; + + const getGroupSetting = (groupName: string): GroupSetting | null => { + return layoutData?.groupSettings.get(groupName) || null; + }; + + const getAllGroups = (): string[] => { + return layoutData ? Array.from(layoutData.groupSettings.keys()) : []; + }; + + const getAllLayouts = (): LayoutInfo[] => { + return layoutData?.layoutSchema || []; + }; + + // Load layouts on mount + useEffect(() => { + loadLayouts(); + }, [presentationId]); // Add presentationId to dependency array + + const contextValue: LayoutContextType = { + getLayoutById, + getLayoutByIdAndGroup, + getLayoutsByGroup, + getGroupSetting, + getAllGroups, + getAllLayouts, + + loading, + error, + getLayout, + isPreloading, + cacheSize: layoutCache.size, + refetch: loadLayouts, + }; + + return ( + + {children} + + ); }; export const useLayout = (): LayoutContextType => { - const context = useContext(LayoutContext); - if (context === undefined) { - throw new Error('useLayout must be used within a LayoutProvider'); - } - return context; -}; \ No newline at end of file + const context = useContext(LayoutContext); + if (context === undefined) { + throw new Error("useLayout must be used within a LayoutProvider"); + } + return context; +}; diff --git a/servers/nextjs/app/(presentation-generator)/outline/components/GroupLayouts.tsx b/servers/nextjs/app/(presentation-generator)/outline/components/GroupLayouts.tsx index 0c9f259b..fcb35750 100644 --- a/servers/nextjs/app/(presentation-generator)/outline/components/GroupLayouts.tsx +++ b/servers/nextjs/app/(presentation-generator)/outline/components/GroupLayouts.tsx @@ -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 = ({ group, onSelectLayoutGroup, selectedLayoutGroup }) => { - const { layoutGroup } = useGroupLayoutLoader(group.id); - return ( -
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 && ( -
- -
- )} - -
-
- {group.name} -
-

- {group.description} -

-
- - {/* Layout previews */} -
- {layoutGroup && layoutGroup?.layouts.slice(0, 4).map((layout: any, index: number) => { - const { component: LayoutComponent, sampleData, layoutId } = layout - return ( -
-
-
- -
-
- ) - })} -
- -
- {layoutGroup?.layouts.length} layouts - - {group.ordered ? 'Structured' : 'Flexible'} - -
+const GroupLayouts: React.FC = ({ + group, + onSelectLayoutGroup, + selectedLayoutGroup, +}) => { + const { layoutGroup } = useGroupLayoutLoader(group.id); + return ( +
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 && ( +
+
- ); + )} + +
+
+ {group.name} +
+

{group.description}

+
+ + {/* Layout previews */} +
+ {layoutGroup && + layoutGroup?.layouts.slice(0, 4).map((layout: any, index: number) => { + const { component: LayoutComponent, sampleData, layoutId } = layout; + return ( +
+
+
+ +
+
+ ); + })} +
+ +
+ {layoutGroup?.layouts.length} layouts + + {group.ordered ? "Structured" : "Flexible"} + +
+
+ ); }; export default GroupLayouts; diff --git a/servers/nextjs/app/custom-layout/page.tsx b/servers/nextjs/app/custom-layout/page.tsx index f6d518aa..357eae4e 100644 --- a/servers/nextjs/app/custom-layout/page.tsx +++ b/servers/nextjs/app/custom-layout/page.tsx @@ -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, diff --git a/servers/nextjs/app/layout-preview/CustomLayouts.tsx b/servers/nextjs/app/layout-preview/CustomLayouts.tsx new file mode 100644 index 00000000..0bed72b9 --- /dev/null +++ b/servers/nextjs/app/layout-preview/CustomLayouts.tsx @@ -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 */} + + + +
+ {/* Bottom horizontal line */} +
+ + {/* Upper horizontal line */} +
+ + {/* Right vertical line */} +
+ + {/* Upper left circular element */} +
+ + {/* Lower right circular element */} +
+ + {/* Left decorative curved shape */} +
+ + + + + + + + +
+ + {/* Right decorative curved shape */} +
+ + + + + + + + +
+ + {/* Small icon/logo near company name */} +
+ {slideData?.logo?.__image_prompt__ +
+ + {/* Company name */} +
+

+ {slideData?.companyName || 'AROWWAI INDUSTRIES'} +

+
+ + {/* Main title */} +
+

+ {titleLines.map((line, index) => ( + + {line} + {index < titleLines.length - 1 &&
} +
+ ))} +

+
+ + {/* Subtitle */} +
+

+ {slideData?.subtitle || 'STRATEGIES FOR GROWTH AND INNOVATION'} +

+
+ + {/* Bottom left text */} +
+

+ {slideData?.authorText || 'BY GROUP 1'} +

+
+
+ + ) +} + +// Return the component + +`; + +interface Layout { + presentation_id: string; + layout_id: string; + layout_name: string; + layout_code: string; +} + +const CustomLayouts = () => { + const [layouts, setLayouts] = useState([]); + const [component, setComponent] = useState | 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(() => () => ( +
+

Compilation Error

+
{err.message}
+
+ )); + } + }, [layouts]); + z; + + return ( +
+

Custom Layout Renderer

+ {component && ( +
+

Rendered Component:

+ {React.createElement(component, { data: {} })} +
+ )} + {layouts.map((layout) => ( +
+

{layout.layout_name}

+
+ View Code +
+              {layout.layout_code}
+            
+
+
+ ))} +
+ ); +}; + +export default CustomLayouts; diff --git a/servers/nextjs/app/layout-preview/page.tsx b/servers/nextjs/app/layout-preview/page.tsx index 4f1d114c..55cd73b9 100644 --- a/servers/nextjs/app/layout-preview/page.tsx +++ b/servers/nextjs/app/layout-preview/page.tsx @@ -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 - } - - // Handle error state - if (error) { - return - } - - // Handle empty state - if (layoutGroups.length === 0 || layouts.length === 0) { - return - } - - return ( -
-
-
-
-

Layout Preview

-

- {layoutGroups.length} groups • {layouts.length} layouts -

- -
-
- - {/* Group Navigation Cards */} -
-
-
- {layoutGroups.map((group) => ( - router.push(`/layout-preview/${group.groupName}`)} - > -
-
-

- {group.groupName} -

-
- - {group.layouts.length} - - -
-
-

- {group.settings.description} -

-
- - {group.layouts.length} layout{group.layouts.length !== 1 ? 's' : ''} - - {group.settings.default && ( - - Default - - )} -
-
-
- ))} -
-
-
-
+ // Handle loading state + if (loading) { + return ; + } + // Handle error state + if (error) { + return ; + } + // Handle empty state + if (layoutGroups.length === 0 || layouts.length === 0) { + return ; + } + return ( +
+
+
+
+

Layout Preview

+

+ {layoutGroups.length} groups • {layouts.length} layouts +

+
- ) -} -export default LayoutPreview + {/* Group Navigation Cards */} +
+
+
+ {layoutGroups.map((group) => ( + + router.push(`/layout-preview/${group.groupName}`) + } + > +
+
+

+ {group.groupName} +

+
+ + {group.layouts.length} + + +
+
+

+ {group.settings.description} +

+
+ + {group.layouts.length} layout + {group.layouts.length !== 1 ? "s" : ""} + + {group.settings.default && ( + + Default + + )} +
+
+
+ ))} + router.push(`/custom-layout`)} + > +
+
+

+ Custom Layouts +

+
+ +
+
+

+ Custom layouts for your presentations +

+
+
+
+
+
+ +
+
+ ); +}; + +export default LayoutPreview; diff --git a/servers/nextjs/package-lock.json b/servers/nextjs/package-lock.json index e8d4af97..54a0c947 100644 --- a/servers/nextjs/package-lock.json +++ b/servers/nextjs/package-lock.json @@ -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", diff --git a/servers/nextjs/package.json b/servers/nextjs/package.json index 7c300b31..2db9ff0a 100644 --- a/servers/nextjs/package.json +++ b/servers/nextjs/package.json @@ -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" }, diff --git a/servers/nextjs/presentation-layouts/ExampleSlideLayout.tsx b/servers/nextjs/presentation-layouts/ExampleSlideLayout.tsx index 583d6524..89ed946b 100644 --- a/servers/nextjs/presentation-layouts/ExampleSlideLayout.tsx +++ b/servers/nextjs/presentation-layouts/ExampleSlideLayout.tsx @@ -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; export default function ExampleSlideLayout({ data }: { data: SchemaType }) { - const { title, subtitle, metrics, chartImage, trendIcon } = data; - return ( -
-
- {title &&

{title}

} - {subtitle &&

{subtitle}

} -
+ const { title, subtitle, metrics, chartImage, trendIcon } = data; + return ( +
+
+ {title &&

{title}

} + {subtitle &&

{subtitle}

} +
-
- {chartImage?.__image_url__ && ( -
- {chartImage.__image_prompt__} -
- )} +
+ {chartImage?.__image_url__ && ( +
+ {chartImage.__image_prompt__} +
+ )} - {metrics && metrics.length > 0 && ( -
-

Key Metrics

- {metrics.map((metric, index) => ( -
-
- {metric.label} - {trendIcon?.__icon_url__ && ( - {metric.trend} - )} -
- - {metric.value} - -
- ))} -
- )} -
-
- ); -} \ No newline at end of file + {metrics && metrics.length > 0 && ( +
+

Key Metrics

+ {metrics.map((metric, index) => ( +
+
+ {metric.label} + {trendIcon?.__icon_url__ && ( + {metric.trend} + )} +
+ + {metric.value} + +
+ ))} +
+ )} + +
+ ); +} diff --git a/servers/nextjs/presentation-layouts/professional/AboutUsSlide.tsx b/servers/nextjs/presentation-layouts/professional/AboutUsSlide.tsx index 5b6d9465..9688c8d2 100644 --- a/servers/nextjs/presentation-layouts/professional/AboutUsSlide.tsx +++ b/servers/nextjs/presentation-layouts/professional/AboutUsSlide.tsx @@ -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; // Component definitionz const AboutUsSlide = ({ data }: { data: Partial }) => { + const { + sectionTitle, + sectionSubtitle, + organizationDescription, + additionalContext, + featuredImage, + showVisualAccents, + showColorBlocks, + showAccentSquare, + } = data; - const { sectionTitle, sectionSubtitle, organizationDescription, additionalContext, featuredImage, showVisualAccents, showColorBlocks, showAccentSquare } = data; + return ( +
+ {/* Main Content Area */} +
+ {/* Left Side - Content */} +
+ {/* Title Section */} +
+ {sectionTitle && ( +

+ {sectionTitle} +

+ )} - return ( -
- {/* Main Content Area */} -
- {/* Left Side - Content */} -
- {/* Title Section */} -
- {sectionTitle && ( -

- {sectionTitle} -

- )} + {sectionSubtitle && ( +

+ {sectionSubtitle} +

+ )} - {sectionSubtitle && ( -

- {sectionSubtitle} -

- )} + {/* Decorative gray line */} +
+
- {/* Decorative gray line */} -
-
+ {/* Description Text */} + {organizationDescription && ( +

+ {organizationDescription} +

+ )} - {/* Description Text */} - {organizationDescription && ( -

- {organizationDescription} -

- - )} - - {/* Additional Text */} - {additionalContext && ( -
-

- {additionalContext} -

-
- )} -
- - {/* Right Side - Image and Decorative Elements */} -
- {/* Yellow Square - Top Right */} - {showAccentSquare && ( -
- )} - - {/* Decorative Circle - On Yellow Square */} - {showVisualAccents && ( -
- )} - - {/* Business Image - Left positioned */} - {featuredImage?.__image_url__ && ( -
- {featuredImage.__image_prompt__} -
- )} - - {/* Teal Accent Areas */} - {showColorBlocks && ( - <> - {/* Vertical Teal Strip - Center */} -
- - - )} -
+ {/* Additional Text */} + {additionalContext && ( +
+

+ {additionalContext} +

+ )}
- ); + + {/* Right Side - Image and Decorative Elements */} +
+ {/* Yellow Square - Top Right */} + {showAccentSquare && ( +
+ )} + + {/* Decorative Circle - On Yellow Square */} + {showVisualAccents && ( +
+ )} + + {/* Business Image - Left positioned */} + {featuredImage?.__image_url__ && ( +
+ {featuredImage.__image_prompt__} +
+ )} + + {/* Teal Accent Areas */} + {showColorBlocks && ( + <> + {/* Vertical Teal Strip - Center */} +
+ + )} +
+
+
+ ); }; -export default AboutUsSlide; \ No newline at end of file +export default AboutUsSlide; diff --git a/servers/nextjs/presentation-layouts/professional/BusinessModelSlide.tsx b/servers/nextjs/presentation-layouts/professional/BusinessModelSlide.tsx index 534aa905..9b1fa1d5 100644 --- a/servers/nextjs/presentation-layouts/professional/BusinessModelSlide.tsx +++ b/servers/nextjs/presentation-layouts/professional/BusinessModelSlide.tsx @@ -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; // Component definition const BusinessModelSlide = ({ data }: { data: Partial }) => { + const { + sectionTitle, + sectionSubtitle, + modelDescription, + headerVisual, + chartData, + showChart, + showVisualAccents, + } = data; - const { sectionTitle, sectionSubtitle, modelDescription, headerVisual, chartData, showChart, showVisualAccents } = data; + return ( +
+ {/* Header Image Section */} + {headerVisual?.__image_url__ && ( +
+ {headerVisual.__image_prompt__} +
+
+ )} - return ( -
- {/* Header Image Section */} - {headerVisual?.__image_url__ && ( -
- {headerVisual.__image_prompt__} -
-
+ {/* Main Content Area */} +
+ {/* Left Side - Content */} +
+ {/* Title Section */} +
+ {sectionTitle && ( +

+ {sectionTitle} +

)} - {/* Main Content Area */} -
- {/* Left Side - Content */} -
- {/* Title Section */} -
- {sectionTitle && ( -

- {sectionTitle} -

- )} + {sectionSubtitle && ( +

+ {sectionSubtitle} +

+ )} +
- {sectionSubtitle && ( -

- {sectionSubtitle} -

- )} -
- - {/* Model Description */} - {modelDescription && ( -
-

- {modelDescription} -

-
- )} - - {/* Visual Accents */} - {showVisualAccents && ( - <> -
-
- - )} -
- - {/* Right Side - Chart */} -
- {showChart && chartData && chartData.length > 0 && ( -
- - - - - - - } /> - - - - -
- )} -
+ {/* Model Description */} + {modelDescription && ( +
+

+ {modelDescription} +

+ )} - {/* Bottom accent strip */} -
+ {/* Visual Accents */} + {showVisualAccents && ( + <> +
+
+ + )}
- ); + + {/* Right Side - Chart */} +
+ {showChart && chartData && chartData.length > 0 && ( +
+ + + + + + } /> + + + +
+ )} +
+
+ + {/* Bottom accent strip */} +
+
+ ); }; -export default BusinessModelSlide; \ No newline at end of file +export default BusinessModelSlide; diff --git a/servers/nextjs/presentation-layouts/professional/MarketSizeSlide.tsx b/servers/nextjs/presentation-layouts/professional/MarketSizeSlide.tsx index df920b88..0ac39595 100644 --- a/servers/nextjs/presentation-layouts/professional/MarketSizeSlide.tsx +++ b/servers/nextjs/presentation-layouts/professional/MarketSizeSlide.tsx @@ -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; // Component definition const MarketSizeSlide = ({ data }: { data: Partial }) => { + const { + sectionTitle, + sectionSubtitle, + marketDefinitions, + visualRepresentation, + showYellowUnderline, + showVisualAccents, + } = data; - const { sectionTitle, sectionSubtitle, marketDefinitions, visualRepresentation, showYellowUnderline, showVisualAccents } = data; + return ( +
+ {/* Main Content Area */} +
+ {/* Left Side - Content */} +
+ {/* Title Section */} +
+ {sectionTitle && ( +

+ {sectionTitle} +

+ )} - return ( -
- {/* Main Content Area */} -
- {/* Left Side - Content */} -
- {/* Title Section */} -
- {sectionTitle && ( -

- {sectionTitle} -

- )} + {sectionSubtitle && ( +

+ {sectionSubtitle} +

+ )} - {sectionSubtitle && ( -

- {sectionSubtitle} -

- )} + {/* Yellow Decorative Underline */} + {showYellowUnderline && ( +
+ )} +
- {/* Yellow Decorative Underline */} - {showYellowUnderline && ( -
- )} -
- - {/* Market Definitions List */} - {marketDefinitions && marketDefinitions.length > 0 && ( -
- {marketDefinitions.map((market, index) => ( -
-
-

- {market.marketType} -

- {market.marketValue && ( - - {market.marketValue} - - )} -
-

- {market.marketDescription} -

-
- ))} -
- )} -
- - {/* Right Side - Visual Representation */} -
- {/* Visual Accents */} - {showVisualAccents && ( - <> - {/* Decorative circles */} -
-
- - )} - - {/* Visual Representation */} - {visualRepresentation?.__image_url__ && ( -
- {visualRepresentation.__image_prompt__} -
+ {/* Market Definitions List */} + {marketDefinitions && marketDefinitions.length > 0 && ( +
+ {marketDefinitions.map((market, index) => ( +
+
+

+ {market.marketType} +

+ {market.marketValue && ( + + {market.marketValue} + )} +
+

+ {market.marketDescription} +

+ ))}
- - {/* Bottom accent strip */} -
+ )}
- ); + + {/* Right Side - Visual Representation */} +
+ {/* Visual Accents */} + {showVisualAccents && ( + <> + {/* Decorative circles */} +
+
+ + )} + + {/* Visual Representation */} + {visualRepresentation?.__image_url__ && ( +
+ {visualRepresentation.__image_prompt__} +
+ )} +
+
+ + {/* Bottom accent strip */} +
+
+ ); }; -export default MarketSizeSlide; \ No newline at end of file +export default MarketSizeSlide; diff --git a/servers/nextjs/presentation-layouts/professional/OurServiceSlide.tsx b/servers/nextjs/presentation-layouts/professional/OurServiceSlide.tsx index e0b55ca0..64a23020 100644 --- a/servers/nextjs/presentation-layouts/professional/OurServiceSlide.tsx +++ b/servers/nextjs/presentation-layouts/professional/OurServiceSlide.tsx @@ -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; // Component definition const OurServiceSlide = ({ data }: { data: Partial }) => { + const { + sectionTitle, + sectionSubtitle, + serviceHighlight, + showVisualAccents, + showColorBlocks, + bulletPoints, + } = data; - const { sectionTitle, sectionSubtitle, serviceHighlight, showVisualAccents, showColorBlocks, bulletPoints } = data; + return ( +
+ {/* Main Content Area */} +
+ {/* Left - Title */} +
+ {sectionTitle && ( +

+ {sectionTitle} +

+ )} - return ( -
- {/* Main Content Area */} -
- {/* Left - Title */} -
- {sectionTitle && ( -

- {sectionTitle} -

- )} + {sectionSubtitle && ( +

+ {sectionSubtitle} +

+ )} - {sectionSubtitle && ( -

- {sectionSubtitle} -

- )} - -
- - {bulletPoints && bulletPoints.map((point, index) => ( -
-
-
-

{point}

-
-
- ))} -
- - {/* Visual Accents */} - {showVisualAccents && ( - <> - {/* Decorative elements */} -
-
- - )} +
+ {bulletPoints && + bulletPoints.map((point, index) => ( +
+
+
+

+ {point} +

+
+ ))} +
- {/* Right - Service Highlight */} -
- {/* Service Highlight Image */} - {serviceHighlight?.__image_url__ && ( -
- {serviceHighlight.__image_prompt__} -
- )} - - {/* Color overlay if enabled */} - {showColorBlocks && ( -
- )} -
-
- - {/* Bottom accent strip */} - {showColorBlocks && ( -
- )} + {/* Visual Accents */} + {showVisualAccents && ( + <> + {/* Decorative elements */} +
+
+ + )}
- ); + + {/* Right - Service Highlight */} +
+ {/* Service Highlight Image */} + {serviceHighlight?.__image_url__ && ( +
+ {serviceHighlight.__image_prompt__} +
+ )} + + {/* Color overlay if enabled */} + {showColorBlocks && ( +
+ )} +
+
+ + {/* Bottom accent strip */} + {showColorBlocks && ( +
+ )} +
+ ); }; -export default OurServiceSlide; \ No newline at end of file +export default OurServiceSlide; diff --git a/servers/nextjs/presentation-layouts/professional/ProblemsSlide.tsx b/servers/nextjs/presentation-layouts/professional/ProblemsSlide.tsx index 3a4aee1d..5e82b403 100644 --- a/servers/nextjs/presentation-layouts/professional/ProblemsSlide.tsx +++ b/servers/nextjs/presentation-layouts/professional/ProblemsSlide.tsx @@ -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; // Component definition const ProblemsSlide = ({ data }: { data: Partial }) => { + const { + sectionTitle, + sectionSubtitle, + challengeItems, + supportingVisual, + showVisualAccents, + showColorBlocks, + } = data; - const { sectionTitle, sectionSubtitle, challengeItems, supportingVisual, showVisualAccents, showColorBlocks } = data; + return ( +
+ {/* Main Content Area */} +
+ {/* Left Side - Content */} +
+ {/* Title Section */} +
+ {sectionTitle && ( +

+ {sectionTitle} +

+ )} - return ( -
- {/* Main Content Area */} -
- {/* Left Side - Content */} -
- {/* Title Section */} -
- {sectionTitle && ( -

- {sectionTitle} -

- )} + {sectionSubtitle && ( +

+ {sectionSubtitle} +

+ )} +
- {sectionSubtitle && ( -

- {sectionSubtitle} -

- )} -
+ {/* Challenge Items List */} + {challengeItems && challengeItems.length > 0 && ( +
+ {challengeItems.map((item, index) => ( +
+ {/* Number Circle */} +
+ + {item.itemNumber} + +
- {/* Challenge Items List */} - {challengeItems && challengeItems.length > 0 && ( -
- {challengeItems.map((item, index) => ( -
- {/* Number Circle */} -
- - {item.itemNumber} - -
- - {/* Content */} -
-

- {item.challengeTitle} -

-

- {item.challengeDescription} -

-
-
- ))} -
- )} -
- - {/* Right Side - Image and Decorative Elements */} -
- {/* Decorative Circle */} - {showVisualAccents && ( -
- )} - - {/* Supporting Visual */} - {supportingVisual?.__image_url__ && ( -
- {supportingVisual.__image_prompt__} -
- )} - - {/* Teal Accent Block - Right Edge */} - {showColorBlocks && ( -
- )} + {/* Content */} +
+

+ {item.challengeTitle} +

+

+ {item.challengeDescription} +

+
+ ))}
- - {/* Bottom Teal Stripe */} -
+ )}
- ); + + {/* Right Side - Image and Decorative Elements */} +
+ {/* Decorative Circle */} + {showVisualAccents && ( +
+ )} + + {/* Supporting Visual */} + {supportingVisual?.__image_url__ && ( +
+ {supportingVisual.__image_prompt__} +
+ )} + + {/* Teal Accent Block - Right Edge */} + {showColorBlocks && ( +
+ )} +
+
+ + {/* Bottom Teal Stripe */} +
+
+ ); }; -export default ProblemsSlide; \ No newline at end of file +export default ProblemsSlide; diff --git a/servers/nextjs/presentation-layouts/professional/SolutionsSlide.tsx b/servers/nextjs/presentation-layouts/professional/SolutionsSlide.tsx index 5eb91707..2b8b731e 100644 --- a/servers/nextjs/presentation-layouts/professional/SolutionsSlide.tsx +++ b/servers/nextjs/presentation-layouts/professional/SolutionsSlide.tsx @@ -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; // Component definition const SolutionsSlide = ({ data }: { data: Partial }) => { + const { + sectionTitle, + sectionSubtitle, + solutionItems, + primaryVisual, + brandingVisual, + showYellowUnderline, + showVisualAccents, + showColorBlocks, + } = data; - const { sectionTitle, sectionSubtitle, solutionItems, primaryVisual, brandingVisual, showYellowUnderline, showVisualAccents, showColorBlocks } = data; - - return ( -
- {/* Main Content Area */} -
- {/* Left Side - Images and Branding */} -
- {/* Top Image Area */} - {primaryVisual?.__image_url__ && ( -
- {primaryVisual.__image_prompt__} -
- )} - - {/* Bottom Branding Area */} -
- {brandingVisual?.__image_url__ && ( - {brandingVisual.__image_prompt__} - )} -
-
- - {/* Right Side - Content */} -
- {/* Title Section */} -
- {sectionTitle && ( -

- {sectionTitle} -

- )} - - {sectionSubtitle && ( -

- {sectionSubtitle} -

- )} - - {/* Yellow Decorative Underline */} - {showYellowUnderline && ( -
- )} -
- - {/* Solution Items List */} - {solutionItems && solutionItems.length > 0 && ( -
- {solutionItems.map((item, index) => ( -
- {/* Number Circle */} -
- - {item.itemNumber} - -
- - {/* Content */} -
-

- {item.solutionTitle} -

-

- {item.solutionDescription} -

-
-
- ))} -
- )} - - {/* Visual Accents */} - {showVisualAccents && ( - <> - {/* Decorative circles */} -
-
- - )} -
+ return ( +
+ {/* Main Content Area */} +
+ {/* Left Side - Images and Branding */} +
+ {/* Top Image Area */} + {primaryVisual?.__image_url__ && ( +
+ {primaryVisual.__image_prompt__}
+ )} - {/* Color blocks for visual hierarchy */} - {showColorBlocks && ( - <> - {/* Bottom accent strip */} -
- {/* Side accent */} -
- + {/* Bottom Branding Area */} +
+ {brandingVisual?.__image_url__ && ( + {brandingVisual.__image_prompt__} )} +
- ); + + {/* Right Side - Content */} +
+ {/* Title Section */} +
+ {sectionTitle && ( +

+ {sectionTitle} +

+ )} + + {sectionSubtitle && ( +

+ {sectionSubtitle} +

+ )} + + {/* Yellow Decorative Underline */} + {showYellowUnderline && ( +
+ )} +
+ + {/* Solution Items List */} + {solutionItems && solutionItems.length > 0 && ( +
+ {solutionItems.map((item, index) => ( +
+ {/* Number Circle */} +
+ + {item.itemNumber} + +
+ + {/* Content */} +
+

+ {item.solutionTitle} +

+

+ {item.solutionDescription} +

+
+
+ ))} +
+ )} + + {/* Visual Accents */} + {showVisualAccents && ( + <> + {/* Decorative circles */} +
+
+ + )} +
+
+ + {/* Color blocks for visual hierarchy */} + {showColorBlocks && ( + <> + {/* Bottom accent strip */} +
+ {/* Side accent */} +
+ + )} +
+ ); }; -export default SolutionsSlide; \ No newline at end of file +export default SolutionsSlide; diff --git a/servers/nextjs/presentation-layouts/professional/StatisticCircularSlide.tsx b/servers/nextjs/presentation-layouts/professional/StatisticCircularSlide.tsx index 3b939f8f..e4178caa 100644 --- a/servers/nextjs/presentation-layouts/professional/StatisticCircularSlide.tsx +++ b/servers/nextjs/presentation-layouts/professional/StatisticCircularSlide.tsx @@ -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; // Component definition const StatisticCircularSlide = ({ data }: { data: Partial }) => { + 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 ( +
+ {/* Main Content Area */} +
+ {/* Header Section */} +
+ {/* Left - Title */} +
+ {sectionTitle && ( +

+ {sectionTitle} +

+ )} - return ( -
- {/* Main Content Area */} -
- {/* Header Section */} -
- {/* Left - Title */} -
- {sectionTitle && ( -

- {sectionTitle} -

- )} + {sectionSubtitle && ( +

+ {sectionSubtitle} +

+ )} +
- {sectionSubtitle && ( -

- {sectionSubtitle} -

- )} -
- - {/* Right - Company Branding */} -
-
- {companyLogo?.__image_url__ && ( -
- {companyLogo.__image_prompt__} -
- )} - {companyName && ( - - {companyName} - - )} -
-
-
- - {/* Content Section */} -
- {/* Left Side - Circular Chart */} -
-
- {/* Circular Progress SVG */} - - {/* Background circle */} - - {/* Progress circle */} - - - - {/* Center Content */} -
- {satisfactionRate?.label && ( -

- {satisfactionRate.label} -

- )} - {satisfactionRate?.percentage && ( - - {satisfactionRate.percentage} - - )} -
-
-
- - {/* Right Side - Content and Statistics */} -
- {/* Description */} -
- {description && ( -

- {description} -

- )} -
- - {/* Statistics Blocks */} - {statisticBlocks && statisticBlocks.length > 0 && ( -
- {statisticBlocks.map((block, index) => ( -
- {/* Percentage Block */} -
- - {block.percentage} - -
- - {/* Description Block */} -
-

- {block.description} -

-
-
- ))} -
- )} -
+ {/* Right - Company Branding */} +
+
+ {companyLogo?.__image_url__ && ( +
+ {companyLogo.__image_prompt__}
+ )} + {companyName && ( + + {companyName} + + )}
+
- ); + + {/* Content Section */} +
+ {/* Left Side - Circular Chart */} +
+
+ {/* Circular Progress SVG */} + + {/* Background circle */} + + {/* Progress circle */} + + + + {/* Center Content */} +
+ {satisfactionRate?.label && ( +

+ {satisfactionRate.label} +

+ )} + {satisfactionRate?.percentage && ( + + {satisfactionRate.percentage} + + )} +
+
+
+ + {/* Right Side - Content and Statistics */} +
+ {/* Description */} +
+ {description && ( +

+ {description} +

+ )} +
+ + {/* Statistics Blocks */} + {statisticBlocks && statisticBlocks.length > 0 && ( +
+ {statisticBlocks.map((block, index) => ( +
+ {/* Percentage Block */} +
+ + {block.percentage} + +
+ + {/* Description Block */} +
+

+ {block.description} +

+
+
+ ))} +
+ )} +
+
+
+
+ ); }; -export default StatisticCircularSlide; \ No newline at end of file +export default StatisticCircularSlide; diff --git a/servers/nextjs/presentation-layouts/professional/StatisticDualChartSlide.tsx b/servers/nextjs/presentation-layouts/professional/StatisticDualChartSlide.tsx index aa5d2f42..3780d301 100644 --- a/servers/nextjs/presentation-layouts/professional/StatisticDualChartSlide.tsx +++ b/servers/nextjs/presentation-layouts/professional/StatisticDualChartSlide.tsx @@ -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; // Component definition const StatisticDualChartSlide = ({ data }: { data: Partial }) => { + const { + sectionTitle, + organizationName, + brandLogo, + barChartData, + areaChartData, + leftChartTitle, + leftChartDescription, + rightChartTitle, + rightChartDescription, + } = data; - const { sectionTitle, organizationName, brandLogo, barChartData, areaChartData, leftChartTitle, leftChartDescription, rightChartTitle, rightChartDescription } = data; + return ( +
+ {/* Header Section */} +
+ {/* Title */} + {sectionTitle && ( +

{sectionTitle}

+ )} - return ( -
- {/* Header Section */} -
- {/* Title */} - {sectionTitle && ( -

- {sectionTitle} -

- )} - - {/* Company Branding */} -
- {brandLogo?.__image_url__ && ( -
- {brandLogo.__image_prompt__} -
- )} - {organizationName && ( - - {organizationName} - - )} -
-
- - {/* Content Section */} -
- {/* Left Chart Section */} -
- {/* Chart Legend */} -
-
-
- Series 1 -
-
-
- Series 2 -
-
-
- Series 3 -
-
- - {/* Bar Chart */} - {barChartData && barChartData.length > 0 && ( -
- - - - - - - } /> - - - - - - -
- )} - - {/* Chart Description */} -
- {leftChartTitle && ( -

- {leftChartTitle} -

- )} - {leftChartDescription && ( -

- {leftChartDescription} -

- )} -
-
- - {/* Right Chart Section */} -
- {/* Chart Legend */} -
-
-
- Series 1 -
-
-
- Series 2 -
-
-
- Series 3 -
-
- - {/* Area Chart */} - {areaChartData && areaChartData.length > 0 && ( -
- - - - - - - } /> - - - - - - -
- )} - - {/* Chart Description */} -
- {rightChartTitle && ( -

- {rightChartTitle} -

- )} - {rightChartDescription && ( -

- {rightChartDescription} -

- )} -
-
+ {/* Company Branding */} +
+ {brandLogo?.__image_url__ && ( +
+ {brandLogo.__image_prompt__}
+ )} + {organizationName && ( + + {organizationName} + + )}
- ); +
+ + {/* Content Section */} +
+ {/* Left Chart Section */} +
+ {/* Chart Legend */} +
+
+
+ Series 1 +
+
+
+ Series 2 +
+
+
+ Series 3 +
+
+ + {/* Bar Chart */} + {barChartData && barChartData.length > 0 && ( +
+ + + + + + } /> + + + + + +
+ )} + + {/* Chart Description */} +
+ {leftChartTitle && ( +

+ {leftChartTitle} +

+ )} + {leftChartDescription && ( +

+ {leftChartDescription} +

+ )} +
+
+ + {/* Right Chart Section */} +
+ {/* Chart Legend */} +
+
+
+ Series 1 +
+
+
+ Series 2 +
+
+
+ Series 3 +
+
+ + {/* Area Chart */} + {areaChartData && areaChartData.length > 0 && ( +
+ + + + + + } /> + + + + + +
+ )} + + {/* Chart Description */} +
+ {rightChartTitle && ( +

+ {rightChartTitle} +

+ )} + {rightChartDescription && ( +

+ {rightChartDescription} +

+ )} +
+
+
+
+ ); }; -export default StatisticDualChartSlide; \ No newline at end of file +export default StatisticDualChartSlide; diff --git a/servers/nextjs/presentation-layouts/professional/StatisticSlide.tsx b/servers/nextjs/presentation-layouts/professional/StatisticSlide.tsx index 15a85c27..74bddd34 100644 --- a/servers/nextjs/presentation-layouts/professional/StatisticSlide.tsx +++ b/servers/nextjs/presentation-layouts/professional/StatisticSlide.tsx @@ -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; // Component definition const StatisticSlide = ({ data }: { data: Partial }) => { + const { + sectionTitle, + sectionSubtitle, + statisticValue, + statisticLabel, + supportingVisual, + bulletPoints, + chartData, + showYellowUnderline, + showVisualAccents, + } = data; - const { sectionTitle, sectionSubtitle, statisticValue, statisticLabel, supportingVisual, bulletPoints, chartData, showYellowUnderline, showVisualAccents } = data; + return ( +
+ {/* Main Content Area */} +
+ {/* Left Side - Teal Background */} +
+ {/* Title Section */} +
+ {sectionTitle && ( +

+ {sectionTitle} +

+ )} - return ( -
- {/* Main Content Area */} -
- {/* Left Side - Teal Background */} -
- {/* Title Section */} -
- {sectionTitle && ( -

- {sectionTitle} -

- )} + {sectionSubtitle && ( +

+ {sectionSubtitle} +

+ )} - {sectionSubtitle && ( -

- {sectionSubtitle} -

- )} + {/* Yellow Decorative Underline */} + {showYellowUnderline && ( +
+ )} +
- {/* Yellow Decorative Underline */} - {showYellowUnderline && ( -
- )} -
+ {/* Large Statistic Display */} +
+ {statisticValue && ( +
+ {statisticValue} +
+ )} + {statisticLabel && ( +

{statisticLabel}

+ )} +
- {/* Large Statistic Display */} -
- {statisticValue && ( -
- {statisticValue} -
- )} - {statisticLabel && ( -

- {statisticLabel} -

- )} -
+ {/* Business Image */} + {supportingVisual?.__image_url__ && ( +
+
+ {supportingVisual.__image_prompt__} +
+
+ )} - {/* Business Image */} - {supportingVisual?.__image_url__ && ( -
-
- {supportingVisual.__image_prompt__} -
-
- )} + {/* Visual Accents */} + {showVisualAccents && ( + <> +
+
+ + )} +
- {/* Visual Accents */} - {showVisualAccents && ( - <> -
-
- - )} -
- - {/* Right Side - White Background with Chart and Bullet Points */} -
- {/* Chart Section */} -
- {/* Chart Legend */} -
-
-
- Series 1 -
-
-
- Series 2 -
-
-
- Series 3 -
-
- - {/* Chart Container */} - {chartData && chartData.length > 0 && ( -
- - - - - - - } /> - - - - - - -
- )} -
- - {/* Bullet Points Section */} -
- {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 ( -
-
-

- {point} -

-
- ); - })} - - )} -
-
+ {/* Right Side - White Background with Chart and Bullet Points */} +
+ {/* Chart Section */} +
+ {/* Chart Legend */} +
+
+
+ Series 1 +
+
+
+ Series 2 +
+
+
+ Series 3 +
- {/* Bottom accent strip */} -
+ {/* Chart Container */} + {chartData && chartData.length > 0 && ( +
+ + + + + + } /> + + + + + +
+ )} +
+ + {/* Bullet Points Section */} +
+ {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 ( +
+
+

+ {point} +

+
+ ); + })} + + )} +
- ); +
+ + {/* Bottom accent strip */} +
+
+ ); }; -export default StatisticSlide; \ No newline at end of file +export default StatisticSlide; diff --git a/servers/nextjs/presentation-layouts/professional/TableOfContentsSlide.tsx b/servers/nextjs/presentation-layouts/professional/TableOfContentsSlide.tsx index 261fe221..b98d4e80 100644 --- a/servers/nextjs/presentation-layouts/professional/TableOfContentsSlide.tsx +++ b/servers/nextjs/presentation-layouts/professional/TableOfContentsSlide.tsx @@ -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; // Component definition const TableOfContentsSlide = ({ data }: { data: Partial }) => { + const { + sectionTitle, + sectionSubtitle, + contentItems, + brandingVisual, + showDecorations, + useColumnLayout, + } = data; - const { sectionTitle, sectionSubtitle, contentItems, brandingVisual, showDecorations, useColumnLayout } = data; + return ( +
+ {/* Main Content Area */} +
+ {/* Left Side - Content */} +
+ {/* Title Section */} +
+ {sectionTitle && ( +

+ {sectionTitle} +

+ )} - return ( -
- {/* Main Content Area */} -
- {/* Left Side - Content */} -
- {/* Title Section */} -
- {sectionTitle && ( -

- {sectionTitle} -

- )} + {/* Decorative underline */} + {showDecorations && ( +
+ )} - {/* Decorative underline */} - {showDecorations && ( -
- )} + {sectionSubtitle && ( +

+ {sectionSubtitle} +

+ )} +
- {sectionSubtitle && ( -

- {sectionSubtitle} -

- )} -
+ {/* Content Items */} + {contentItems && contentItems.length > 0 && ( +
+ {contentItems.map((item, index) => ( +
+ {/* Number Circle */} +
+ + {item.itemNumber} + +
- {/* Content Items */} - {contentItems && contentItems.length > 0 && ( -
- {contentItems.map((item, index) => ( -
- {/* Number Circle */} -
- - {item.itemNumber} - -
- - {/* Content */} -
-

- {item.contentTitle} -

- {item.contentDescription && ( -

- {item.contentDescription} -

- )} -
-
- ))} -
- )} -
- - {/* Right Side - Branding and Visual Elements */} -
- {/* Branding Visual */} - {brandingVisual?.__image_url__ && ( -
- {brandingVisual.__image_prompt__} -
- )} - - {/* Decorative Elements */} - {showDecorations && ( - <> - {/* Decorative circles */} -
-
-
- + {/* Content */} +
+

+ {item.contentTitle} +

+ {item.contentDescription && ( +

+ {item.contentDescription} +

)} +
+ ))}
- - {/* Bottom accent strip */} -
+ )}
- ); + + {/* Right Side - Branding and Visual Elements */} +
+ {/* Branding Visual */} + {brandingVisual?.__image_url__ && ( +
+ {brandingVisual.__image_prompt__} +
+ )} + + {/* Decorative Elements */} + {showDecorations && ( + <> + {/* Decorative circles */} +
+
+
+ + )} +
+
+ + {/* Bottom accent strip */} +
+
+ ); }; -export default TableOfContentsSlide; \ No newline at end of file +export default TableOfContentsSlide; diff --git a/servers/nextjs/presentation-layouts/professional/TestimonialSlide.tsx b/servers/nextjs/presentation-layouts/professional/TestimonialSlide.tsx index 25fe2f88..3dfe7365 100644 --- a/servers/nextjs/presentation-layouts/professional/TestimonialSlide.tsx +++ b/servers/nextjs/presentation-layouts/professional/TestimonialSlide.tsx @@ -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; // Component definition const TestimonialSlide = ({ data }: { data: Partial }) => { + 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) => ( + + + + )); + }; - // Helper function to render stars - const renderStars = (rating: number) => { - return Array.from({ length: 5 }, (_, i) => ( - - - - )); - }; + return ( +
+ {/* Header Section */} +
+ {/* Title */} + {sectionTitle && ( +

{sectionTitle}

+ )} - return ( -
- {/* Header Section */} -
- {/* Title */} - {sectionTitle && ( -

- {sectionTitle} -

- )} - - {/* Company Branding */} -
- {brandLogo?.__image_url__ && ( -
- {brandLogo.__image_prompt__} -
- )} - {organizationName && ( - - {organizationName} - - )} -
-
- - {/* Testimonials Content */} -
- {testimonialItems && testimonialItems.length > 0 && ( -
- {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 ( -
- {/* Stars Rating */} - {showRatings && ( -
- {renderStars(item.rating)} -
- )} - - {/* Testimonial Text */} -

- "{item.testimonialText}" -

- - {/* Client Info */} -
- {/* Client Photo */} - {showClientPhotos && item.clientPhoto?.__image_url__ && ( -
- {item.clientPhoto.__image_prompt__} -
- )} - - {/* Client Details */} -
-

- {item.clientName} -

-

- {item.clientTitle} -

-

- {item.clientCompany} -

-
-
-
- ); - })} -
- )} + {/* Company Branding */} +
+ {brandLogo?.__image_url__ && ( +
+ {brandLogo.__image_prompt__}
+ )} + {organizationName && ( + + {organizationName} + + )}
- ); +
+ + {/* Testimonials Content */} +
+ {testimonialItems && testimonialItems.length > 0 && ( +
+ {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 ( +
+ {/* Stars Rating */} + {showRatings && ( +
+ {renderStars(item.rating)} +
+ )} + + {/* Testimonial Text */} +

+ "{item.testimonialText}" +

+ + {/* Client Info */} +
+ {/* Client Photo */} + {showClientPhotos && item.clientPhoto?.__image_url__ && ( +
+ {item.clientPhoto.__image_prompt__} +
+ )} + + {/* Client Details */} +
+

+ {item.clientName} +

+

+ {item.clientTitle} +

+

+ {item.clientCompany} +

+
+
+
+ ); + })} +
+ )} +
+
+ ); }; -export default TestimonialSlide; \ No newline at end of file +export default TestimonialSlide; diff --git a/servers/nextjs/presentation-layouts/professional/ThankYouSlide.tsx b/servers/nextjs/presentation-layouts/professional/ThankYouSlide.tsx index 237e3e0d..4467b4a6 100644 --- a/servers/nextjs/presentation-layouts/professional/ThankYouSlide.tsx +++ b/servers/nextjs/presentation-layouts/professional/ThankYouSlide.tsx @@ -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; // Component definition const ThankYouSlide = ({ data }: { data: Partial }) => { + const { + organizationName, + primaryMessage, + secondaryMessage, + brandLogo, + contactDetails, + presentationDate, + showDecorations, + showNavigationArrow, + showContactInfo, + } = data; - const { organizationName, primaryMessage, secondaryMessage, brandLogo, contactDetails, presentationDate, showDecorations, showNavigationArrow, showContactInfo } = data; - - return ( -
- {/* Header with Logo and Arrow */} -
- {/* Company Logo and Name */} -
- {brandLogo?.__image_url__ && ( -
- {brandLogo.__image_prompt__} -
- )} - {organizationName && ( - - {organizationName} - - )} -
- - {/* Arrow Button */} - {showNavigationArrow && ( -
- - - -
- )} + return ( +
+ {/* Header with Logo and Arrow */} +
+ {/* Company Logo and Name */} +
+ {brandLogo?.__image_url__ && ( +
+ {brandLogo.__image_prompt__}
- - {/* Decorative Circle */} - {showDecorations && ( -
- )} - - {/* Main Content */} -
-
- {/* Main Title */} - {primaryMessage && ( -

- {primaryMessage} -

- )} - - {/* Subtitle with Circle Bullet */} - {secondaryMessage && ( -
-
-

- {secondaryMessage} -

-
- )} -
-
- - {/* Footer with Contact Info */} - {showContactInfo && ( -
-
-
- {/* Telephone */} - {contactDetails?.phoneNumber && ( -
-
Telephone
-
{contactDetails.phoneNumber}
-
- )} - - {/* Address */} - {contactDetails?.physicalAddress && ( -
-
Address
-
{contactDetails.physicalAddress}
-
- )} - - {/* Website */} - {contactDetails?.websiteUrl && ( -
-
Website
-
{contactDetails.websiteUrl}
-
- )} -
- - {/* Presentation Date */} - {presentationDate && ( -
-
- {presentationDate} -
-
- )} -
-
- )} + )} + {organizationName && ( + + {organizationName} + + )}
- ); + + {/* Arrow Button */} + {showNavigationArrow && ( +
+ + + +
+ )} +
+ + {/* Decorative Circle */} + {showDecorations && ( +
+ )} + + {/* Main Content */} +
+
+ {/* Main Title */} + {primaryMessage && ( +

+ {primaryMessage} +

+ )} + + {/* Subtitle with Circle Bullet */} + {secondaryMessage && ( +
+
+

+ {secondaryMessage} +

+
+ )} +
+
+ + {/* Footer with Contact Info */} + {showContactInfo && ( +
+
+
+ {/* Telephone */} + {contactDetails?.phoneNumber && ( +
+
+ Telephone +
+
{contactDetails.phoneNumber}
+
+ )} + + {/* Address */} + {contactDetails?.physicalAddress && ( +
+
+ Address +
+
{contactDetails.physicalAddress}
+
+ )} + + {/* Website */} + {contactDetails?.websiteUrl && ( +
+
+ Website +
+
{contactDetails.websiteUrl}
+
+ )} +
+ + {/* Presentation Date */} + {presentationDate && ( +
+
+ {presentationDate} +
+
+ )} +
+
+ )} +
+ ); }; -export default ThankYouSlide; \ No newline at end of file +export default ThankYouSlide; diff --git a/servers/nextjs/presentation-layouts/professional/TitleSlide.tsx b/servers/nextjs/presentation-layouts/professional/TitleSlide.tsx index 5de16285..943c1dd6 100644 --- a/servers/nextjs/presentation-layouts/professional/TitleSlide.tsx +++ b/servers/nextjs/presentation-layouts/professional/TitleSlide.tsx @@ -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; // Component definition const ThynkTitleSlide = ({ data }: { data: Partial }) => { + const { + organizationName, + primaryTitle, + secondaryTitle, + brandLogo, + contactDetails, + presentationDate, + showDecorations, + showNavigationArrow, + } = data; - - const { organizationName, primaryTitle, secondaryTitle, brandLogo, contactDetails, presentationDate, showDecorations, showNavigationArrow } = data; - - return ( -
- {/* Header with Logo and Arrow */} -
- {/* Company Logo and Name */} -
- {brandLogo?.__image_url__ && ( -
- {brandLogo.__image_prompt__} -
- )} - {organizationName && ( - - {organizationName} - - )} -
- - {/* Arrow Button */} - {showNavigationArrow && ( -
- - - -
- )} + return ( +
+ {/* Header with Logo and Arrow */} +
+ {/* Company Logo and Name */} +
+ {brandLogo?.__image_url__ && ( +
+ {brandLogo.__image_prompt__}
+ )} + {organizationName && ( + + {organizationName} + + )} +
- {/* Decorative Circle */} - {showDecorations && ( -
+ {/* Arrow Button */} + {showNavigationArrow && ( +
+ + + +
+ )} +
+ + {/* Decorative Circle */} + {showDecorations && ( +
+ )} + + {/* Main Content */} +
+
+ {/* Main Title */} + {primaryTitle && ( +

+ {primaryTitle} +

+ )} + + {/* Subtitle with Circle Bullet */} + {secondaryTitle && ( +
+
+

+ {secondaryTitle} +

+
+ )} +
+
+ + {/* Footer with Contact Info */} +
+
+
+ {/* Telephone */} + {contactDetails?.phoneNumber && ( +
+
+ Telephone +
+
{contactDetails.phoneNumber}
+
)} - {/* Main Content */} -
-
- {/* Main Title */} - {primaryTitle && ( -

- {primaryTitle} -

- )} + {/* Address */} + {contactDetails?.physicalAddress && ( +
+
Address
+
{contactDetails.physicalAddress}
+
+ )} - {/* Subtitle with Circle Bullet */} - {secondaryTitle && ( -
-
-

- {secondaryTitle} -

-
- )} -
-
- - {/* Footer with Contact Info */} -
-
-
- {/* Telephone */} - {contactDetails?.phoneNumber && ( -
-
Telephone
-
{contactDetails.phoneNumber}
-
- )} - - {/* Address */} - {contactDetails?.physicalAddress && ( -
-
Address
-
{contactDetails.physicalAddress}
-
- )} - - {/* Website */} - {contactDetails?.websiteUrl && ( -
-
Website
-
{contactDetails.websiteUrl}
-
- )} -
- - {/* Presentation Date */} - {presentationDate && ( -
-
- {presentationDate} -
-
- )} -
+ {/* Website */} + {contactDetails?.websiteUrl && ( +
+
Website
+
{contactDetails.websiteUrl}
+
+ )} +
+ + {/* Presentation Date */} + {presentationDate && ( +
+
+ {presentationDate} +
+ )}
- ); +
+
+ ); }; -export default ThynkTitleSlide; \ No newline at end of file +export default ThynkTitleSlide; diff --git a/servers/nextjs/presentation-layouts/professional/WhatWeBelieveSlide.tsx b/servers/nextjs/presentation-layouts/professional/WhatWeBelieveSlide.tsx index 95c9bdb7..8d6b4f67 100644 --- a/servers/nextjs/presentation-layouts/professional/WhatWeBelieveSlide.tsx +++ b/servers/nextjs/presentation-layouts/professional/WhatWeBelieveSlide.tsx @@ -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; // Component definition const WhatWeBelieveSlide = ({ data }: { data: Partial }) => { + const { + sectionTitle, + sectionSubtitle, + visionStatement, + missionContent, + supportingVisual, + showVisualAccents, + showColorBlocks, + } = data; - const { sectionTitle, sectionSubtitle, visionStatement, missionContent, supportingVisual, showVisualAccents, showColorBlocks } = data; - - return ( -
- {/* Main Content Area */} -
- {/* Left Side - Image */} -
- {supportingVisual?.__image_url__ && ( -
- {supportingVisual.__image_prompt__} -
- )} - - {/* Visual Accents */} - {showVisualAccents && ( - <> - {/* Decorative circles */} -
-
- - )} -
- - {/* Right Side - Content */} -
- {/* Title Section */} -
- {sectionTitle && ( -

- {sectionTitle} -

- )} - - {sectionSubtitle && ( -

- {sectionSubtitle} -

- )} -
- - {/* Vision Section */} - {visionStatement && ( -
-

Vision

-

- {visionStatement} -

-
- )} - - {/* Mission Section with Teal Background */} - {missionContent && ( -
- {missionContent.missionTitle && ( -

- {missionContent.missionTitle} -

- )} - {missionContent.missionDescription && ( -

- {missionContent.missionDescription} -

- )} -
- )} -
+ return ( +
+ {/* Main Content Area */} +
+ {/* Left Side - Image */} +
+ {supportingVisual?.__image_url__ && ( +
+ {supportingVisual.__image_prompt__}
+ )} - {/* Color blocks for visual hierarchy */} - {showColorBlocks && ( - <> - {/* Bottom accent strip */} -
- {/* Left accent */} -
- - )} + {/* Visual Accents */} + {showVisualAccents && ( + <> + {/* Decorative circles */} +
+
+ + )}
- ); + + {/* Right Side - Content */} +
+ {/* Title Section */} +
+ {sectionTitle && ( +

+ {sectionTitle} +

+ )} + + {sectionSubtitle && ( +

+ {sectionSubtitle} +

+ )} +
+ + {/* Vision Section */} + {visionStatement && ( +
+

Vision

+

+ {visionStatement} +

+
+ )} + + {/* Mission Section with Teal Background */} + {missionContent && ( +
+ {missionContent.missionTitle && ( +

+ {missionContent.missionTitle} +

+ )} + {missionContent.missionDescription && ( +

+ {missionContent.missionDescription} +

+ )} +
+ )} +
+
+ + {/* Color blocks for visual hierarchy */} + {showColorBlocks && ( + <> + {/* Bottom accent strip */} +
+ {/* Left accent */} +
+ + )} +
+ ); }; -export default WhatWeBelieveSlide; \ No newline at end of file +export default WhatWeBelieveSlide;