From 604bb953867830af122de8f7a0ea656367364805 Mon Sep 17 00:00:00 2001 From: shiva raj badu Date: Tue, 7 Apr 2026 12:40:39 +0545 Subject: [PATCH] feat: Product Overview template added --- .../BusinessChallengesCardsSlide.tsx | 133 +++++++++++++ .../BusinessChallengesGridSlide.tsx | 84 ++++++++ .../ProductOverview/ComparisonChartSlide.tsx | 187 ++++++++++++++++++ .../ComparisonTableWithTextSlide.tsx | 138 +++++++++++++ .../ProductOverview/CoverSlide.tsx | 94 +++++++++ .../ProductOverview/ImageGallerySlide.tsx | 139 +++++++++++++ .../ProductOverview/IntroductionSlide.tsx | 101 ++++++++++ .../ProductOverview/KpiCardsSlide.tsx | 114 +++++++++++ .../MarketOpportunitySlide.tsx | 108 ++++++++++ .../ProductOverview/MeetTeamSlide.tsx | 150 ++++++++++++++ .../ProductOverview/MissionVisionSlide.tsx | 88 +++++++++ .../ProductOverview/OurServicesSlide.tsx | 127 ++++++++++++ .../ProductOverview/PricingPlanSlide.tsx | 153 ++++++++++++++ .../ProductOverview/ProcessSlide.tsx | 173 ++++++++++++++++ .../ProductOverview/ReportSnapshotSlide.tsx | 162 +++++++++++++++ .../ProductOverview/TableOfContentSlide.tsx | 94 +++++++++ .../ProductOverview/settings.json | 5 + .../app/presentation-templates/index.tsx | 46 +++++ 18 files changed, 2096 insertions(+) create mode 100644 electron/servers/nextjs/app/presentation-templates/ProductOverview/BusinessChallengesCardsSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/ProductOverview/BusinessChallengesGridSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/ProductOverview/ComparisonChartSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/ProductOverview/ComparisonTableWithTextSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/ProductOverview/CoverSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/ProductOverview/ImageGallerySlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/ProductOverview/IntroductionSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/ProductOverview/KpiCardsSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/ProductOverview/MarketOpportunitySlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/ProductOverview/MeetTeamSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/ProductOverview/MissionVisionSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/ProductOverview/OurServicesSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/ProductOverview/PricingPlanSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/ProductOverview/ProcessSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/ProductOverview/ReportSnapshotSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/ProductOverview/TableOfContentSlide.tsx create mode 100644 electron/servers/nextjs/app/presentation-templates/ProductOverview/settings.json diff --git a/electron/servers/nextjs/app/presentation-templates/ProductOverview/BusinessChallengesCardsSlide.tsx b/electron/servers/nextjs/app/presentation-templates/ProductOverview/BusinessChallengesCardsSlide.tsx new file mode 100644 index 00000000..1258be58 --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/ProductOverview/BusinessChallengesCardsSlide.tsx @@ -0,0 +1,133 @@ +import * as z from "zod"; + + + +export const slideLayoutId = "product-overview-business-challenges-cards-slide"; +export const slideLayoutName = "Product Overview Business Challenges Cards Slide"; +export const slideLayoutDescription = + "A business challenges slide with headline and tagline on the left, a large supporting image in the top-right area, and three vertical detail cards across the lower center-right section."; + +const CardSchema = z.object({ + heading: z.string().min(4).max(12).meta({ + description: "Card heading for one challenge column.", + }), + body: z.string().min(30).max(60).meta({ + description: "Card body copy for one challenge column.", + }), + dark: z.boolean().default(false).meta({ + description: "Controls whether the card uses a dark emphasis style.", + }), +}); + +export const Schema = z.object({ + title: z.string().min(8).max(24).default("Business Challenges").meta({ + description: "Main slide title.", + }), + taglineLabel: z.string().min(3).max(10).default("TAGLINE").meta({ + description: "Short label above the left-side paragraph.", + }), + taglineBody: z.string().min(40).max(100).default( + "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea." + ).meta({ + description: "Supporting paragraph on the left side.", + }), + heroImage: z.object({ + __image_url__: z.string().url().default("https://images.unsplash.com/photo-1552664730-d307ca884978?auto=format&fit=crop&w=1400&q=80"), + __image_prompt__: z.string().min(10).max(100).default("Team meeting and stressed analyst"), + }).default({ + __image_url__: + "https://images.unsplash.com/photo-1552664730-d307ca884978?auto=format&fit=crop&w=1400&q=80", + __image_prompt__: "Team meeting and stressed analyst", + }).meta({ + description: "Primary image shown in the upper right area.", + }), + cards: z + .array(CardSchema) + .min(3) + .max(3) + .default([ + { + heading: "HEADING 1", + body: "Lorem ipsum dolor sit amet, consectetur elit.", + dark: false, + }, + { + heading: "HEADING 2", + body: "Lorem ipsum dolor sit amet, consectetur elit.", + dark: false, + }, + { + heading: "HEADING 3", + body: "Lorem ipsum dolor sit amet, consectetur elit.", + dark: true, + }, + ]) + .meta({ + description: "Three vertical challenge cards rendered under the image.", + }), +}); + +export type SchemaType = z.infer; + +const BusinessChallengesCardsSlide = ({ data }: { data: Partial }) => { + const { title, taglineLabel, taglineBody, heroImage, cards } = data; + + return ( +
+
+

+ {title} +

+ +
+

+ {taglineLabel} +

+

{taglineBody}

+
+
+ + {heroImage?.__image_url__ && ( + {heroImage.__image_prompt__} + )} + +
+ {cards?.map((card, index) => ( +
+

+ {card.heading} +

+

+ {card.body} +

+
+ ))} +
+
+ ); +}; + +export default BusinessChallengesCardsSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/ProductOverview/BusinessChallengesGridSlide.tsx b/electron/servers/nextjs/app/presentation-templates/ProductOverview/BusinessChallengesGridSlide.tsx new file mode 100644 index 00000000..b6cdce83 --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/ProductOverview/BusinessChallengesGridSlide.tsx @@ -0,0 +1,84 @@ +import * as z from "zod"; + +export const slideLayoutId = "product-overview-business-challenges-grid-slide"; +export const slideLayoutName = "Product Overview Business Challenges Grid Slide"; +export const slideLayoutDescription = + "A slide with a light title band on top and a dark content section below containing four challenge blocks in a two-by-two grid."; + +const ChallengeSchema = z.object({ + heading: z.string().min(4).max(12).meta({ + description: "Short heading for a single challenge block.", + }), + body: z.string().min(40).max(96).meta({ + description: "Description text for a single challenge block.", + }), +}); + +export const Schema = z.object({ + title: z.string().min(8).max(24).default("Business Challenges").meta({ + description: "Main title shown in the top band.", + }), + challenges: z + .array(ChallengeSchema) + .min(2) + .max(4) + .default([ + { + heading: "HEADING 1", + body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore.", + }, + { + heading: "HEADING 2", + body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore.", + }, + { + heading: "HEADING 1", + body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore.", + }, + { + heading: "HEADING 2", + body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore.", + }, + ]) + .meta({ + description: "Four challenge blocks rendered in a 2x2 arrangement.", + }), +}); + +export type SchemaType = z.infer; + +const BusinessChallengesGridSlide = ({ data }: { data: Partial }) => { + const { title, challenges } = data; + + return ( +
+
+

+ {title} +

+
+ +
+ {challenges?.map((challenge, index) => ( +
+

+ {challenge.heading} +

+

{challenge.body}

+
+ ))} +
+
+ ); +}; + +export default BusinessChallengesGridSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/ProductOverview/ComparisonChartSlide.tsx b/electron/servers/nextjs/app/presentation-templates/ProductOverview/ComparisonChartSlide.tsx new file mode 100644 index 00000000..3a19af33 --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/ProductOverview/ComparisonChartSlide.tsx @@ -0,0 +1,187 @@ +import * as z from "zod"; + + +export const slideLayoutId = "product-overview-comparison-chart-slide"; +export const slideLayoutName = "Product Overview Comparison Chart Slide"; +export const slideLayoutDescription = + "A comparison table slide with a headline, short description, four column headers, and three data rows using check, cross, or empty cells."; + +const CellStatusSchema = z.enum(["check", "cross", "empty"]); + +const RowSchema = z.object({ + label: z.string().min(4).max(18).meta({ + description: "Row heading shown in the first column.", + }), + cell1: CellStatusSchema.default("check"), + cell2: CellStatusSchema.default("empty"), + cell3: CellStatusSchema.default("check"), + cell4: CellStatusSchema.default("empty"), +}); + +export const Schema = z.object({ + title: z.string().min(8).max(32).default("Comparison Chart").meta({ + description: "Main heading shown above the table.", + }), + subtitle: z.string().min(30).max(140).default( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt." + ).meta({ + description: "Short subtitle shown under the main heading.", + }), + columns: z + .array(z.string().min(4).max(18)) + .min(4) + .max(4) + .default(["HEADING 1", "HEADING 1", "HEADING 2", "HEADING 3"]) + .meta({ + description: "Four table column headings.", + }), + rows: z + .array(RowSchema) + .min(3) + .max(3) + .default([ + { + label: "HEADING 1", + cell1: "check", + cell2: "cross", + cell3: "check", + cell4: "cross", + }, + { + label: "HEADING 1", + cell1: "check", + cell2: "empty", + cell3: "check", + cell4: "empty", + }, + { + label: "HEADING 2", + cell1: "check", + cell2: "check", + cell3: "check", + cell4: "check", + }, + ]) + .meta({ + description: "Three table rows with status indicators.", + }), + checkIcon: z.object({ + __icon_url__: z.string(), + __icon_query__: z.string(), + }).default({ + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg", + __icon_query__: "check icon", + }).meta({ + description: "Icon used for positive comparison status.", + }), + crossIcon: z.object({ + __icon_url__: z.string(), + __icon_query__: z.string(), + }).default({ + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg", + __icon_query__: "cross icon", + }).meta({ + description: "Icon used for negative comparison status.", + }), +}); + +export type SchemaType = z.infer; + +function StatusIcon({ + status, + checkIconUrl, + checkIconAlt, + crossIconUrl, + crossIconAlt, +}: { + status: "check" | "cross" | "empty"; + checkIconUrl: string | undefined; + checkIconAlt: string | undefined; + crossIconUrl: string | undefined; + crossIconAlt: string | undefined; +}) { + if (status === "empty") { + return ; + } + + if (status === "cross") { + return {crossIconAlt}; + } + + return {checkIconAlt}; +} + +const ComparisonChartSlide = ({ data }: { data: Partial }) => { + const { title, subtitle, columns, rows, checkIcon, crossIcon } = data; + + return ( +
+
+

+ {title} +

+

{subtitle}

+
+ +
+
+
+ {columns?.map((column, index) => ( +
+ {column} +
+ ))} +
+ + {rows?.map((row, index) => { + const cells: ("check" | "cross" | "empty")[] = [ + row.cell1, + row.cell2, + row.cell3, + row.cell4, + ]; + + return ( +
+
+ {row.label} +
+ + {cells?.map((status, cellIndex) => ( +
+ +
+ ))} +
+ ); + })} +
+
+ ); +}; + +export default ComparisonChartSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/ProductOverview/ComparisonTableWithTextSlide.tsx b/electron/servers/nextjs/app/presentation-templates/ProductOverview/ComparisonTableWithTextSlide.tsx new file mode 100644 index 00000000..7475160b --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/ProductOverview/ComparisonTableWithTextSlide.tsx @@ -0,0 +1,138 @@ +import * as z from "zod"; + +export const slideLayoutId = "product-overview-comparison-table-with-text-slide"; +export const slideLayoutName = "Product Overview Comparison Table With Text Slide"; +export const slideLayoutDescription = + "A comparison table slide with a title, a subtitle, four headers, and three text rows."; + +const RowSchema = z.object({ + cell1: z.string().min(8).max(24).meta({ + description: "First column cell text.", + }), + cell2: z.string().min(8).max(24).meta({ + description: "Second column cell text.", + }), + cell3: z.string().min(8).max(24).meta({ + description: "Third column cell text.", + }), + cell4: z.string().min(8).max(24).meta({ + description: "Fourth column cell text.", + }), +}); + +export const Schema = z.object({ + title: z.string().min(8).max(32).default("Comparison Chart").meta({ + description: "Main heading shown above the table.", + }), + subtitle: z + .string() + .min(30) + .max(100) + .default( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt." + ) + .meta({ + description: "Short subtitle shown under the main heading.", + }), + columns: z + .array(z.string().min(4).max(18)) + .min(4) + .max(4) + .default(["HEADING 1", "HEADING 1", "HEADING 2", "HEADING 3"]) + .meta({ + description: "Four table column headings.", + }), + highlightedHeaderIndex: z.number().int().min(1).max(4).default(4).meta({ + description: "1-based column index for the dark highlighted table header.", + }), + rows: z + .array(RowSchema) + .min(3) + .max(3) + .default([ + { + cell1: "Lorem ipsum dolor sit.", + cell2: "Lorem ipsum dolor sit.", + cell3: "Lorem ipsum dolor sit.", + cell4: "Lorem ipsum dolor sit.", + }, + { + cell1: "Lorem ipsum dolor sit.", + cell2: "Lorem ipsum dolor sit.", + cell3: "Lorem ipsum dolor sit.", + cell4: "Lorem ipsum dolor sit.", + }, + { + cell1: "Lorem ipsum dolor sit.", + cell2: "Lorem ipsum dolor sit.", + cell3: "Lorem ipsum dolor sit.", + cell4: "Lorem ipsum dolor sit.", + }, + ]) + .meta({ + description: "Three table rows of text content.", + }), +}); + +export type SchemaType = z.infer; + +const ComparisonTableWithTextSlide = ({ data }: { data: Partial }) => { + const { title, subtitle, columns, highlightedHeaderIndex, rows } = data; + + return ( +
+
+

+ {title} +

+

+ {subtitle} +

+
+ +
+ + + + {columns?.map((column, index) => { + const isHighlighted = index + 1 === highlightedHeaderIndex; + return ( + + ); + })} + + + + + {rows?.map((row, rowIndex) => { + const cells = [row.cell1, row.cell2, row.cell3, row.cell4]; + return ( + + {cells?.map((cell, cellIndex) => ( + + ))} + + ); + })} + +
+ {column} +
+ {cell} +
+
+
+ ); +}; + +export default ComparisonTableWithTextSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/ProductOverview/CoverSlide.tsx b/electron/servers/nextjs/app/presentation-templates/ProductOverview/CoverSlide.tsx new file mode 100644 index 00000000..2073ac26 --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/ProductOverview/CoverSlide.tsx @@ -0,0 +1,94 @@ +import * as z from "zod"; + + + +export const slideLayoutId = "product-overview-cover-slide"; +export const slideLayoutName = "Product Overview Cover Slide"; +export const slideLayoutDescription = + "A cover slide with a compact logo in the top-left, a date in the top-right, a two-line centered title, and a city/building image anchored to the bottom with a soft fade into the background."; + +export const Schema = z.object({ + logoImage: z.object({ + __image_url__: z.string().default("https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/images/placeholder.jpg"), + + __image_prompt__: z.string().min(10).max(100).default("Professional logo of the company"), + }).default({ + __image_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/images/placeholder.jpg", + __image_prompt__: "Professional logo of the company", + }), + label: z.string().min(3).max(16).default("MARCH 2026").meta({ + description: "Date label shown at the top-right corner.", + }), + titleLine1: z.string().min(3).max(18).default("Social Media").meta({ + description: "First line of the cover title.", + }), + titleLine2: z.string().min(3).max(20).default("Marketing Report").meta({ + description: "Second line of the cover title.", + }), + backgroundImage: z.object({ + __image_url__: z.string().url().default("https://images.unsplash.com/photo-1486406146926-c627a92ad1ab?auto=format&fit=crop&w=1920&q=80"), + __image_prompt__: z.string().min(10).max(100).default("Tall glass buildings from street view"), + }).default({ + __image_url__: "https://images.unsplash.com/photo-1486406146926-c627a92ad1ab?auto=format&fit=crop&w=1920&q=80", + __image_prompt__: "Tall glass buildings from street view", + }), +}); + +export type SchemaType = z.infer; + +const CoverSlide = ({ data }: { data: Partial }) => { + + const { logoImage, label, titleLine1, titleLine2, backgroundImage } = data; + + return ( +
+
+
+ + {logoImage?.__image_prompt__ + +

+ {label} +

+
+ +
+

+ {titleLine1} +
+ {titleLine2} +

+
+
+ + {backgroundImage?.__image_url__ && ( + {backgroundImage.__image_prompt__ + )} + +
+
+ ); +}; + +export default CoverSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/ProductOverview/ImageGallerySlide.tsx b/electron/servers/nextjs/app/presentation-templates/ProductOverview/ImageGallerySlide.tsx new file mode 100644 index 00000000..093d7e8e --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/ProductOverview/ImageGallerySlide.tsx @@ -0,0 +1,139 @@ +import * as z from "zod"; + + +export const slideLayoutId = "product-overview-image-gallery-slide"; +export const slideLayoutName = "Product Overview Image Gallery Slide"; +export const slideLayoutDescription = + "A gallery slide with a title and paragraph on the left and a five-image collage on the right and bottom."; + +export const Schema = z.object({ + title: z.string().min(5).max(16).default("Image Gallery").meta({ + description: "Main gallery heading.", + }), + description: z.string().min(50).max(130).default( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore." + ).meta({ + description: "Supporting paragraph shown under the title.", + }), + topCenterImage: z.object({ + __image_url__: + z.string().default("https://images.unsplash.com/photo-1521737711867-e3b97375f902?auto=format&fit=crop&w=800&q=80"), + __image_prompt__: z.string().min(10).max(100).default("Design team discussing project board"), + }).default({ + __image_url__: "https://images.unsplash.com/photo-1521737711867-e3b97375f902?auto=format&fit=crop&w=800&q=80", + __image_prompt__: "Design team discussing project board", + }).meta({ + description: "Top-middle gallery image.", + }), + topRightImage: z.object({ + __image_url__: + z.string().default("https://images.unsplash.com/photo-1455390582262-044cdead277a?auto=format&fit=crop&w=800&q=80"), + __image_prompt__: z.string().min(10).max(100).default("Creative desk with notebook and photos"), + }).default({ + __image_url__: + "https://images.unsplash.com/photo-1455390582262-044cdead277a?auto=format&fit=crop&w=800&q=80", + __image_prompt__: "Creative desk with notebook and photos", + }).meta({ + description: "Top-right gallery image.", + }), + bottomWideImage: z.object({ + __image_url__: + z.string().default("https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?auto=format&fit=crop&w=1300&q=80"), + __image_prompt__: z.string().min(10).max(100).default("City skyline seen from office window"), + }).default({ + __image_url__: + "https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?auto=format&fit=crop&w=1300&q=80", + __image_prompt__: "City skyline seen from office window", + }).meta({ + description: "Bottom-left wide gallery image.", + }), + bottomCenterImage: z.object({ + __image_url__: + z.string().default("https://images.unsplash.com/photo-1517048676732-d65bc937f952?auto=format&fit=crop&w=900&q=80"), + __image_prompt__: z.string().min(10).max(100).default("Art gallery wall with framed photos"), + }).default({ + __image_url__: + "https://images.unsplash.com/photo-1517048676732-d65bc937f952?auto=format&fit=crop&w=900&q=80", + __image_prompt__: "Art gallery wall with framed photos", + }).meta({ + description: "Bottom-center gallery image.", + }), + bottomRightImage: z.object({ + __image_url__: + z.string().default("https://images.unsplash.com/photo-1521791136064-7986c2920216?auto=format&fit=crop&w=900&q=80"), + __image_prompt__: z.string().min(10).max(100).default("Office workshop with presentation board"), + }).default({ + __image_url__: + "https://images.unsplash.com/photo-1521791136064-7986c2920216?auto=format&fit=crop&w=900&q=80", + __image_prompt__: "Office workshop with presentation board", + }).meta({ + description: "Bottom-right gallery image.", + }), +}); + +export type SchemaType = z.infer; + +const ImageGallerySlide = ({ data }: { data: Partial }) => { + const { + title, + description, + topCenterImage, + topRightImage, + bottomWideImage, + bottomCenterImage, + bottomRightImage, + } = data; + + return ( +
+
+ +
+

+ {title} +

+

{description}

+
+
+ {topCenterImage?.__image_prompt__ + {topRightImage?.__image_prompt__ +
+
+ + +
+ {bottomWideImage?.__image_prompt__} + {bottomCenterImage?.__image_prompt__} + {bottomRightImage?.__image_prompt__} +
+
+ ); +}; + +export default ImageGallerySlide; diff --git a/electron/servers/nextjs/app/presentation-templates/ProductOverview/IntroductionSlide.tsx b/electron/servers/nextjs/app/presentation-templates/ProductOverview/IntroductionSlide.tsx new file mode 100644 index 00000000..f30cd3f2 --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/ProductOverview/IntroductionSlide.tsx @@ -0,0 +1,101 @@ +import * as z from "zod"; + + + +export const slideLayoutId = "product-overview-introduction-slide"; +export const slideLayoutName = "Product Overview Introduction Slide"; +export const slideLayoutDescription = + "A split slide with a large portrait image on the left and a structured introduction column on the right containing a main title and two labeled body paragraphs."; + +const IntroBlockSchema = z.object({ + label: z.string().min(3).max(12).meta({ + description: "Uppercase mini-heading shown above each intro paragraph.", + }), + body: z.string().min(40).max(96).meta({ + description: "Supporting paragraph content for the intro block.", + }), +}); + +export const Schema = z.object({ + title: z.string().min(4).max(16).default("Introduction").meta({ + description: "Primary title in the right column.", + }), + portraitImage: z.object({ + __image_url__: z.string().url().default("https://images.unsplash.com/photo-1521119989659-a83eee488004?auto=format&fit=crop&w=1200&q=80"), + __image_prompt__: z.string().min(10).max(100).default("Two business professionals in office"), + }).default({ + __image_url__: + "https://images.unsplash.com/photo-1521119989659-a83eee488004?auto=format&fit=crop&w=1200&q=80", + __image_prompt__: "Two business professionals in office", + }).meta({ + description: "Main portrait image shown on the left half.", + }), + blocks: z + .array(IntroBlockSchema) + .min(2) + .max(2) + .default([ + { + label: "TAGLINE", + body: "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea.", + }, + { + label: "TAGLINE", + body: "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea.", + }, + ]) + .meta({ + description: "Two short intro content blocks shown in the text column.", + }), +}); + +export type SchemaType = z.infer; + +const IntroductionSlide = ({ data }: { data: Partial }) => { + const { title, portraitImage, blocks } = data; + + return ( +
+
+
+ + {portraitImage?.__image_url__ && ( + {portraitImage.__image_prompt__} + )} +
+ +
+

+ {title} +

+ +
+ {blocks?.map((block, index) => ( +
+

+ {block.label} +

+

+ {block.body} +

+
+ ))} +
+
+
+
+ ); +}; + +export default IntroductionSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/ProductOverview/KpiCardsSlide.tsx b/electron/servers/nextjs/app/presentation-templates/ProductOverview/KpiCardsSlide.tsx new file mode 100644 index 00000000..705803fb --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/ProductOverview/KpiCardsSlide.tsx @@ -0,0 +1,114 @@ +import * as z from "zod"; + + +export const slideLayoutId = "product-overview-kpi-cards-slide"; +export const slideLayoutName = "Product Overview KPI Cards Slide"; +export const slideLayoutDescription = + "A KPI overview slide with a dark-tinted background image, large title, and a two-row grid of six white KPI cards."; + +const KpiSchema = z.object({ + value: z.string().min(1).max(8).meta({ + description: "Primary KPI value shown in a card.", + }), + body: z.string().min(10).max(28).meta({ + description: "Short KPI supporting text.", + }), +}); + +export const Schema = z.object({ + title: z.string().min(3).max(10).default("KPIs").meta({ + description: "Main title shown in the top-left corner.", + }), + kpiIcon: z.object({ + __icon_url__: z.string().default("https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg"), + __icon_query__: z.string().min(3).max(30).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 KPI card badge.", + }), + backgroundImage: z.object({ + __image_url__: z.string().url().default("https://images.unsplash.com/photo-1521737604893-d14cc237f11d?auto=format&fit=crop&w=1600&q=80"), + __image_prompt__: z.string().min(10).max(100).default("Business team using laptop in meeting"), + }).default({ + __image_url__: + "https://images.unsplash.com/photo-1521737604893-d14cc237f11d?auto=format&fit=crop&w=1600&q=80", + __image_prompt__: "Business team using laptop in meeting", + }).meta({ + description: "Background image behind the KPI cards.", + }), + items: z + .array(KpiSchema) + .min(3) + .max(6) + .default([ + { value: "X 5", body: "Lorem ipsum dolor sit." }, + { value: "X 5", body: "Lorem ipsum dolor sit." }, + { value: "X 5", body: "Lorem ipsum dolor sit." }, + { value: "X 5", body: "Lorem ipsum dolor sit." }, + { value: "X 5", body: "Lorem ipsum dolor sit." }, + { value: "X 5", body: "Lorem ipsum dolor sit." }, + ]) + .meta({ + description: "Six KPI cards displayed in a 3x2 grid.", + }), +}); + +export type SchemaType = z.infer; + +const KpiCardsSlide = ({ data }: { data: Partial }) => { + const { title, kpiIcon, backgroundImage, items } = data; + + return ( +
+ {backgroundImage?.__image_url__ && ( + {backgroundImage?.__image_prompt__} + )} + +
+ +
+

+ {title} +

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

+ {item.value} +

+

+ {item.body} +

+
+ ))} +
+
+ ); +}; + +export default KpiCardsSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/ProductOverview/MarketOpportunitySlide.tsx b/electron/servers/nextjs/app/presentation-templates/ProductOverview/MarketOpportunitySlide.tsx new file mode 100644 index 00000000..377c2b0e --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/ProductOverview/MarketOpportunitySlide.tsx @@ -0,0 +1,108 @@ +import * as z from "zod"; + +export const slideLayoutId = "product-overview-market-opportunity-slide"; +export const slideLayoutName = "Product Overview Market Opportunity Slide"; +export const slideLayoutDescription = + "A market opportunity slide with title and intro text on the left, four bullet lines extending toward the right, and concentric value circles as the visual focal point."; + +const BulletSchema = z.object({ + text: z.string().min(12).max(46).meta({ + description: "Bullet text shown on the left side of a line.", + }), +}); + +export const Schema = z.object({ + title: z.string().min(8).max(22).default("Market Opportunity").meta({ + description: "Main heading shown at the top-left.", + }), + subtitle: z.string().min(40).max(110).default( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt." + ).meta({ + description: "Supporting text under the main heading.", + }), + bullets: z + .array(BulletSchema) + .min(4) + .max(4) + .default([ + { text: "Ut enim ad minim veniam, quis" }, + { text: "Ut enim ad minim veniam, quis" }, + { text: "Ut enim ad minim veniam, quis" }, + { text: "Ut enim ad minim veniam, quis" }, + ]) + .meta({ + description: "Four bullet-line entries shown on the left.", + }), + values: z + .array(z.string().min(2).max(6)) + .min(4) + .max(4) + .default(["$33", "$20", "$120", "$200"]) + .meta({ + description: "Four values shown from outer to inner circles.", + }), +}); + +export type SchemaType = z.infer; + + +const COLORS = [ + "#5f7f79", + "#1f5a4f", + "#0d4f43", + "#06463d", +]; + +const MarketOpportunitySlide = ({ data }: { data: Partial }) => { + const { title, subtitle, bullets, values } = data; + + return ( +
+
+

+ {title} +

+

{subtitle}

+
+ +
+ {bullets?.map((bullet, index) => ( +
+ +

{bullet.text}

+ + +
+ ))} +
+ +
+ {values?.map((value, index) => ( +
+

+ {value} +

+
+ ))} +
+
+ ); +}; + +export default MarketOpportunitySlide; diff --git a/electron/servers/nextjs/app/presentation-templates/ProductOverview/MeetTeamSlide.tsx b/electron/servers/nextjs/app/presentation-templates/ProductOverview/MeetTeamSlide.tsx new file mode 100644 index 00000000..5922c14d --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/ProductOverview/MeetTeamSlide.tsx @@ -0,0 +1,150 @@ +import * as z from "zod"; + + +export const slideLayoutId = "product-overview-meet-team-slide"; +export const slideLayoutName = "Product Overview Meet Team Slide"; +export const slideLayoutDescription = + "A team introduction slide with a title and intro text on top, followed by four profile cards where one card can be highlighted with a dark footer style."; + +const MemberSchema = z.object({ + title: z.string().min(2).max(12).meta({ + description: "Member role or short heading.", + }), + name: z.string().min(2).max(16).meta({ + description: "Member name shown in the card footer.", + }), + image: z.object({ + __image_url__: z.string().url().default("https://i.pravatar.cc/600?img=12"), + __image_prompt__: z.string().min(10).max(100).default("Professional male portrait with suit"), + }).default({ + __image_url__: "https://i.pravatar.cc/600?img=12", + __image_prompt__: "Professional male portrait with suit", + }).meta({ + description: "Portrait image for a team member card.", + }), + highlighted: z.boolean().default(false).meta({ + description: "Whether this card uses the dark highlight footer.", + }), +}); + +export const Schema = z.object({ + title: z.string().min(8).max(18).default("Meet Our Team").meta({ + description: "Main title at the top-left.", + }), + taglineLabel: z.string().min(3).max(10).default("TAGLINE").meta({ + description: "Small heading above team description.", + }), + taglineBody: z.string().min(50).max(100).default( + "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea." + ).meta({ + description: "Short descriptive paragraph at top-right.", + }), + members: z + .array(MemberSchema) + .min(4) + .max(4) + .default([ + { + title: "CEO", + name: "Lanny LA", + image: { + __image_url__: "https://i.pravatar.cc/600?img=12", + __image_prompt__: "Professional male portrait with suit", + }, + highlighted: false, + }, + { + title: "HEADING 2", + name: "Lanny LA", + image: { + __image_url__: "https://i.pravatar.cc/600?img=13", + __image_prompt__: "Professional male portrait with tie", + }, + highlighted: false, + }, + { + title: "HEADING 3", + name: "Lanny LA", + image: { + __image_url__: "https://i.pravatar.cc/600?img=14", + __image_prompt__: "Professional male portrait smiling", + }, + highlighted: true, + }, + { + title: "HEADING 2", + name: "Lanny LA", + image: { + __image_url__: "https://i.pravatar.cc/600?img=15", + __image_prompt__: "Professional male portrait office", + }, + highlighted: false, + }, + ]) + .meta({ + description: "Four team member cards shown in one row.", + }), +}); + +export type SchemaType = z.infer; + +const MeetTeamSlide = ({ data }: { data: Partial }) => { + const { title, taglineLabel, taglineBody, members } = data; + + return ( +
+
+

+ {title} +

+ +
+

+ {taglineLabel} +

+

{taglineBody}

+
+
+ +
+ {members?.map((member, index) => ( +
+ {member.image.__image_prompt__} +
+

+ {member.title} +

+

+ {member.name} +

+
+
+ ))} +
+
+ ); +}; + +export default MeetTeamSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/ProductOverview/MissionVisionSlide.tsx b/electron/servers/nextjs/app/presentation-templates/ProductOverview/MissionVisionSlide.tsx new file mode 100644 index 00000000..7b94a574 --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/ProductOverview/MissionVisionSlide.tsx @@ -0,0 +1,88 @@ +import * as z from "zod"; + + +export const slideLayoutId = "product-overview-mission-vision-slide"; +export const slideLayoutName = "Product Overview Mission and Vision Slide"; +export const slideLayoutDescription = + "A quadrant layout with a large title in the top-left block, mission text in the top-right dark block, vision text in the bottom-left dark block, and an image in the bottom-right block."; + +export const Schema = z.object({ + title: z.string().min(8).max(20).default("Mission & Vision").meta({ + description: "Primary heading shown in the top-left tile.", + }), + + missionLabel: z.string().min(3).max(10).default("MISSION").meta({ + description: "Mission section label.", + }), + missionBody: z.string().min(40).max(98).default( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore." + ).meta({ + description: "Mission paragraph content.", + }), + visionLabel: z.string().min(3).max(10).default("VISION").meta({ + description: "Vision section label.", + }), + visionBody: z.string().min(40).max(98).default( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore." + ).meta({ + description: "Vision paragraph content.", + }), + image: z.object({ + __image_url__: z.string(), + __image_prompt__: z.string(), + }).optional().meta({ + description: "Bottom-right supporting image. Optional.", + }).default({ + __image_url__: "https://images.unsplash.com/photo-1517248135467-4c7edcad34c4?auto=format&fit=crop&w=1400&q=80", + __image_prompt__: "Business silhouette at window skyline", + }), +}); + +export type SchemaType = z.infer; + +const MissionVisionSlide = ({ data }: { data: Partial }) => { + const { title, missionLabel, missionBody, visionLabel, visionBody, image } = data; + + return ( +
+
+
+

+ {title} +

+
+ +
+

+ {missionLabel} +

+

{missionBody}

+
+ +
+

+ {visionLabel} +

+

{visionBody}

+
+
+ + {image?.__image_url__ && ( + {image.__image_prompt__} + )} +
+
+
+ ); +}; + +export default MissionVisionSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/ProductOverview/OurServicesSlide.tsx b/electron/servers/nextjs/app/presentation-templates/ProductOverview/OurServicesSlide.tsx new file mode 100644 index 00000000..ce8067fa --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/ProductOverview/OurServicesSlide.tsx @@ -0,0 +1,127 @@ +import * as z from "zod"; + + +export const slideLayoutId = "product-overview-our-services-slide"; +export const slideLayoutName = "Product Overview Our Services Slide"; +export const slideLayoutDescription = + "A services slide with title and intro copy on the left, a large image below, and a two-by-two service card matrix on the right."; + +const ServiceSchema = z.object({ + heading: z.string().min(4).max(12).meta({ + description: "Service card heading.", + }), + body: z.string().min(20).max(44).meta({ + description: "Service card short description.", + }), + dark: z.boolean().default(false).meta({ + description: "Whether this service card uses the dark style.", + }), +}); + +export const Schema = z.object({ + title: z.string().min(6).max(18).default("Our Services").meta({ + description: "Main heading shown at the top-left.", + }), + taglineLabel: z.string().min(3).max(10).default("TAGLINE").meta({ + description: "Small label above left paragraph.", + }), + taglineBody: z.string().min(40).max(100).default( + "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea." + ).meta({ + description: "Supporting text shown beneath the tagline label.", + }), + featureImage: z.object({ + __image_url__: z.string().url().default("https://images.unsplash.com/photo-1521737604893-d14cc237f11d?auto=format&fit=crop&w=1200&q=80"), + __image_prompt__: z.string().min(10).max(100).default("Customer support team in office"), + }).default({ + __image_url__: + "https://images.unsplash.com/photo-1521737604893-d14cc237f11d?auto=format&fit=crop&w=1200&q=80", + __image_prompt__: "Customer support team in office", + }).meta({ + description: "Main image shown at the lower left side.", + }), + services: z + .array(ServiceSchema) + .min(4) + .max(4) + .default([ + { heading: "HEADING 1", body: "Lorem ipsum dolor sit amet, consectetur", dark: false }, + { heading: "HEADING 2", body: "Lorem ipsum dolor sit amet, consectetur", dark: true }, + { heading: "HEADING 3", body: "Lorem ipsum dolor sit amet, consectetur", dark: false }, + { heading: "HEADING 4", body: "Lorem ipsum dolor sit amet, consectetur", dark: false }, + ]) + .meta({ + description: "Four service cards rendered on the right side.", + }), +}); + +export type SchemaType = z.infer; + +const OurServicesSlide = ({ data }: { data: Partial }) => { + const { title, taglineLabel, taglineBody, featureImage, services } = data; + + return ( +
+
+
+ +

+ {title} +

+ +
+

+ {taglineLabel} +

+

{taglineBody}

+
+
+
+ + {featureImage?.__image_url__ && ( + {featureImage?.__image_prompt__} + )} +
+
+ + + +
+ {services?.map((service, index) => ( +
+

+ {service.heading} +

+

+ {service.body} +

+
+ ))} +
+
+ ); +}; + +export default OurServicesSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/ProductOverview/PricingPlanSlide.tsx b/electron/servers/nextjs/app/presentation-templates/ProductOverview/PricingPlanSlide.tsx new file mode 100644 index 00000000..fd24e29a --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/ProductOverview/PricingPlanSlide.tsx @@ -0,0 +1,153 @@ +import * as z from "zod"; + + +export const slideLayoutId = "product-overview-pricing-plan-slide"; +export const slideLayoutName = "Product Overview Pricing Plan Slide"; +export const slideLayoutDescription = + "A three-column pricing slide with one emphasized center plan and feature bullet lists for each plan."; + +const PlanSchema = z.object({ + price: z.string().min(4).max(12).meta({ + description: "Plan price label shown at the top of each card.", + }), + description: z.string().min(18).max(26).meta({ + description: "Short statement describing the plan.", + }), + features: z + .array(z.string().min(10).max(24)) + .min(4) + .max(4) + .meta({ + description: "Four bullet features shown in the pricing card.", + }), + highlighted: z.boolean().default(false).meta({ + description: "Whether this card uses the highlighted dark style.", + }), +}); + +export const Schema = z.object({ + title: z.string().min(6).max(18).default("Pricing Plan").meta({ + description: "Main slide title.", + }), + featureIcon: z.object({ + __icon_url__: z.string().default("https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg"), + __icon_query__: z.string().min(3).max(30).default("check icon"), + }).default({ + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg", + __icon_query__: "check icon", + }).meta({ + description: "Icon used for each feature bullet in plan cards.", + }), + plans: z + .array(PlanSchema) + .min(3) + .max(3) + .default([ + { + price: "$80/MONTH", + description: "Lorem ipsum dolor sit.", + features: [ + "Lorem ipsum dolor sit.", + "Lorem ipsum dolor sit.", + "Lorem ipsum dolor sit.", + "Lorem ipsum dolor sit.", + ], + highlighted: false, + }, + { + price: "$80/MONTH", + description: "Lorem ipsum dolor sit.", + features: [ + "Lorem ipsum dolor sit.", + "Lorem ipsum dolor sit.", + "Lorem ipsum dolor sit.", + "Lorem ipsum dolor sit.", + ], + highlighted: true, + }, + { + price: "$80/MONTH", + description: "Lorem ipsum dolor sit.", + features: [ + "Lorem ipsum dolor sit.", + "Lorem ipsum dolor sit.", + "Lorem ipsum dolor sit.", + "Lorem ipsum dolor sit.", + ], + highlighted: false, + }, + ]) + .meta({ + description: "Three pricing cards displayed across the slide.", + }), +}); + +export type SchemaType = z.infer; + +const PricingPlanSlide = ({ data }: { data: Partial }) => { + const { title, featureIcon, plans } = data; + + return ( +
+
+

+ {title} +

+
+ +
+ {plans?.map((plan, index) => { + const active = plan.highlighted; + return ( +
+

+ {plan.price} +

+

+ {plan.description} +

+ +
+ {plan.features.map((feature, featureIndex) => ( +
+ {featureIcon?.__icon_query__} +

+ {feature} +

+
+ ))} +
+
+ ); + })} +
+
+ ); +}; + +export default PricingPlanSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/ProductOverview/ProcessSlide.tsx b/electron/servers/nextjs/app/presentation-templates/ProductOverview/ProcessSlide.tsx new file mode 100644 index 00000000..8e57213f --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/ProductOverview/ProcessSlide.tsx @@ -0,0 +1,173 @@ +import * as z from "zod"; + + +export const slideLayoutId = "product-overview-process-slide"; +export const slideLayoutName = "Product Overview Process Slide"; +export const slideLayoutDescription = + "A process diagram slide with five connected hexagon steps and alternating caption blocks above and below the flow."; + +const StepSchema = z.object({ + label: z.string().min(3).max(10).meta({ + description: "Short uppercase label for a process step.", + }), + body: z.string().max(20).meta({ + description: "Brief explanatory text for the process step.", + }), + icon: z.object({ + __icon_url__: z.string(), + __icon_query__: z.string(), + }).default({ + __icon_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg", + __icon_query__: "pulse icon", + }), + highlighted: z.boolean().default(false).meta({ + description: "Whether the hexagon is emphasized with dark fill.", + }), +}); + +export const Schema = z.object({ + title: z.string().min(5).max(14).default("PROCESS").meta({ + description: "Main title shown in the top-left corner.", + }), + + steps: z + .array(StepSchema) + .min(5) + .max(5) + .default([ + { + label: "TAGLINE", body: "Ut enim ad minim.", icon: { + __icon_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg", + __icon_query__: "pulse icon", + }, highlighted: false + }, + { + label: "TAGLINE", body: "Ut enim ad minim.", icon: { + __icon_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg", + __icon_query__: "upload icon", + }, highlighted: false + }, + { + label: "TAGLINE", body: "Ut enim ad minim.", icon: { + __icon_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg", + __icon_query__: "pulse icon", + }, highlighted: false + }, + { + label: "TAGLINE", body: "Ut enim ad minim.", icon: { + __icon_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg", + __icon_query__: "upload icon", + }, highlighted: false + }, + { + label: "TAGLINE", body: "Ut enim ad minim.", icon: { + __icon_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg", + __icon_query__: "pulse icon", + }, highlighted: true + }, + ]) + .meta({ + description: "Five process steps rendered from left to right.", + }), +}); + +export type SchemaType = z.infer; + + +const ProcessSlide = ({ data }: { data: Partial }) => { + const { title, steps } = data; + + return ( +
+
+

+ {title} +

+
+ + + +
+ {steps?.map((step, index) => { + if (index % 2 === 0) { + return ( +
+
+
+ {step.icon.__icon_query__} + + + +
+
+ + + +
+
+
+

+ {step.label} +

+

{step.body}

+
+
+ ) + } + else { + return ( +
+
+

+ {step.label} +

+

{step.body}

+
+ +
+
+ + + + {step.icon.__icon_query__} +
+
+ + + +
+
+
+ ) + } + })} +
+
+ ); +}; + +export default ProcessSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/ProductOverview/ReportSnapshotSlide.tsx b/electron/servers/nextjs/app/presentation-templates/ProductOverview/ReportSnapshotSlide.tsx new file mode 100644 index 00000000..bb0063ec --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/ProductOverview/ReportSnapshotSlide.tsx @@ -0,0 +1,162 @@ +import * as z from "zod"; + + +export const slideLayoutId = "product-overview-report-snapshot-slide"; +export const slideLayoutName = "Product Overview Report Snapshot Slide"; +export const slideLayoutDescription = + "A report summary slide with a left-edge photo strip, title and intro copy, a compact bar chart card, and a KPI callout card on the right."; + +const BarSchema = z.object({ + value: z.number().min(10).max(100).meta({ + description: "Relative bar value used in the spending mini chart.", + }), +}); + +export const Schema = z.object({ + title: z.string().min(4).max(12).default("Report").meta({ + description: "Slide heading text.", + }), + taglineLabel: z.string().min(3).max(10).default("TAGLINE").meta({ + description: "Small label above intro paragraph.", + }), + taglineBody: z.string().min(40).max(100).default( + "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea." + ).meta({ + description: "Intro paragraph shown beneath the heading.", + }), + sideImage: z.object({ + __image_url__: z.string().url().default("https://images.unsplash.com/photo-1520607162513-77705c0f0d4a?auto=format&fit=crop&w=700&q=80"), + __image_prompt__: z.string().min(10).max(100).default("Team members reviewing charts together"), + }).default({ + __image_url__: + "https://images.unsplash.com/photo-1520607162513-77705c0f0d4a?auto=format&fit=crop&w=700&q=80", + __image_prompt__: "Team members reviewing charts together", + }).meta({ + description: "Left-side vertical image strip.", + }), + chartTitle: z.string().min(3).max(20).default("Sandro Tavares").meta({ + description: "Name displayed in the chart card.", + }), + bars: z + .array(BarSchema) + .min(8) + .max(8) + .default([ + { value: 52 }, + { value: 24 }, + { value: 35 }, + { value: 48 }, + { value: 26 }, + { value: 72 }, + { value: 47 }, + { value: 55 }, + ]) + .meta({ + description: "Eight bars used in the spending card chart.", + }), + metricValue: z.string().min(1).max(8).default("X 5").meta({ + description: "KPI value in the callout card.", + }), + metricBody: z.string().min(10).max(18).default("Lorem ipsum.").meta({ + description: "KPI short text in the callout card.", + }), + metricIcon: z.object({ + __icon_url__: z.string().default("https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/placeholder.svg"), + __icon_query__: z.string().min(3).max(30).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 KPI callout card.", + }), +}); + +export type SchemaType = z.infer; + +const ReportSnapshotSlide = ({ data }: { data: Partial }) => { + const { + title, + taglineLabel, + taglineBody, + sideImage, + chartTitle, + bars, + metricValue, + metricBody, + metricIcon, + } = data; + + return ( +
+ {sideImage?.__image_url__ && ( + {sideImage?.__image_prompt__} + )} + +
+

+ {title} +

+ +
+

+ {taglineLabel} +

+

{taglineBody}

+
+
+ +
+

Spendings

+

+ {chartTitle} +

+ +
+ {bars?.map((bar, index) => ( +
+ ))} +
+ +
+

Current margin: April Spendings

+

$350.00 / $640.00

+
+
+ +
+
+
+ {metricIcon?.__icon_query__} +
+

+ {metricValue} +

+
+

+ {metricBody} +

+
+
+ ); +}; + +export default ReportSnapshotSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/ProductOverview/TableOfContentSlide.tsx b/electron/servers/nextjs/app/presentation-templates/ProductOverview/TableOfContentSlide.tsx new file mode 100644 index 00000000..5cdba0a3 --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/ProductOverview/TableOfContentSlide.tsx @@ -0,0 +1,94 @@ +import * as z from "zod"; + +const PRODUCT_BG = "#d7dddd"; +const PRODUCT_DARK = "#05463d"; + + +export const slideLayoutId = "product-overview-table-of-content-slide"; +export const slideLayoutName = "Product Overview Table of Content Slide"; +export const slideLayoutDescription = + "A two-column table-of-content slide with section titles and numbers on a dark left panel and a large title plus description paragraph on the right panel."; + +const SectionSchema = z.object({ + title: z.string().min(4).max(14).meta({ + description: "Section label shown in the left navigation column.", + }), + number: z.string().min(2).max(3).meta({ + description: "Section number shown beside the section label.", + }), + description: z.string().min(4).max(22).optional().meta({ + description: "Section description shown in the right column.", + }), +}); + +export const Schema = z.object({ + title: z.string().min(6).max(18).default("Table Of Content").meta({ + description: "Heading in the right-side content area.", + }), + description: z.string().min(50).max(120).default( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore." + ).meta({ + description: "Supporting descriptive paragraph under the heading.", + }), + sections: z + .array(SectionSchema) + .min(6) + .max(6) + .default([ + { title: "SECTION TITLE", number: "01", description: "Lorem ipsum dolor sit." }, + { title: "SECTION TITLE", number: "02", description: "Lorem ipsum dolor sit." }, + { title: "SECTION TITLE", number: "03", description: "Lorem ipsum dolor sit." }, + { title: "SECTION TITLE", number: "04", description: "Lorem ipsum dolor sit." }, + { title: "SECTION TITLE", number: "05", description: "Lorem ipsum dolor sit." }, + { title: "SECTION TITLE", number: "06", description: "Lorem ipsum dolor sit." }, + ]) + .meta({ + description: "Six rows listed in the table of contents panel.", + }), +}); + +export type SchemaType = z.infer; + +const TableOfContentSlide = ({ data }: { data: Partial }) => { + const { title, description, sections } = data; + + return ( +
+
+
+
+ {sections?.map((section, index) => ( +
+
+ +

+ {section.title} +

+

{section.description}

+
+

{section.number}

+
+ ))} +
+
+ +
+

+ {title} +

+

+ {description} +

+
+
+
+ ); +}; + +export default TableOfContentSlide; diff --git a/electron/servers/nextjs/app/presentation-templates/ProductOverview/settings.json b/electron/servers/nextjs/app/presentation-templates/ProductOverview/settings.json new file mode 100644 index 00000000..64702b29 --- /dev/null +++ b/electron/servers/nextjs/app/presentation-templates/ProductOverview/settings.json @@ -0,0 +1,5 @@ +{ + "description": "Product pitch layouts for cover, narrative, comparisons, KPIs, pricing, and team 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 bbf1872b..c35ed584 100644 --- a/electron/servers/nextjs/app/presentation-templates/index.tsx +++ b/electron/servers/nextjs/app/presentation-templates/index.tsx @@ -27,6 +27,24 @@ import EducationServicesSplitSlide, { Schema as EduServicesSplitSchema, slideLay import EducationStatisticsGridSlide, { Schema as EduStatisticsGridSchema, slideLayoutId as EduStatisticsGridId, slideLayoutName as EduStatisticsGridName, slideLayoutDescription as EduStatisticsGridDesc } from "./Education/EducationStatisticsGridSlide"; import EducationTimelineSlide, { Schema as EduTimelineSchema, slideLayoutId as EduTimelineId, slideLayoutName as EduTimelineName, slideLayoutDescription as EduTimelineDesc } from "./Education/EducationTimelineSlide"; +// Product Overview templates +import BusinessChallengesCardsSlide, { Schema as PoBizChallengesCardsSchema, slideLayoutId as PoBizChallengesCardsId, slideLayoutName as PoBizChallengesCardsName, slideLayoutDescription as PoBizChallengesCardsDesc } from "./ProductOverview/BusinessChallengesCardsSlide"; +import BusinessChallengesGridSlide, { Schema as PoBizChallengesGridSchema, slideLayoutId as PoBizChallengesGridId, slideLayoutName as PoBizChallengesGridName, slideLayoutDescription as PoBizChallengesGridDesc } from "./ProductOverview/BusinessChallengesGridSlide"; +import ComparisonChartSlide, { Schema as PoComparisonChartSchema, slideLayoutId as PoComparisonChartId, slideLayoutName as PoComparisonChartName, slideLayoutDescription as PoComparisonChartDesc } from "./ProductOverview/ComparisonChartSlide"; +import ComparisonTableWithTextSlide, { Schema as PoComparisonTableSchema, slideLayoutId as PoComparisonTableId, slideLayoutName as PoComparisonTableName, slideLayoutDescription as PoComparisonTableDesc } from "./ProductOverview/ComparisonTableWithTextSlide"; +import CoverSlide, { Schema as PoCoverSchema, slideLayoutId as PoCoverId, slideLayoutName as PoCoverName, slideLayoutDescription as PoCoverDesc } from "./ProductOverview/CoverSlide"; +import ImageGallerySlide, { Schema as PoImageGallerySchema, slideLayoutId as PoImageGalleryId, slideLayoutName as PoImageGalleryName, slideLayoutDescription as PoImageGalleryDesc } from "./ProductOverview/ImageGallerySlide"; +import IntroductionSlide, { Schema as PoIntroductionSchema, slideLayoutId as PoIntroductionId, slideLayoutName as PoIntroductionName, slideLayoutDescription as PoIntroductionDesc } from "./ProductOverview/IntroductionSlide"; +import KpiCardsSlide, { Schema as PoKpiCardsSchema, slideLayoutId as PoKpiCardsId, slideLayoutName as PoKpiCardsName, slideLayoutDescription as PoKpiCardsDesc } from "./ProductOverview/KpiCardsSlide"; +import MarketOpportunitySlide, { Schema as PoMarketOpportunitySchema, slideLayoutId as PoMarketOpportunityId, slideLayoutName as PoMarketOpportunityName, slideLayoutDescription as PoMarketOpportunityDesc } from "./ProductOverview/MarketOpportunitySlide"; +import MeetTeamSlide, { Schema as PoMeetTeamSchema, slideLayoutId as PoMeetTeamId, slideLayoutName as PoMeetTeamName, slideLayoutDescription as PoMeetTeamDesc } from "./ProductOverview/MeetTeamSlide"; +import MissionVisionSlide, { Schema as PoMissionVisionSchema, slideLayoutId as PoMissionVisionId, slideLayoutName as PoMissionVisionName, slideLayoutDescription as PoMissionVisionDesc } from "./ProductOverview/MissionVisionSlide"; +import OurServicesSlide, { Schema as PoOurServicesSchema, slideLayoutId as PoOurServicesId, slideLayoutName as PoOurServicesName, slideLayoutDescription as PoOurServicesDesc } from "./ProductOverview/OurServicesSlide"; +import PricingPlanSlide, { Schema as PoPricingPlanSchema, slideLayoutId as PoPricingPlanId, slideLayoutName as PoPricingPlanName, slideLayoutDescription as PoPricingPlanDesc } from "./ProductOverview/PricingPlanSlide"; +import ProcessSlide, { Schema as PoProcessSchema, slideLayoutId as PoProcessId, slideLayoutName as PoProcessName, slideLayoutDescription as PoProcessDesc } from "./ProductOverview/ProcessSlide"; +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"; + // 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"; @@ -201,6 +219,7 @@ import neoModernSettings from "./neo-modern/settings.json"; 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"; // Helper to create template entry @@ -234,6 +253,25 @@ export const educationTemplates: TemplateWithData[] = [ createTemplateEntry(EducationTimelineSlide, EduTimelineSchema, EduTimelineId, EduTimelineName, EduTimelineDesc, "education", "EducationTimelineSlide"), ]; +export const productOverviewTemplates: TemplateWithData[] = [ + createTemplateEntry(CoverSlide, PoCoverSchema, PoCoverId, PoCoverName, PoCoverDesc, "product-overview", "CoverSlide"), + createTemplateEntry(TableOfContentSlide, PoTableOfContentSchema, PoTableOfContentId, PoTableOfContentName, PoTableOfContentDesc, "product-overview", "TableOfContentSlide"), + createTemplateEntry(IntroductionSlide, PoIntroductionSchema, PoIntroductionId, PoIntroductionName, PoIntroductionDesc, "product-overview", "IntroductionSlide"), + createTemplateEntry(MissionVisionSlide, PoMissionVisionSchema, PoMissionVisionId, PoMissionVisionName, PoMissionVisionDesc, "product-overview", "MissionVisionSlide"), + createTemplateEntry(MarketOpportunitySlide, PoMarketOpportunitySchema, PoMarketOpportunityId, PoMarketOpportunityName, PoMarketOpportunityDesc, "product-overview", "MarketOpportunitySlide"), + createTemplateEntry(BusinessChallengesGridSlide, PoBizChallengesGridSchema, PoBizChallengesGridId, PoBizChallengesGridName, PoBizChallengesGridDesc, "product-overview", "BusinessChallengesGridSlide"), + createTemplateEntry(BusinessChallengesCardsSlide, PoBizChallengesCardsSchema, PoBizChallengesCardsId, PoBizChallengesCardsName, PoBizChallengesCardsDesc, "product-overview", "BusinessChallengesCardsSlide"), + createTemplateEntry(OurServicesSlide, PoOurServicesSchema, PoOurServicesId, PoOurServicesName, PoOurServicesDesc, "product-overview", "OurServicesSlide"), + createTemplateEntry(ProcessSlide, PoProcessSchema, PoProcessId, PoProcessName, PoProcessDesc, "product-overview", "ProcessSlide"), + createTemplateEntry(ComparisonChartSlide, PoComparisonChartSchema, PoComparisonChartId, PoComparisonChartName, PoComparisonChartDesc, "product-overview", "ComparisonChartSlide"), + createTemplateEntry(ComparisonTableWithTextSlide, PoComparisonTableSchema, PoComparisonTableId, PoComparisonTableName, PoComparisonTableDesc, "product-overview", "ComparisonTableWithTextSlide"), + createTemplateEntry(KpiCardsSlide, PoKpiCardsSchema, PoKpiCardsId, PoKpiCardsName, PoKpiCardsDesc, "product-overview", "KpiCardsSlide"), + createTemplateEntry(ReportSnapshotSlide, PoReportSnapshotSchema, PoReportSnapshotId, PoReportSnapshotName, PoReportSnapshotDesc, "product-overview", "ReportSnapshotSlide"), + createTemplateEntry(PricingPlanSlide, PoPricingPlanSchema, PoPricingPlanId, PoPricingPlanName, PoPricingPlanDesc, "product-overview", "PricingPlanSlide"), + createTemplateEntry(MeetTeamSlide, PoMeetTeamSchema, PoMeetTeamId, PoMeetTeamName, PoMeetTeamDesc, "product-overview", "MeetTeamSlide"), + createTemplateEntry(ImageGallerySlide, PoImageGallerySchema, PoImageGalleryId, PoImageGalleryName, PoImageGalleryDesc, "product-overview", "ImageGallerySlide"), +]; + export const neoGeneralTemplates: TemplateWithData[] = [ createTemplateEntry(TextSplitWithEmphasisBlockLayout, TextSplitWithEmphasisBlockSchema, TextSplitWithEmphasisBlockId, TextSplitWithEmphasisBlockName, TextSplitWithEmphasisBlockDesc, 'neo-general', 'TextSplitWithEmphasisBlock'), @@ -406,6 +444,7 @@ export const allLayouts: TemplateWithData[] = [ ...swiftTemplates, ...codeTemplates, ...educationTemplates, + ...productOverviewTemplates, ]; @@ -482,6 +521,13 @@ export const templates: TemplateLayoutsWithSettings[] = [ settings: educationSettings as TemplateGroupSettings, layouts: educationTemplates, }, + { + id: "product-overview", + name: "Product Overview", + description: productOverviewSettings.description, + settings: productOverviewSettings as TemplateGroupSettings, + layouts: productOverviewTemplates, + }, ]; // Helper to get templates by group ID