From 8f0b3b9e855892ba4da002ce6c2e1078d586f245 Mon Sep 17 00:00:00 2001 From: shiva raj badu Date: Tue, 7 Apr 2026 12:58:18 +0545 Subject: [PATCH] feat: Report template added --- .../Report/DataAnalysisBarSlide.tsx | 133 +++++++ .../Report/DataAnalysisDashboardSlide.tsx | 350 ++++++++++++++++++ .../Report/DataAnalysisInsightBarSlide.tsx | 114 ++++++ .../Report/DataAnalysisLineStatsSlide.tsx | 185 +++++++++ .../Report/DataAnalysisListSlide.tsx | 93 +++++ .../Report/IntroSlide.tsx | 42 +++ .../Report/IntroductionImageSlide.tsx | 89 +++++ .../Report/IntroductionStatsSlide.tsx | 162 ++++++++ .../Report/MilestoneSlide.tsx | 119 ++++++ .../Report/PerformanceSnapshotSlide.tsx | 142 +++++++ .../Report/ServicesSlide.tsx | 209 +++++++++++ .../Report/SolutionSlide.tsx | 139 +++++++ .../Report/TeamSlide.tsx | 110 ++++++ .../Report/chartPrimitives.tsx | 338 +++++++++++++++++ .../Report/settings.json | 5 + .../app/presentation-templates/index.tsx | 40 ++ 16 files changed, 2270 insertions(+) create mode 100644 electron/servers/nextjs/app/presentation-templates/Report/DataAnalysisBarSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/Report/DataAnalysisDashboardSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/Report/DataAnalysisInsightBarSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/Report/DataAnalysisLineStatsSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/Report/DataAnalysisListSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/Report/IntroSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/Report/IntroductionImageSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/Report/IntroductionStatsSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/Report/MilestoneSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/Report/PerformanceSnapshotSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/Report/ServicesSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/Report/SolutionSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/Report/TeamSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/Report/chartPrimitives.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/Report/settings.json diff --git a/electron/servers/nextjs/app/presentation-templates/Report/DataAnalysisBarSlide.tsx b/electron/servers/nextjs/app/presentation-templates/Report/DataAnalysisBarSlide.tsx new file mode 100644 index 00000000..6778d07e --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/Report/DataAnalysisBarSlide.tsx @@ -0,0 +1,133 @@ +import * as z from "zod"; + + +import { WorkflowBarChart } from "./chartPrimitives"; + +const InsightItemSchema = z.object({ + title: z.string().min(3).max(18).meta({ + description: "Short insight title shown next to the icon.", + }), + description: z.string().min(20).max(84).meta({ + description: "Supporting text shown below the insight title.", + }), +}); + +const ChartPointSchema = z.object({ + label: z.string().min(1).max(12).meta({ + description: "Chart axis label.", + }), + value: z.number().min(0).max(1000).meta({ + description: "Bar chart value.", + }), +}); + +export const slideLayoutId = "data-analysis-bar-slide"; +export const slideLayoutName = "Data Analysis Bar Slide"; +export const slideLayoutDescription = + "A slide with a title at the top, a vertical list of three analysis points on the left, and a bar chart on the right. Each analysis point contains a small icon badge, a short title, and a supporting description."; + +export const Schema = z.object({ + title: z.string().min(3).max(28).default("Data Analysis").meta({ + description: "Slide title shown at the top-left.", + }), + itemIcon: z.object({ + __icon_url__: z.string().default("https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg"), + __icon_query__: z.string().default("pulse icon"), + }).default({ + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg", + __icon_query__: "pulse icon", + }).meta({ + description: "Icon shown in each analysis item badge.", + }), + items: z + .array(InsightItemSchema) + .min(3) + .max(3) + .default([ + { title: "Title 1", description: "Ut enim ad minima veniam, quis." }, + { title: "Title 2", description: "Ut enim ad minima veniam, quis." }, + { title: "Title 2", description: "Ut enim ad minima veniam, quis." }, + ]) + .meta({ + description: "Three analysis points shown in the left column.", + }), + chartData: z + .array(ChartPointSchema) + .min(7) + .max(7) + .default([ + { label: "Mon", value: 120 }, + { label: "Tue", value: 200 }, + { label: "Wed", value: 150 }, + { label: "Thu", value: 80 }, + { label: "Fri", value: 70 }, + { label: "Sat", value: 110 }, + { label: "Sun", value: 130 }, + ]) + .meta({ + description: "Weekly values shown in the bar chart.", + }), + legendLabel: z.string().min(3).max(32).default("Traditional Workflow").meta({ + description: "Legend label shown below the chart.", + }), +}); + +export type SchemaType = z.infer; + +const DataAnalysisBarSlide = ({ data }: { data: Partial }) => { + + const { title, itemIcon, items, chartData, legendLabel } = data; + + return ( +
+
+ +
+

+ {title} +

+
+ +
+
+ {items?.map((item, index) => ( +
+
+
+ {itemIcon?.__icon_query__} +
+

+ {item.title} +

+
+

+ {item.description} +

+
+ ))} +
+ +
+
+ +
+
+ +

{legendLabel}

+
+
+
+
+ ); +}; + +export default DataAnalysisBarSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/Report/DataAnalysisDashboardSlide.tsx b/electron/servers/nextjs/app/presentation-templates/Report/DataAnalysisDashboardSlide.tsx new file mode 100644 index 00000000..c5af4502 --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/Report/DataAnalysisDashboardSlide.tsx @@ -0,0 +1,350 @@ +import type { ReactNode } from "react"; +import * as z from "zod"; + +import { + AreaTrendChart, + CompactBarChart, + CompactPieChart, + SemiDonutChart, + TrendLineChart, +} from "./chartPrimitives"; + +const SummaryCardSchema = z.object({ + value: z.string().min(1).max(8).meta({ + description: "Primary metric value shown in the compact summary card.", + }), + label: z.string().min(3).max(20).meta({ + description: "Short summary card label.", + }), +}); + +const ChartPointSchema = z.object({ + label: z.string().min(1).max(12).meta({ + description: "Chart axis label.", + }), + value: z.number().min(0).max(1000).meta({ + description: "Single-series chart value.", + }), +}); + +const DualChartPointSchema = z.object({ + label: z.string().min(1).max(12).meta({ + description: "Chart axis label.", + }), + valueA: z.number().min(0).max(1000).meta({ + description: "First series value.", + }), + valueB: z.number().min(0).max(1000).meta({ + description: "Second series value.", + }), +}); + +const PieSegmentSchema = z.object({ + name: z.string().min(1).max(18).meta({ + description: "Category name shown in chart legends.", + }), + value: z.number().min(1).max(1000).meta({ + description: "Category value used in the chart.", + }), +}); + +export const slideLayoutId = "data-analysis-dashboard-slide"; +export const slideLayoutName = "Data Analysis Dashboard Slide"; +export const slideLayoutDescription = + "A dashboard-style slide with a title at the top, a row of compact summary cards underneath, and two stacked dashboard panels below. Each panel is split into three chart cells, creating a six-chart overview made of bar, donut, line, area, pie, and comparison charts."; + +export const Schema = z.object({ + title: z.string().min(3).max(28).default("Data Analysis").meta({ + description: "Slide title shown at the top-left.", + }), + summaryIcon: z.object({ + __icon_url__: z.string().default("https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg"), + __icon_query__: z.string().default("pulse icon"), + }).default({ + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg", + __icon_query__: "pulse icon", + }).meta({ + description: "Icon shown in each compact summary card.", + }), + summaryCards: z + .array(SummaryCardSchema) + .min(4) + .max(4) + .default([ + { value: "5", label: "Text 1" }, + { value: "52", label: "Text 2" }, + { value: "4", label: "Text 3" }, + { value: "80%", label: "Text 4" }, + ]) + .meta({ + description: "Four compact summary cards displayed above the dashboard panels.", + }), + workflowBars: z + .array(ChartPointSchema) + .min(7) + .max(7) + .default([ + { label: "Mon", value: 120 }, + { label: "Tue", value: 200 }, + { label: "Wed", value: 150 }, + { label: "Thu", value: 80 }, + { label: "Fri", value: 70 }, + { label: "Sat", value: 110 }, + { label: "Sun", value: 130 }, + ]) + .meta({ + description: "Bar chart data shown in the top-left dashboard cell.", + }), + gaugeSegments: z + .array(PieSegmentSchema) + .min(3) + .max(3) + .default([ + { name: "Category A", value: 45 }, + { name: "Category B", value: 30 }, + { name: "Category C", value: 25 }, + ]) + .meta({ + description: "Three segments used in the top-center semi-donut chart.", + }), + trendSeries: z + .array(DualChartPointSchema) + .min(7) + .max(7) + .default([ + { label: "Label", valueA: 22, valueB: 35 }, + { label: "Label", valueA: 54, valueB: 26 }, + { label: "Label", valueA: 44, valueB: 70 }, + { label: "Label", valueA: 78, valueB: 52 }, + { label: "Label", valueA: 50, valueB: 44 }, + { label: "Label", valueA: 32, valueB: 60 }, + { label: "Label", valueA: 58, valueB: 40 }, + ]) + .meta({ + description: "Two-series line chart data shown in the top-right cell.", + }), + detailedArea: z + .array(ChartPointSchema) + .min(7) + .max(7) + .default([ + { label: "12:00", value: 22 }, + { label: "13:00", value: 64 }, + { label: "14:00", value: 48 }, + { label: "15:00", value: 56 }, + { label: "16:00", value: 41 }, + { label: "17:00", value: 58 }, + { label: "18:00", value: 63 }, + ]) + .meta({ + description: "Area chart data shown in the bottom-left dashboard cell.", + }), + shareBreakdown: z + .array(PieSegmentSchema) + .min(3) + .max(3) + .default([ + { name: "Category A", value: 50 }, + { name: "Category B", value: 30 }, + { name: "Category C", value: 20 }, + ]) + .meta({ + description: "Pie chart data shown in the bottom-center dashboard cell.", + }), + comparisonBars: z + .array(ChartPointSchema) + .min(7) + .max(7) + .default([ + { label: "Jan", value: 70 }, + { label: "Feb", value: 170 }, + { label: "Mar", value: 110 }, + { label: "Apr", value: 42 }, + { label: "May", value: 88 }, + { label: "Jun", value: 106 }, + { label: "Jul", value: 112 }, + ]) + .meta({ + description: "Bar chart data shown in the bottom-right dashboard cell.", + }), +}); + +export type SchemaType = z.infer; + +function SummaryCard({ + value, + label, + iconUrl, + iconAlt, +}: { + value: string; + label: string; + iconUrl?: string; + iconAlt?: string; +}) { + return ( +
+
+ {iconAlt +
+
+

+ {value} +

+

{label}

+
+
+ ); +} + +function ChartCell({ + children, + footer, + topLegend, +}: { + children: ReactNode; + footer?: ReactNode; + topLegend?: ReactNode; +}) { + return ( +
+ {topLegend &&
{topLegend}
} +
{children}
+ {footer &&
{footer}
} +
+ ); +} + +function DotLegend({ + items, +}: { + items: { label: string; color: string }[]; +}) { + return ( +
+ {items.map((item) => ( + + + {item.label} + + ))} +
+ ); +} + +const DataAnalysisDashboardSlide = ({ data }: { data: Partial }) => { + + const { title, summaryIcon, summaryCards, workflowBars, gaugeSegments, trendSeries, detailedArea, shareBreakdown, comparisonBars } = data; + + return ( +
+
+ +
+

+ {title} +

+
+ +
+ {summaryCards?.map((card, index) => ( + + ))} +
+ +
+
+ + } + > + + + + + } + > + + + + +

Category A

+

Category B

+
+ } + > + + +
+ +
+ + } + > + + + + + } + > + + + + + } + > + + +
+
+
+ ); +}; + +export default DataAnalysisDashboardSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/Report/DataAnalysisInsightBarSlide.tsx b/electron/servers/nextjs/app/presentation-templates/Report/DataAnalysisInsightBarSlide.tsx new file mode 100644 index 00000000..bb4839a1 --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/Report/DataAnalysisInsightBarSlide.tsx @@ -0,0 +1,114 @@ +import * as z from "zod"; + + +import { WorkflowBarChart } from "./chartPrimitives"; + +const ChartPointSchema = z.object({ + label: z.string().min(1).max(12).meta({ + description: "Chart axis label.", + }), + value: z.number().min(0).max(1000).meta({ + description: "Bar chart value.", + }), +}); + +export const slideLayoutId = "data-analysis-insight-bar-slide"; +export const slideLayoutName = "Data Analysis Insight Bar Slide"; +export const slideLayoutDescription = + "A slide with a title at the top, a single featured insight block on the left containing an icon badge and a paragraph, and a bar chart on the right with a legend below it."; + +export const Schema = z.object({ + title: z.string().min(3).max(28).default("Data Analysis").meta({ + description: "Slide title shown at the top-left.", + }), + insightIcon: z.object({ + __icon_url__: z.string().default("https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg"), + __icon_query__: z.string().default("pulse icon"), + }).default({ + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg", + __icon_query__: "pulse icon", + }).meta({ + description: "Icon shown in the featured insight badge.", + }), + insightBody: z.string().min(80).max(320).default( + "Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut alut enim ad minima veniam, quis. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut alut enim ad minima veniam, quis" + ).meta({ + description: "Featured insight paragraph shown in the left content area.", + }), + chartData: z + .array(ChartPointSchema) + .min(7) + .max(7) + .default([ + { label: "Mon", value: 120 }, + { label: "Tue", value: 200 }, + { label: "Wed", value: 150 }, + { label: "Thu", value: 80 }, + { label: "Fri", value: 70 }, + { label: "Sat", value: 110 }, + { label: "Sun", value: 130 }, + ]) + .meta({ + description: "Weekly values shown in the right-side bar chart.", + }), + legendLabel: z.string().min(3).max(32).default("Traditional Workflow").meta({ + description: "Legend label shown below the chart.", + }), +}); + +export type SchemaType = z.infer; + +const DataAnalysisInsightBarSlide = ({ + data, +}: { + data: Partial; +}) => { + + const { title, insightIcon, insightBody, chartData, legendLabel } = data; + + return ( +
+
+ +
+

+ {title} +

+
+ +
+
+
+
+ {insightIcon?.__icon_query__} +
+
+

+ {insightBody} +

+
+ +
+
+ +
+
+ +

{legendLabel}

+
+
+
+
+ ); +}; + +export default DataAnalysisInsightBarSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/Report/DataAnalysisLineStatsSlide.tsx b/electron/servers/nextjs/app/presentation-templates/Report/DataAnalysisLineStatsSlide.tsx new file mode 100644 index 00000000..c19cb3d7 --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/Report/DataAnalysisLineStatsSlide.tsx @@ -0,0 +1,185 @@ +import * as z from "zod"; + +import { DualLineChart } from "./chartPrimitives"; + +const LinePointSchema = z.object({ + label: z.string().min(1).max(12).meta({ + description: "Chart axis label.", + }), + valueA: z.number().min(0).max(1000).meta({ + description: "First series value.", + }), + valueB: z.number().min(0).max(1000).meta({ + description: "Second series value.", + }), +}); + +const MetricSchema = z.object({ + value: z.string().min(1).max(12).meta({ + description: "Primary metric value shown in the stat card.", + }), + label: z.string().min(3).max(24).meta({ + description: "Metric label shown below the value.", + }), + description: z.string().min(6).max(36).meta({ + description: "Supporting description shown below the label.", + }), +}); + +const StatColumnSchema = z.object({ + metrics: z.array(MetricSchema).min(2).max(2).meta({ + description: "Two stacked metrics shown in one stat card.", + }), +}); + +export const slideLayoutId = "data-analysis-line-stats-slide"; +export const slideLayoutName = "Data Analysis Line Stats Slide"; +export const slideLayoutDescription = + "A slide with a title at the top, a two-series line chart in the left content area, and two tall metric cards arranged side by side on the right. Each metric card contains two stacked metric blocks."; + +export const Schema = z.object({ + title: z.string().min(3).max(28).default("Data Analysis").meta({ + description: "Slide title shown at the top-left.", + }), + seriesALabel: z.string().min(3).max(20).default("Category A").meta({ + description: "Legend label for the first line series.", + }), + seriesBLabel: z.string().min(3).max(20).default("Category B").meta({ + description: "Legend label for the second line series.", + }), + lineData: z + .array(LinePointSchema) + .min(7) + .max(7) + .default([ + { label: "label", valueA: 24, valueB: 40 }, + { label: "label", valueA: 55, valueB: 72 }, + { label: "label", valueA: 50, valueB: 98 }, + { label: "label", valueA: 97, valueB: 86 }, + { label: "label", valueA: 70, valueB: 52 }, + { label: "label", valueA: 42, valueB: 78 }, + { label: "label", valueA: 63, valueB: 51 }, + ]) + .meta({ + description: "Line chart data displayed on the left side of the slide.", + }), + statColumns: z + .array(StatColumnSchema) + .min(2) + .max(2) + .default([ + { + metrics: [ + { value: "25K", label: "Students", description: "Ut enim ad minima" }, + { value: "25K", label: "Students", description: "Ut enim ad minima" }, + ], + }, + { + metrics: [ + { value: "25K", label: "Students", description: "Ut enim ad minima" }, + { value: "25K", label: "Students", description: "Ut enim ad minima" }, + ], + }, + ]) + .meta({ + description: "Two stat cards shown on the right side of the slide.", + }), +}); + +export type SchemaType = z.infer; + +type StatMetric = { + value: string; + label: string; + description: string; +}; + +function StatPill({ + metrics, + +}: { + metrics: StatMetric[]; + +}) { + + + return ( +
+ + {metrics.map((metric, index) => ( + <> +
+

+ {metric.value} +

+

{metric.label}

+

+ {metric.description} +

+
+ {index === 0 &&
+ + + + +
+ } + + ))} + + +
+ ); +} +const DataAnalysisLineStatsSlide = ({ data }: { data: Partial }) => { + const { title, seriesALabel, seriesBLabel, lineData, statColumns } = data; + + return ( +
+
+ +
+

+ {title} +

+
+ +
+
+
+ + + {seriesALabel} + + + + {seriesBLabel} + +
+ +
+ +
+ +
+ X axis name +
+
+ +
+ {statColumns?.map((column, index) => ( + + ))} +
+
+
+ ); +}; + +export default DataAnalysisLineStatsSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/Report/DataAnalysisListSlide.tsx b/electron/servers/nextjs/app/presentation-templates/Report/DataAnalysisListSlide.tsx new file mode 100644 index 00000000..60c07657 --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/Report/DataAnalysisListSlide.tsx @@ -0,0 +1,93 @@ +import * as z from "zod"; + + +const AnalysisItemSchema = z.object({ + title: z.string().min(3).max(18).meta({ + description: "Short item title displayed next to the icon.", + }), + description: z.string().min(20).max(84).meta({ + description: "Supporting sentence shown below the title.", + }), +}); + +export const slideLayoutId = "data-analysis-list-slide"; +export const slideLayoutName = "Data Analysis List Slide"; +export const slideLayoutDescription = + "A slide with a title at the top and a two-column list of analysis points underneath. Each point contains a small circular icon badge, a short title on the same row, and a supporting description directly below."; + +export const Schema = z.object({ + title: z.string().min(3).max(28).default("Data Analysis").meta({ + description: "Slide title shown at the top-left.", + }), + itemIcon: z.object({ + __icon_url__: z.string().default("https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg"), + __icon_query__: z.string().default("pulse icon"), + }).default({ + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg", + __icon_query__: "pulse icon", + }).meta({ + description: "Icon shown in each analysis list badge.", + }), + items: z + .array(AnalysisItemSchema) + .min(6) + .max(6) + .default([ + { title: "Title 1", description: "Ut enim ad minima veniam, quis." }, + { title: "Title 3", description: "Ut enim ad minima veniam, quis." }, + { title: "Title 2", description: "Ut enim ad minima veniam, quis." }, + { title: "Title 4", description: "Ut enim ad minima veniam, quis." }, + { title: "Title 2", description: "Ut enim ad minima veniam, quis." }, + { title: "Title 5", description: "Ut enim ad minima veniam, quis." }, + ]) + .meta({ + description: "Six analysis items distributed across two columns.", + }), +}); + +export type SchemaType = z.infer; + +const DataAnalysisListSlide = ({ data }: { data: Partial }) => { + const { title, itemIcon, items } = data; + + return ( +
+
+ +
+

+ {title} +

+
+ +
+ {items?.map((item, index) => ( +
+
+
+ {itemIcon?.__icon_query__} +
+

+ {item.title} +

+
+

+ {item.description} +

+
+ ))} +
+
+ ); +}; + +export default DataAnalysisListSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/Report/IntroSlide.tsx b/electron/servers/nextjs/app/presentation-templates/Report/IntroSlide.tsx new file mode 100644 index 00000000..d84fa166 --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/Report/IntroSlide.tsx @@ -0,0 +1,42 @@ +import * as z from "zod"; + +export const Schema = z.object({ + title: z.string().min(1).default("Company's "), + subtitle: z.string().min(1).default("Report"), + name: z.string().min(1).default("John Doe"), + position: z.string().min(1).default("Company Name | Strategy, Content, growth"), +}) +export type SchemaType = z.infer; +export const slideLayoutId = "intro-slide"; +export const slideLayoutName = "Intro Slide"; +export const slideLayoutDescription = + "A report cover slide with decorative corner accents, a centered two-line title section, a divider directly beneath the title, and a presenter information block below the divider containing a name line and a supporting role or company line."; +const IntroSlide = ({ data }: { data: Partial }) => { + const { title, subtitle, name, position } = data; + + return ( +
+ + + + + + + + + +
+

{title}

+

{subtitle}

+
+
+
+

{name}

+

{position}

+
+ +
+ ) +} + +export default IntroSlide diff --git a/electron/servers/nextjs/app/presentation-templates/Report/IntroductionImageSlide.tsx b/electron/servers/nextjs/app/presentation-templates/Report/IntroductionImageSlide.tsx new file mode 100644 index 00000000..b9529372 --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/Report/IntroductionImageSlide.tsx @@ -0,0 +1,89 @@ +import * as z from "zod"; + + +export const slideLayoutId = "introduction-image-slide"; +export const slideLayoutName = "Introduction Image Slide"; +export const slideLayoutDescription = + "A slide with a title at the top-left, a paragraph block beneath the title, a short bulleted list in the lower-left area, and a large supporting image anchored on the right side of the slide."; + +export const Schema = z.object({ + title: z.string().min(3).max(32).default("Introduction").meta({ + description: "Slide title shown at the top-left.", + }), + body: z.string().min(60).max(280).default( + "Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut alut enim ad minima veniam, quis. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut alut enim ad minima veniam, quis" + ).meta({ + description: "Primary paragraph shown under the title.", + }), + bullets: z + .array(z.string().min(20).max(80)) + .min(4) + .max(4) + .default([ + "Ut enim ad minima veniam, quis nostrum", + "Exercitationem ullam corporis suscipit", + "Ut enim ad minima veniam, quis nostrum", + "exercitationem ullam corporis suscipit", + ]) + .meta({ + description: "Bullet list shown in the lower-left area.", + }), + featureImage: z.object({ + __image_url__: z.string(), + __image_prompt__: z.string(), + }).optional().meta({ + description: "Large image shown on the right side of the slide or optional.", + }).default({ + __image_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/images/placeholder.jpg", + __image_prompt__: "Thoughtful woman portrait on a neutral backdrop", + }), +}); + +export type SchemaType = z.infer; + +const IntroductionImageSlide = ({ data }: { data: Partial }) => { + const { title, body, bullets, featureImage } = data; + + return ( +
+
+ +
+

+ {title} +

+
+ +
+
+

+ {body} +

+ +
    + {bullets?.map((bullet, index) => ( +
  • + {bullet} +
  • + ))} +
+
+ +
+
+ {featureImage?.__image_prompt__} +
+
+
+
+ ); +}; + +export default IntroductionImageSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/Report/IntroductionStatsSlide.tsx b/electron/servers/nextjs/app/presentation-templates/Report/IntroductionStatsSlide.tsx new file mode 100644 index 00000000..f938a485 --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/Report/IntroductionStatsSlide.tsx @@ -0,0 +1,162 @@ +import * as z from "zod"; + +const MetricSchema = z.object({ + value: z.string().min(1).max(12).meta({ + description: "Primary metric value shown in the card.", + }), + label: z.string().min(3).max(24).meta({ + description: "Short metric label shown below the value.", + }), + description: z.string().min(6).max(36).meta({ + description: "Supporting text shown below the label.", + }), +}); + +const StatColumnSchema = z.object({ + metrics: z.array(MetricSchema).min(2).max(2).meta({ + description: "Two stacked metrics shown in one tall card.", + }), +}); + +export const slideLayoutId = "introduction-stats-slide"; +export const slideLayoutName = "Introduction Stats Slide"; +export const slideLayoutDescription = + "A slide with a title and explanatory text on the left, a bulleted list underneath the text, and two tall metric cards placed side by side on the right. Each metric card contains two stacked metric blocks."; + +export const Schema = z.object({ + title: z.string().min(3).max(32).default("Introduction").meta({ + description: "Slide title shown at the top-left.", + }), + body: z.string().min(60).max(320).default( + "Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut alut enim ad minima veniam, quis. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut alut enim ad minima veniam, quis" + ).meta({ + description: "Primary paragraph shown below the title.", + }), + bullets: z + .array(z.string().min(20).max(80)) + .min(4) + .max(4) + .default([ + "Ut enim ad minima veniam, quis nostrum", + "Exercitationem ullam corporis suscipit", + "Ut enim ad minima veniam, quis nostrum", + "exercitationem ullam corporis suscipit", + ]) + .meta({ + description: "Bullet list shown in the lower-left area.", + }), + statColumns: z + .array(StatColumnSchema) + .min(2) + .max(2) + .default([ + { + metrics: [ + { value: "25K", label: "Students", description: "Ut enim ad minima" }, + { value: "25K", label: "Students", description: "Ut enim ad minima" }, + ], + }, + { + metrics: [ + { value: "25K", label: "Students", description: "Ut enim ad minima" }, + { value: "25K", label: "Students", description: "Ut enim ad minima" }, + ], + }, + ]) + .meta({ + description: "Two stat cards shown on the right side of the slide.", + }), +}); + +export type SchemaType = z.infer; + +type StatMetric = { + value: string; + label: string; + description: string; +}; + +function StatPill({ + metrics, + +}: { + metrics: StatMetric[]; + +}) { + + + return ( +
+ + {metrics.map((metric, index) => ( + <> +
+

+ {metric.value} +

+

{metric.label}

+

+ {metric.description} +

+
+ {index === 0 &&
+ + + + +
+ } + + ))} + + +
+ ); +} + +const IntroductionStatsSlide = ({ data }: { data: Partial }) => { + + const { title, body, bullets, statColumns } = data; + + return ( +
+
+ +
+

+ {title} +

+
+ +
+
+

+ {body} +

+ +
    + {bullets?.map((bullet, index) => ( +
  • + {bullet} +
  • + ))} +
+
+ +
+ {statColumns?.map((column, index) => ( + + ))} +
+
+
+ ); +}; + +export default IntroductionStatsSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/Report/MilestoneSlide.tsx b/electron/servers/nextjs/app/presentation-templates/Report/MilestoneSlide.tsx new file mode 100644 index 00000000..5db5a866 --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/Report/MilestoneSlide.tsx @@ -0,0 +1,119 @@ +import * as z from "zod"; + +const MilestoneItemSchema = z.object({ + stepNumber: z.string().min(2).max(4).meta({ + description: "Short milestone number such as 01 or 05.", + }), + heading: z.string().min(3).max(18).meta({ + description: "Heading displayed below the milestone marker.", + }), + description: z.string().min(20).max(80).meta({ + description: "Supporting milestone description shown under the heading.", + }), +}); + +export const slideLayoutId = "milestone-slide"; +export const slideLayoutName = "Milestone Slide"; +export const slideLayoutDescription = + "A slide with a title at the top and a single horizontal milestone sequence below it. The sequence contains five circular markers aligned in one row, and each marker has a heading and description placed directly underneath. The activeIndex field controls which marker is emphasized while the remaining markers stay in the default state."; + +export const Schema = z.object({ + title: z.string().min(3).max(24).default("Milestone").meta({ + description: "Slide title shown at the top-left.", + }), + activeIndex: z.number().int().min(0).max(4).default(4).meta({ + description: "Zero-based index of the highlighted milestone.", + }), + items: z + .array(MilestoneItemSchema) + .min(5) + .max(5) + .default([ + { + stepNumber: "01", + heading: "Heading", + description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + }, + { + stepNumber: "02", + heading: "Heading", + description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + }, + { + stepNumber: "03", + heading: "Heading", + description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + }, + { + stepNumber: "04", + heading: "Heading", + description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + }, + { + stepNumber: "05", + heading: "Heading", + description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + }, + ]) + .meta({ + description: "Five milestone entries rendered across the slide.", + }), +}); + +export type SchemaType = z.infer; + +const MilestoneSlide = ({ data }: { data: Partial }) => { + const { title, activeIndex, items } = data; + + return ( +
+
+ +
+

+ {title} +

+
+ +
+
+ {items?.map((item, index) => { + const isActive = index === activeIndex; + + return ( +
+ +
0 ? "ml-[-45px]" : ""} `} + > + + {item.stepNumber} + + +
+
0 ? 'pr-[33px]' : ''} ${index === 0 ? 'px-[33px]' : ''}`}> +

+ {item.heading} +

+

+ {item.description} +

+
+
+ ); + })} +
+ + +
+
+ ); +}; + +export default MilestoneSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/Report/PerformanceSnapshotSlide.tsx b/electron/servers/nextjs/app/presentation-templates/Report/PerformanceSnapshotSlide.tsx new file mode 100644 index 00000000..eea9d05e --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/Report/PerformanceSnapshotSlide.tsx @@ -0,0 +1,142 @@ +import * as z from "zod"; + +const MetricSchema = z.object({ + value: z.string().min(1).max(12).meta({ + description: "Primary metric value shown in the pill.", + }), + label: z.string().min(3).max(24).meta({ + description: "Short label shown below the metric value.", + }), + description: z.string().min(6).max(36).meta({ + description: "Supporting metric description shown below the label.", + }), +}); + +const MetricColumnSchema = z.object({ + metrics: z.array(MetricSchema).min(1).max(2).meta({ + description: "One or two metrics shown in a single snapshot pill.", + }), +}); + +export const slideLayoutId = "performance-snapshot-slide"; +export const slideLayoutName = "Performance Snapshot Slide"; +export const slideLayoutDescription = + "A slide with a title at the top and three tall metric cards arranged horizontally below it. Each card can contain one or two stacked metric blocks, and each block includes a main value, a label, and a supporting description."; + +export const Schema = z.object({ + title: z.string().min(3).max(40).default("Performance Snapshot").meta({ + description: "Slide title shown at the top-left.", + }), + columns: z + .array(MetricColumnSchema) + .min(3) + .max(4) + .default([ + { + metrics: [ + { value: "25K", label: "Students", description: "Ut enim ad minima" }, + { value: "25K", label: "Students", description: "Ut enim ad minima" }, + ], + }, + { + metrics: [ + { value: "25K", label: "Students", description: "Ut enim ad minima" }, + { value: "25K", label: "Students", description: "Ut enim ad minima" }, + ], + }, + { + metrics: [ + { value: "25K", label: "Students", description: "Ut enim ad minima" }, + { value: "25K", label: "Students", description: "Ut enim ad minima" }, + ], + }, + { + metrics: [ + { value: "25K", label: "Students", description: "Ut enim ad minima" }, + ], + }, + ]) + .meta({ + description: "Three metric columns displayed beneath the title.", + }), +}); + +export type SchemaType = z.infer; + +type StatMetric = { + value: string; + label: string; + description: string; +}; + +function StatPill({ + metrics, + +}: { + metrics: StatMetric[]; + +}) { + + + return ( +
+ + {metrics.map((metric, index) => ( + <> +
+

+ {metric.value} +

+

{metric.label}

+

+ {metric.description} +

+
+ {index === 0 &&
+ + + + +
+ } + + ))} + + +
+ ); +} + +const PerformanceSnapshotSlide = ({ data }: { data: Partial }) => { + const { title, columns } = data; + + return ( +
+
+ +
+

+ {title} +

+
+ +
+ {columns?.map((column, index) => ( + + ))} +
+
+ ); +}; + +export default PerformanceSnapshotSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/Report/ServicesSlide.tsx b/electron/servers/nextjs/app/presentation-templates/Report/ServicesSlide.tsx new file mode 100644 index 00000000..497eebf1 --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/Report/ServicesSlide.tsx @@ -0,0 +1,209 @@ +import * as z from "zod"; + +import { Fragment } from "react/jsx-runtime"; + +const ServiceItemSchema = z.object({ + icon: z.object({ + __icon_url__: z.string().default("https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg"), + __icon_query__: z.string().default("pulse icon"), + }).default({ + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg", + __icon_query__: "pulse icon", + }).meta({ + description: "Icon used for service circles.", + }), + heading: z.string().min(3).max(18).meta({ + description: "Heading shown below the service icon.", + }), + description: z.string().min(20).max(84).meta({ + description: "Supporting description below the service heading.", + }), +}); + +export const slideLayoutId = "services-slide"; +export const slideLayoutName = "Services Slide"; +export const slideLayoutDescription = + "A slide with a title and a three-step horizontal service flow. Each step contains a circular icon area, a heading, and a description placed underneath. Directional connectors between the circles indicate sequence, and the activeIndex field determines which step is emphasized."; + +export const Schema = z.object({ + title: z.string().min(3).max(24).default("Services").meta({ + description: "Slide title shown at the top-left.", + }), + + activeIndex: z.number().int().min(0).max(2).default(2).meta({ + description: "Zero-based index of the emphasized service step.", + }), + items: z + .array(ServiceItemSchema) + .min(3) + .max(5) + .default([ + { + icon: { + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg", + __icon_query__: "pulse icon", + }, + heading: "Heading", + description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + }, + { + icon: { + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg", + __icon_query__: "upload icon", + }, + heading: "Heading", + description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + }, + { + icon: { + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg", + __icon_query__: "pulse icon", + }, + heading: "Heading", + description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + }, + { + icon: { + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg", + __icon_query__: "pulse icon", + }, + heading: "Heading", + description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + }, + + ]) + .meta({ + description: "Three sequential service items displayed on the slide.", + }), +}); + +export type SchemaType = z.infer; + +function ServiceGlyph({ + iconUrl, + iconAlt, + isActive, +}: { + iconUrl: string; + iconAlt: string; + isActive: boolean; +}) { + return ( + {iconAlt} + ); +} + +const ServicesSlide = ({ data }: { data: Partial }) => { + + const { title, activeIndex, items } = data; + + return ( +
+
+ +
+

+ {title} +

+
+ +
+ {items?.map((item, index) => { + const isActive = index === activeIndex; + + return ( + +
+
+ +
+ +

+ {item.heading} +

+

+ {item.description} +

+
+ + {index < items?.length - 1 && ( +
+ {/*
+ + + */} + + + +
+ )} + + ); + })} +
+
+ ); +}; + +export default ServicesSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/Report/SolutionSlide.tsx b/electron/servers/nextjs/app/presentation-templates/Report/SolutionSlide.tsx new file mode 100644 index 00000000..e724d4a9 --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/Report/SolutionSlide.tsx @@ -0,0 +1,139 @@ +import * as z from "zod"; + + +export const slideLayoutId = "report-solution-slide"; +export const slideLayoutName = "Report Solution Slide"; +export const slideLayoutDescription = + "A solution slide with a title at the top and a main content area below. The content area supports two structural modes controlled by the showImage boolean: when true, it places one image panel on the left and two numbered content cards on the right; when false, it removes the image and displays three numbered content cards arranged across the content area. Each card contains a short step label and a descriptive text block."; + +const CardSchema = z.object({ + stepNumber: z.string().min(2).max(4).meta({ + description: "Short card step number such as 01, 02, or 03.", + }), + description: z.string().min(20).max(90).meta({ + description: "Card body copy displayed inside the feature pill.", + }), +}); + +export const Schema = z.object({ + title: z.string().min(3).max(24).default("Solution").meta({ + description: "Slide heading shown in the top-left corner.", + }), + showImage: z.boolean().default(true).meta({ + description: "Controls whether the image is shown beside the cards.", + }), + featureImage: z.object({ + __image_url__: z.string().default("https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/images/placeholder.jpg"), + __image_prompt__: z.string().default("Thinking woman portrait on a neutral background"), + }).default({ + __image_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/images/placeholder.jpg", + __image_prompt__: "Thinking woman portrait on a neutral background", + }).meta({ + description: "Optional image used on the left side of the slide.", + }), + cards: z + .array(CardSchema) + .min(3) + .max(3) + .default([ + { + stepNumber: "01", + description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + }, + { + stepNumber: "02", + description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + }, + { + stepNumber: "03", + description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + }, + ]) + .meta({ + description: + "Three solution cards. When the image is enabled, only the first two cards are displayed.", + }), +}); + +export type SchemaType = z.infer; + +type SolutionSlideProps = { + data: Partial; +}; + +function SolutionCard({ + stepNumber, + description, +}: { + stepNumber: string; + description: string; +}) { + return ( +
+

{stepNumber}

+

+ {description} +

+
+ ); +} + +const SolutionSlide = ({ data }: SolutionSlideProps) => { + const { title, showImage, featureImage, cards } = data; + const visibleCards = showImage ? cards?.slice(0, 2) : cards; + + return ( +
+
+ +
+ {title && ( +

+ {title} +

+ )} + +
+ {showImage ? ( +
+ {featureImage?.__image_url__ && ( +
+ {featureImage?.__image_prompt__} +
+ )} + +
+ {visibleCards?.map((card, index) => ( + + ))} +
+
+ ) : ( +
+ {visibleCards?.map((card, index) => ( + + ))} +
+ )} +
+
+
+ ); +}; + +export default SolutionSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/Report/TeamSlide.tsx b/electron/servers/nextjs/app/presentation-templates/Report/TeamSlide.tsx new file mode 100644 index 00000000..0e4b62f3 --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/Report/TeamSlide.tsx @@ -0,0 +1,110 @@ +import * as z from "zod"; + + +const MemberSchema = z.object({ + title: z.string().min(2).max(24).meta({ + description: "Short role or title shown above the member name.", + }), + name: z.string().min(2).max(32).meta({ + description: "Member name shown at the bottom of the card.", + }), + image: z.object({ + __image_url__: z.string().default("https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/images/placeholder.jpg"), + __image_prompt__: z.string().default("Professional portrait of a team member"), + }).default({ + __image_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/images/placeholder.jpg", + __image_prompt__: "Professional portrait of a team member", + }), +}); + +export const slideLayoutId = "team-slide"; +export const slideLayoutName = "Team Slide"; +export const slideLayoutDescription = + "A team slide made of five vertical portrait cards placed side by side from edge to edge. Each card uses a full-height image background with a content overlay at the bottom containing a short title line and a larger name line."; + +export const Schema = z.object({ + members: z + .array(MemberSchema) + .min(5) + .max(5) + .default([ + { + title: "Title", + name: "Lanny LA", + image: { + __image_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/images/placeholder.jpg", + __image_prompt__: "Professional portrait of a male team member", + }, + }, + { + title: "Title", + name: "Lanny LA", + image: { + __image_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/images/placeholder.jpg", + __image_prompt__: "Professional portrait of a female team member", + }, + }, + { + title: "Title", + name: "Lanny LA", + image: { + __image_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/images/placeholder.jpg", + __image_prompt__: "Professional portrait of a business manager", + }, + }, + { + title: "Title", + name: "Lanny LA", + image: { + __image_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/images/placeholder.jpg", + __image_prompt__: "Professional portrait of a senior employee", + }, + }, + { + title: "Title", + name: "Lanny LA", + image: { + __image_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/images/placeholder.jpg", + __image_prompt__: "Professional portrait of a young executive", + }, + }, + ]) + .meta({ + description: "Five team members rendered as portrait cards.", + }), +}); + +export type SchemaType = z.infer; + +const TeamSlide = ({ data }: { data: Partial }) => { + + return ( +
+
+ {data?.members?.map((member, index) => ( +
+ {member.image.__image_prompt__} +
+
+

{member.title}

+

+ {member.name} +

+
+
+ ))} +
+
+ ); +}; + +export default TeamSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/Report/chartPrimitives.tsx b/electron/servers/nextjs/app/presentation-templates/Report/chartPrimitives.tsx new file mode 100644 index 00000000..87b7b82f --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/Report/chartPrimitives.tsx @@ -0,0 +1,338 @@ +"use client"; + +import { + Area, + AreaChart, + Bar, + BarChart, + CartesianGrid, + Cell, + LabelList, + Line, + LineChart, + Pie, + PieChart, + ResponsiveContainer, + XAxis, + YAxis, +} from "recharts"; + +type SimpleBarDatum = { + label: string; + value: number; +}; + +type DualSeriesDatum = { + label: string; + valueA: number; + valueB: number; +}; + +type PieDatum = { + name: string; + value: number; +}; + +type PieLabelProps = { + cx?: number; + cy?: number; + midAngle?: number; + innerRadius?: number; + outerRadius?: number; + percent?: number; +}; + +const PRIMARY = "#4d4ef3"; +const SECONDARY = "#9fb6ff"; +const LIGHT = "#e8eefb"; +const GRID = "#8f96aa"; + +function renderOutsidePieLabel({ + cx = 0, + cy = 0, + midAngle = 0, + outerRadius = 0, + percent = 0, +}: PieLabelProps) { + const radius = outerRadius + 18; + const x = cx + radius * Math.cos((-midAngle * Math.PI) / 180); + const y = cy + radius * Math.sin((-midAngle * Math.PI) / 180); + + return ( + cx ? "start" : "end"} + dominantBaseline="central" + fontSize="10" + fontWeight="500" + > + {(percent * 100).toFixed(1)}% + + ); +} + +export function CompactBarChart({ + data, +}: { + data: SimpleBarDatum[]; +}) { + return ( + + + + + + + + + + + ); +} + +export function WorkflowBarChart({ + data, +}: { + data: SimpleBarDatum[]; +}) { + return ( + + + + + + + + + + + ); +} + +export function TrendLineChart({ + data, +}: { + data: DualSeriesDatum[]; +}) { + return ( + + + + + + + + + + ); +} + +export function DualLineChart({ + data, +}: { + data: DualSeriesDatum[]; +}) { + return ( + + + + + + + + + + ); +} + +export function AreaTrendChart({ + data, + idPrefix, +}: { + data: SimpleBarDatum[]; + idPrefix: string; +}) { + return ( + + + + + + + + + + + + + + + ); +} + +export function SemiDonutChart({ + data, +}: { + data: PieDatum[]; +}) { + return ( + + + + {data.map((entry, index) => ( + + ))} + + + + ); +} + +export function CompactPieChart({ + data, +}: { + data: PieDatum[]; +}) { + return ( + + + + {data.map((entry, index) => ( + + ))} + + + + ); +} diff --git a/electron/servers/nextjs/app/presentation-templates/Report/settings.json b/electron/servers/nextjs/app/presentation-templates/Report/settings.json new file mode 100644 index 00000000..d8f8abde --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/Report/settings.json @@ -0,0 +1,5 @@ +{ + "description": "Data and narrative report layouts for intros, analysis charts, dashboards, and closing slides", + "ordered": false, + "default": false +} diff --git a/electron/servers/nextjs/app/presentation-templates/index.tsx b/electron/servers/nextjs/app/presentation-templates/index.tsx index c35ed584..e35e7e0c 100644 --- a/electron/servers/nextjs/app/presentation-templates/index.tsx +++ b/electron/servers/nextjs/app/presentation-templates/index.tsx @@ -45,6 +45,21 @@ import ProcessSlide, { Schema as PoProcessSchema, slideLayoutId as PoProcessId, import ReportSnapshotSlide, { Schema as PoReportSnapshotSchema, slideLayoutId as PoReportSnapshotId, slideLayoutName as PoReportSnapshotName, slideLayoutDescription as PoReportSnapshotDesc } from "./ProductOverview/ReportSnapshotSlide"; import TableOfContentSlide, { Schema as PoTableOfContentSchema, slideLayoutId as PoTableOfContentId, slideLayoutName as PoTableOfContentName, slideLayoutDescription as PoTableOfContentDesc } from "./ProductOverview/TableOfContentSlide"; +// Report templates +import ReportIntroSlide, { Schema as RepIntroSchema, slideLayoutId as RepIntroId, slideLayoutName as RepIntroName, slideLayoutDescription as RepIntroDesc } from "./Report/IntroSlide"; +import IntroductionImageSlide, { Schema as RepIntroductionImageSchema, slideLayoutId as RepIntroductionImageId, slideLayoutName as RepIntroductionImageName, slideLayoutDescription as RepIntroductionImageDesc } from "./Report/IntroductionImageSlide"; +import IntroductionStatsSlide, { Schema as RepIntroductionStatsSchema, slideLayoutId as RepIntroductionStatsId, slideLayoutName as RepIntroductionStatsName, slideLayoutDescription as RepIntroductionStatsDesc } from "./Report/IntroductionStatsSlide"; +import SolutionSlide, { Schema as RepSolutionSchema, slideLayoutId as RepSolutionId, slideLayoutName as RepSolutionName, slideLayoutDescription as RepSolutionDesc } from "./Report/SolutionSlide"; +import MilestoneSlide, { Schema as RepMilestoneSchema, slideLayoutId as RepMilestoneId, slideLayoutName as RepMilestoneName, slideLayoutDescription as RepMilestoneDesc } from "./Report/MilestoneSlide"; +import DataAnalysisListSlide, { Schema as RepDataAnalysisListSchema, slideLayoutId as RepDataAnalysisListId, slideLayoutName as RepDataAnalysisListName, slideLayoutDescription as RepDataAnalysisListDesc } from "./Report/DataAnalysisListSlide"; +import DataAnalysisBarSlide, { Schema as RepDataAnalysisBarSchema, slideLayoutId as RepDataAnalysisBarId, slideLayoutName as RepDataAnalysisBarName, slideLayoutDescription as RepDataAnalysisBarDesc } from "./Report/DataAnalysisBarSlide"; +import DataAnalysisInsightBarSlide, { Schema as RepDataAnalysisInsightBarSchema, slideLayoutId as RepDataAnalysisInsightBarId, slideLayoutName as RepDataAnalysisInsightBarName, slideLayoutDescription as RepDataAnalysisInsightBarDesc } from "./Report/DataAnalysisInsightBarSlide"; +import DataAnalysisLineStatsSlide, { Schema as RepDataAnalysisLineStatsSchema, slideLayoutId as RepDataAnalysisLineStatsId, slideLayoutName as RepDataAnalysisLineStatsName, slideLayoutDescription as RepDataAnalysisLineStatsDesc } from "./Report/DataAnalysisLineStatsSlide"; +import DataAnalysisDashboardSlide, { Schema as RepDataAnalysisDashboardSchema, slideLayoutId as RepDataAnalysisDashboardId, slideLayoutName as RepDataAnalysisDashboardName, slideLayoutDescription as RepDataAnalysisDashboardDesc } from "./Report/DataAnalysisDashboardSlide"; +import PerformanceSnapshotSlide, { Schema as RepPerformanceSnapshotSchema, slideLayoutId as RepPerformanceSnapshotId, slideLayoutName as RepPerformanceSnapshotName, slideLayoutDescription as RepPerformanceSnapshotDesc } from "./Report/PerformanceSnapshotSlide"; +import ReportServicesSlide, { Schema as RepServicesSchema, slideLayoutId as RepServicesId, slideLayoutName as RepServicesName, slideLayoutDescription as RepServicesDesc } from "./Report/ServicesSlide"; +import ReportTeamSlide, { Schema as RepTeamSchema, slideLayoutId as RepTeamId, slideLayoutName as RepTeamName, slideLayoutDescription as RepTeamDesc } from "./Report/TeamSlide"; + // General templates import GeneralIntroSlideLayout, { Schema as GeneralIntroSchema, layoutId as GeneralIntroId, layoutName as GeneralIntroName, layoutDescription as GeneralIntroDesc } from "./general/IntroSlideLayout"; import BasicInfoSlideLayout, { Schema as BasicInfoSchema, layoutId as BasicInfoId, layoutName as BasicInfoName, layoutDescription as BasicInfoDesc } from "./general/BasicInfoSlideLayout"; @@ -220,6 +235,7 @@ import neoSwiftSettings from "./neo-swift/settings.json"; import codeSettings from "./Code/settings.json"; import educationSettings from "./Education/settings.json"; import productOverviewSettings from "./ProductOverview/settings.json"; +import reportSettings from "./Report/settings.json"; // Helper to create template entry @@ -272,6 +288,22 @@ export const productOverviewTemplates: TemplateWithData[] = [ createTemplateEntry(ImageGallerySlide, PoImageGallerySchema, PoImageGalleryId, PoImageGalleryName, PoImageGalleryDesc, "product-overview", "ImageGallerySlide"), ]; +export const reportTemplates: TemplateWithData[] = [ + createTemplateEntry(ReportIntroSlide, RepIntroSchema, RepIntroId, RepIntroName, RepIntroDesc, "report", "IntroSlide"), + createTemplateEntry(IntroductionImageSlide, RepIntroductionImageSchema, RepIntroductionImageId, RepIntroductionImageName, RepIntroductionImageDesc, "report", "IntroductionImageSlide"), + createTemplateEntry(IntroductionStatsSlide, RepIntroductionStatsSchema, RepIntroductionStatsId, RepIntroductionStatsName, RepIntroductionStatsDesc, "report", "IntroductionStatsSlide"), + createTemplateEntry(SolutionSlide, RepSolutionSchema, RepSolutionId, RepSolutionName, RepSolutionDesc, "report", "SolutionSlide"), + createTemplateEntry(MilestoneSlide, RepMilestoneSchema, RepMilestoneId, RepMilestoneName, RepMilestoneDesc, "report", "MilestoneSlide"), + createTemplateEntry(DataAnalysisListSlide, RepDataAnalysisListSchema, RepDataAnalysisListId, RepDataAnalysisListName, RepDataAnalysisListDesc, "report", "DataAnalysisListSlide"), + createTemplateEntry(DataAnalysisBarSlide, RepDataAnalysisBarSchema, RepDataAnalysisBarId, RepDataAnalysisBarName, RepDataAnalysisBarDesc, "report", "DataAnalysisBarSlide"), + createTemplateEntry(DataAnalysisInsightBarSlide, RepDataAnalysisInsightBarSchema, RepDataAnalysisInsightBarId, RepDataAnalysisInsightBarName, RepDataAnalysisInsightBarDesc, "report", "DataAnalysisInsightBarSlide"), + createTemplateEntry(DataAnalysisLineStatsSlide, RepDataAnalysisLineStatsSchema, RepDataAnalysisLineStatsId, RepDataAnalysisLineStatsName, RepDataAnalysisLineStatsDesc, "report", "DataAnalysisLineStatsSlide"), + createTemplateEntry(DataAnalysisDashboardSlide, RepDataAnalysisDashboardSchema, RepDataAnalysisDashboardId, RepDataAnalysisDashboardName, RepDataAnalysisDashboardDesc, "report", "DataAnalysisDashboardSlide"), + createTemplateEntry(PerformanceSnapshotSlide, RepPerformanceSnapshotSchema, RepPerformanceSnapshotId, RepPerformanceSnapshotName, RepPerformanceSnapshotDesc, "report", "PerformanceSnapshotSlide"), + createTemplateEntry(ReportServicesSlide, RepServicesSchema, RepServicesId, RepServicesName, RepServicesDesc, "report", "ServicesSlide"), + createTemplateEntry(ReportTeamSlide, RepTeamSchema, RepTeamId, RepTeamName, RepTeamDesc, "report", "TeamSlide"), +]; + export const neoGeneralTemplates: TemplateWithData[] = [ createTemplateEntry(TextSplitWithEmphasisBlockLayout, TextSplitWithEmphasisBlockSchema, TextSplitWithEmphasisBlockId, TextSplitWithEmphasisBlockName, TextSplitWithEmphasisBlockDesc, 'neo-general', 'TextSplitWithEmphasisBlock'), @@ -445,6 +477,7 @@ export const allLayouts: TemplateWithData[] = [ ...codeTemplates, ...educationTemplates, ...productOverviewTemplates, + ...reportTemplates, ]; @@ -528,6 +561,13 @@ export const templates: TemplateLayoutsWithSettings[] = [ settings: productOverviewSettings as TemplateGroupSettings, layouts: productOverviewTemplates, }, + { + id: "report", + name: "Report", + description: reportSettings.description, + settings: reportSettings as TemplateGroupSettings, + layouts: reportTemplates, + }, ]; // Helper to get templates by group ID