first commit
This commit is contained in:
298
src/components/ReusableCarousel.tsx
Normal file
298
src/components/ReusableCarousel.tsx
Normal file
@@ -0,0 +1,298 @@
|
||||
import React, { useState } from 'react';
|
||||
import { navigateTo } from './Router';
|
||||
import { Card, CardContent } from './ui/card';
|
||||
import { PrimaryCTAButton } from './PrimaryCTAButton';
|
||||
import { CheckCircle, Target, ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
|
||||
// Types for the reusable carousel
|
||||
export interface CarouselItem {
|
||||
title: string;
|
||||
description: string;
|
||||
icon: React.ComponentType<any>;
|
||||
features?: string[];
|
||||
outcome?: string;
|
||||
// Additional flexible properties for different content types
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface CarouselConfig {
|
||||
eyebrowText: string;
|
||||
title: string;
|
||||
description: string;
|
||||
ctaText: string;
|
||||
ctaAction: () => void;
|
||||
ctaAriaLabel: string;
|
||||
}
|
||||
|
||||
export interface ReusableCarouselProps {
|
||||
items: CarouselItem[];
|
||||
config: CarouselConfig;
|
||||
className?: string;
|
||||
cardWidth?: string; // Default: "70%"
|
||||
cardSpacing?: string; // Default: "mr-7" (28px)
|
||||
featuresTitle?: string; // Default: "Key Features & Benefits:"
|
||||
outcomesTitle?: string; // Default: "Expected Outcomes:"
|
||||
customCardRenderer?: (item: CarouselItem, index: number) => React.ReactNode;
|
||||
}
|
||||
|
||||
export function ReusableCarousel({
|
||||
items,
|
||||
config,
|
||||
className = "",
|
||||
cardWidth = "70%",
|
||||
cardSpacing = "mr-7",
|
||||
featuresTitle = "Key Features & Benefits:",
|
||||
outcomesTitle = "Expected Outcomes:",
|
||||
customCardRenderer
|
||||
}: ReusableCarouselProps) {
|
||||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
|
||||
const nextSlide = () => {
|
||||
setCurrentIndex((prev) => (prev + 1) % items.length);
|
||||
};
|
||||
|
||||
const prevSlide = () => {
|
||||
setCurrentIndex((prev) => (prev - 1 + items.length) % items.length);
|
||||
};
|
||||
|
||||
const renderDefaultCard = (item: CarouselItem, index: number) => (
|
||||
<Card
|
||||
className="border border-gray-200 h-full overflow-hidden"
|
||||
style={{ backgroundColor: '#FFFFFF' }}
|
||||
>
|
||||
<CardContent className="p-6">
|
||||
{/* Service Icon & Header - Inline Layout */}
|
||||
<div className="flex items-center gap-4 mb-4">
|
||||
<div
|
||||
className="w-14 h-14 rounded-xl flex items-center justify-center flex-shrink-0"
|
||||
style={{ backgroundColor: 'rgba(4, 4, 91, 0.1)' }}
|
||||
>
|
||||
{React.createElement(item.icon, {
|
||||
className: "w-7 h-7 text-primary"
|
||||
})}
|
||||
</div>
|
||||
|
||||
<h3 className="text-h4 text-black leading-tight">
|
||||
{item.title}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
{/* Divider Line */}
|
||||
<div
|
||||
className="w-full h-px mb-4"
|
||||
style={{ backgroundColor: 'rgba(0, 0, 0, 0.1)' }}
|
||||
></div>
|
||||
|
||||
{/* Description */}
|
||||
<p className="text-body text-muted leading-relaxed mb-6">
|
||||
{item.description}
|
||||
</p>
|
||||
|
||||
{/* Key Features */}
|
||||
{item.features && item.features.length > 0 && (
|
||||
<div className="mb-6">
|
||||
<h4 className="text-subhead text-black mb-4 font-medium">
|
||||
{featuresTitle}
|
||||
</h4>
|
||||
<div className="grid md:grid-cols-2 gap-3">
|
||||
{item.features.map((feature: string, featureIndex: number) => (
|
||||
<div key={featureIndex} className="flex items-start gap-2">
|
||||
<CheckCircle className="w-4 h-4 flex-shrink-0 mt-0.5 text-primary" />
|
||||
<span className="text-small text-muted leading-relaxed">
|
||||
{feature}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Expected Outcomes */}
|
||||
{item.outcome && (
|
||||
<div
|
||||
className="rounded-lg p-4"
|
||||
style={{ backgroundColor: 'rgba(247, 247, 253, 0.5)' }}
|
||||
>
|
||||
<h4 className="text-subhead text-black mb-3 font-medium">
|
||||
{outcomesTitle}
|
||||
</h4>
|
||||
<div className="flex items-start gap-3">
|
||||
<Target className="w-4 h-4 flex-shrink-0 mt-0.5 text-primary" />
|
||||
<span className="text-small text-muted leading-relaxed">
|
||||
{item.outcome}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={`max-w-7xl mx-auto mb-16 ${className}`}>
|
||||
{/* Header Section */}
|
||||
<div className="mb-12 relative">
|
||||
{/* Left Side - Eyebrow Text, Header & Subtext */}
|
||||
<div className="flex-1 max-w-2xl">
|
||||
<div className="branded-tag-system mb-6">
|
||||
<div className="dot"></div>
|
||||
<span className="text">{config.eyebrowText}</span>
|
||||
</div>
|
||||
<h2 className="text-h2 mb-4 leading-tight">{config.title}</h2>
|
||||
<p className="text-body-lg text-muted leading-relaxed">
|
||||
{config.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Navigation Controls - Bottom Right of Header */}
|
||||
<div className="absolute bottom-0 right-0 flex items-center gap-4">
|
||||
<span className="text-body text-muted font-medium">
|
||||
{String(currentIndex + 1).padStart(2, '0')} / {String(items.length).padStart(2, '0')}
|
||||
</span>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={prevSlide}
|
||||
className="w-12 h-12 rounded-lg border-2 border-gray-200 flex items-center justify-center hover:border-primary hover:bg-primary hover:text-white transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
disabled={currentIndex === 0}
|
||||
aria-label="Previous item"
|
||||
>
|
||||
<ChevronLeft className="w-5 h-5" />
|
||||
</button>
|
||||
<button
|
||||
onClick={nextSlide}
|
||||
className="w-12 h-12 rounded-lg border-2 border-gray-200 flex items-center justify-center hover:border-primary hover:bg-primary hover:text-white transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
disabled={currentIndex === items.length - 1}
|
||||
aria-label="Next item"
|
||||
>
|
||||
<ChevronRight className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Carousel Content */}
|
||||
<div className="relative">
|
||||
{/* Carousel Container */}
|
||||
<div className="overflow-hidden">
|
||||
<div
|
||||
className="flex transition-transform duration-700 ease-in-out"
|
||||
style={{ transform: `translateX(-${currentIndex * 75}%)` }}
|
||||
>
|
||||
{items.map((item, index) => (
|
||||
<div
|
||||
key={`${item.title}-${index}`}
|
||||
className={`flex-shrink-0 w-[${cardWidth}] ${cardSpacing}`}
|
||||
>
|
||||
{customCardRenderer ? customCardRenderer(item, index) : renderDefaultCard(item, index)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CTA Button - Right Bottom with 40px spacing */}
|
||||
<div className="flex justify-end" style={{ marginTop: '40px' }}>
|
||||
<PrimaryCTAButton
|
||||
text={config.ctaText}
|
||||
onClick={config.ctaAction}
|
||||
ariaLabel={config.ctaAriaLabel}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Pre-configured carousel variants for common use cases
|
||||
export function ConsultingServicesCarousel({
|
||||
consultingServices
|
||||
}: {
|
||||
consultingServices: CarouselItem[]
|
||||
}) {
|
||||
const config: CarouselConfig = {
|
||||
eyebrowText: "CONSULTING & ADVISORY SERVICES",
|
||||
title: "Our Services",
|
||||
description: "Comprehensive consulting solutions designed to drive organizational transformation and leadership excellence across all levels of your business.",
|
||||
ctaText: "Explore Consulting Services",
|
||||
ctaAction: () => navigateTo('/services/consulting'),
|
||||
ctaAriaLabel: "Explore our consulting services"
|
||||
};
|
||||
|
||||
return (
|
||||
<ReusableCarousel
|
||||
items={consultingServices}
|
||||
config={config}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function LeadershipProgramsCarousel({
|
||||
leadershipPrograms
|
||||
}: {
|
||||
leadershipPrograms: CarouselItem[]
|
||||
}) {
|
||||
const config: CarouselConfig = {
|
||||
eyebrowText: "LEADERSHIP DEVELOPMENT PROGRAMS",
|
||||
title: "Our Programs",
|
||||
description: "Comprehensive leadership development programs designed to build capabilities and drive organizational transformation with measurable impact.",
|
||||
ctaText: "Explore Leadership Programs",
|
||||
ctaAction: () => navigateTo('/services/leadership-development'),
|
||||
ctaAriaLabel: "Explore our leadership development programs"
|
||||
};
|
||||
|
||||
return (
|
||||
<ReusableCarousel
|
||||
items={leadershipPrograms}
|
||||
config={config}
|
||||
featuresTitle="Program Features:"
|
||||
outcomesTitle="Program Outcomes:"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function ExpertServicesCarousel({
|
||||
expertServices
|
||||
}: {
|
||||
expertServices: CarouselItem[]
|
||||
}) {
|
||||
const config: CarouselConfig = {
|
||||
eyebrowText: "EXPERT CONSULTATION & SERVICES",
|
||||
title: "Expert Services",
|
||||
description: "Personalized guidance from industry thought leaders and senior practitioners who bring decades of real-world experience.",
|
||||
ctaText: "Connect with Experts",
|
||||
ctaAction: () => navigateTo('/services/executive-coaching'),
|
||||
ctaAriaLabel: "Connect with our expert consultants"
|
||||
};
|
||||
|
||||
return (
|
||||
<ReusableCarousel
|
||||
items={expertServices}
|
||||
config={config}
|
||||
featuresTitle="Service Features:"
|
||||
outcomesTitle="Expected Results:"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function PlatformFeaturesCarousel({
|
||||
platformFeatures
|
||||
}: {
|
||||
platformFeatures: CarouselItem[]
|
||||
}) {
|
||||
const config: CarouselConfig = {
|
||||
eyebrowText: "DIGITAL PLATFORM FEATURES",
|
||||
title: "Platform Capabilities",
|
||||
description: "State-of-the-art digital learning platform with cutting-edge technology and interactive experiences for modern learning.",
|
||||
ctaText: "Explore Platform",
|
||||
ctaAction: () => navigateTo('/learning-online'),
|
||||
ctaAriaLabel: "Explore our online learning platform"
|
||||
};
|
||||
|
||||
return (
|
||||
<ReusableCarousel
|
||||
items={platformFeatures}
|
||||
config={config}
|
||||
featuresTitle="Platform Features:"
|
||||
outcomesTitle="Learning Benefits:"
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user