diff --git a/servers/nextjs/app/api/layouts/route.ts b/servers/nextjs/app/api/layouts/route.ts index a179122e..6fe1a392 100644 --- a/servers/nextjs/app/api/layouts/route.ts +++ b/servers/nextjs/app/api/layouts/route.ts @@ -47,8 +47,10 @@ export async function GET() { ordered: false, isDefault: false } + // write the settings to the file + fs.writeFile(settingsPath, JSON.stringify(settings, null, 2)) } - + if (layoutFiles.length > 0) { allLayouts.push({ groupName: groupName, diff --git a/servers/nextjs/app/layout-preview/hooks/useGroupLayoutLoader.ts b/servers/nextjs/app/layout-preview/hooks/useGroupLayoutLoader.ts index 278607f5..7825bc3c 100644 --- a/servers/nextjs/app/layout-preview/hooks/useGroupLayoutLoader.ts +++ b/servers/nextjs/app/layout-preview/hooks/useGroupLayoutLoader.ts @@ -76,8 +76,9 @@ export const useGroupLayoutLoader = (groupSlug: string): UseGroupLayoutLoaderRet toast.error(`${layoutName} has no default export`, { description: 'Please ensure the layout file exports a default component', }) + console.warn(`${layoutName} has no default export`) - continue + return; } if (!module.Schema) { @@ -85,7 +86,7 @@ export const useGroupLayoutLoader = (groupSlug: string): UseGroupLayoutLoaderRet description: 'Please ensure the layout file exports a Schema', }) console.error(`${layoutName} is missing required Schema export`) - continue + return; } // Use empty object to let schema apply its default values @@ -114,6 +115,7 @@ export const useGroupLayoutLoader = (groupSlug: string): UseGroupLayoutLoaderRet if (module.default && module.Schema) { const sampleData = module.Schema.parse({}) + // if layoutId is not provided, use the layoutName const layoutId = module.layoutId || layoutName.toLowerCase().replace(/layout$/, '') const layoutInfo: LayoutInfo = { name: layoutName, diff --git a/servers/nextjs/app/layout-preview/hooks/useLayoutLoader.ts b/servers/nextjs/app/layout-preview/hooks/useLayoutLoader.ts index 3d4e3a2d..8154f07e 100644 --- a/servers/nextjs/app/layout-preview/hooks/useLayoutLoader.ts +++ b/servers/nextjs/app/layout-preview/hooks/useLayoutLoader.ts @@ -53,7 +53,7 @@ export const useLayoutLoader = (): UseLayoutLoaderReturn => { description: 'Please ensure the layout file exports a default component', }) console.warn(`${layoutName} has no default export`) - continue + throw new Error(`${layoutName} has no default export`) } if (!module.Schema) { @@ -61,7 +61,7 @@ export const useLayoutLoader = (): UseLayoutLoaderReturn => { description: 'Please ensure the layout file exports a Schema', }) console.error(`${layoutName} is missing required Schema export`) - continue + throw new Error(`${layoutName} is missing required Schema export`) } // Use empty object to let schema apply its default values @@ -93,7 +93,9 @@ export const useLayoutLoader = (): UseLayoutLoaderReturn => { if (module.default && module.Schema) { // Use empty object to let schema apply its default values const sampleData = module.Schema.parse({}) + // if layoutId is not provided, use the layoutName const layoutId = module.layoutId || layoutName.toLowerCase().replace(/layout$/, '') + const layoutInfo: LayoutInfo = { name: layoutName, component: module.default, diff --git a/servers/nextjs/presentation-layouts/default/Type10SlideLayout.tsx b/servers/nextjs/presentation-layouts/default/Type10SlideLayout.tsx new file mode 100644 index 00000000..d124c960 --- /dev/null +++ b/servers/nextjs/presentation-layouts/default/Type10SlideLayout.tsx @@ -0,0 +1,274 @@ +import { ChartContainer, ChartLegend, ChartLegendContent, ChartTooltip, ChartTooltipContent } from '@/components/ui/chart'; +import React from 'react' +import { BarChart, Bar, LineChart, Line, PieChart, Pie, AreaChart, Area, ScatterChart, Scatter, XAxis, YAxis, CartesianGrid, Cell, ResponsiveContainer } from "recharts"; +import * as z from "zod"; +import { IconSchema } from '../defaultSchemes'; + +export const layoutId = 'type4-slide' +export const layoutName = 'Type4 Slide' +export const layoutDescription = 'A chart-focused layout with title, chart visualization, and description text.' + +const chartDataSchema = z.object({ + name: z.string().meta({ description: "Data point name" }), + value: z.number().meta({ description: "Data point value" }), + category: z.string().optional().meta({ description: "Category for grouping" }), + x: z.number().optional().meta({ description: "X coordinate for scatter plots" }), + y: z.number().optional().meta({ description: "Y coordinate for scatter plots" }), +}); + + +const type10SlideSchema = z.object({ + title: z.string().min(3).max(50).default('Chart Analysis').meta({ + description: "Main title of the slide", + }), + description: z.string().min(8).max(40).default('This is a description of the chart analysis').meta({ + description: " Short description of the chart analysis", + }), + items: z.array(z.object({ + icon: IconSchema.meta({ + description: "Item icon", + }), + heading: z.string().min(2).max(50).meta({ + description: "Item heading", + }), + description: z.string().min(10).max(130).meta({ + description: "Item description", + }) + })).min(2).max(3).default(() => [ + { + icon: { + __icon_url__: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg', + __icon_query__: 'A beautiful road in the mountains' + }, + heading: 'First Key Point', + description: 'Detailed explanation of the first important point that supports the main topic' + }, + { + icon: { + __icon_url__: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg', + __icon_query__: 'A beautiful road in the mountains' + }, + heading: 'Second Key Point', + description: 'Detailed explanation of the second important point with relevant information' + } + ]).meta({ + description: "List of numbered items (2-3 items)", + }), + chartData: z.any().optional().meta({ + description: "Chart data object", + }), + isFullSizeChart: z.boolean().default(false).meta({ + description: "Whether to display chart in full size mode", + }), + chartType: z.enum(['bar', 'line', 'pie', 'area', 'scatter']).default('line').meta({ + description: "Type of chart to display", + }), + data: z.array(chartDataSchema).min(2).max(10).default([ + { name: '2021', value: 5 }, + { name: '2022', value: 12 }, + { name: '2023', value: 18 }, + { name: '2024', value: 23 }, + { name: '2025', value: 26 }, + ]).meta({ + description: "Chart data points", + }), + dataKey: z.string().default('value').meta({ + description: "Key field for chart values", + }), + categoryKey: z.string().default('name').meta({ + description: "Key field for chart categories", + }), + color: z.string().default('#3b82f6').meta({ + description: "Primary color for chart elements", + }), + showLegend: z.boolean().default(false).meta({ + description: "Whether to show chart legend", + }), + showTooltip: z.boolean().default(true).meta({ + description: "Whether to show chart tooltip", + }), +}) + + +const chartConfig = { + value: { + label: "Value", + }, + name: { + label: "Name", + }, +}; +const CHART_COLORS = [ + '#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6', + '#06b6d4', '#84cc16', '#f97316', '#ec4899', '#6366f1' +]; + +export const Schema = type10SlideSchema + +export type Type10SlideData = z.infer + +interface Type10SlideLayoutProps { + data?: Partial +} + +const Type10SlideLayout: React.FC = ({ data: slideData }) => { + const chartData = slideData?.data || []; + const chartType = slideData?.chartType || 'line'; + const color = slideData?.color || '#3b82f6'; + const dataKey = slideData?.dataKey || 'value'; + const categoryKey = slideData?.categoryKey || 'name'; + const showLegend = slideData?.showLegend || false; + const showTooltip = slideData?.showTooltip || true; + const renderChart = () => { + const commonProps = { + data: chartData, + margin: { top: 10, right: 20, left: 0, bottom: 30 }, + }; + + switch (chartType) { + case 'bar': + return ( + + + + + {showTooltip && } />} + {showLegend && } />} + + + ); + + case 'line': + return ( + + + + + {showTooltip && } />} + {showLegend && } />} + + + ); + + case 'area': + return ( + + + + + {showTooltip && } />} + {showLegend && } />} + + + ); + + case 'pie': + return ( + + {showTooltip && } />} + {showLegend && } />} + `${name} ${(percent * 100).toFixed(0)}%`} + > + {chartData.map((entry, index) => ( + + ))} + + + ); + + case 'scatter': + return ( + + + + + {showTooltip && } />} + {showLegend && } />} + + + ); + + default: + return
Unsupported chart type
; + } + }; + + return ( +
+
+ +

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

+ +
+
+
+
+ + {renderChart()} + +
+
+
+
+ {slideData?.items?.map((item, index) => ( +
+
+
+
+ {item.icon?.__icon_query__ +
+
+
+

+ {item.heading} +

+

+ {item.description} +

+
+
+
+ ))} +
+
+
+
+ ) +} + +export default Type10SlideLayout + diff --git a/servers/nextjs/presentation-layouts/default/Type1SlideLayout.tsx b/servers/nextjs/presentation-layouts/default/Type1SlideLayout.tsx new file mode 100644 index 00000000..df4c74f2 --- /dev/null +++ b/servers/nextjs/presentation-layouts/default/Type1SlideLayout.tsx @@ -0,0 +1,64 @@ +import React from 'react' +import * as z from "zod"; +import { ImageSchema } from '@/presentation-layouts/defaultSchemes'; + +export const layoutId = 'type1-slide' +export const layoutName = 'Type1 Slide' +export const layoutDescription = 'A clean two-column layout with title and description on the left and a featured image on the right.' + +const type1SlideSchema = z.object({ + title: z.string().min(3).max(50).default('Hot NOT Reload Working!').meta({ + description: "Main title of the slide", + }), + description: z.string().min(10).max(130).default('This is a test of the hot reload system! If you can see this text, hot reload is working perfectly. Changes should appear instantly without page refresh.').meta({ + description: "Main description text", + }), + image: ImageSchema.default({ + __image_url__: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg', + __image_prompt__: 'A beautiful road in the mountains' + }).meta({ + description: "Main slide image", + }) +}) + +export const Schema = type1SlideSchema + +export type Type1SlideData = z.infer + +interface Type1SlideLayoutProps { + data?: Partial +} + +const Type1SlideLayout: React.FC = ({ data: slideData }) => { + return ( +
+
+
+ {/* Title */} +

+ {slideData?.title || ' This is the title of slide'} +

+ + {/* Description */} +

+ {slideData?.description || 'This is a test of the hot reload system! If you can see this text, hot reload is working perfectly. Changes should appear instantly without page refresh.'} +

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

+ {item.heading} +

+

+ {item.description} +

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

+ {item.heading} +

+

+ {item.description} +

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

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

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

+ {item.heading} +

+

+ {item.description} +

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

+ {item.heading} +

+

+ {item.description} +

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

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

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

+ {item.heading} +

+

+ {item.description} +

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

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

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

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

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

+ {item.heading} +

+

+ {item.description} +

+
+
+ ))} +
+
+ ) +} + +export default Type3SlideLayout \ No newline at end of file diff --git a/servers/nextjs/presentation-layouts/default/Type4SlideLayout.tsx b/servers/nextjs/presentation-layouts/default/Type4SlideLayout.tsx new file mode 100644 index 00000000..d103b18a --- /dev/null +++ b/servers/nextjs/presentation-layouts/default/Type4SlideLayout.tsx @@ -0,0 +1,212 @@ +import { ChartContainer, ChartLegend, ChartLegendContent, ChartTooltip, ChartTooltipContent } from '@/components/ui/chart'; +import React from 'react' +import { BarChart, Bar, LineChart, Line, PieChart, Pie, AreaChart, Area, ScatterChart, Scatter, XAxis, YAxis, CartesianGrid, Cell, ResponsiveContainer } from "recharts"; +import * as z from "zod"; + +export const layoutId = 'type4-slide' +export const layoutName = 'Type4 Slide' +export const layoutDescription = 'A chart-focused layout with title, chart visualization, and description text.' + +const chartDataSchema = z.object({ + name: z.string().meta({ description: "Data point name" }), + value: z.number().meta({ description: "Data point value" }), + category: z.string().optional().meta({ description: "Category for grouping" }), + x: z.number().optional().meta({ description: "X coordinate for scatter plots" }), + y: z.number().optional().meta({ description: "Y coordinate for scatter plots" }), +}); + + +const type4SlideSchema = z.object({ + title: z.string().min(3).max(50).default('Chart Analysis').meta({ + description: "Main title of the slide", + }), + description: z.string().min(10).max(130).default('This chart shows important data trends and insights that help understand the current situation and make informed decisions.').meta({ + description: "Description text for the chart", + }), + chartData: z.any().optional().meta({ + description: "Chart data object", + }), + isFullSizeChart: z.boolean().default(false).meta({ + description: "Whether to display chart in full size mode", + }), + chartType: z.enum(['bar', 'line', 'pie', 'area', 'scatter']).default('bar').meta({ + description: "Type of chart to display", + }), + data: z.array(chartDataSchema).min(2).max(10).default([ + { name: '2021', value: 5 }, + { name: '2022', value: 12 }, + { name: '2023', value: 18 }, + { name: '2024', value: 23 }, + { name: '2025', value: 26 }, + ]).meta({ + description: "Chart data points", + }), + dataKey: z.string().default('value').meta({ + description: "Key field for chart values", + }), + categoryKey: z.string().default('name').meta({ + description: "Key field for chart categories", + }), + color: z.string().default('#3b82f6').meta({ + description: "Primary color for chart elements", + }), + showLegend: z.boolean().default(false).meta({ + description: "Whether to show chart legend", + }), + showTooltip: z.boolean().default(true).meta({ + description: "Whether to show chart tooltip", + }), +}) + + +const chartConfig = { + value: { + label: "Value", + }, + name: { + label: "Name", + }, +}; +const CHART_COLORS = [ + '#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6', + '#06b6d4', '#84cc16', '#f97316', '#ec4899', '#6366f1' +]; + +export const Schema = type4SlideSchema + +export type Type4SlideData = z.infer + +interface Type4SlideLayoutProps { + data?: Partial +} + +const Type4SlideLayout: React.FC = ({ data: slideData }) => { + const chartData = slideData?.data || []; + const chartType = slideData?.chartType || 'line'; + const color = slideData?.color || '#3b82f6'; + const dataKey = slideData?.dataKey || 'value'; + const categoryKey = slideData?.categoryKey || 'name'; + const showLegend = slideData?.showLegend || false; + const showTooltip = slideData?.showTooltip || true; + const renderChart = () => { + const commonProps = { + data: chartData, + margin: { top: 10, right: 20, left: 0, bottom: 30 }, + }; + + switch (chartType) { + case 'bar': + return ( + + + + + {showTooltip && } />} + {showLegend && } />} + + + ); + + case 'line': + return ( + + + + + {showTooltip && } />} + {showLegend && } />} + + + ); + + case 'area': + return ( + + + + + {showTooltip && } />} + {showLegend && } />} + + + ); + + case 'pie': + return ( + + {showTooltip && } />} + {showLegend && } />} + `${name} ${(percent * 100).toFixed(0)}%`} + > + {chartData.map((entry, index) => ( + + ))} + + + ); + + case 'scatter': + return ( + + + + + {showTooltip && } />} + {showLegend && } />} + + + ); + + default: + return
Unsupported chart type
; + } + }; + + return ( +
+

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

+ +
+
+
+ + {renderChart()} + +
+
+
+

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

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

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

+ +

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

+
+ + {/* Right section - Numbered items */} +
+
+ {slideData?.items?.map((item, index) => ( +
+
+
+ {`0${index + 1}`} +
+
+

+ {item.heading} +

+

+ {item.description} +

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

+ {item.heading} +

+

+ {item.description} +

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

+ {item.heading} +

+

+ {item.description} +

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

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

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

+ {item.heading} +

+

+ {item.description} +

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

+ {item.heading} +

+

+ {item.description} +

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

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

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

+ {item.heading} +

+

+ {item.description} +

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

+ {item.heading} +

+

+ {item.description} +

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

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

+ +

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

+
+ + {/* Right section - Items */} +
+ {renderItems()} +
+
+
+ ) +} + +export default Type8SlideLayout \ No newline at end of file diff --git a/servers/nextjs/presentation-layouts/default/Type9SlideLayout.tsx b/servers/nextjs/presentation-layouts/default/Type9SlideLayout.tsx new file mode 100644 index 00000000..61d1d71d --- /dev/null +++ b/servers/nextjs/presentation-layouts/default/Type9SlideLayout.tsx @@ -0,0 +1,254 @@ +import { ChartContainer, ChartLegend, ChartLegendContent, ChartTooltip, ChartTooltipContent } from '@/components/ui/chart'; +import React from 'react' +import { BarChart, Bar, LineChart, Line, PieChart, Pie, AreaChart, Area, ScatterChart, Scatter, XAxis, YAxis, CartesianGrid, Cell, ResponsiveContainer } from "recharts"; +import * as z from "zod"; + +export const layoutId = 'type4-slide' +export const layoutName = 'Type4 Slide' +export const layoutDescription = 'A chart-focused layout with title, chart visualization, and description text.' + +const chartDataSchema = z.object({ + name: z.string().meta({ description: "Data point name" }), + value: z.number().meta({ description: "Data point value" }), + category: z.string().optional().meta({ description: "Category for grouping" }), + x: z.number().optional().meta({ description: "X coordinate for scatter plots" }), + y: z.number().optional().meta({ description: "Y coordinate for scatter plots" }), +}); + + +const type9SlideSchema = z.object({ + title: z.string().min(3).max(50).default('Chart Analysis').meta({ + description: "Main title of the slide", + }), + items: z.array(z.object({ + heading: z.string().min(2).max(50).meta({ + description: "Item heading", + }), + description: z.string().min(10).max(130).meta({ + description: "Item description", + }) + })).min(2).max(3).default([ + { + heading: 'First Key Point', + description: 'Detailed explanation of the first important point that supports the main topic' + }, + { + heading: 'Second Key Point', + description: 'Detailed explanation of the second important point with relevant information' + }, + { + heading: 'Third Key Point', + description: 'Detailed explanation of the third important point that concludes the discussion' + } + ]).meta({ + description: "List of numbered items (2-3 items)", + }), + chartData: z.any().optional().meta({ + description: "Chart data object", + }), + isFullSizeChart: z.boolean().default(false).meta({ + description: "Whether to display chart in full size mode", + }), + chartType: z.enum(['bar', 'line', 'pie', 'area', 'scatter']).default('pie').meta({ + description: "Type of chart to display", + }), + data: z.array(chartDataSchema).min(2).max(10).default([ + { name: '2021', value: 5 }, + { name: '2022', value: 12 }, + { name: '2023', value: 18 }, + { name: '2024', value: 23 }, + { name: '2025', value: 26 }, + ]).meta({ + description: "Chart data points", + }), + dataKey: z.string().default('value').meta({ + description: "Key field for chart values", + }), + categoryKey: z.string().default('name').meta({ + description: "Key field for chart categories", + }), + color: z.string().default('#3b82f6').meta({ + description: "Primary color for chart elements", + }), + showLegend: z.boolean().default(false).meta({ + description: "Whether to show chart legend", + }), + showTooltip: z.boolean().default(true).meta({ + description: "Whether to show chart tooltip", + }), +}) + + +const chartConfig = { + value: { + label: "Value", + }, + name: { + label: "Name", + }, +}; +const CHART_COLORS = [ + '#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6', + '#06b6d4', '#84cc16', '#f97316', '#ec4899', '#6366f1' +]; + +export const Schema = type9SlideSchema + +export type Type9SlideData = z.infer + +interface Type9SlideLayoutProps { + data?: Partial +} + +const Type9SlideLayout: React.FC = ({ data: slideData }) => { + const chartData = slideData?.data || []; + const chartType = slideData?.chartType || 'line'; + const color = slideData?.color || '#3b82f6'; + const dataKey = slideData?.dataKey || 'value'; + const categoryKey = slideData?.categoryKey || 'name'; + const showLegend = slideData?.showLegend || false; + const showTooltip = slideData?.showTooltip || true; + const renderChart = () => { + const commonProps = { + data: chartData, + margin: { top: 10, right: 20, left: 0, bottom: 30 }, + }; + + switch (chartType) { + case 'bar': + return ( + + + + + {showTooltip && } />} + {showLegend && } />} + + + ); + + case 'line': + return ( + + + + + {showTooltip && } />} + {showLegend && } />} + + + ); + + case 'area': + return ( + + + + + {showTooltip && } />} + {showLegend && } />} + + + ); + + case 'pie': + return ( + + {showTooltip && } />} + {showLegend && } />} + `${name} ${(percent * 100).toFixed(0)}%`} + > + {chartData.map((entry, index) => ( + + ))} + + + ); + + case 'scatter': + return ( + + + + + {showTooltip && } />} + {showLegend && } />} + + + ); + + default: + return
Unsupported chart type
; + } + }; + + return ( +
+

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

+ +
+
+
+ + {renderChart()} + +
+
+
+
+ {slideData?.items?.map((item, index) => ( +
+
+
+ {`0${index + 1}`} +
+
+

+ {item.heading} +

+

+ {item.description} +

+
+
+
+ ))} +
+
+
+
+ ) +} + +export default Type9SlideLayout + diff --git a/servers/nextjs/presentation-layouts/default/setting.json b/servers/nextjs/presentation-layouts/default/setting.json new file mode 100644 index 00000000..d6a15682 --- /dev/null +++ b/servers/nextjs/presentation-layouts/default/setting.json @@ -0,0 +1,5 @@ +{ + "description": "Default layout for presentations", + "ordered": false, + "isDefault": true +} \ No newline at end of file