feat: Education Template added

This commit is contained in:
shiva raj badu 2026-04-07 11:00:33 +05:45
parent 974abecbc3
commit 9061bf3e6f
No known key found for this signature in database
11 changed files with 1049 additions and 0 deletions

View file

@ -0,0 +1,112 @@
import * as z from "zod";
export const slideLayoutId = "education-about-slide";
export const slideLayoutName = "Education About Slide";
export const slideLayoutDescription =
"A left text column with company introduction and a right-side visual grid made from one repeated image and tinted text panels.";
export const Schema = z.object({
companyName: z.string().min(3).max(22).default("Company Name").meta({
description: "Main heading in the left content column.",
}),
intro: z.string().min(40).max(120).default(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et."
).meta({
description: "Bold intro text shown beneath the company heading.",
}),
body: z.string().min(120).max(280).default(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi."
).meta({
description: "Body paragraph in the left content section.",
}),
topPanelText: z.string().min(20).max(70).default("Insert info about the company.").meta({
description: "Short text inside the top-right dark panel. ",
}),
bottomPanelText: z.string().min(20).max(70).default("Insert info about the company and your mission statement.").meta({
description: "Short text inside the bottom-right dark panel.",
}),
topFeatureImage: z.object({
__image_url__: z.string().default("https://images.unsplash.com/photo-1521737604893-d14cc237f11d?auto=format&fit=crop&w=1200&q=80"),
__image_prompt__: z.string().min(10).max(200).default("Office team collaboration"),
}).default({
__image_url__:
"https://images.unsplash.com/photo-1521737604893-d14cc237f11d?auto=format&fit=crop&w=1200&q=80",
__image_prompt__: "Office team collaboration",
}).meta({
description: "Single image reused in the right-side visual grid.",
}),
bottomFeatureImage: z.object({
__image_url__: z.string().default("https://images.unsplash.com/photo-1521737604893-d14cc237f11d?auto=format&fit=crop&w=1200&q=80"),
__image_prompt__: z.string().min(10).max(200).default("Office team collaboration"),
}).default({
__image_url__:
"https://images.unsplash.com/photo-1521737604893-d14cc237f11d?auto=format&fit=crop&w=1200&q=80",
__image_prompt__: "Office team collaboration",
}).meta({
description: "Single image reused in the right-side visual grid.",
}),
});
export type SchemaType = z.infer<typeof Schema>;
const EducationAboutSlide = ({ data }: { data: Partial<SchemaType> }) => {
return (<>
<link href="https://fonts.googleapis.com/css2?family=Corben:wght@400;700&family=Playfair+Display:ital,wght@0,400..900;1,400..900&display=swap" rel="stylesheet" />
<div className="relative h-[720px] w-[1280px] overflow-hidden bg-[#efeff1]">
<div className="grid items-end grid-cols-[1fr_1fr]">
<div className="px-[53px] pb-[56px] ">
<h2 className="font-serif text-[64px] leading-[98%] tracking-[-0.02em] text-[#101C3D]">
{data.companyName}
</h2>
<p className="mt-[30px] max-w-[610px] text-[22px] font-semibold leading-[1.24] text-[#34394C]">
{data.intro}
</p>
<p className="mt-[18px] max-w-[620px] text-[22px] leading-[1.28] text-[#46474C]">
{data.body}
</p>
</div>
<div className=" ">
<div className="relative flex overflow-hidden h-[360px]">
<img
src={data.topFeatureImage?.__image_url__}
alt={data.topFeatureImage?.__image_prompt__}
className="absolute inset-0 h-full w-full object-cover z-1 "
/>
<div className=" w-1/2 bg-[#28256f]/60 z-10 flex justify-center items-center">
<p className=" text-[24px] leading-[1.22] text-[#f5f7ff] px-[42px]">
{data.topPanelText}
</p>
</div>
<div className=" w-1/2 ">
</div>
</div>
<div className="relative flex overflow-hidden h-[360px]">
<img
src={data.bottomFeatureImage?.__image_url__}
alt={data.bottomFeatureImage?.__image_prompt__}
className="absolute inset-0 h-full w-full object-cover "
/>
<div className=" w-1/2 ">
</div>
<div className=" w-1/2 bg-[#28256f]/60 z-10 flex justify-center items-center">
<p className=" text-[24px] leading-[1.22] text-[#f5f7ff] px-[42px]">
{data.bottomPanelText}
</p>
</div>
</div>
</div>
</div>
</div>
</>
);
};
export default EducationAboutSlide;

View file

