diff --git a/servers/nextjs/presentation-templates/swift/BulletsWithIconsTitleDescription.tsx b/servers/nextjs/presentation-templates/swift/BulletsWithIconsTitleDescription.tsx new file mode 100644 index 00000000..39c7548f --- /dev/null +++ b/servers/nextjs/presentation-templates/swift/BulletsWithIconsTitleDescription.tsx @@ -0,0 +1,242 @@ +import React from "react" +import * as z from "zod" + +import { IconSchema, ImageSchema } from '@/presentation-templates/defaultSchemes'; +import { RemoteSvgIcon } from "@/app/hooks/useRemoteSvgIcon"; + +const layoutId = "bullet-with-icons-title-description" +const layoutName = "Bullet With Icons Title Description" +const layoutDescription = "Bullet with icons with title and description and title and description for whole" + +const ItemSchema = z + .object({ + icon: IconSchema, + title: z.string().min(3).max(40).default("Lorem ipsum dolor"), + description: z + .string() + .min(0) + .max(160) + .default( + "Short supporting description that fits under the icon title." + ), + }) + .default({ + icon: { + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/graduation-cap-bold.svg", + __icon_query__: "feature icon", + }, + title: "Lorem ipsum dolor", + description: "Short supporting description that fits under the icon title.", + }) + +const Schema = z + .object({ + title: z + .string() + .min(3) + .max(60) + .default("Our Infographic"), + sideHeading: z.string().min(0).max(60).default("Lorem ipsum dolor sit amet,"), + sideParagraph: z + .string() + .min(0) + .max(300) + .default( + "Concise paragraph describing context. Keep it short and readable across one or two lines." + ), + items: z + .array(ItemSchema) + .min(3) + .max(4) + .default([ + { + icon: { + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/graduation-cap-bold.svg", + __icon_query__: "feature icon", + }, + title: "Lorem ipsum dolor", + description: + "Concise supporting text under the first icon explaining the point.", + }, + { + icon: { + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/user-bold.svg", + __icon_query__: "feature icon", + }, + title: "Lorem ipsum dolor", + description: + "Concise supporting text under the second icon explaining the point.", + }, + { + icon: { + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/calendar-blank-bold.svg", + __icon_query__: "feature icon", + }, + title: "Lorem ipsum dolor", + description: + "Concise supporting text under the third icon explaining the point.", + }, + { + icon: { + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/x-bold.svg", + __icon_query__: "feature icon", + }, + title: "Lorem ipsum dolor", + description: + "Concise supporting text under the fourth icon explaining the point.", + }, + ]), + website: z.string().min(6).max(60).default("www.yourwebsite.com"), + }) + .default({ + title: "Our Infographic", + sideHeading: "Lorem ipsum dolor sit amet,", + sideParagraph: + "Concise paragraph describing context. Keep it short and readable across one or two lines.", + items: [ + { + icon: { __icon_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/graduation-cap-bold.svg", __icon_query__: "feature icon" }, + title: "Lorem ipsum dolor", + description: + "Concise supporting text under the first icon explaining the point.", + }, + { + icon: { __icon_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/user-bold.svg", __icon_query__: "feature icon" }, + title: "Lorem ipsum dolor", + description: + "Concise supporting text under the second icon explaining the point.", + }, + { + icon: { __icon_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/calendar-blank-bold.svg", __icon_query__: "feature icon" }, + title: "Lorem ipsum dolor", + description: + "Concise supporting text under the third icon explaining the point.", + }, + { + icon: { __icon_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/x-bold.svg", __icon_query__: "feature icon" }, + title: "Lorem ipsum dolor", + description: + "Concise supporting text under the fourth icon explaining the point.", + }, + ], + website: "www.yourwebsite.com", + }) + +type SlideData = z.infer + +interface SlideLayoutProps { + data?: Partial +} + +const InfographicFourIcons: React.FC = ({ data }) => { + const slideData = data || {} + const items = slideData.items || [] + + const renderTitle = (title?: string) => { + if (!title) return null + const parts = title.split("\n") + return ( + <> + {parts.map((p, i) => ( +
{p}
+ ))} + + ) + } + + return ( + <> + + +
+ {/* Header */} +
+
+
+
+ {(slideData as any )?.__companyName__ && {(slideData as any)?.__companyName__}} +
+
+
+ + {/* Title + right paragraph */} +
+
+
+ {renderTitle(slideData.title)} +
+
+ {slideData.sideHeading && ( +
+ {slideData.sideHeading} +
+ )} + {slideData.sideParagraph && ( +
+ {slideData.sideParagraph} +
+ )} +
+
+
+ + {/* Icons row */} +
+
+ {items.slice(0, 4).map((item, idx) => ( +
+
+
+ {/* Icon */} + +
+
+
+ {item.title} +
+
+ {item.description} +
+
+ ))} +
+
+ + {/* Footer (standardized like IntroSlideLayout) */} +
+ {slideData.website} +
+
+
+
+ + ) +} + +export { Schema, layoutId, layoutName, layoutDescription } +export default InfographicFourIcons + + diff --git a/servers/nextjs/presentation-templates/swift/IconBulletListDescription.tsx b/servers/nextjs/presentation-templates/swift/IconBulletListDescription.tsx new file mode 100644 index 00000000..7a2e393d --- /dev/null +++ b/servers/nextjs/presentation-templates/swift/IconBulletListDescription.tsx @@ -0,0 +1,200 @@ +import React from "react" +import * as z from "zod" +import { RemoteSvgIcon } from "@/app/hooks/useRemoteSvgIcon"; + +const layoutId = "icon-bullet-list-description-slide" +const layoutName = "Icon Bullet List Description" +const layoutDescription = "Bullet list with title, description, and icon" + +const IconSchema = z + .object({ + __icon_url__: z + .string() + .default( + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/file-text-bold.svg" + ), + __icon_query__: z.string().min(0).max(80).default("feature icon"), + }) + .default({ + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/file-text-bold.svg", + __icon_query__: "feature icon", + }) + +const FeatureSchema = z + .object({ + title: z.string().min(4).max(28).default("Customizable Workflows"), + body: z + .string() + .min(20) + .max(140) + .default("Lorem ipsum dolor sit amet, dolor sit amet."), + icon: IconSchema, + }) + .default({ + title: "Customizable Workflows", + body: "Lorem ipsum dolor sit amet, dolor sit amet.", + icon: IconSchema.parse({}), + }) + +const Schema = z + .object({ + + title: z + .string() + .min(8) + .max(48) + .default("Key Product Features"), + description: z + .string() + .min(30) + .max(200) + .default( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean commodo ligula eget dolor." + ), + features: z + .array(FeatureSchema) + .min(3) + .max(4) + .default([ + FeatureSchema.parse({}), + { + title: "Multi-Device Access", + body: "Lorem ipsum dolor sit amet.", + icon: IconSchema.parse({ + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/devices-bold.svg", + }), + }, + { + title: "Scalable Architecture", + body: "Lorem ipsum dolor sit amet.", + icon: IconSchema.parse({ + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/chart-line-up-bold.svg", + }), + }, + { + title: "Detailed Reports", + body: "Lorem ipsum dolor sit amet.", + icon: IconSchema.parse({ + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/checks-bold.svg", + }), + }, + ]), + website: z.string().min(6).max(60).default("www.yourwebsite.com"), + }) + .default({ + + title: "Key Product Features", + description: + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean commodo ligula eget dolor.", + features: [ + FeatureSchema.parse({}), + { + title: "Multi-Device Access", + body: "Lorem ipsum dolor sit amet.", + icon: IconSchema.parse({ + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/devices-bold.svg", + }), + }, + { + title: "Scalable Architecture", + body: "Lorem ipsum dolor sit amet.", + icon: IconSchema.parse({ + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/chart-line-up-bold.svg", + }), + }, + { + title: "Detailed Reports", + body: "Lorem ipsum dolor sit amet.", + icon: IconSchema.parse({ + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/checks-bold.svg", + }), + }, + ], + website: "www.yourwebsite.com", + }) + +type SlideData = z.infer + +interface SlideLayoutProps { + data?: Partial +} + +const FeatureCards: React.FC = ({ data: slideData }) => { + const features = slideData?.features || [] + return ( + <> + + +
+ {/* Header */} +
+
+
+ {(slideData as any)?.__companyName__ && {(slideData as any)?.__companyName__}} +
+
+ + {/* Decorative right image area removed to keep imagery-driven design */} + +
+

{slideData?.title}

+

{slideData?.description}

+
+ + {/* Cyan band */} +
+ + {/* Feature cards */} +
+
+ {features.slice(0,4).map((f, i) => ( +
+
+
+ +
+
{f.title}
+

{f.body}

+
+
+ ))} +
+
+ + {/* Footer (standardized like IntroSlideLayout) */} +
+ {slideData?.website} +
+
+
+
+ + ) +} + +export { Schema, layoutId, layoutName, layoutDescription } +export default FeatureCards + + diff --git a/servers/nextjs/presentation-templates/swift/ImageListDescription.tsx b/servers/nextjs/presentation-templates/swift/ImageListDescription.tsx new file mode 100644 index 00000000..1ef23463 --- /dev/null +++ b/servers/nextjs/presentation-templates/swift/ImageListDescription.tsx @@ -0,0 +1,171 @@ +import React from "react" +import * as z from "zod" + +const layoutId = "image-list-description-slide" +const layoutName = "Image List Description" +const layoutDescription = "List of Images with subtitle and description with one description for the entire page" + +const ImageSchema = z + .object({ + __image_url__: z + .string() + .url() + .default( + "https://images.unsplash.com/photo-1522199710521-72d69614c702?w=1200&q=80&auto=format&fit=crop" + ), + __image_prompt__: z + .string() + .min(0) + .max(120) + .default("abstract gradient background"), + }) + .default({ + __image_url__: + "https://images.unsplash.com/photo-1522199710521-72d69614c702?w=1200&q=80&auto=format&fit=crop", + __image_prompt__: "abstract gradient background", + }) + +const ItemSchema = z + .object({ + title: z.string().min(2).max(40).default("Sample Title"), + description: z + .string() + .min(10) + .max(140) + .default("Short description for the image or item."), + image: ImageSchema, + }) + .default({ + title: "Sample Title", + description: "Short description for the image or item.", + image: ImageSchema.parse({}), + }) + +const Schema = z + .object({ + titleLine1: z.string().min(3).max(24).default("Meet Our"), + titleLine2: z.string().min(3).max(24).default("Team"), + description: z + .string() + .min(20) + .max(200) + .default( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit." + ), + items: z + .array(ItemSchema) + .min(3) + .max(6) + .default([ + ItemSchema.parse({}), + ItemSchema.parse({ title: "Another Item", description: "Concise supporting text.", image: ImageSchema.parse({}) }), + ItemSchema.parse({ title: "Third Item", description: "Concise supporting text.", image: ImageSchema.parse({}) }), + ]), + website: z.string().min(6).max(60).default("www.yourwebsite.com"), + }) + .default({ + + titleLine1: "Meet Our", + titleLine2: "Team", + description: + "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + items: [ + ItemSchema.parse({}), + ItemSchema.parse({ title: "Another Item", description: "Concise supporting text.", image: ImageSchema.parse({}) }), + ItemSchema.parse({ title: "Third Item", description: "Concise supporting text.", image: ImageSchema.parse({}) }), + ], + website: "www.yourwebsite.com", + }) + +type SlideData = z.infer + +interface SlideLayoutProps { + data?: Partial +} + +const TeamMembers: React.FC = ({ data: slideData }) => { + const items = slideData?.items || [] + return ( + <> + + +
+ {/* Header: diamond + business name */} +
+
+
+ { (slideData as any)?.__companyName__ && + {(slideData as any)?.__companyName__} + } +
+
+ +
+ {/* Left text stack */} +
+
+ {slideData?.titleLine1} +
+ {slideData?.titleLine2} +
+

+ {slideData?.description} +

+
+ + {/* Right generic image cards */} +
+ {items.slice(0, 3).map((it, i) => ( +
+ {/* Photo block uses provided image */} +
+ {it.image.__image_prompt__} +
+ {/* Cyan details panel */} +
+
{it.title}
+

{it.description}

+
+
+ ))} +
+
+ + {/* Footer line with website and end diamond */} +
+ {slideData?.website} +
+
+ + {/* Big bottom-right diamond */} +
+
+ + ) +} + +export { Schema, layoutId, layoutName, layoutDescription } +export default TeamMembers + + diff --git a/servers/nextjs/presentation-templates/swift/IntroSlideLayout.tsx b/servers/nextjs/presentation-templates/swift/IntroSlideLayout.tsx new file mode 100644 index 00000000..cd7b587b --- /dev/null +++ b/servers/nextjs/presentation-templates/swift/IntroSlideLayout.tsx @@ -0,0 +1,177 @@ +import React from "react" +import * as z from "zod" + +const layoutId = "IntroSlideLayout" +const layoutName = "Intro Slide Layout" +const layoutDescription = "Intro slide with header, title, subtitle, body, image. If used for last slide, then intro card should be disabled." + +const ImageSchema = z + .object({ + __image_url__: z.string().url().default("https://images.unsplash.com/photo-1522199710521-72d69614c702?w=1200&q=80&auto=format&fit=crop"), + __image_prompt__: z.string().min(0).max(120).default("abstract gradient background"), + }) + .default({ + __image_url__: + "https://images.unsplash.com/photo-1522199710521-72d69614c702?w=1200&q=80&auto=format&fit=crop", + __image_prompt__: "abstract gradient background", + }) + +const Schema = z + .object({ + + title: z + .string() + .min(12) + .max(68) + .default("Pitch Deck") + .meta({ description: "Main slide title" }), + + subtitlePrefix: z.string().min(3).max(40).default("Presentation"), + subtitleAccent: z.string().min(3).max(40).default("Template"), + + paragraph: z + .string() + .min(40) + .max(200) + .default( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." + ), + + website: z + .string() + .min(6) + .max(60) + .default("www.yourwebsite.com"), + + introCard: z + .object({ + enabled: z.boolean().default(false), + name: z.string().min(3).max(40).default("John Doe"), + date: z.string().min(4).max(40).default("Jan 1, 2025"), + }) + .default({ enabled: true, name: "John Doe", date: "Jan 1, 2025" }), + + media: z + .object({ + type: z.literal("image").default("image"), + image: ImageSchema, + }) + .default({ type: "image", image: ImageSchema.parse({}) }), + }) + .default({ + title: "Pitch Deck", + subtitlePrefix: "Presentation", + subtitleAccent: "Template", + paragraph: + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + website: "www.yourwebsite.com", + introCard: { enabled: true, name: "John Doe", date: "Jan 1, 2025" }, + media: { type: "image", image: ImageSchema.parse({}) }, + }) + +type SlideData = z.infer + +interface SlideLayoutProps { + data?: Partial +} + +const IntroSlideLayout: React.FC = ({ data: slideData }) => { + return ( + <> + + +
+ {/* Header: diamond + business name */} +
+
+
+ { (slideData as any)?.__companyName__ && + {(slideData as any)?.__companyName__} + } +
+
+ + {/* Right panel image (replaces dark gradient box) */} +
+ {slideData?.media?.image?.__image_prompt__} +
+ + {/* Vertical diamond decorations */} +
+
+
+
+
+ +
+
+

+ {slideData?.title} +

+

{slideData?.paragraph}

+ + {slideData?.introCard?.enabled && ( +
+
+
+ + {slideData?.introCard?.name} + + + {slideData?.introCard?.date} + +
+
+ )} +
+
+
+ + {/* Footer line with website and end diamond */} +
+ {slideData?.website} +
+
+ + {/* Big bottom-right diamond */} +
+
+ + ) +} + +export { Schema, layoutId, layoutName, layoutDescription } +export default IntroSlideLayout + + diff --git a/servers/nextjs/presentation-templates/swift/MetricsNumbers.tsx b/servers/nextjs/presentation-templates/swift/MetricsNumbers.tsx new file mode 100644 index 00000000..433bb6fc --- /dev/null +++ b/servers/nextjs/presentation-templates/swift/MetricsNumbers.tsx @@ -0,0 +1,178 @@ +import React from "react" +import * as z from "zod" + +const layoutId = "MetricsNumbers" +const layoutName = "Metrics Numbers" +const layoutDescription = "Swift: Our Impact in Numbers with three stacked metric cards" + +const MetricSchema = z + .object({ + value: z.string().min(1).max(8).default("10K+"), + line1: z.string().min(2).max(22).default("Total"), + line2: z.string().min(0).max(22).default("Users"), + description: z + .string() + .min(10) + .max(140) + .default("active users across multiple industries"), + }) + .default({ + value: "10K+", + line1: "Total", + line2: "Users", + description: "active users across multiple industries", + }) + +const Schema = z + .object({ + title: z + .string() + .min(8) + .max(60) + .default("Our Impact in Numbers"), + leftTitle: z + .string() + .min(6) + .max(40) + .default("Proven Results\nThrough Data"), + leftBody: z + .string() + .min(30) + .max(220) + .default( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit." + ), + website: z.string().min(6).max(60).default("www.yourwebsite.com"), + metrics: z + .array(MetricSchema) + .min(1) + .max(4) + .default([ + MetricSchema.parse({ + value: "10K+", + line1: "Total", + line2: "Users", + description: "active users across multiple industries", + }), + MetricSchema.parse({ + value: "150%", + line1: "Revenue", + line2: "Growth", + description: "year-over-year revenue growth", + }), + MetricSchema.parse({ + value: "95%", + line1: "Customer", + line2: "Satisfaction", + description: "retention rate with an average rating of 4.8/5", + }), + ]), + }) + .default({ + title: "Our Impact in Numbers", + leftTitle: "Proven Results\nThrough Data", + leftBody: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + website: "www.yourwebsite.com", + metrics: [ + MetricSchema.parse({ + value: "10K+", + line1: "Total", + line2: "Users", + description: "active users across multiple industries", + }), + MetricSchema.parse({ + value: "150%", + line1: "Revenue", + line2: "Growth", + description: "year-over-year revenue growth", + }), + MetricSchema.parse({ + value: "95%", + line1: "Customer", + line2: "Satisfaction", + description: "retention rate with an average rating of 4.8/5", + }), + ], + }) + +type SlideData = z.infer + +interface SlideLayoutProps { + data?: Partial +} + +const MetricsNumbers: React.FC = ({ data: slideData }) => { + const metrics = slideData?.metrics || [] + return ( + <> + + +
+ {/* Header */} +
+
+
+ { (slideData as any)?.__companyName__ && {(slideData as any)?.__companyName__}} +
+
+ + {/* Separator line like the reference */} +
+ +
+ {/* Left content */} +
+

{slideData?.title}

+
+
+
+
{slideData?.leftTitle}
+
+
+

{slideData?.leftBody}

+
+ + {/* Right stacked metric cards */} +
+ {/* decorative circle on the right */} +
+ +
+ {metrics.slice(0,3).map((m, i) => ( +
+
{m.value}
+
+
{m.line1}
+ {m.line2 &&
{m.line2}
} +

{m.description}

+
+
+ ))} +
+
+
+ + {/* Footer (standardized like IntroSlideLayout) */} +
+ {slideData?.website} +
+
+
+
+ + ) +} + +export { Schema, layoutId, layoutName, layoutDescription } +export default MetricsNumbers + + diff --git a/servers/nextjs/presentation-templates/swift/SimpleBulletPointsLayout.tsx b/servers/nextjs/presentation-templates/swift/SimpleBulletPointsLayout.tsx new file mode 100644 index 00000000..bee2833c --- /dev/null +++ b/servers/nextjs/presentation-templates/swift/SimpleBulletPointsLayout.tsx @@ -0,0 +1,129 @@ +import React from "react" +import * as z from "zod" + +const layoutId = "simple-bullet-points-layout" +const layoutName = "Simple Bullet Points" +const layoutDescription = "Bullet Points with title and description" + +const PointSchema = z + .object({ + title: z.string().min(6).max(60).default("Your Title Here"), + body: z + .string() + .min(30) + .max(220) + .default( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa." + ), + }) + .default({ title: "Your Title Here", body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa." }) + +const Schema = z + .object({ + title: z.string().min(4).max(36).default("Our Commitment"), + statement: z + .string() + .min(20) + .max(260) + .default( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit." + ), + points: z + .array(PointSchema) + .min(1) + .max(4) + .default([PointSchema.parse({}), PointSchema.parse({}), PointSchema.parse({}), PointSchema.parse({ title: "Your Title Here", body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa." })]), + website: z.string().min(6).max(60).default("www.yourwebsite.com"), + }) + .default({ + title: "Our Commitment to Innovation", + statement: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + points: [ + { title: "Your Title Here", body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa." }, + { title: "Your Title Here", body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa." }, + { title: "Your Title Here", body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa." }, + { title: "Your Title Here", body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa." }, + + ], + website: "www.yourwebsite.com", + }) + +type SlideData = z.infer + +interface SlideLayoutProps { + data?: Partial +} + +const CommitmentTwoPoints: React.FC = ({ data: slideData }) => { + const points = slideData?.points || [] + return ( + <> + + +
+ {/* Header */} +
+
+
+ { (slideData as any)?.__companyName__ && {(slideData as any)?.__companyName__}} +
+
+ + {/* Subtle background motif */} +
+ + {/* Content grid */} +
+ {/* Left heading and statement */} +
+
+ {slideData?.title} +
+ +

+ {slideData?.statement} +

+
+ + {/* Right numbered points (up to 4) */} +
+ {points.slice(0, 4).map((p, i) => ( +
+
{p.title}
+

{p.body}

+
+ ))} +
+
+ + {/* Footer (align with other Swift layouts) */} +
+ {slideData?.website} +
+
+
+
+ + ) +} + +export { Schema, layoutId, layoutName, layoutDescription } +export default CommitmentTwoPoints + + diff --git a/servers/nextjs/presentation-templates/swift/TableOfContents.tsx b/servers/nextjs/presentation-templates/swift/TableOfContents.tsx new file mode 100644 index 00000000..85094861 --- /dev/null +++ b/servers/nextjs/presentation-templates/swift/TableOfContents.tsx @@ -0,0 +1,144 @@ +import React from "react" +import * as z from "zod" + +const layoutId = "SwiftTableOfContents" +const layoutName = "Table Of Contents" +const layoutDescription = "Swift: Table of contents with up to 10 items (title + description)" + +const ToCItemSchema = z + .object({ + title: z.string().min(3).max(40).default("Introduction"), + description: z + .string() + .min(0) + .max(60) + .default("A brief overview of the section."), + }) + .default({ title: "Introduction", description: "A brief overview of the section." }) + +const Schema = z + .object({ + title: z + .string() + .min(3) + .max(60) + .default("Table of Contents"), + items: z + .array(ToCItemSchema) + .min(1) + .max(10) + .default([ + { title: "Introduction", description: "A brief description of our company and goals." }, + { title: "Our Team", description: "Leadership and core contributors." }, + { title: "Timeline", description: "High-level execution plan and milestones." }, + { title: "Recommendations", description: "Key suggestions based on initial requirements." }, + { title: "Solution", description: "What we propose and why it works." }, + { title: "Market", description: "Audience, segments, and opportunity size." }, + { title: "Business Model", description: "How we create and capture value." }, + { title: "Conclusion", description: "Closing notes and next steps." }, + { title: "Business Model", description: "How we create and capture value." }, + { title: "Conclusion", description: "Closing notes and next steps." }, + ]), + website: z.string().min(6).max(60).default("www.yourwebsite.com"), + }) + .default({ + title: "Table of Contents", + items: [ + { title: "Introduction", description: "A brief description of our company and goals." }, + { title: "Our Team", description: "Leadership and core contributors." }, + { title: "Timeline", description: "High-level execution plan and milestones." }, + { title: "Recommendations", description: "Key suggestions based on initial requirements." }, + { title: "Solution", description: "What we propose and why it works." }, + { title: "Market", description: "Audience, segments, and opportunity size." }, + { title: "Business Model", description: "How we create and capture value." }, + { title: "Conclusion", description: "Closing notes and next steps." }, + { title: "Business Model", description: "How we create and capture value." }, + { title: "Conclusion", description: "Closing notes and next steps." }, + + ], + website: "www.yourwebsite.com", + }) + +type SlideData = z.infer + +interface SlideLayoutProps { + data?: Partial +} + +const TableOfContents: React.FC = ({ data: slideData }) => { + const items = slideData?.items || [] + return ( + <> + + +
+ {/* Header */} +
+
+
+ { (slideData as any)?.__companyName__ && {(slideData as any)?.__companyName__}} +
+
+ +
+

{slideData?.title}

+
+ + {/* List */} +
+
+ {items.slice(0, 10).map((item, idx) => ( +
+
+
+
+ {String(idx + 1).padStart(2, "0")} +
+
+
+
+ {item.title} +
+ {item.description && ( +
+ {item.description} +
+ )} +
+
+
+
+ ))} +
+
+ + {/* Footer (standardized like IntroSlideLayout) */} +
+ {slideData?.website} +
+
+
+
+ + ) +} + +export { Schema, layoutId, layoutName, layoutDescription } +export default TableOfContents + + diff --git a/servers/nextjs/presentation-templates/swift/TableorChart.tsx b/servers/nextjs/presentation-templates/swift/TableorChart.tsx new file mode 100644 index 00000000..da36a7a7 --- /dev/null +++ b/servers/nextjs/presentation-templates/swift/TableorChart.tsx @@ -0,0 +1,279 @@ +import React from "react" +import * as z from "zod" +import { + ResponsiveContainer, + BarChart, + Bar, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + LineChart, + Line, + PieChart, + Pie, + Cell, +} from "recharts" + +const layoutId = "tableorChart" +const layoutName = "Table Or Chart" +const layoutDescription = "Swift: Generic data table with option to render a chart (bar, horizontalBar, line, pie)" + +const ChartDatumSchema = z.object({ + label: z.string().min(1).max(12).default("A"), + value: z.number().min(0).max(1000000).default(60), +}) + +const TableRowSchema = z.object({ + cells: z + .array(z.string().min(0).max(200)) + .min(2) + .max(10) + .default(["Row 1", "Value", "Value"]) + .meta({ description: "Row cells; count should match columns length" }), +}) + +const Schema = z + .object({ + title: z.string().min(6).max(60).default("Data Table or Chart"), + description: z + .string() + .min(20) + .max(220) + .default( + "Present structured information in a flexible table or visualize it with a chart." + ), + + mode: z.enum(["table", "chart"]).default("table"), + + // Table configuration (generic) + columns: z + .array(z.string().min(1).max(40)) + .min(2) + .max(10) + .default(["Column 1", "Column 2", "Column 3"]), + rows: z + .array(TableRowSchema) + .min(1) + .max(30) + .default([ + { cells: ["Row A", "✓", "-"] }, + { cells: ["Row B", "Text", "123"] }, + { cells: ["Row C", "More text", "456"] }, + ]), + + // Chart configuration (parity with @standard ChartLeftTextRightLayout) + chart: z + .object({ + type: z.enum(["bar", "horizontalBar", "line", "pie"]).default("line"), + data: z.array(ChartDatumSchema).min(3).max(12).default([ + { label: "A", value: 60 }, + { label: "B", value: 42 }, + { label: "C", value: 75 }, + { label: "D", value: 30 }, + ]), + primaryColor: z.string().default("var(--text-heading-color, #111827)"), + gridColor: z.string().default("var(--primary-accent-color, #BFF4FF)"), + pieColors: z + .array(z.string()) + .min(1) + .max(10) + .default(["var(--text-heading-color, #111827)", "#3b82f6", "#f59e0b", "#10b981", "#ef4444"]), + showLabels: z.boolean().default(true), + }) + .default({ + type: "line", + data: [ + { label: "A", value: 60 }, + { label: "B", value: 42 }, + { label: "C", value: 75 }, + { label: "D", value: 30 }, + ], + primaryColor: "#1B8C2D", + gridColor: "#E5E7EB", + pieColors: ["#1B8C2D", "#3b82f6", "#f59e0b", "#10b981", "#ef4444"], + showLabels: true, + }), + + website: z.string().min(6).max(60).default("www.yourwebsite.com"), + }) + .default({ + title: "Data Table or Chart", + description: + "Present structured information in a flexible table or visualize it with a chart.", + mode: "table", + columns: ["Column 1", "Column 2", "Column 3"], + rows: [ + { cells: ["Row A", "✓", "-"] }, + { cells: ["Row B", "Text", "123"] }, + { cells: ["Row C", "More text", "456"] }, + ], + chart: { + type: "line", + data: [ + { label: "A", value: 60 }, + { label: "B", value: 42 }, + { label: "C", value: 75 }, + { label: "D", value: 30 }, + ], + primaryColor: "#1B8C2D", + gridColor: "#E5E7EB", + pieColors: ["#1B8C2D", "#3b82f6", "#f59e0b", "#10b981", "#ef4444"], + showLabels: true, + }, + website: "www.yourwebsite.com", + }) + +type SlideData = z.infer + +interface SlideLayoutProps { + data?: Partial +} + +const TableOrChart: React.FC = ({ data: slideData }) => { + const mode = slideData?.mode || "table" + const columns = slideData?.columns || [] + const rows = slideData?.rows || [] + + const cData = slideData?.chart?.data || [] + const type = slideData?.chart?.type || "bar" + const primaryColor = slideData?.chart?.primaryColor || "var(--text-heading-color, #111827)" + const gridColor = slideData?.chart?.gridColor || "var(--primary-accent-color, #BFF4FF)" + const pieColors = slideData?.chart?.pieColors || ["var(--text-heading-color, #111827)"] + const showLabels = slideData?.chart?.showLabels !== false + + return ( + <> + + +
+ {/* Header */} +
+
+
+ {(slideData as any)?.__companyName__ && {(slideData as any)?.__companyName__}} +
+
+ + {/* Title and description */} +
+

{slideData?.title}

+

{slideData?.description}

+
+ + {/* Content area: Table or Chart */} +
+ {mode === "table" ? ( +
+
+ + + + {columns.map((col, idx) => ( + + ))} + + + + {rows.map((row, rIdx) => ( + + {columns.map((_, cIdx) => ( + + ))} + + ))} + +
+ {col} +
+ {row.cells[cIdx] || ''} +
+
+
+ ) : ( +
+ + {type === 'bar' ? ( + + + + + + + + + ) : type === 'horizontalBar' ? ( + + + + + + + + + ) : type === 'line' ? ( + + + + + + + + + ) : ( + + + + + {cData.map((_, i) => ( + + ))} + + + )} + +
+ )} +
+ + {/* Footer (standardized like IntroSlideLayout) */} +
+ {slideData?.website} +
+
+
+
+ + ) +} + +export { Schema, layoutId, layoutName, layoutDescription } +export default TableOrChart + + diff --git a/servers/nextjs/presentation-templates/swift/Timeline.tsx b/servers/nextjs/presentation-templates/swift/Timeline.tsx new file mode 100644 index 00000000..2fdf9516 --- /dev/null +++ b/servers/nextjs/presentation-templates/swift/Timeline.tsx @@ -0,0 +1,170 @@ +import React from "react" +import * as z from "zod" + +const layoutId = "Timeline" +const layoutName = "Timeline" +const layoutDescription = "Timeline of cards with title, subtitle banner" + +const IconSchema = z + .object({ + __icon_url__: z + .string() + .default( + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/clipboard-text-bold.svg" + ), + __icon_query__: z.string().min(0).max(80).default("timeline icon"), + }) + .default({ + __icon_url__: + "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/clipboard-text-bold.svg", + __icon_query__: "timeline icon", + }) + +const ItemSchema = z + .object({ + year: z.string().min(3).max(6).default("2018"), + heading: z.string().min(3).max(28).default("Founded in 2020"), + body: z + .string() + .min(10) + .max(160) + .default("Lorem ipsum dolor"), + icon: IconSchema, + }) + .default({ year: "2018", heading: "Founded in 2020", body: "Lorem ipsum dolor", icon: IconSchema.parse({}) }) + +const Schema = z + .object({ + title: z + .string() + .min(8) + .max(60) + .default("Our Journey at a Glance"), + subtitle: z + .string() + .min(20) + .max(200) + .default( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa." + ), + items: z + .array(ItemSchema) + .min(1) + .max(4) + .default([ + ItemSchema.parse({ year: "2018", heading: "Founded in 2020", body: "Lorem ipsum dolor" }), + ItemSchema.parse({ year: "2019", heading: "First Product in 2021", body: "Lorem ipsum dolor" }), + ItemSchema.parse({ year: "2020", heading: "Key Milestone in 2022", body: "Lorem ipsum dolor" }), + ItemSchema.parse({ year: "2021", heading: "Global Expansion in 2024", body: "Lorem ipsum dolor" }), + ]), + website: z.string().min(6).max(60).default("www.yourwebsite.com"), + }) + .default({ + title: "Our Journey at a Glance", + subtitle: + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa.", + items: [ + ItemSchema.parse({ year: "2018", heading: "Founded in 2020", body: "Lorem ipsum dolor" }), + ItemSchema.parse({ year: "2019", heading: "First Product in 2021", body: "Lorem ipsum dolor" }), + ItemSchema.parse({ year: "2020", heading: "Key Milestone in 2022", body: "Lorem ipsum dolor" }), + ItemSchema.parse({ year: "2021", heading: "Global Expansion in 2024", body: "Lorem ipsum dolor" }), + ], + website: "www.yourwebsite.com", + }) + +type SlideData = z.infer + +interface SlideLayoutProps { + data?: Partial +} + +const Timeline: React.FC = ({ data: slideData }) => { + const items = slideData?.items || [] + return ( + <> + + +
+ {/* Keep white background to match Swift layouts */} + + {/* Header: diamond + business name */} +
+
+
+ {(slideData as any)?.__companyName__ && {(slideData as any)?.__companyName__}} +
+
+ + {/* Right vertical diamonds */} +
+
+
+
+
+ + {/* Title */} +
+

{slideData?.title}

+ {/* Subtitle banner */} +
+ {slideData?.subtitle} +
+
+ + {/* Horizontal timeline */} +
+ {/* center line */} +
+ +
+ {items.slice(0, 4).map((it, idx) => ( +
+ {/* year badge - placed close to the card */} +
{it.year}
+ + {/* connector dot */} +
+ + {/* card container */} +
+
+
+ {it.icon.__icon_query__} +
+
{it.heading}
+

{it.body}

+
+
+
+ ))} +
+
+ + {/* Footer (standardized like IntroSlideLayout) */} +
+ {slideData?.website} +
+
+
+
+ + ) +} + +export { Schema, layoutId, layoutName, layoutDescription } +export default Timeline + + diff --git a/servers/nextjs/presentation-templates/swift/settings.json b/servers/nextjs/presentation-templates/swift/settings.json new file mode 100644 index 00000000..e1a89369 --- /dev/null +++ b/servers/nextjs/presentation-templates/swift/settings.json @@ -0,0 +1,6 @@ +{ + "description": "Swift layouts for presentations", + "ordered": false, + "default": false +} +