presenton/electron/servers/nextjs/app/presentation-templates/Report/IntroductionStatsSlide.tsx
2026-04-09 21:46:31 +05:45

189 lines
6.1 KiB
TypeScript

import { Fragment } from "react/jsx-runtime";
import * as z from "zod";
const MetricSchema = z.object({
value: z.string().min(1).max(6).meta({
description: "Primary metric value shown in the card.",
}),
label: z.string().min(0).max(10).optional().meta({
description: "Short metric label shown below the value.",
}),
description: z.string().min(0).max(20).optional().meta({
description: "Supporting text shown below the label.",
}),
});
const StatColumnSchema = z.object({
metrics: z.array(MetricSchema).min(2).max(2).meta({
description: "Two stacked metrics shown in one tall card.",
}),
});
export const slideLayoutId = "metrics-slide";
export const slideLayoutName = "Metrics Slide";
export const slideLayoutDescription =
"A slide with a title and explanatory text on the left, an optional bulleted list underneath the text, and metric cards on the right. Each metric card contains two stacked metric blocks.";
export const Schema = z.object({
title: z.string().min(3).max(12).default("Introduction").meta({
description: "Slide title shown at the top-left.",
}),
body: z.string().max(250).default(
"Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut alut enim ad minima veniam, quis. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut alut enim ad minima veniam, quis"
).meta({
description: "Primary paragraph shown below the title.",
}),
bullets: z
.array(z.string().max(100))
.max(4)
.optional()
.default([
"Ut enim ad minima veniam, quis nostrum",
"Exercitationem ullam corporis suscipit",
"Ut enim ad minima veniam, quis nostrum",
"exercitationem ullam corporis suscipit",
])
.meta({
description: "Optional bullet list shown after the description if required.",
}),
statColumns: z
.array(StatColumnSchema)
.max(2)
.default([
{
metrics: [
{ value: "25K", label: "Students", description: "Ut enim ad minima" },
{ value: "25K", label: "Students", description: "Ut enim ad minima" },
],
},
{
metrics: [
{ value: "25K", label: "Students", description: "Ut enim ad minima" },
{ value: "25K", label: "Students", description: "Ut enim ad minima" },
],
},
])
.meta({
description: "Two stat cards shown on the right side of the slide.",
}),
});
export type SchemaType = z.infer<typeof Schema>;
type StatMetric = {
value: string;
label?: string;
description?: string;
};
function StatPill({
metrics,
}: {
metrics: StatMetric[];
}) {
return (
<div
className=" h-[438px] w-[248px] overflow-hidden rounded-[127px] bg-[#157CFF] px-[28px] py-[74px] text-center text-white"
style={{
backgroundColor: "var(--primary-color,#157CFF)",
color: "var(--primary-text,#ffffff)",
}}
>
{metrics.map((metric, index) => (
<Fragment key={`${metric.value}-${metric.label}-${index}`}>
<div
key={`${metric.value}-${metric.label}-${index}`}
className={``}
>
<p className="text-[55px] leading-[44.353px] tracking-[-1.09px]">
{metric.value}
</p>
{metric.label && <p className="mt-[6px] text-[20px] leading-none">{metric.label}</p>}
{metric.description && <p className=" text-[20px] mt-1 leading-[1.15] text-white/90" style={{ color: "var(--primary-text,#ffffff)", opacity: 0.9 }}>
{metric.description}
</p>}
</div>
{index === 0 && <div className="py-[22px]">
<svg xmlns="http://www.w3.org/2000/svg" width="181" height="1" viewBox="0 0 181 1" fill="none">
<path
opacity="0.2"
d="M0 0.487305H180.122"
stroke="var(--primary-text,#ffffff)"
strokeWidth="0.974913"
strokeDasharray="3.9 1.95"
/>
</svg>
</div>
}
</Fragment>
))}
</div>
);
}
const IntroductionStatsSlide = ({ data }: { data: Partial<SchemaType> }) => {
const { title, body, bullets, statColumns } = data;
return (
<div
className="relative h-[720px] w-[1280px] overflow-hidden rounded-[24px] bg-[#f9f8f8]"
style={{
backgroundColor: "var(--background-color,#f9f8f8)",
fontFamily: "var(--body-font-family,Helvetica Neue)",
}}
>
<div
className="absolute left-0 top-0 w-[42px] rounded-b-[22px] bg-[#157CFF]"
style={{ height: 185, backgroundColor: "var(--primary-color,#157CFF)" }}
/>
<div className="px-[64px] pt-[48px]">
<h2
className="text-[80px] font-bold leading-[108.4%] tracking-[-2.419px] text-[#232223]"
style={{ color: "var(--background-text,#232223)" }}
>
{title}
</h2>
</div>
<div className="flex justify-between px-[96px] pt-[38px]">
<div className="">
<p className="max-w-[400px] text-[24px] leading-[26.667px] text-[#232223]" style={{ color: "var(--background-text,#232223)" }}>
{body}
</p>
<div
className="mt-[34px] list-disc pl-[28px] text-[24px] leading-[26.667px] text-[#232223]"
style={{ color: "var(--background-text,#232223)" }}
>
{bullets?.map((bullet, index) => (
<div key={`${bullet}-${index}`} className="mt-[8px] flex items-center gap-2">
<div className="w-[8px] h-[8px] rounded-full bg-[#232223]" style={{ backgroundColor: "var(--background-text,#232223)" }} /> <p className="text-[24px] leading-[26.667px] text-[#232223]" style={{ color: "var(--background-text,#232223)" }}>
{bullet}
</p>
</div>
))}
</div>
</div>
<div className="ml-[48px] flex gap-[34px]">
{statColumns?.map((column, index) => (
<StatPill key={`intro-stat-column-${index}`} metrics={column.metrics} />
))}
</div>
</div>
</div>
);
};
export default IntroductionStatsSlide;