@ -0,0 +1,75 @@
import * as z from "zod";
export const slideLayoutId = "education-content-split-slide";
export const slideLayoutName = "Education Content Split Slide";
export const slideLayoutDescription =
"A left collage built from one repeated image and a right content block containing heading, tagline, and paragraph text.";
export const Schema = z.object({
heading: z.string().min(3).max(16).default("Heading").meta({
description: "Main right-side heading.",
}),
tagline: z.string().min(3).max(12).default("TAGLINE").meta({
description: "Small uppercase label shown under the heading.",
}),
body: z.string().min(80).max(300).default(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
).meta({
description: "Main descriptive paragraph on the right side.",
}),
collageImage: z.object({
__image_url__: z.string().default("https://images.unsplash.com/photo-1521737604893-d14cc237f11d?auto=format&fit=crop&w=1200&q=80"),
__image_prompt__: z.string().min(10).max(200).default("Business team around a laptop"),
}).default({
__image_url__:
"https://images.unsplash.com/photo-1521737604893-d14cc237f11d?auto=format&fit=crop&w=1200&q=80",
__image_prompt__: "Business team around a laptop",
}).meta({
description: "Single image reused to create the left collage composition.",
}),
});
export type SchemaType = z.infer<typeof Schema>;
const EducationContentSplitSlide = ({ data }: { data: Partial<SchemaType> }) => {
const { heading, tagline, body, collageImage } = data;
return (
<div className="relative h-[720px] w-[1280px] overflow-hidden bg-[#E6E7E8]">
<div className="w-full flex items-center h-full">
<div className="w-[660px] h-full">
<div className="h-[394px] w-full mb-[6px]">
<img
src={collageImage?.__image_url__}
alt={collageImage?.__image_prompt__}
className=" h-full w-full object-cover object-center"
/>
</div>
<div className="flex w-full gap-1.5 h-[320px] ">
<div className="w-[327px]"> <img
src={collageImage?.__image_url__}
alt={collageImage?.__image_prompt__}
className="h-full object-cover "
/></div>
<div className="w-[327px]"> <img
src={collageImage?.__image_url__}
alt={collageImage?.__image_prompt__}
className="h-full object-cover "
/></div>
</div>
</div>
<div className="w-1/2 px-[56px]">
<h2 className="text-[24px] font-medium leading-none text-[#34394C]">{data.heading}</h2>
<p className=" text-[14px] font-medium mt-1 leading-none tracking-[0.04em] text-[#454962]">
{data.tagline}
</p>
<p className="mt-[18px] text-[22px] leading-[1.28] text-[#34394C]">{body}</p>
</div>
</div>
</div>
);
};
export default EducationContentSplitSlide;

View file

@ -0,0 +1,54 @@
import * as z from "zod";
export const slideLayoutId = "education-cover-slide";
export const slideLayoutName = "Education Cover Slide";
export const slideLayoutDescription =
"A full-bleed cover slide with a single background image, a strong violet overlay, and centered company/title text.";
export const Schema = z.object({
companyName: z.string().min(3).max(24).default("COMPANY NAME").meta({
description: "Small uppercase company label shown above the main title.",
}),
title: z.string().min(6).max(32).default("PowerPoint Template").meta({
description: "Main centered title of the cover slide.",
}),
backgroundImage: z.object({
__image_url__: z.string().default("https://images.unsplash.com/photo-1486406146926-c627a92ad1ab?auto=format&fit=crop&w=1920&q=80"),
__image_prompt__: z.string().min(10).max(200).default("City business district buildings"),
}).default({
__image_url__:
"https://images.unsplash.com/photo-1486406146926-c627a92ad1ab?auto=format&fit=crop&w=1920&q=80",
__image_prompt__: "City business district buildings",
}).meta({
description: "Single background image used across the cover.",
}),
});
export type SchemaType = z.infer<typeof Schema>;
const EducationCoverSlide = ({ data }: { data: Partial<SchemaType> }) => {
const { companyName, title, backgroundImage } = data;
return (
<div className="relative h-[720px] w-[1280px] overflow-hidden bg-[#2e0a8a]">
<img
src={backgroundImage?.__image_url__}
alt={backgroundImage?.__image_prompt__}
className="absolute inset-0 h-full w-full object-cover"
/>
<div className="absolute inset-0 bg-[#3b0bb6]/85" />
<div className="relative z-10 flex h-full flex-col items-center justify-center text-center text-white">
<p className="text-[22px] font-normal uppercase tracking-[0.64px]">{data.companyName}</p>
<h1 className="mt-[12px] px-[53px] text-[64px] font-medium leading-[98%]">
{title}
</h1>
</div>
</div>
);
};
export default EducationCoverSlide;

View file

