2025-08-28 13:14:51 +05:30
import { motion } from "motion/react" ;
import { ImageWithFallback } from "./figma/ImageWithFallback" ;
import { Play , X , ChevronLeft , ChevronRight , Star } from "lucide-react" ;
import { useState , useRef , useEffect } from "react" ;
import { BrandedTag } from "./about/BrandedTag" ;
interface Testimonial {
2026-03-27 12:43:34 +05:30
id? : number | string ;
2025-08-28 13:14:51 +05:30
name : string ;
role : string ;
company? : string ;
avatar? : string ;
image? : string ;
quote : string ;
rating : number ;
isVideo? : boolean ;
videoThumbnail? : string ;
videoUrl? : string ;
2026-03-27 12:43:34 +05:30
designation? : string ;
content? : string ;
video_url? : string ;
profile_xid? : string ;
2025-08-28 13:14:51 +05:30
}
2026-03-27 12:43:34 +05:30
// Default testimonials as fallback
2025-08-28 13:14:51 +05:30
const defaultTestimonialsData : Testimonial [ ] = [
{
id : 1 ,
name : "Sarah Chen" ,
role : "Chief Executive Officer" ,
company : "TechCorp Solutions" ,
avatar : "https://images.unsplash.com/photo-1494790108755-2616b612b786?w=400&h=400&fit=crop&crop=face" ,
quote : "KLC has revolutionized how we approach leadership development. The AI-powered insights are incredibly precise and have transformed our management effectiveness across our entire organization." ,
rating : 5 ,
isVideo : true ,
videoThumbnail : "https://images.unsplash.com/photo-1552664730-d307ca884978?w=600&h=300&fit=crop" ,
videoUrl : "https://example.com/testimonial-video-1.mp4"
} ,
{
id : 2 ,
name : "Michael Rodriguez" ,
role : "VP of Operations" ,
company : "Global Industries" ,
avatar : "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=400&h=400&fit=crop&crop=face" ,
quote : "The strategic leadership programs have equipped our team with the tools needed to navigate complex business challenges with confidence and clarity. The transformation has been remarkable." ,
rating : 5 ,
isVideo : false
} ,
{
id : 3 ,
name : "Jennifer Park" ,
role : "Director of Human Resources" ,
company : "Innovation Labs" ,
avatar : "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=400&h=400&fit=crop&crop=face" ,
quote : "KLC's approach to leadership development is refreshingly practical. Our managers have shown remarkable improvement in team engagement and decision-making capabilities." ,
rating : 4 ,
isVideo : true ,
videoThumbnail : "https://images.unsplash.com/photo-1560472355-109703aa3edc?w=600&h=300&fit=crop" ,
videoUrl : "https://example.com/testimonial-video-2.mp4"
}
] ;
// Star Rating Component
function StarRating ( { rating } : { rating : number } ) {
return (
< div className = "flex gap-1 mb-3" >
{ [ 1 , 2 , 3 , 4 , 5 ] . map ( ( star ) = > (
< Star
key = { star }
size = { 16 }
className = { ` ${
star <= rating
? 'fill-current'
: 'text-gray-300'
} ` }
style = { {
color : star <= rating ? 'var(--color-accent)' : '#D1D5DB'
} }
/ >
) ) }
< / div >
) ;
}
// Video Modal Component
function VideoModal ( { isOpen , onClose , videoUrl } : {
isOpen : boolean ;
onClose : ( ) = > void ;
videoUrl : string ;
} ) {
if ( ! isOpen ) return null ;
return (
< div className = "fixed inset-0 bg-black/80 backdrop-blur-sm z-50 flex items-center justify-center p-4" >
< div className = "relative w-full max-w-4xl aspect-video bg-black rounded-xl overflow-hidden" >
< button
onClick = { onClose }
className = "absolute top-4 right-4 z-10 w-10 h-10 bg-black/50 hover:bg-black/70 rounded-full flex items-center justify-center text-white transition-all duration-200"
>
< X size = { 20 } / >
< / button >
< div className = "w-full h-full flex items-center justify-center text-white" >
< div className = "text-center" >
< Play size = { 64 } className = "mx-auto mb-4 opacity-60" / >
< p className = "text-lg opacity-80" > Video Testimonial < / p >
< p className = "text-sm opacity-60 mt-2" > { videoUrl } < / p >
< / div >
< / div >
< / div >
< / div >
) ;
}
2026-03-27 12:43:34 +05:30
// Individual Testimonial Card
2025-08-28 13:14:51 +05:30
function TestimonialCard ( { testimonial , onPlayVideo } : {
testimonial : Testimonial ;
onPlayVideo : ( videoUrl : string ) = > void ;
} ) {
const avatarSrc = testimonial . avatar || testimonial . image ;
2026-03-27 12:43:34 +05:30
const isVideo = testimonial . isVideo || ! ! testimonial . video_url ;
const videoUrl = testimonial . videoUrl || testimonial . video_url || "" ;
const role = testimonial . role || testimonial . designation || "" ;
const quote = testimonial . quote || testimonial . content || "" ;
const name = testimonial . name || "" ;
const rating = testimonial . rating || 5 ;
2025-08-28 13:14:51 +05:30
return (
< motion.div
className = "testimonial-card bg-white rounded-xl border transition-all duration-300 flex-shrink-0 w-[350px] h-[300px] card-hover-lift"
style = { {
borderColor : 'rgba(0, 0, 0, 0.1)' ,
boxShadow : '0 2px 8px rgba(0, 0, 0, 0.1)'
} }
initial = { { opacity : 0 , y : 20 } }
whileInView = { { opacity : 1 , y : 0 } }
transition = { { duration : 0.5 } }
viewport = { { once : true } }
whileHover = { {
boxShadow : '0 8px 25px rgba(0, 0, 0, 0.15)' ,
transform : 'translateY(-4px)'
} }
>
{ /* Video Testimonials */ }
2026-03-27 12:43:34 +05:30
{ isVideo ? (
2025-08-28 13:14:51 +05:30
< div
className = "relative h-full cursor-pointer overflow-hidden group rounded-xl"
2026-03-27 12:43:34 +05:30
onClick = { ( ) = > onPlayVideo ( videoUrl ) }
2025-08-28 13:14:51 +05:30
>
< ImageWithFallback
2026-03-27 12:43:34 +05:30
src = { testimonial . videoThumbnail || avatarSrc || "https://images.unsplash.com/photo-1552664730-d307ca884978?w=600&h=300&fit=crop" }
alt = { ` ${ name } video testimonial ` }
2025-08-28 13:14:51 +05:30
className = "w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
/ >
{ /* Video Overlay with Gradient */ }
< div className = "absolute inset-0 bg-gradient-to-t from-black/60 via-black/20 to-transparent group-hover:from-black/70 transition-all duration-300" / >
{ /* Play Button - Compact Design */ }
< div className = "absolute inset-0 flex items-center justify-center" >
< motion.div
className = "flex items-center justify-center w-16 h-16 bg-white/90 backdrop-blur-sm rounded-full shadow-lg"
whileHover = { { scale : 1.1 } }
whileTap = { { scale : 0.95 } }
>
< Play
className = "w-7 h-7 ml-1 text-blue-600"
fill = "currentColor"
/ >
< / motion.div >
< / div >
{ /* Video Label - Compact Style */ }
< div className = "absolute top-4 left-4" >
< div className = "px-3 py-1 rounded-full text-xs font-medium text-white bg-blue-600" >
🎥 Video
< / div >
< / div >
{ /* Profile Info - Bottom Section */ }
< div className = "absolute bottom-0 left-0 right-0 p-4" >
< div className = "flex items-center gap-3" >
< div className = "w-10 h-10 rounded-full overflow-hidden bg-white shadow-lg flex-shrink-0" >
< ImageWithFallback
src = { avatarSrc || "" }
2026-03-27 12:43:34 +05:30
alt = { name }
2025-08-28 13:14:51 +05:30
className = "w-full h-full object-cover"
/ >
< / div >
< div className = "min-w-0 flex-1" >
< h4 className = "font-semibold text-white mb-1 text-sm" >
2026-03-27 12:43:34 +05:30
{ name }
2025-08-28 13:14:51 +05:30
< / h4 >
< p className = "text-xs text-white/80 truncate" >
2026-03-27 12:43:34 +05:30
{ role }
2025-08-28 13:14:51 +05:30
{ testimonial . company && ` • ${ testimonial . company } ` }
< / p >
< / div >
{ /* Star Rating */ }
< div className = "flex gap-1" >
{ [ 1 , 2 , 3 , 4 , 5 ] . map ( ( star ) = > (
< Star
key = { star }
size = { 14 }
2026-03-27 12:43:34 +05:30
className = { star <= rating ? 'fill-current text-yellow-400' : 'text-white/40' }
2025-08-28 13:14:51 +05:30
/ >
) ) }
< / div >
< / div >
< / div >
< / div >
) : (
/* Text Testimonials - Compact Design */
< div className = "h-full flex flex-col p-6" >
{ /* Header Section */ }
< div className = "flex items-start justify-between mb-4" >
< div className = "flex items-center gap-3" >
< div className = "w-12 h-12 rounded-full overflow-hidden bg-gray-100 flex-shrink-0" >
< ImageWithFallback
src = { avatarSrc || "" }
2026-03-27 12:43:34 +05:30
alt = { name }
2025-08-28 13:14:51 +05:30
className = "w-full h-full object-cover"
/ >
< / div >
< div className = "min-w-0" >
< h4 className = "font-semibold text-black mb-1 text-sm" >
2026-03-27 12:43:34 +05:30
{ name }
2025-08-28 13:14:51 +05:30
< / h4 >
< p className = "text-xs text-gray-600" >
2026-03-27 12:43:34 +05:30
{ role }
2025-08-28 13:14:51 +05:30
< / p >
{ testimonial . company && (
< p className = "text-xs text-gray-500 font-medium" >
{ testimonial . company }
< / p >
) }
< / div >
< / div >
{ /* Star Rating - Top Right */ }
< div className = "flex gap-1" >
{ [ 1 , 2 , 3 , 4 , 5 ] . map ( ( star ) = > (
< Star
key = { star }
size = { 14 }
2026-03-27 12:43:34 +05:30
className = { star <= rating ? 'fill-current text-yellow-400' : 'text-gray-300' }
2025-08-28 13:14:51 +05:30
/ >
) ) }
< / div >
< / div >
{ /* Quote Section - Compact Typography */ }
< blockquote className = "flex-1 mb-4" >
< div className = "text-sm leading-relaxed text-black relative" >
< span className = "text-4xl absolute -top-1 -left-1 leading-none opacity-20 text-blue-600" >
"
< / span >
< span className = "relative z-10" >
2026-03-27 12:43:34 +05:30
{ quote }
2025-08-28 13:14:51 +05:30
< / span >
< / div >
< / blockquote >
{ /* Bottom Accent Line */ }
< div className = "w-12 h-1 rounded-full bg-blue-600" / >
< / div >
) }
< / motion.div >
) ;
}
export function TestimonialsSection ( {
customTestimonials ,
title = "What Our Leaders Say" ,
subtitle = "Hear from executives and managers who have transformed their leadership approach through our comprehensive development programs." ,
tagText = "Success Stories"
} : {
customTestimonials? : Testimonial [ ] ;
title? : string ;
subtitle? : string ;
tagText? : string ;
} ) {
const [ isVideoModalOpen , setIsVideoModalOpen ] = useState ( false ) ;
const [ currentVideoUrl , setCurrentVideoUrl ] = useState ( "" ) ;
const [ canScrollLeft , setCanScrollLeft ] = useState ( false ) ;
const [ canScrollRight , setCanScrollRight ] = useState ( true ) ;
const [ isDragging , setIsDragging ] = useState ( false ) ;
const scrollContainerRef = useRef < HTMLDivElement > ( null ) ;
// Use custom testimonials if provided, otherwise use default
const testimonialsData = customTestimonials || defaultTestimonialsData ;
// Handle scroll state
const handleScroll = ( ) = > {
if ( scrollContainerRef . current ) {
const { scrollLeft , scrollWidth , clientWidth } = scrollContainerRef . current ;
setCanScrollLeft ( scrollLeft > 0 ) ;
setCanScrollRight ( scrollLeft < scrollWidth - clientWidth - 10 ) ;
}
} ;
// Scroll to direction - Updated for compact card width
const scrollToDirection = ( direction : 'left' | 'right' ) = > {
if ( scrollContainerRef . current ) {
const scrollAmount = 390 ; // Adjusted for compact card width (350px + 32px gap)
const currentScroll = scrollContainerRef . current . scrollLeft ;
const targetScroll = direction === 'left'
? Math . max ( 0 , currentScroll - scrollAmount )
: currentScroll + scrollAmount ;
scrollContainerRef . current . scrollTo ( {
left : targetScroll ,
behavior : 'smooth'
} ) ;
}
} ;
// Mouse drag functionality
const [ dragStart , setDragStart ] = useState ( { x : 0 , scrollLeft : 0 } ) ;
const handleMouseDown = ( e : React.MouseEvent ) = > {
setIsDragging ( true ) ;
if ( scrollContainerRef . current ) {
setDragStart ( {
x : e.pageX ,
scrollLeft : scrollContainerRef.current.scrollLeft
} ) ;
}
} ;
const handleMouseMove = ( e : React.MouseEvent ) = > {
if ( ! isDragging || ! scrollContainerRef . current ) return ;
e . preventDefault ( ) ;
const x = e . pageX ;
const walk = ( x - dragStart . x ) * 2 ;
scrollContainerRef . current . scrollLeft = dragStart . scrollLeft - walk ;
} ;
const handleMouseUp = ( ) = > {
setIsDragging ( false ) ;
} ;
// Touch functionality
const [ touchStart , setTouchStart ] = useState ( { x : 0 , scrollLeft : 0 } ) ;
const handleTouchStart = ( e : React.TouchEvent ) = > {
if ( scrollContainerRef . current ) {
setTouchStart ( {
x : e.touches [ 0 ] . clientX ,
scrollLeft : scrollContainerRef.current.scrollLeft
} ) ;
}
} ;
const handleTouchMove = ( e : React.TouchEvent ) = > {
if ( ! scrollContainerRef . current ) return ;
const x = e . touches [ 0 ] . clientX ;
const walk = ( x - touchStart . x ) * 2 ;
scrollContainerRef . current . scrollLeft = touchStart . scrollLeft - walk ;
} ;
const handleTouchEnd = ( ) = > {
// Touch ended
} ;
const handlePlayVideo = ( videoUrl : string ) = > {
setCurrentVideoUrl ( videoUrl ) ;
setIsVideoModalOpen ( true ) ;
} ;
const handleCloseModal = ( ) = > {
setIsVideoModalOpen ( false ) ;
setCurrentVideoUrl ( "" ) ;
} ;
// Initialize scroll state and keyboard navigation
useEffect ( ( ) = > {
const timer = setTimeout ( ( ) = > {
handleScroll ( ) ;
} , 100 ) ;
// Keyboard navigation support
const handleKeyDown = ( e : KeyboardEvent ) = > {
if ( e . key === 'ArrowLeft' && canScrollLeft ) {
e . preventDefault ( ) ;
scrollToDirection ( 'left' ) ;
} else if ( e . key === 'ArrowRight' && canScrollRight ) {
e . preventDefault ( ) ;
scrollToDirection ( 'right' ) ;
}
} ;
document . addEventListener ( 'keydown' , handleKeyDown ) ;
return ( ) = > {
clearTimeout ( timer ) ;
document . removeEventListener ( 'keydown' , handleKeyDown ) ;
} ;
} , [ canScrollLeft , canScrollRight ] ) ;
return (
< section
className = "py-24"
style = { { backgroundColor : '#FFFFFF' } }
aria - labelledby = "testimonials-heading"
>
< div className = "max-w-7xl mx-auto section-margin-x" data-layout = "testimonials-v1" >
{ /* Section Header */ }
< div className = "text-center mb-16" >
{ /* Branded Tag */ }
< div className = "flex justify-center" >
< BrandedTag text = { tagText } / >
< / div >
< h2 id = "testimonials-heading" className = "text-h2 mb-6" > { title } < / h2 >
< p className = "text-body-lg text-muted max-w-3xl mx-auto" > { subtitle } < / p >
< / div >
{ /* Testimonials Cards Area */ }
< div className = "relative" >
{ /* Compact Navigation Controls */ }
< div className = "flex justify-end gap-3 mb-8 relative z-20" >
< button
className = { ` w-12 h-12 flex items-center justify-center rounded-lg border-2 transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 ${
! canScrollLeft
? 'opacity-40 cursor-not-allowed bg-gray-50 border-gray-200'
: 'bg-white border-blue-600 hover:bg-blue-600 hover:text-white'
} ` }
onClick = { ( ) = > scrollToDirection ( 'left' ) }
disabled = { ! canScrollLeft }
aria - label = "Scroll testimonials left"
>
< ChevronLeft size = { 18 } / >
< / button >
< button
className = { ` w-12 h-12 flex items-center justify-center rounded-lg border-2 transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 ${
! canScrollRight
? 'opacity-40 cursor-not-allowed bg-gray-50 border-gray-200'
: 'bg-white border-blue-600 hover:bg-blue-600 hover:text-white'
} ` }
onClick = { ( ) = > scrollToDirection ( 'right' ) }
disabled = { ! canScrollRight }
aria - label = "Scroll testimonials right"
>
< ChevronRight size = { 18 } / >
< / button >
< / div >
{ /* Cards Container with Enhanced Design */ }
< div className = "relative" >
{ /* Scrollable Cards Container */ }
< div
ref = { scrollContainerRef }
className = { ` ${ isDragging ? 'cursor-grabbing' : 'cursor-grab' } flex overflow-x-auto gap-6 py-4 pb-6 scrollbar-hide ` }
onScroll = { handleScroll }
onMouseDown = { handleMouseDown }
onMouseMove = { handleMouseMove }
onMouseUp = { handleMouseUp }
onMouseLeave = { handleMouseUp }
onTouchStart = { handleTouchStart }
onTouchMove = { handleTouchMove }
onTouchEnd = { handleTouchEnd }
style = { {
userSelect : 'none' ,
WebkitUserSelect : 'none' ,
msUserSelect : 'none' ,
MozUserSelect : 'none' ,
scrollbarWidth : 'none' ,
msOverflowStyle : 'none' ,
scrollBehavior : 'smooth'
} }
>
< div className = "flex gap-6 px-2" >
{ testimonialsData . map ( ( testimonial , index ) = > (
< TestimonialCard
key = { testimonial . id || index }
testimonial = { testimonial }
onPlayVideo = { handlePlayVideo }
/ >
) ) }
< / div >
< / div >
{ /* Left Side White Fade Overlay */ }
< div
className = "absolute top-0 left-0 bottom-0 w-20 pointer-events-none z-5"
style = { {
background : 'linear-gradient(to right, #FFFFFF, transparent)'
} }
/ >
{ /* Right Side Fade Gradient Overlay - Now properly positioned */ }
< div
className = "absolute top-0 right-0 bottom-0 w-20 pointer-events-none z-5"
style = { {
background : 'linear-gradient(to right, transparent, #FFFFFF)'
} }
/ >
< / div >
< / div >
< / div >
{ /* Video Modal */ }
< VideoModal
isOpen = { isVideoModalOpen }
onClose = { handleCloseModal }
videoUrl = { currentVideoUrl }
/ >
< style > { `
. scrollbar - hide {
- ms - overflow - style : none ;
scrollbar - width : none ;
}
. scrollbar - hide : : - webkit - scrollbar {
display : none ;
}
/* Enhanced testimonial card animations */
. testimonial - card {
transition : all 0.3 s cubic - bezier ( 0.4 , 0 , 0.2 , 1 ) ;
}
. testimonial - card :hover {
transform : translateY ( - 4 px ) ;
}
` }</style>
< / section >
) ;
}