298 lines
9.7 KiB
TypeScript
298 lines
9.7 KiB
TypeScript
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:"
|
|
/>
|
|
);
|
|
} |