@ -0,0 +1,79 @@
import * as z from "zod";
export const slideLayoutId = "education-image-gallery-slide";
export const slideLayoutName = "Education Image Gallery Slide";
export const slideLayoutDescription =
"A slide with a left image collage (one repeated image) and right text block for gallery heading and description.";
export const Schema = z.object({
title: z.string().min(3).max(16).default("Image Gallery").meta({
description: "Heading on the right side.",
}),
body: z.string().min(70).max(200).default(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris."
).meta({
description: "Supporting paragraph shown below the heading.",
}),
galleryImages: z.array(z.object({
__image_url__: z.string(),
__image_prompt__: z.string(),
})).max(5).min(5).default(Array(5).fill({
__image_url__: "https://images.unsplash.com/photo-1521737604893-d14cc237f11d?auto=format&fit=crop&w=1200&q=80",
__image_prompt__: "Office team collaboration",
})).meta({
description: "Image gallery images.",
}),
});
export type SchemaType = z.infer<typeof Schema>;
const EducationImageGallerySlide = ({ data }: { data: Partial<SchemaType> }) => {
const { title, body, galleryImages } = data;
return (
<div className="relative h-[720px] w-[1280px] overflow-hidden bg-[#E6E7E8]">
<div className="grid h-full items-end grid-cols-[590px_1fr]">
<div className="grid h-full grid-cols-2 grid-rows-[245px_245px_230px] gap-[2px]">
<img
src={galleryImages?.[0].__image_url__}
alt={galleryImages?.[0].__image_prompt__}
className="h-full w-full object-cover object-left"
/>
<img
src={galleryImages?.[1].__image_url__}
alt={galleryImages?.[1].__image_prompt__}
className="h-full w-full object-cover object-right"
/>
<img
src={galleryImages?.[2].__image_url__}
alt={galleryImages?.[2].__image_prompt__}
className="h-full w-full object-cover object-top"
/>
<img
src={galleryImages?.[3].__image_url__}
alt={galleryImages?.[3].__image_prompt__}
className="h-full w-full object-cover object-center"
/>
<img
src={galleryImages?.[4].__image_url__}
alt={galleryImages?.[4].__image_prompt__}
className="col-span-2 h-full w-full object-cover object-bottom"
/>
</div>
<div className="px-[64px] pb-[56px] ">
<h2 className="font-serif text-[64px] font-medium leading-[98%] text-[#101C3D]">
{title}
</h2>
<p className="mt-[37px] text-[22px] text-[#34394C]">
{body}
</p>
</div>
</div>
</div>
);
};
export default EducationImageGallerySlide;

View file

@ -0,0 +1,130 @@
import * as z from "zod";
export const slideLayoutId = "education-report-donut-slide";
export const slideLayoutName = "Education Report Donut Slide";
export const slideLayoutDescription =
"A report slide with left-side title/content and a right-side donut chart with legend values.";
const SegmentSchema = z.object({
label: z.string().min(3).max(12).meta({
description: "Legend label for one donut chart segment.",
}),
value: z.number().min(1).max(100).meta({
description: "Percentage value for one chart segment.",
}),
color: z.string().min(4).max(20).meta({
description: "Hex color value for one chart segment.",
}),
});
export const Schema = z.object({
title: z.string().min(3).max(14).default("Report").meta({
description: "Main heading in the left content area.",
}),
body: z.string().min(80).max(220).default(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris."
).meta({
description: "Main report paragraph on the left.",
}),
footnote: z.string().min(20).max(110).default(
"(Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt.)"
).meta({
description: "Footnote text shown at the bottom of the left area.",
}),
chartTitle: z.string().min(8).max(26).default("Students by Grade Level").meta({
description: "Heading shown above the donut chart.",
}),
dateRange: z.string().min(8).max(22).default("Apr 10 - Apr 17").meta({
description: "Date range label under the chart heading.",
}),
segments: z
.array(SegmentSchema)
.min(4)
.max(4)
.default([
{ label: "Option A", value: 17.07, color: "#4A15A8" },
{ label: "Option B", value: 45.23, color: "#5B45AD" },
{ label: "Option C", value: 21.61, color: "#876FC1" },
{ label: "Option D", value: 16.36, color: "#A89ACF" },
])
.meta({
description: "Four donut segments with labels and percentages.",
}),
});
export type SchemaType = z.infer<typeof Schema>;
const EducationReportDonutSlide = ({ data }: { data: Partial<SchemaType> }) => {
const { title, body, footnote, chartTitle, dateRange, segments } = data;
const total = segments?.reduce((sum, item) => sum + item.value, 0) || 0;
let cursor = 0;
const conicStops = segments
?.map((segment) => {
const start = cursor;
const span = total > 0 ? (segment.value / total) * 100 : 0;
cursor += span;
return `${segment.color} ${start}% ${cursor}%`;
})
.join(", ");
return (
<div className="relative h-[720px] w-[1280px] overflow-hidden bg-[#efeff1]">
<div className="h-[147px] w-[147px] mx-[40px] bg-[#2C3592] p-[20px]">
</div>
<div className="grid h-full grid-cols-[1fr_600px]">
<div className="px-[53px] flex flex-col justify-between">
<div>
<h2 className="mt-[131px] font-serif text-[64px] leading-[98%] tracking-[-0.02em] text-[#1a1752]">
{title}
</h2>
<p className="mt-[30px] max-w-[610px] text-[22px] font-medium leading-[1.24] text-[#34394C]">
{body}
</p>
</div>
<p className=" max-w-[620px] text-[18px] leading-[1.28] text-[#46474C]">
{footnote}
</p>
</div>
<div className="px-[56px] pt-[106px]">
<h3 className="text-center text-[24px] font-medium leading-none text-[#34394C]">
{chartTitle}
</h3>
<p className="mt-[12px] text-center text-[14px] leading-none text-[#454962]">{dateRange}</p>
<div className="mt-[28px] flex justify-center">
<div
className="relative h-[300px] w-[300px] rounded-full"
style={{ background: `conic-gradient(${conicStops})` }}
>
<div className="absolute left-1/2 top-1/2 h-[222px] w-[222px] -translate-x-1/2 -translate-y-1/2 rounded-full bg-[#efeff1]" />
</div>
</div>
<div className="mt-[52px] grid grid-cols-2 gap-x-[34px] gap-y-[26px]">
{segments?.map((segment, index) => (
<div key={`${segment.label}-${index}`} className="flex items-center gap-[14px]">
<span className="h-[18px] w-[18px] rounded-full" style={{ backgroundColor: segment.color }} />
<span className="text-[20px] leading-none text-[#46474C]">{segment.label}</span>
<span className="text-[20px] font-medium leading-none text-[#34394C]">
{segment.value.toFixed(2)}%
</span>
</div>
))}
</div>
</div>
</div>
</div>
);
};
export default EducationReportDonutSlide;

