presenton/servers/nextjs/presentation-layouts/professional/StatisticDualChartSlide.tsx

321 lines
No EOL
16 KiB
TypeScript

import React from "react";
import * as z from "zod";
import { ImageSchema, IconSchema } from "../defaultSchemes";
import { ChartContainer, ChartTooltip, ChartTooltipContent } from '@/components/ui/chart';
import { BarChart, Bar, AreaChart, Area, XAxis, YAxis, CartesianGrid } from "recharts";
// Schema definition
export const Schema = z.object({
sectionTitle: z.string()
.min(3)
.max(30)
.default("PERFORMANCE METRICS")
.meta({
description: "Main section heading - adapt to presentation topic (e.g., 'Climate Analysis', 'Health Outcomes', 'Research Data', 'Impact Assessment')",
}),
organizationName: z.string()
.min(2)
.max(30)
.default("Your Organization")
.meta({
description: "Name of the organization or entity presenting the data",
}),
brandLogo: ImageSchema.default({
__image_url__: "https://via.placeholder.com/40x40/22C55E/FFFFFF?text=L",
__image_prompt__: "Professional organization logo - clean and modern design"
}).meta({
description: "Logo or brand mark representing the organization",
}),
barChartData: z.array(z.object({
name: z.string(),
series1: z.number(),
series2: z.number(),
series3: z.number()
})).min(5).max(5).default([
{ name: "Item 1", series1: 5, series2: 5, series3: 8 },
{ name: "Item 2", series1: 8, series2: 8, series3: 15 },
{ name: "Item 3", series1: 15, series2: 10, series3: 18 },
{ name: "Item 4", series1: 18, series2: 14, series3: 22 },
{ name: "Item 5", series1: 22, series2: 20, series3: 8 }
]).meta({
description: "CRITICAL: Provide topic-specific data for the left bar chart. For global warming: 5 years of data (2020-2024) with CO2 emissions by sector (Transport, Industry, Energy) with actual values. For healthcare: Patient outcomes across 5 categories (Prevention, Treatment, Recovery) with real percentages. For education: Student performance across 5 metrics (Reading, Math, Science) with grade levels. Use realistic data patterns and values.",
}),
areaChartData: z.array(z.object({
name: z.string(),
series1: z.number(),
series2: z.number(),
series3: z.number()
})).min(5).max(5).default([
{ name: "Item 1", series1: 20, series2: 30, series3: 15 },
{ name: "Item 2", series1: 40, series2: 45, series3: 35 },
{ name: "Item 3", series1: 45, series2: 50, series3: 80 },
{ name: "Item 4", series1: 50, series2: 45, series3: 85 },
{ name: "Item 5", series1: 80, series2: 75, series3: 120 }
]).meta({
description: "CRITICAL: Provide topic-specific data for the right area chart. For global warming: Cumulative data over 5 time periods showing renewable energy adoption, carbon reduction efforts, and policy implementations with realistic growth trends. For healthcare: Cumulative patient care metrics showing improvement over time. For education: Progressive learning outcomes showing student advancement. Ensure data shows meaningful trends relevant to the topic.",
}),
leftChartTitle: z.string()
.min(5)
.max(40)
.default("Our Customer's Satisfaction")
.meta({
description: "IMPORTANT: Provide topic-specific title for left chart. For global warming: 'Global CO2 Emissions by Sector', 'Temperature Rise by Region', 'Renewable Energy Adoption'. For healthcare: 'Patient Treatment Outcomes', 'Healthcare Quality Metrics', 'Recovery Success Rates'. For education: 'Student Performance by Subject', 'Learning Progress Assessment', 'Academic Achievement Trends'.",
}),
leftChartDescription: z.string()
.min(20)
.max(200)
.default("An impressive client satisfaction rate underscores our unwavering commitment to delivering exceptional service and exceeding expectations.")
.meta({
description: "ESSENTIAL: Provide topic-relevant description explaining the left chart data. For global warming: Explain emission sources, trends, and implications. For healthcare: Describe treatment effectiveness and patient outcomes. For education: Explain performance metrics and learning indicators. Make it informative and specific to the data shown.",
}),
rightChartTitle: z.string()
.min(5)
.max(40)
.default("Repeat Order Rate")
.meta({
description: "IMPORTANT: Provide topic-specific title for right chart. For global warming: 'Climate Action Progress', 'Carbon Reduction Timeline', 'Sustainability Milestones'. For healthcare: 'Patient Recovery Timeline', 'Treatment Progress Tracking', 'Health Improvement Trajectory'. For education: 'Learning Progress Over Time', 'Student Development Path', 'Academic Growth Timeline'.",
}),
rightChartDescription: z.string()
.min(20)
.max(200)
.default("Our remarkable client repeat order rate of 123 times are testament to the quality of our products/services and the trust our clients place in our ability.")
.meta({
description: "ESSENTIAL: Provide topic-relevant description explaining the right chart's cumulative/timeline data. For global warming: Describe progress in climate action, policy impact, or environmental improvements. For healthcare: Explain patient journey and recovery progression. For education: Describe learning advancement and skill development over time. Make it specific and data-driven.",
}),
})
// Chart configuration
const chartConfig = {
series1: {
label: "Series 1",
color: "#1D9A8A",
},
series2: {
label: "Series 2",
color: "#A8C97F",
},
series3: {
label: "Series 3",
color: "#E8F4B8",
},
};
// Type inference
type SchemaType = z.infer<typeof Schema>;
// Component definition
const StatisticDualChartSlide = ({ data }: { data: Partial<SchemaType> }) => {
const { sectionTitle, organizationName, brandLogo, barChartData, areaChartData, leftChartTitle, leftChartDescription, rightChartTitle, rightChartDescription } = data;
return (
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
{/* Header Section */}
<div className="h-20 bg-teal-600 px-16 py-4 flex justify-between items-center">
{/* Title */}
{sectionTitle && (
<h1 className="text-4xl font-black text-white">
{sectionTitle}
</h1>
)}
{/* Company Branding */}
<div className="flex items-center space-x-3">
{brandLogo?.__image_url__ && (
<div className="w-8 h-8">
<img
src={brandLogo.__image_url__}
alt={brandLogo.__image_prompt__}
className="w-full h-full object-contain"
/>
</div>
)}
{organizationName && (
<span className="text-lg font-bold text-white">
{organizationName}
</span>
)}
</div>
</div>
{/* Content Section */}
<div className="flex-1 h-[calc(100%-80px)] flex">
{/* Left Chart Section */}
<div className="w-1/2 p-8 bg-gray-50 flex flex-col">
{/* Chart Legend */}
<div className="flex items-center justify-start mb-4 space-x-4">
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-teal-600 rounded-full"></div>
<span className="text-sm text-gray-600">Series 1</span>
</div>
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-green-400 rounded-full"></div>
<span className="text-sm text-gray-600">Series 2</span>
</div>
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-yellow-200 rounded-full"></div>
<span className="text-sm text-gray-600">Series 3</span>
</div>
</div>
{/* Bar Chart */}
{barChartData && barChartData.length > 0 && (
<div className="flex-1 mb-6">
<ChartContainer config={chartConfig} className="h-full w-full">
<BarChart
data={barChartData}
margin={{ top: 10, right: 20, left: 0, bottom: 30 }}
barCategoryGap="20%"
>
<CartesianGrid strokeDasharray="3 3" stroke="#f0f0f0" />
<XAxis
dataKey="name"
axisLine={false}
tickLine={false}
tick={{ fontSize: 12, fill: '#666' }}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fontSize: 12, fill: '#666' }}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<Bar
dataKey="series1"
fill="#1D9A8A"
radius={[2, 2, 0, 0]}
barSize={15}
/>
<Bar
dataKey="series2"
fill="#A8C97F"
radius={[2, 2, 0, 0]}
barSize={15}
/>
<Bar
dataKey="series3"
fill="#E8F4B8"
radius={[2, 2, 0, 0]}
barSize={15}
/>
</BarChart>
</ChartContainer>
</div>
)}
{/* Chart Description */}
<div className="space-y-3">
{leftChartTitle && (
<h3 className="text-xl font-bold text-gray-900">
{leftChartTitle}
</h3>
)}
{leftChartDescription && (
<p className="text-base leading-relaxed text-gray-700">
{leftChartDescription}
</p>
)}
</div>
</div>
{/* Right Chart Section */}
<div className="w-1/2 p-8 bg-white flex flex-col">
{/* Chart Legend */}
<div className="flex items-center justify-end mb-4 space-x-4">
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-yellow-200 rounded-full"></div>
<span className="text-sm text-gray-600">Series 1</span>
</div>
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-green-400 rounded-full"></div>
<span className="text-sm text-gray-600">Series 2</span>
</div>
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-teal-600 rounded-full"></div>
<span className="text-sm text-gray-600">Series 3</span>
</div>
</div>
{/* Area Chart */}
{areaChartData && areaChartData.length > 0 && (
<div className="flex-1 mb-6">
<ChartContainer config={chartConfig} className="h-full w-full">
<AreaChart
data={areaChartData}
margin={{ top: 10, right: 20, left: 0, bottom: 30 }}
>
<CartesianGrid strokeDasharray="3 3" stroke="#f0f0f0" />
<XAxis
dataKey="name"
axisLine={false}
tickLine={false}
tick={{ fontSize: 12, fill: '#666' }}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fontSize: 12, fill: '#666' }}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<Area
type="monotone"
dataKey="series3"
stackId="1"
stroke="#1D9A8A"
fill="#1D9A8A"
fillOpacity={0.8}
/>
<Area
type="monotone"
dataKey="series2"
stackId="1"
stroke="#A8C97F"
fill="#A8C97F"
fillOpacity={0.8}
/>
<Area
type="monotone"
dataKey="series1"
stackId="1"
stroke="#E8F4B8"
fill="#E8F4B8"
fillOpacity={0.8}
/>
</AreaChart>
</ChartContainer>
</div>
)}
{/* Chart Description */}
<div className="space-y-3">
{rightChartTitle && (
<h3 className="text-xl font-bold text-gray-900">
{rightChartTitle}
</h3>
)}
{rightChartDescription && (
<p className="text-base leading-relaxed text-gray-700">
{rightChartDescription}
</p>
)}
</div>
</div>
</div>
</div>
);
};
export default StatisticDualChartSlide;