fix hero btn and navigate on click bar
This commit is contained in:
@@ -2,7 +2,8 @@ import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
import { navigateTo } from './Router';
|
||||
import svgPaths from "../imports/svg-i1joeov37f";
|
||||
|
||||
import PrimaryCTAButton from './PrimaryCTAButton';
|
||||
|
||||
interface SlideData {
|
||||
id: number;
|
||||
title: string;
|
||||
@@ -11,12 +12,12 @@ interface SlideData {
|
||||
ctaText: string;
|
||||
route: string;
|
||||
}
|
||||
|
||||
|
||||
export default function HeroSection() {
|
||||
const [currentSlide, setCurrentSlide] = useState(0);
|
||||
const [isAutoPlaying, setIsAutoPlaying] = useState(true);
|
||||
const [progressValues, setProgressValues] = useState([0, 0, 0, 0, 0, 0]);
|
||||
|
||||
|
||||
const slides: SlideData[] = [
|
||||
{
|
||||
id: 1,
|
||||
@@ -32,7 +33,7 @@ export default function HeroSection() {
|
||||
backgroundImage: "https://images.unsplash.com/photo-1705234384669-c6d31c61b789?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxleGVjdXRpdmUlMjBsZWFkZXJzaGlwJTIwZGV2ZWxvcG1lbnQlMjB0cmFpbmluZ3xlbnwxfHx8fDE3NTY4MDcyNjJ8MA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral",
|
||||
shortTitle: "Leadership Development",
|
||||
ctaText: "Explore Leadership Journeys",
|
||||
route: '/services/leadership-development'
|
||||
route: '/services/leadership-development'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
@@ -67,25 +68,25 @@ export default function HeroSection() {
|
||||
route: '/services/learning-facility'
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
const totalSlides = slides.length;
|
||||
const slideDuration = 5000; // 5 seconds per slide
|
||||
|
||||
|
||||
// Auto-advance slides
|
||||
useEffect(() => {
|
||||
if (!isAutoPlaying) return;
|
||||
|
||||
|
||||
const interval = setInterval(() => {
|
||||
setCurrentSlide((prev) => (prev + 1) % totalSlides);
|
||||
}, slideDuration);
|
||||
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [isAutoPlaying, totalSlides]);
|
||||
|
||||
|
||||
// Progress bar animation
|
||||
useEffect(() => {
|
||||
if (!isAutoPlaying) return;
|
||||
|
||||
|
||||
const interval = setInterval(() => {
|
||||
setProgressValues(prev => {
|
||||
const newProgress = [...prev];
|
||||
@@ -103,10 +104,10 @@ export default function HeroSection() {
|
||||
return newProgress;
|
||||
});
|
||||
}, 100);
|
||||
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [currentSlide, isAutoPlaying]);
|
||||
|
||||
|
||||
// Reset progress when manually changing slides
|
||||
useEffect(() => {
|
||||
setProgressValues(prev => {
|
||||
@@ -115,7 +116,7 @@ export default function HeroSection() {
|
||||
return newProgress;
|
||||
});
|
||||
}, [currentSlide]);
|
||||
|
||||
|
||||
const goToSlide = useCallback((slideIndex: number) => {
|
||||
if (slideIndex !== currentSlide) {
|
||||
setCurrentSlide(slideIndex);
|
||||
@@ -124,23 +125,23 @@ export default function HeroSection() {
|
||||
setTimeout(() => setIsAutoPlaying(true), 3000);
|
||||
}
|
||||
}, [currentSlide]);
|
||||
|
||||
|
||||
const nextSlide = useCallback(() => {
|
||||
const next = (currentSlide + 1) % totalSlides;
|
||||
goToSlide(next);
|
||||
}, [currentSlide, totalSlides, goToSlide]);
|
||||
|
||||
|
||||
const prevSlide = useCallback(() => {
|
||||
const prev = (currentSlide - 1 + totalSlides) % totalSlides;
|
||||
goToSlide(prev);
|
||||
}, [currentSlide, totalSlides, goToSlide]);
|
||||
|
||||
|
||||
// Pause auto-play on hover
|
||||
const handleMouseEnter = () => setIsAutoPlaying(false);
|
||||
const handleMouseLeave = () => setIsAutoPlaying(true);
|
||||
|
||||
|
||||
return (
|
||||
<section
|
||||
<section
|
||||
className="hero-section"
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
@@ -157,7 +158,7 @@ export default function HeroSection() {
|
||||
<div className="hero-overlay" />
|
||||
</div>
|
||||
))}
|
||||
|
||||
|
||||
{/* Hero Content */}
|
||||
<div className="hero-content">
|
||||
<div className="hero-text-section">
|
||||
@@ -167,144 +168,54 @@ export default function HeroSection() {
|
||||
</h1>
|
||||
|
||||
{/* Dynamic CTA Button - Enhanced with Proper Navigation */}
|
||||
<button
|
||||
className="hero-slide-button group box-border content-stretch flex flex-row gap-2.5 items-center justify-start p-0 relative cursor-pointer overflow-hidden"
|
||||
style={{
|
||||
background: 'transparent',
|
||||
border: 'none'
|
||||
}}
|
||||
onClick={() => {
|
||||
console.log('Hero button clicked - navigating to contact page');
|
||||
navigateTo(slides[currentSlide].route);
|
||||
}}
|
||||
aria-label={slides[currentSlide].ctaText}
|
||||
>
|
||||
{/* Icon Container with Slide Animation */}
|
||||
<div className="relative shrink-0 size-[50px] overflow-hidden">
|
||||
{/* Background Rectangle - Consistent Blue Color */}
|
||||
<div className="absolute inset-0 bg-[#04045B]" />
|
||||
|
||||
{/* Icon Layer - Sliding Animation */}
|
||||
<div className="icon-layer absolute inset-0 w-full h-full">
|
||||
{/* Primary Arrow - Slides out diagonally up-right */}
|
||||
<div className="icon absolute inset-0 flex items-center justify-center transition-all duration-300 ease-in-out group-hover:translate-x-6 group-hover:-translate-y-6 group-hover:opacity-0">
|
||||
<svg
|
||||
className="block w-full h-full"
|
||||
fill="none"
|
||||
preserveAspectRatio="none"
|
||||
viewBox="0 0 50 50"
|
||||
>
|
||||
<g clipPath="url(#clip0_95_1043_primary)">
|
||||
<path d={svgPaths.p5b8d700} fill="white" />
|
||||
<path d={svgPaths.p30b71a00} fill="white" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_95_1043_primary">
|
||||
<rect fill="white" height="50" width="50" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{/* Secondary Arrow - Slides in from bottom-left */}
|
||||
<div className="icon absolute inset-0 flex items-center justify-center opacity-0 -translate-x-6 translate-y-6 transition-all duration-300 ease-in-out group-hover:translate-x-0 group-hover:translate-y-0 group-hover:opacity-100">
|
||||
<svg
|
||||
className="block w-full h-full"
|
||||
fill="none"
|
||||
preserveAspectRatio="none"
|
||||
viewBox="0 0 50 50"
|
||||
>
|
||||
<g clipPath="url(#clip0_95_1043_secondary)">
|
||||
<path d={svgPaths.p5b8d700} fill="white" />
|
||||
<path d={svgPaths.p30b71a00} fill="white" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_95_1043_secondary">
|
||||
<rect fill="white" height="50" width="50" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Text Section with Vertical Slide Animation */}
|
||||
<div className="text-layer relative shrink-0 overflow-hidden flex items-center" style={{
|
||||
height: '28px',
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
fontSize: '20px',
|
||||
fontWeight: '400',
|
||||
lineHeight: '28px',
|
||||
whiteSpace: 'nowrap',
|
||||
color: '#ffffff'
|
||||
}}>
|
||||
{/* Primary Text - Slides up and out */}
|
||||
<div
|
||||
className="text-element absolute inset-0 flex items-center justify-start transition-all duration-300 ease-in-out group-hover:-translate-y-full group-hover:opacity-0"
|
||||
style={{
|
||||
color: '#ffffff !important',
|
||||
textShadow: '0 1px 2px rgba(0, 0, 0, 0.4)',
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
fontSize: '20px',
|
||||
fontWeight: '400',
|
||||
lineHeight: '28px'
|
||||
}}
|
||||
>
|
||||
{slides[currentSlide].ctaText}
|
||||
</div>
|
||||
|
||||
{/* Secondary Text - Slides in from bottom */}
|
||||
<div
|
||||
className="text-element absolute inset-0 flex items-center justify-start translate-y-full opacity-0 transition-all duration-300 ease-in-out group-hover:translate-y-0 group-hover:opacity-100"
|
||||
style={{
|
||||
color: '#ffffff !important',
|
||||
textShadow: '0 1px 2px rgba(0, 0, 0, 0.4)',
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
fontSize: '20px',
|
||||
fontWeight: '400',
|
||||
lineHeight: '28px'
|
||||
}}
|
||||
>
|
||||
{slides[currentSlide].ctaText}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<PrimaryCTAButton
|
||||
text={slides[currentSlide].ctaText}
|
||||
onClick={() => navigateTo(slides[currentSlide].route)}
|
||||
ariaLabel="Learn more about KLC"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{/* Bottom Navigation */}
|
||||
<div className="hero-navigation">
|
||||
{/* Progress Section */}
|
||||
<div className="hero-progress-container">
|
||||
{slides.map((slide, index) => (
|
||||
<div
|
||||
<div
|
||||
key={slide.id}
|
||||
className="hero-progress-item"
|
||||
onClick={() => goToSlide(index)}
|
||||
>
|
||||
{/* Progress Bar */}
|
||||
<div
|
||||
className={`hero-progress-segment ${index === currentSlide ? 'active' : ''}`}
|
||||
<div
|
||||
key={slide.id}
|
||||
className="hero-progress-item"
|
||||
onClick={() => navigateTo(slide.route)}
|
||||
>
|
||||
<div
|
||||
className="hero-progress-fill"
|
||||
style={{ width: index === currentSlide ? `${progressValues[index]}%` : '0%' }}
|
||||
/>
|
||||
</div>
|
||||
{/* Progress Bar */}
|
||||
<div className={`hero-progress-segment ${index === currentSlide ? 'active' : ''}`}>
|
||||
<div
|
||||
className="hero-progress-fill"
|
||||
style={{ width: index === currentSlide ? `${progressValues[index]}%` : '0%' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Progress Number */}
|
||||
<div className={`hero-progress-number ${index === currentSlide ? 'active' : ''}`}>
|
||||
{String(index + 1).padStart(2, '0')}
|
||||
</div>
|
||||
{/* Progress Number */}
|
||||
<div className={`hero-progress-number ${index === currentSlide ? 'active' : ''}`}>
|
||||
{String(index + 1).padStart(2, '0')}
|
||||
</div>
|
||||
|
||||
{/* Progress Text */}
|
||||
<div className={`hero-progress-text ${index === currentSlide ? 'active' : ''}`}>
|
||||
{slide.shortTitle}
|
||||
{/* Progress Text */}
|
||||
<div className={`hero-progress-text ${index === currentSlide ? 'active' : ''}`}>
|
||||
{slide.shortTitle}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
|
||||
{/* Navigation Arrows */}
|
||||
<div className="hero-controls">
|
||||
<button
|
||||
|
||||
@@ -1,146 +1,101 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import svgPaths from "../imports/svg-i1joeov37f";
|
||||
import { navigateTo } from './Router';
|
||||
import React, { useState } from 'react';
|
||||
import { ArrowRight } from 'lucide-react';
|
||||
|
||||
interface PrimaryCTAButtonProps {
|
||||
text: string;
|
||||
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
className?: string;
|
||||
onClick: () => void;
|
||||
ariaLabel?: string;
|
||||
debugId?: string; // Add debug identifier
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const PrimaryCTAButton: React.FC<PrimaryCTAButtonProps> = ({
|
||||
text,
|
||||
onClick,
|
||||
export function PrimaryCTAButton({
|
||||
text,
|
||||
onClick,
|
||||
ariaLabel,
|
||||
className = '',
|
||||
ariaLabel,
|
||||
debugId = 'unknown'
|
||||
}) => {
|
||||
// Debug: Log when component mounts
|
||||
useEffect(() => {
|
||||
console.log(`PrimaryCTAButton ${debugId} mounted`);
|
||||
}, [debugId]);
|
||||
|
||||
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.preventDefault();
|
||||
console.log(`Primary CTA Button ${debugId} clicked with text: "${text}"`); // Debug log
|
||||
// Handle webinars navigation
|
||||
if (text === "Join Our Webinars" || text === "Explore Webinars" || text === "View Webinars") {
|
||||
console.log(`Navigating to webinars page for ${debugId}`);
|
||||
navigateTo('/webinars');
|
||||
} else if (text === "Register Free" || text === "Watch Replay" || text === "Launch in Zoom") {
|
||||
// These are handled by individual webinar detail pages
|
||||
console.log(`Webinar CTA handled by detail page for ${debugId}`);
|
||||
} else if (onClick) {
|
||||
console.log(`Custom onClick handler found for ${debugId}, executing it`); // Debug log
|
||||
onClick(e);
|
||||
} else {
|
||||
console.log(`No custom onClick for ${debugId}, navigating to webcast page`); // Debug log
|
||||
// Navigate to webcast page by default
|
||||
navigateTo('/learning/webcast');
|
||||
}
|
||||
};
|
||||
disabled = false
|
||||
}: PrimaryCTAButtonProps) {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
return (
|
||||
<button
|
||||
className={`primary-cta-button group box-border content-stretch flex flex-row gap-2.5 items-center justify-start p-0 relative cursor-pointer overflow-hidden ${className}`}
|
||||
style={{
|
||||
background: 'transparent',
|
||||
border: 'none',
|
||||
width: 'fit-content' // Perfect width - no extra horizontal space
|
||||
<button
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
aria-label={ariaLabel || text}
|
||||
className={`primary-cta-button ${className}`}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: '12px',
|
||||
padding: '8px 20px',
|
||||
borderRadius: '10px',
|
||||
border: 'none',
|
||||
cursor: disabled ? 'not-allowed' : 'pointer',
|
||||
fontSize: 'var(--font-body-lg)',
|
||||
fontWeight: 'var(--font-weight-h3)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
backgroundColor: disabled ? '#9CA3AF' : 'var(--color-primary)',
|
||||
color: '#FFFFFF',
|
||||
boxShadow: disabled
|
||||
? 'none'
|
||||
: '0 4px 6px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.06)',
|
||||
transition: 'all 300ms ease-in-out',
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
opacity: disabled ? 0.6 : 1,
|
||||
transform: isHovered && !disabled ? 'translateY(-2px)' : 'translateY(0)',
|
||||
...(isHovered && !disabled && {
|
||||
backgroundColor: '#030359', // Darker shade of primary brand color
|
||||
boxShadow: '0 8px 25px rgba(4, 4, 91, 0.3), 0 4px 6px rgba(4, 4, 91, 0.1)'
|
||||
})
|
||||
}}
|
||||
>
|
||||
{/* Text */}
|
||||
<span
|
||||
style={{
|
||||
fontSize: 'var(--font-body-lg)',
|
||||
fontWeight: 'var(--font-weight-h3)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: '#FFFFFF',
|
||||
whiteSpace: 'nowrap'
|
||||
}}
|
||||
onClick={handleClick}
|
||||
aria-label={ariaLabel || text}
|
||||
>
|
||||
{/* Icon Container with Slide Animation */}
|
||||
<div className="relative shrink-0 size-[50px] overflow-hidden">
|
||||
{/* Background Rectangle - Consistent Yellow Color */}
|
||||
<div className="absolute inset-0 bg-[#F8C301]" />
|
||||
|
||||
{/* Icon Layer - Sliding Animation */}
|
||||
<div className="icon-layer absolute inset-0 w-full h-full">
|
||||
{/* Primary Arrow - Slides out diagonally up-right */}
|
||||
<div className="icon absolute inset-0 flex items-center justify-center transition-all duration-300 ease-in-out group-hover:translate-x-6 group-hover:-translate-y-6 group-hover:opacity-0">
|
||||
<svg
|
||||
className="block w-full h-full"
|
||||
fill="none"
|
||||
preserveAspectRatio="none"
|
||||
viewBox="0 0 50 50"
|
||||
>
|
||||
<g clipPath="url(#clip0_primary_cta_primary)">
|
||||
<path d={svgPaths.p5b8d700} fill="white" />
|
||||
<path d={svgPaths.p30b71a00} fill="white" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_primary_cta_primary">
|
||||
<rect fill="white" height="50" width="50" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{/* Secondary Arrow - Slides in from bottom-left */}
|
||||
<div className="icon absolute inset-0 flex items-center justify-center opacity-0 -translate-x-6 translate-y-6 transition-all duration-300 ease-in-out group-hover:translate-x-0 group-hover:translate-y-0 group-hover:opacity-100">
|
||||
<svg
|
||||
className="block w-full h-full"
|
||||
fill="none"
|
||||
preserveAspectRatio="none"
|
||||
viewBox="0 0 50 50"
|
||||
>
|
||||
<g clipPath="url(#clip0_primary_cta_secondary)">
|
||||
<path d={svgPaths.p5b8d700} fill="white" />
|
||||
<path d={svgPaths.p30b71a00} fill="white" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_primary_cta_secondary">
|
||||
<rect fill="white" height="50" width="50" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Text Section with Vertical Slide Animation */}
|
||||
<div className="text-layer relative shrink-0 overflow-hidden flex items-center" style={{
|
||||
height: '28px',
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
fontSize: '20px',
|
||||
fontWeight: '400',
|
||||
lineHeight: '28px',
|
||||
whiteSpace: 'nowrap',
|
||||
color: '#ffffff',
|
||||
width: 'fit-content' // Perfect text width - no extra horizontal space
|
||||
}}>
|
||||
{/* Primary Text - Slides up and out */}
|
||||
<div
|
||||
className="text-element absolute inset-0 flex items-center justify-start transition-all duration-300 ease-in-out group-hover:-translate-y-full group-hover:opacity-0"
|
||||
style={{
|
||||
color: '#ffffff',
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
fontSize: '20px',
|
||||
fontWeight: '400',
|
||||
lineHeight: '28px'
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
|
||||
{/* Secondary Text - Slides in from bottom */}
|
||||
<div
|
||||
className="text-element absolute inset-0 flex items-center justify-start translate-y-full opacity-0 transition-all duration-300 ease-in-out group-hover:translate-y-0 group-hover:opacity-100"
|
||||
style={{
|
||||
color: '#ffffff',
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
fontSize: '20px',
|
||||
fontWeight: '400',
|
||||
lineHeight: '28px'
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
{text}
|
||||
</span>
|
||||
|
||||
{/* Arrow icon with enhanced animation */}
|
||||
<span
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.2)',
|
||||
transition: 'all 300ms ease-in-out',
|
||||
transform: isHovered && !disabled ? 'translateX(4px) scale(1.1)' : 'translateX(0) scale(1)',
|
||||
...(isHovered && !disabled && {
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.3)'
|
||||
})
|
||||
}}
|
||||
>
|
||||
<ArrowRight
|
||||
size={14}
|
||||
style={{
|
||||
color: '#FFFFFF',
|
||||
transition: 'all 300ms ease-in-out'
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
// Named exports for backward compatibility
|
||||
export const PrimaryCTAButtonProps = PrimaryCTAButton;
|
||||
export default PrimaryCTAButton;
|
||||
Reference in New Issue
Block a user