View file

@ -0,0 +1,146 @@
import * as z from "zod";
export const slideLayoutId = "education-services-split-slide";
export const slideLayoutName = "Education Services Split Slide";
export const slideLayoutDescription =
"A services layout with left heading, one repeated image column, and two stacked service description blocks on the right.";
const ServiceSchema = z.object({
serviceImage: z.object({
__image_url__: z.string(),
__image_prompt__: z.string(),
}).default({
__image_url__:
"https://images.unsplash.com/photo-1521737604893-d14cc237f11d?auto=format&fit=crop&w=1200&q=80",
__image_prompt__: "Team meeting image reused across two rows",
}).meta({
description: "Single image reused in the middle column.",
}),
heading: z.string().min(3).max(18).meta({
description: "Service heading shown in the right column.",
}),
tagline: z.string().min(3).max(12).meta({
description: "Short label under each service heading.",
}),
body: z.string().min(40).max(90).meta({
description: "Service description paragraph.",
}),
});
export const Schema = z.object({
title: z.string().min(4).max(12).default("Services").meta({
description: "Main slide title shown on the left.",
}),
sections: z
.array(ServiceSchema)
.min(2)
.max(4)
.default([
{
serviceImage: {
__image_url__:
"https://images.unsplash.com/photo-1521737604893-d14cc237f11d?auto=format&fit=crop&w=1200&q=80",
__image_prompt__: "Team meeting image reused across two rows",
},
heading: "Service 1",
tagline: "TAGLINE",
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor.",
},
{
serviceImage: {
__image_url__:
"https://images.unsplash.com/photo-1521737604893-d14cc237f11d?auto=format&fit=crop&w=1200&q=80",
__image_prompt__: "Team meeting image reused across two rows",
},
heading: "Service 2",
tagline: "TAGLINE",
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor.",
},
{
serviceImage: {
__image_url__:
"https://images.unsplash.com/photo-1521737604893-d14cc237f11d?auto=format&fit=crop&w=1200&q=80",
__image_prompt__: "Team meeting image reused across two rows",
},
heading: "Service 3",
tagline: "TAGLINE",
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor.",
},
{
serviceImage: {
__image_url__:
"https://images.unsplash.com/photo-1521737604893-d14cc237f11d?auto=format&fit=crop&w=1200&q=80",
__image_prompt__: "Team meeting image reused across two rows",
},
heading: "Service 4",
tagline: "TAGLINE",
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor.",
},
])
.meta({
description: "Two stacked service content sections on the right side.",
}),
});
export type SchemaType = z.infer<typeof Schema>;
const EducationServicesSplitSlide = ({ data }: { data: Partial<SchemaType> }) => {
const { title, sections } = data;
return (
<div className="relative h-[720px] w-[1280px] overflow-hidden bg-[#E6E7E8]">
<div className="grid h-full grid-cols-[365px_1fr]">
<div className="px-[53px] pt-[53px]">
<h2 className="font-serif text-[64px] leading-[98%] tracking-[-0.02em] text-[#1a1752]">
{title}
</h2>
</div>
<div className=" grid "
style={{
gridTemplateRows: `repeat(${sections?.length}, 1fr)`,
}}
>
{sections?.map((section, index) => (
<div key={`${section.heading}-${index}`} className=" flex items-center"
style={{
borderBottom: index !== (sections?.length ?? 1) - 1 ? "5px solid rgba(255, 255, 255, 0.10)" : "none",
}}
>
<div className=" min-w-[316px] max-w-[316px] "
style={{
height: sections?.length === 4 ? '175px' : sections?.length === 3 ? '240px' : '357px'
}}
>
<img
src={section.serviceImage.__image_url__}
alt={section.serviceImage.__image_prompt__}
className="h-full w-full object-cover "
/>
</div>
<div
className={`px-[56px] `}
>
<h3 className="text-[24px] font-medium leading-none text-[#34394C]">{section.heading}</h3>
<p className="mt-[10px] text-[14px] font-medium uppercase leading-none text-[#454962]">
{section.tagline}
</p>
<p className="mt-[18px] text-[22px] leading-[1.26] text-[#34394C] tracking-[0.04em]">
{section.body}
</p>
</div>
</div>
))}
</div>
</div>
</div>
);
};
export default EducationServicesSplitSlide;

View file

@ -0,0 +1,153 @@
import * as z from "zod";
export const slideLayoutId = "education-statistics-grid-slide";
export const slideLayoutName = "Education Statistics Grid Slide";
export const slideLayoutDescription =
"A two-column layout with a left title block and a right 2x4 grid of statistics cards, using one subtle background image texture.";
const StatisticSchema = z.object({
value: z.string().min(1).max(8).meta({
description: "Main metric value shown at the top of one card.",
}),
label: z.string().min(3).max(22).meta({
description: "Label shown under the value.",
}),
});
export const Schema = z.object({
title: z.string().min(4).max(14).default("Statistics").meta({
description: "Main title shown in the left column.",
}),
description: z.string().min(40).max(120).default(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit."
).meta({
description: "Supporting line shown under the left title.",
}),
stats: z
.array(StatisticSchema)
.min(2)
.max(8)
.default([
{ value: "120", label: "Sales Team Strength" },
{ value: "15", label: "Senior Sales Officer" },
{ value: "1", label: "National Manager" },
{ value: "25", label: "Sales Officers" },
{ value: "2", label: "Regional Manager" },
{ value: "50", label: "Distributor Reps" },
{ value: "5", label: "Zonal Manager" },
{ value: "20", label: "Merchandising Team" },
])
.meta({
description: "Eight statistic cards shown in a 2-column, 4-row grid.",
}),
});
export type SchemaType = z.infer<typeof Schema>;
const EducationStatisticsGridSlide = ({ data }: { data: Partial<SchemaType> }) => {
return (
<div className="relative h-[720px] w-[1280px] overflow-hidden bg-[#efeff1]">
<div className="relative z-10 grid h-full grid-cols-[490px_1fr]">
<div className="bg-[#f1efef] px-[44px] pb-[78px] pt-[96px]">
<div className="flex h-full flex-col justify-end">
<h2 className="font-serif text-[64px] leading-[98%] tracking-[-0.02em] text-[#1a1752]">
{data.title}
</h2>
<p className="mt-[40px] max-w-[330px] text-[22px] leading-[1.24] text-[#34394C]">
{data.description}
</p>
</div>
</div>
{data.stats && data.stats?.length <= 4 && <div className="grid h-full grid-cols-1">
{data.stats?.map((stat, index) => (
<div
key={`${stat.value}-${index}`}
className="px-[52px] pt-[22px]"
style={{ backgroundColor: index % 2 === 1 ? '#5C0FD908' : 'white' }}
>
<p className="font-serif text-[58px] leading-[56px] text-[#434A63]">
{stat?.value}
</p>
<p className="mt-[12px] text-[24px] text-[#434A63]">
{stat?.label}
</p>
</div>
))}
</div>}
{/* {stats && stats?.length > 4 && stats?.length <= 8 && <div className="grid h-full grid-cols-[repeat(auto-fill,minmax(300px,1fr))]">
{stats?.map((stat, index) => (
<div
key={`${stat.value}-${index}`}
className="px-[52px] pt-[22px]"
style={{ backgroundColor: index % 2 === 1 ? '#5C0FD908' : 'white' }}
>
<p className="font-serif text-[58px] leading-[56px] text-[#283E51]">
{stat.value}
</p>
<p className="mt-[12px] text-[24px] text-[#434A63]">
{stat.label}
</p>
</div>
))}
</div>} */}
{data.stats && data.stats?.length > 4 && data.stats?.length <= 8 && (() => {
const rightArray = data.stats?.slice(0, Math.floor(data.stats?.length / 2));
const leftArray = data.stats?.slice(Math.floor(data.stats?.length / 2));
return (
<div className="h-full flex w-full">
<div className="flex flex-col h-full w-full">
{leftArray?.map((stat: any, index: number) => (
<div
key={`${stat?.value}-${index}`}
className="px-[52px] pt-[22px] h-full"
style={{ backgroundColor: index % 2 === 0 ? '#5C0FD908' : 'white' }}
>
<p className="font-serif text-[58px] leading-[56px] text-[#283E51]">
{stat?.value}
</p>
<p className="mt-[12px] text-[24px] text-[#434A63]">
{stat?.label}
</p>
</div>
))}
</div>
<div className="flex flex-col w-full">
{rightArray?.map((stat: any, index: number) => (
<div
key={`${stat.value}-${index}`}
className="px-[52px] pt-[22px] h-full"
style={{ backgroundColor: index % 2 === 1 ? '#5C0FD908' : 'white' }}
>
<p className="font-serif text-[58px] leading-[56px] text-[#283E51]">
{stat.value}
</p>
<p className="mt-[12px] text-[24px] text-[#434A63]">
{stat.label}
</p>
</div>
))}
</div>
</div>
);
})()}
</div>
</div>
);
};
export default EducationStatisticsGridSlide;

View file

@ -0,0 +1,81 @@
import * as z from "zod";
export const slideLayoutId = "education-table-of-contents-slide";
export const slideLayoutName = "Education Table Of Contents Slide";
export const slideLayoutDescription =
"A split layout with a left title panel and a right list of numbered sections, with one subtle background image overlay.";
const TocItemSchema = z.object({
number: z.string().min(2).max(3).meta({
description: "Section number displayed before each section title.",
}),
label: z.string().min(3).max(30).meta({
description: "Section title shown in the right column list.",
}),
});
export const Schema = z.object({
titleLine1: z.string().min(4).max(12).default("Table of").meta({
description: "First line of the left-side heading.",
}),
titleLine2: z.string().min(4).max(12).default("Contents").meta({
description: "Second line of the left-side heading.",
}),
items: z
.array(TocItemSchema)
.min(8)
.max(8)
.default([
{ number: "03", label: "ABOUT" },
{ number: "04", label: "TIMELINE" },
{ number: "05", label: "GROUP OF COMPANIES" },
{ number: "06", label: "SERVICES" },
{ number: "07", label: "IMAGE GALLERY" },
{ number: "08", label: "STATISTICS" },
{ number: "09", label: "REPORT" },
{ number: "10", label: "CONCLUSION" },
])
.meta({
description: "Eight table-of-content entries listed on the right.",
}),
});
export type SchemaType = z.infer<typeof Schema>;
const EducationTableOfContentsSlide = ({ data }: { data: Partial<SchemaType> }) => {
return (
<div className="relative h-[720px] w-[1280px] overflow-hidden bg-[#efeff1]">
<div className="relative z-10 grid h-full grid-cols-[430px_1fr]">
<div className="bg-[#f1efef] px-[56px] pt-[74px]">
<h2 className="font-serif text-[64px] leading-[98%] tracking-[-0.02em] text-[#1a1752]">
{data.titleLine1}
<br />
{data.titleLine2}
</h2>
</div>
<div className="px-[88px] pt-[84px] bg-[#FFFFFF80]">
<div className="space-y-[32px]">
{data.items?.map((item, index) => (
<div key={`${item.number}-${item.label}-${index}`} className="flex items-center gap-[16px]">
<span className="w-[42px] text-[20px] font-semibold leading-none text-[#3f414a]">
{item.number}
</span>
<span className="text-[24px] font-medium leading-none text-[#3f414a]">
{item.label}
</span>
</div>
))}
</div>
</div>
</div>
</div>
);
};
export default EducationTableOfContentsSlide;

View file

@ -0,0 +1,182 @@
import * as z from "zod";
export const slideLayoutId = "education-timeline-slide";
export const slideLayoutName = "Education Timeline Slide";
export const slideLayoutDescription =
"A timeline slide with a title, a horizontal progress line, and year-based milestones with short descriptions.";
const MilestoneSchema = z.object({
year: z.string().min(4).max(6).meta({
description: "Year label displayed under each timeline marker.",
}),
description: z.string().min(20).max(50).meta({
description: "Short text shown under each year label.",
}),
});
export const Schema = z.object({
title: z.string().min(4).max(14).default("Timeline").meta({
description: "Main timeline heading shown at the top-left.",
}),
milestones: z
.array(MilestoneSchema)
.min(6)
.max(12)
.default([
{ year: "2022", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit." },
{ year: "1994", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit." },
{ year: "1993", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit." },
{ year: "1991", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit." },
{ year: "1991", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit." },
{ year: "1988", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " },
{ year: "1988", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit." },
{ year: "1988", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit." },
{ year: "1988", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit." },
{ year: "1988", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit." },
{ year: "1988", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit." },
])
.meta({
description: "Timeline milestones displayed left to right.",
}),
});
export type SchemaType = z.infer<typeof Schema>;
const EducationTimelineSlide = ({ data }: { data: Partial<SchemaType> }) => {
const { title, milestones } = data;
const isSixOrLess = milestones?.length && milestones?.length <= 6;
return (
<div className="relative h-[720px] w-[1280px] overflow-hidden bg-[#efeff1]">
<div className="relative z-10 px-[56px] pt-[86px]">
<h2 className="font-serif text-[84px] leading-none tracking-[-0.02em] text-[#1a1752]">
{title}
</h2>
</div>
{isSixOrLess ? (
<TimelineUpToSix milestones={milestones || []} />
) : (
<TimelineMoreThanSix milestones={milestones || []} />
)}
</div>
);
};
function TimelineUpToSix({
milestones,
}: {
milestones: any;
}) {
return (
<div className="relative z-10 mt-[80px] px-[56px]">
<div
className="grid "
style={{ gridTemplateColumns: `repeat(${milestones.length}, minmax(0, 1fr))` }}
>
{milestones.map((milestone: any, index: number) => (
<div key={`${milestone.year}-${index}`} className="">
<div className="flex items-center ">
<div className="h-[22px] z-10 relative w-[22px] rounded-full bg-[#272272]" />
{index !== milestones.length - 1 && <div className="h-[3px] bg-[#d8d8dd] flex-1" />}
</div>
<p className="mt-[18px] text-[20px] font-medium leading-none text-[#3c3f4b]">
{milestone.year}
</p>
<p className=" text-[18px] leading-[1.2] text-[#3a3d4c]">
{milestone.description}
</p>
</div>
))}
</div>
</div>
);
}
function TimelineMoreThanSix({
milestones,
}: {
milestones: SchemaType["milestones"];
}) {
const topItems = milestones.slice(0, 6);
const bottomItems = milestones.slice(6);
return (
<div className="relative z-10 mt-[84px] px-[56px]">
{/* vertical connector on left */}
<svg className="absolute z-[-1] right-[100px] top-[10px]" xmlns="http://www.w3.org/2000/svg" width="139" height="231" viewBox="0 0 139 231" fill="none">
<mask id="path-1-inside-1_220_636" fill="white">
<path d="M0 0H128C133.891 0 138.667 4.77563 138.667 10.6667V220C138.667 225.891 133.891 230.667 128 230.667H0V0Z" />
</mask>
<path d="M0 -2.66667H128C135.364 -2.66667 141.333 3.30287 141.333 10.6667H136C136 6.24839 132.418 2.66667 128 2.66667H0V-2.66667ZM141.333 220C141.333 227.364 135.364 233.333 128 233.333H0V228H128C132.418 228 136 224.418 136 220H141.333ZM136 220M0 230.667V0V230.667M128 -2.66667C135.364 -2.66667 141.333 3.30287 141.333 10.6667V220C141.333 227.364 135.364 233.333 128 233.333V228C132.418 228 136 224.418 136 220V10.6667C136 6.24839 132.418 2.66667 128 2.66667V-2.66667Z" fill="#101C3D" fillOpacity="0.1" mask="url(#path-1-inside-1_220_636)" />
</svg>
{/* bottom horizontal line */}
{/* <div className="absolute z-[-1] right-[110px] top-[220px] w-[150px] h-[3px] bg-[#d8d8dd]" /> */}
<div className="relative z-10 px-[56px]">
<div
className="grid "
style={{ gridTemplateColumns: `repeat(${topItems.length}, minmax(0, 1fr))` }}
>
{topItems.map((milestone: any, index: number) => (
<div key={`${milestone.year}-${index}`} className="">
<div className="flex items-center ">
<div className="h-[22px] z-10 relative w-[22px] rounded-full bg-[#272272]" />
{index !== milestones.length - 1 && <div className="h-[3px] bg-[#d8d8dd] flex-1" />}
</div>
<div className="pr-2 mt-[18px]">
<p className=" text-[20px] font-medium leading-none text-[#3c3f4b]">
{milestone.year}
</p>
<p className="mt-2 text-[18px] leading-[1.2] text-[#3a3d4c]">
{milestone.description}
</p>
</div>
</div>
))}
</div>
</div>
{/* bottom row */}
<div
className="mt-[75px] grid items-end px-[56px] pr-[180px] "
style={{ gridTemplateColumns: `repeat(${bottomItems.length}, minmax(0, 1fr))` }}
>
{bottomItems.map((_, colIndex) => {
const item = bottomItems[colIndex];
if (!item) return <div key={colIndex} />;
return (
<div key={`${item.year}-${colIndex + 8}`} className="flex flex-col items-end">
<div className="flex w-full items-center ">
{/* {colIndex === 0 && <div className="absolute h-[3px] flex-1 bg-[#d8d8dd]" />} */}
{true && <div className={` h-[3px] flex-1 ${colIndex === 0 ? '' : 'bg-[#d8d8dd]'}`} />}
<div className="h-[22px] z-10 relative w-[22px] rounded-full bg-[#272272]" />
</div>
<div className="pl-2 mt-[18px]">
<p className=" text-right text-[20px] font-medium leading-none text-[#3c3f4b]">
{item.year}
</p>
<p className="mt-2 text-[18px] text-right leading-[1.2] text-[#3a3d4c]">
{item.description}
</p>
</div>
</div>
);
})}
</div>
</div>
);
}
export default EducationTimelineSlide;

View file

@ -0,0 +1,5 @@
{
"description": "School and training layouts for covers, outlines, timelines, statistics, and visual storytelling",
"ordered": false,
"default": false
}

View file

@ -16,6 +16,17 @@ import CodeSlide09TableOfContent, { Schema as CodeTableOfContentSchema, slideLay
import CodeSlide10MetricsSplit, { Schema as CodeMetricsSplitSchema, slideLayoutId as CodeMetricsSplitId, slideLayoutName as CodeMetricsSplitName, slideLayoutDescription as CodeMetricsSplitDesc } from "./Code/CodeSlide10MetricsSplit";
import CodeSlide11MetricsGrid, { Schema as CodeMetricsGridSchema, slideLayoutId as CodeMetricsGridId, slideLayoutName as CodeMetricsGridName, slideLayoutDescription as CodeMetricsGridDesc } from "./Code/CodeSlide11MetricsGrid";
// Education templates
import EducationCoverSlide, { Schema as EduCoverSchema, slideLayoutId as EduCoverId, slideLayoutName as EduCoverName, slideLayoutDescription as EduCoverDesc } from "./Education/EducationCoverSlide";
import EducationTableOfContentsSlide, { Schema as EduTocSchema, slideLayoutId as EduTocId, slideLayoutName as EduTocName, slideLayoutDescription as EduTocDesc } from "./Education/EducationTableOfContentsSlide";
import EducationAboutSlide, { Schema as EduAboutSchema, slideLayoutId as EduAboutId, slideLayoutName as EduAboutName, slideLayoutDescription as EduAboutDesc } from "./Education/EducationAboutSlide";
import EducationContentSplitSlide, { Schema as EduContentSplitSchema, slideLayoutId as EduContentSplitId, slideLayoutName as EduContentSplitName, slideLayoutDescription as EduContentSplitDesc } from "./Education/EducationContentSplitSlide";
import EducationImageGallerySlide, { Schema as EduImageGallerySchema, slideLayoutId as EduImageGalleryId, slideLayoutName as EduImageGalleryName, slideLayoutDescription as EduImageGalleryDesc } from "./Education/EducationImageGallerySlide";
import EducationReportDonutSlide, { Schema as EduReportDonutSchema, slideLayoutId as EduReportDonutId, slideLayoutName as EduReportDonutName, slideLayoutDescription as EduReportDonutDesc } from "./Education/EducationReportDonutSlide";
import EducationServicesSplitSlide, { Schema as EduServicesSplitSchema, slideLayoutId as EduServicesSplitId, slideLayoutName as EduServicesSplitName, slideLayoutDescription as EduServicesSplitDesc } from "./Education/EducationServicesSplitSlide";
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";
// 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";
@ -189,6 +200,7 @@ import neoStandardSettings from "./neo-standard/settings.json";
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";
// Helper to create template entry
@ -210,6 +222,18 @@ export const codeTemplates: TemplateWithData[] = [
createTemplateEntry(CodeSlide11MetricsGrid, CodeMetricsGridSchema, CodeMetricsGridId, CodeMetricsGridName, CodeMetricsGridDesc, "code", "CodeSlide11MetricsGrid"),
];
export const educationTemplates: TemplateWithData[] = [
createTemplateEntry(EducationCoverSlide, EduCoverSchema, EduCoverId, EduCoverName, EduCoverDesc, "education", "EducationCoverSlide"),
createTemplateEntry(EducationTableOfContentsSlide, EduTocSchema, EduTocId, EduTocName, EduTocDesc, "education", "EducationTableOfContentsSlide"),
createTemplateEntry(EducationAboutSlide, EduAboutSchema, EduAboutId, EduAboutName, EduAboutDesc, "education", "EducationAboutSlide"),
createTemplateEntry(EducationContentSplitSlide, EduContentSplitSchema, EduContentSplitId, EduContentSplitName, EduContentSplitDesc, "education", "EducationContentSplitSlide"),
createTemplateEntry(EducationImageGallerySlide, EduImageGallerySchema, EduImageGalleryId, EduImageGalleryName, EduImageGalleryDesc, "education", "EducationImageGallerySlide"),
createTemplateEntry(EducationReportDonutSlide, EduReportDonutSchema, EduReportDonutId, EduReportDonutName, EduReportDonutDesc, "education", "EducationReportDonutSlide"),
createTemplateEntry(EducationServicesSplitSlide, EduServicesSplitSchema, EduServicesSplitId, EduServicesSplitName, EduServicesSplitDesc, "education", "EducationServicesSplitSlide"),
createTemplateEntry(EducationStatisticsGridSlide, EduStatisticsGridSchema, EduStatisticsGridId, EduStatisticsGridName, EduStatisticsGridDesc, "education", "EducationStatisticsGridSlide"),
createTemplateEntry(EducationTimelineSlide, EduTimelineSchema, EduTimelineId, EduTimelineName, EduTimelineDesc, "education", "EducationTimelineSlide"),
];
export const neoGeneralTemplates: TemplateWithData[] = [
createTemplateEntry(TextSplitWithEmphasisBlockLayout, TextSplitWithEmphasisBlockSchema, TextSplitWithEmphasisBlockId, TextSplitWithEmphasisBlockName, TextSplitWithEmphasisBlockDesc, 'neo-general', 'TextSplitWithEmphasisBlock'),
@ -381,6 +405,7 @@ export const allLayouts: TemplateWithData[] = [
...standardTemplates,
...swiftTemplates,
...codeTemplates,
...educationTemplates,
];
@ -450,6 +475,13 @@ export const templates: TemplateLayoutsWithSettings[] = [
settings: codeSettings as TemplateGroupSettings,
layouts: codeTemplates,
},
{
id: "education",
name: "Education",
description: educationSettings.description,
settings: educationSettings as TemplateGroupSettings,
layouts: educationTemplates,
},
];
// Helper to get templates by group ID