first commit
@@ -0,0 +1,79 @@
|
||||
import svgPaths from "./svg-9nb29n5e63";
|
||||
|
||||
function Frame1597884903() {
|
||||
return (
|
||||
<div className="absolute font-['Inter:Regular',_sans-serif] font-normal inset-[22%_-4.71%_22%_20.59%] leading-[0] not-italic overflow-clip text-[#ffffff] text-[20px] text-left text-nowrap">
|
||||
<div className="absolute left-0 top-0">
|
||||
<p className="block leading-[28px] text-nowrap whitespace-pre">
|
||||
Build Your Leadership Pipeline
|
||||
</p>
|
||||
</div>
|
||||
<div className="absolute left-0 top-[22px]">
|
||||
<p className="block leading-[28px] text-nowrap whitespace-pre">
|
||||
Build Your Leadership Pipeline
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Frame18() {
|
||||
return (
|
||||
<div className="absolute bg-[#04045b] bottom-0 left-0 overflow-clip right-[85.29%] top-0">
|
||||
<div
|
||||
className="absolute flex h-[25.953px] items-center justify-center translate-x-[-50%] translate-y-[-50%] w-[25.953px]"
|
||||
style={{ top: "calc(50% - 0.226px)", left: "calc(50% - 0.645px)" }}
|
||||
>
|
||||
<div className="flex-none rotate-[225deg] scale-y-[-100%]">
|
||||
<div className="h-5 relative w-[16.717px]" data-name="Vector">
|
||||
<svg
|
||||
className="block size-full"
|
||||
fill="none"
|
||||
preserveAspectRatio="none"
|
||||
viewBox="0 0 17 20"
|
||||
>
|
||||
<path
|
||||
d={svgPaths.pac4fa00}
|
||||
fill="var(--fill-0, white)"
|
||||
id="Vector"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="absolute flex h-[38.935px] items-center justify-center translate-x-[-50%] translate-y-[-50%] w-[38.935px]"
|
||||
style={{ top: "calc(50% + 35.775px)", left: "calc(50% - 35.646px)" }}
|
||||
>
|
||||
<div className="flex-none rotate-[225deg] scale-y-[-100%]">
|
||||
<div className="h-[30px] relative w-[25.075px]" data-name="Vector">
|
||||
<svg
|
||||
className="block size-full"
|
||||
fill="none"
|
||||
preserveAspectRatio="none"
|
||||
viewBox="0 0 26 30"
|
||||
>
|
||||
<path
|
||||
d={svgPaths.p177af00}
|
||||
fill="var(--fill-0, white)"
|
||||
id="Vector"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function BuildYourLeadershipPipelineButton() {
|
||||
return (
|
||||
<div
|
||||
className="relative size-full"
|
||||
data-name="Build Your Leadership Pipeline (Button)"
|
||||
>
|
||||
<Frame1597884903 />
|
||||
<Frame18 />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import svgPaths from "./svg-sphymsws2o";
|
||||
|
||||
function Frame1597884903() {
|
||||
return (
|
||||
<div className="absolute bottom-[22%] font-['Inter:Regular',_sans-serif] font-normal leading-[0] left-[19.66%] not-italic overflow-clip right-0 text-[#ffffff] text-[20px] text-left text-nowrap top-[22%]">
|
||||
<div className="absolute left-0 top-0">
|
||||
<p className="block leading-[28px] text-nowrap whitespace-pre">
|
||||
Build Your Leadership Pipeline
|
||||
</p>
|
||||
</div>
|
||||
<div className="absolute left-0 top-[22px]">
|
||||
<p className="block leading-[28px] text-nowrap whitespace-pre">
|
||||
Build Your Leadership Pipeline
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Frame18() {
|
||||
return (
|
||||
<div className="absolute bg-[#04045b] bottom-0 left-0 overflow-clip right-[85.95%] top-0">
|
||||
<div
|
||||
className="absolute flex h-[25.953px] items-center justify-center translate-x-[-50%] translate-y-[-50%] w-[25.953px]"
|
||||
style={{ top: "calc(50% - 0.226px)", left: "calc(50% - 0.645px)" }}
|
||||
>
|
||||
<div className="flex-none rotate-[225deg] scale-y-[-100%]">
|
||||
<div className="h-5 relative w-[16.717px]" data-name="Vector">
|
||||
<svg
|
||||
className="block size-full"
|
||||
fill="none"
|
||||
preserveAspectRatio="none"
|
||||
viewBox="0 0 17 20"
|
||||
>
|
||||
<path
|
||||
d={svgPaths.pac4fa00}
|
||||
fill="var(--fill-0, white)"
|
||||
id="Vector"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="absolute flex h-[38.935px] items-center justify-center translate-x-[-50%] translate-y-[-50%] w-[38.935px]"
|
||||
style={{ top: "calc(50% + 35.775px)", left: "calc(50% - 35.646px)" }}
|
||||
>
|
||||
<div className="flex-none rotate-[225deg] scale-y-[-100%]">
|
||||
<div className="h-[30px] relative w-[25.075px]" data-name="Vector">
|
||||
<svg
|
||||
className="block size-full"
|
||||
fill="none"
|
||||
preserveAspectRatio="none"
|
||||
viewBox="0 0 26 30"
|
||||
>
|
||||
<path
|
||||
d={svgPaths.p177af00}
|
||||
fill="var(--fill-0, white)"
|
||||
id="Vector"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function BuildYourLeadershipPipelineButton() {
|
||||
return (
|
||||
<div
|
||||
className="relative size-full"
|
||||
data-name="Build Your Leadership Pipeline (Button)"
|
||||
>
|
||||
<Frame1597884903 />
|
||||
<Frame18 />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
79
src/.figma_internal/BuildYourLeadershipPipelineButton.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import svgPaths from "./svg-k5kyhbfrwc";
|
||||
|
||||
function Frame1597884903() {
|
||||
return (
|
||||
<div className="absolute bottom-[22%] font-['Source_Sans_Pro:Regular',_sans-serif] leading-[0] left-[21.61%] not-italic overflow-clip right-0 text-[#ffffff] text-[20px] text-left text-nowrap top-[22%]">
|
||||
<div className="absolute left-0 top-0">
|
||||
<p className="block leading-[28px] text-nowrap whitespace-pre">
|
||||
Build Your Leadership Pipeline
|
||||
</p>
|
||||
</div>
|
||||
<div className="absolute left-0 top-[22px]">
|
||||
<p className="block leading-[28px] text-nowrap whitespace-pre">
|
||||
Build Your Leadership Pipeline
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Frame18() {
|
||||
return (
|
||||
<div className="absolute bg-[#04045b] bottom-0 left-0 overflow-clip right-[84.57%] top-0">
|
||||
<div
|
||||
className="absolute flex h-[25.953px] items-center justify-center translate-x-[-50%] translate-y-[-50%] w-[25.953px]"
|
||||
style={{ top: "calc(50% - 0.226px)", left: "calc(50% - 0.645px)" }}
|
||||
>
|
||||
<div className="flex-none rotate-[225deg] scale-y-[-100%]">
|
||||
<div className="h-5 relative w-[16.717px]" data-name="Vector">
|
||||
<svg
|
||||
className="block size-full"
|
||||
fill="none"
|
||||
preserveAspectRatio="none"
|
||||
viewBox="0 0 17 20"
|
||||
>
|
||||
<path
|
||||
d={svgPaths.pac4fa00}
|
||||
fill="var(--fill-0, white)"
|
||||
id="Vector"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="absolute flex h-[38.935px] items-center justify-center translate-x-[-50%] translate-y-[-50%] w-[38.935px]"
|
||||
style={{ top: "calc(50% + 35.775px)", left: "calc(50% - 35.645px)" }}
|
||||
>
|
||||
<div className="flex-none rotate-[225deg] scale-y-[-100%]">
|
||||
<div className="h-[30px] relative w-[25.075px]" data-name="Vector">
|
||||
<svg
|
||||
className="block size-full"
|
||||
fill="none"
|
||||
preserveAspectRatio="none"
|
||||
viewBox="0 0 26 30"
|
||||
>
|
||||
<path
|
||||
d={svgPaths.p177af00}
|
||||
fill="var(--fill-0, white)"
|
||||
id="Vector"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function BuildYourLeadershipPipelineButton() {
|
||||
return (
|
||||
<div
|
||||
className="relative size-full"
|
||||
data-name="Build Your Leadership Pipeline (Button)"
|
||||
>
|
||||
<Frame1597884903 />
|
||||
<Frame18 />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
178
src/.figma_internal/OurApproachSection.tsx
Normal file
@@ -0,0 +1,178 @@
|
||||
function Frame1597884955() {
|
||||
return (
|
||||
<div className="box-border content-stretch flex flex-col gap-7 items-center justify-start leading-[0] not-italic p-0 relative shrink-0 text-center w-[652px]">
|
||||
<div className="font-['Inter:Semi_Bold',_sans-serif] font-semibold relative shrink-0 text-[#26231a] text-[48px] tracking-[-0.96px] w-full">
|
||||
<p className="block leading-[53px]">Our Approach</p>
|
||||
</div>
|
||||
<div className="font-['Inter:Regular',_sans-serif] font-normal relative shrink-0 text-[18px] text-[rgba(38,35,26,0.8)] w-full">
|
||||
<p className="block leading-[24px]">
|
||||
Programmatic journey over 6–12 weeks combining practical application with peer learning for maximum management
|
||||
effectiveness.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Component01() {
|
||||
return (
|
||||
<div className="absolute bg-[#ffffff] h-[234px] left-0 top-0 w-[1230px]" data-name="01">
|
||||
<div
|
||||
className="absolute font-['Inter:Regular',_sans-serif] font-normal leading-[0] not-italic text-[18px] text-[rgba(38,35,26,0.8)] text-left top-[186px] w-[652px]"
|
||||
style={{ left: "calc(50% - 400px)" }}
|
||||
>
|
||||
<p className="block leading-[24px]">
|
||||
Comprehensive assessment of current management skills and development needs
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className="absolute font-['Inter:Semi_Bold',_sans-serif] font-semibold leading-[0] not-italic text-[#26231a] text-[40px] text-center text-nowrap top-10 tracking-[-0.8px] translate-x-[-50%]"
|
||||
style={{ left: "calc(50% - 577.5px)" }}
|
||||
>
|
||||
<p className="adjustLetterSpacing block leading-[53px] whitespace-pre">(01)</p>
|
||||
</div>
|
||||
<div
|
||||
className="absolute font-['Inter:Semi_Bold',_sans-serif] font-semibold leading-[53px] not-italic text-[#26231a] text-[40px] text-left text-nowrap top-10 tracking-[-0.8px] whitespace-pre"
|
||||
style={{ left: "calc(50% - 400px)" }}
|
||||
>
|
||||
<p className="adjustLetterSpacing block mb-0">{`Skills `}</p>
|
||||
<p className="adjustLetterSpacing block">Assessment</p>
|
||||
</div>
|
||||
<div className="absolute h-0 left-0 top-0 w-[1230px]">
|
||||
<div className="absolute bottom-0 left-0 right-0 top-[-1px]">
|
||||
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 1230 1">
|
||||
<line id="Line 62" stroke="var(--stroke-0, black)" x2="1230" y1="0.5" y2="0.5" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Component02() {
|
||||
return (
|
||||
<div className="absolute bg-[#ffffff] h-[234px] left-0 top-[274px] w-[1230px]" data-name="02">
|
||||
<div
|
||||
className="absolute font-['Inter:Regular',_sans-serif] font-normal leading-[0] not-italic text-[#26231a] text-[18px] text-left top-[186px] w-[652px]"
|
||||
style={{ left: "calc(50% - 399px)" }}
|
||||
>
|
||||
<p className="block leading-[24px]">
|
||||
Customized curriculum design based on assessment results and operational context
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className="absolute font-['Inter:Semi_Bold',_sans-serif] font-semibold leading-[0] not-italic text-[#26231a] text-[40px] text-center text-nowrap top-10 tracking-[-0.8px] translate-x-[-50%]"
|
||||
style={{ left: "calc(50% - 576.5px)" }}
|
||||
>
|
||||
<p className="adjustLetterSpacing block leading-[53px] whitespace-pre">(02)</p>
|
||||
</div>
|
||||
<div
|
||||
className="absolute font-['Inter:Semi_Bold',_sans-serif] font-semibold leading-[53px] not-italic text-[#26231a] text-[40px] text-left text-nowrap top-10 tracking-[-0.8px] whitespace-pre"
|
||||
style={{ left: "calc(50% - 399px)" }}
|
||||
>
|
||||
<p className="adjustLetterSpacing block mb-0">{`Program `}</p>
|
||||
<p className="adjustLetterSpacing block">Design</p>
|
||||
</div>
|
||||
<div className="absolute h-0 left-[3px] top-0 w-[1230px]">
|
||||
<div className="absolute bottom-0 left-0 right-0 top-[-1px]">
|
||||
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 1230 1">
|
||||
<line id="Line 62" stroke="var(--stroke-0, black)" x2="1230" y1="0.5" y2="0.5" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Component03() {
|
||||
return (
|
||||
<div className="absolute bg-[#ffffff] h-[234px] left-0 top-[548px] w-[1230px]" data-name="03">
|
||||
<div
|
||||
className="absolute font-['Inter:Regular',_sans-serif] font-normal leading-[0] not-italic text-[18px] text-[rgba(38,35,26,0.8)] text-left top-[186px] w-[601px]"
|
||||
style={{ left: "calc(50% - 398px)" }}
|
||||
>
|
||||
<p className="block leading-[24px]">
|
||||
Hands-on workshops and peer learning sessions with real-world application
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className="absolute font-['Inter:Semi_Bold',_sans-serif] font-semibold leading-[0] not-italic text-[#26231a] text-[40px] text-center text-nowrap top-10 tracking-[-0.8px] translate-x-[-50%]"
|
||||
style={{ left: "calc(50% - 576px)" }}
|
||||
>
|
||||
<p className="adjustLetterSpacing block leading-[53px] whitespace-pre">(03)</p>
|
||||
</div>
|
||||
<div
|
||||
className="absolute font-['Inter:Semi_Bold',_sans-serif] font-semibold leading-[53px] not-italic text-[#26231a] text-[40px] text-left text-nowrap top-10 tracking-[-0.8px] whitespace-pre"
|
||||
style={{ left: "calc(50% - 398px)" }}
|
||||
>
|
||||
<p className="adjustLetterSpacing block mb-0">{`Interactive `}</p>
|
||||
<p className="adjustLetterSpacing block">Learning</p>
|
||||
</div>
|
||||
<div className="absolute h-0 left-1 top-0 w-[1230px]">
|
||||
<div className="absolute bottom-0 left-0 right-0 top-[-1px]">
|
||||
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 1230 1">
|
||||
<line id="Line 62" stroke="var(--stroke-0, black)" x2="1230" y1="0.5" y2="0.5" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Component04() {
|
||||
return (
|
||||
<div className="absolute bg-[#ffffff] h-[234px] left-0 top-[822px] w-[1230px]" data-name="04">
|
||||
<div
|
||||
className="absolute font-['Inter:Regular',_sans-serif] font-normal leading-[0] not-italic text-[18px] text-[rgba(38,35,26,0.8)] text-left top-[186px] w-[601px]"
|
||||
style={{ left: "calc(50% - 398px)" }}
|
||||
>
|
||||
<p className="block leading-[24px]">
|
||||
Ongoing support and progress tracking to ensure skills transfer and improvement
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className="absolute font-['Inter:Semi_Bold',_sans-serif] font-semibold leading-[0] not-italic text-[#26231a] text-[40px] text-center text-nowrap top-10 tracking-[-0.8px] translate-x-[-50%]"
|
||||
style={{ left: "calc(50% - 575.5px)" }}
|
||||
>
|
||||
<p className="adjustLetterSpacing block leading-[53px] whitespace-pre">(04)</p>
|
||||
</div>
|
||||
<div
|
||||
className="absolute font-['Inter:Semi_Bold',_sans-serif] font-semibold leading-[53px] not-italic text-[#26231a] text-[40px] text-left text-nowrap top-10 tracking-[-0.8px] whitespace-pre"
|
||||
style={{ left: "calc(50% - 398px)" }}
|
||||
>
|
||||
<p className="adjustLetterSpacing block mb-0">{`Application & `}</p>
|
||||
<p className="adjustLetterSpacing block">Review</p>
|
||||
</div>
|
||||
<div className="absolute h-0 left-1 top-0 w-[1230px]">
|
||||
<div className="absolute bottom-0 left-0 right-0 top-[-1px]">
|
||||
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 1230 1">
|
||||
<line id="Line 62" stroke="var(--stroke-0, black)" x2="1230" y1="0.5" y2="0.5" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Frame1597884956() {
|
||||
return (
|
||||
<div className="h-[1056px] relative shrink-0 w-full">
|
||||
<Component01 />
|
||||
<Component02 />
|
||||
<Component03 />
|
||||
<Component04 />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function OurApproachSection() {
|
||||
return (
|
||||
<div
|
||||
className="box-border content-stretch flex flex-col gap-20 items-center justify-start p-0 relative size-full"
|
||||
data-name="Our approach section"
|
||||
>
|
||||
<Frame1597884955 />
|
||||
<Frame1597884956 />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
4
src/.figma_internal/svg-9nb29n5e63.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export default {
|
||||
p177af00: "M24.7045 12.0426L12.5992 0L0.493934 12.0426C0.35111 12.1514 0.233321 12.2895 0.148542 12.4478C0.0637634 12.606 0.0139736 12.7806 0.00254296 12.9598C-0.00888768 13.1389 0.0183085 13.3185 0.0822895 13.4862C0.14627 13.6539 0.245543 13.806 0.373386 13.932C0.501229 14.058 0.654659 14.1551 0.823287 14.2167C0.991916 14.2783 1.17181 14.3029 1.35078 14.2889C1.52975 14.2749 1.70363 14.2226 1.86065 14.1356C2.01766 14.0486 2.15414 13.9288 2.26085 13.7845L11.3461 4.77444V28.7469C11.3461 29.0792 11.4781 29.398 11.7131 29.633C11.9481 29.868 12.2668 30 12.5992 30C12.9315 30 13.2503 29.868 13.4853 29.633C13.7203 29.398 13.8523 29.0792 13.8523 28.7469V4.77444L22.9375 13.7845C23.1735 14.0188 23.4929 14.1497 23.8254 14.1486C24.158 14.1474 24.4764 14.0142 24.7107 13.7782C24.945 13.5422 25.076 13.2228 25.0748 12.8903C25.0737 12.5578 24.9404 12.2393 24.7045 12.005V12.0426Z",
|
||||
pac4fa00: "M16.4696 8.0284L8.39946 0L0.329289 8.0284C0.234073 8.10091 0.155547 8.19303 0.0990282 8.29852C0.042509 8.40402 0.00931573 8.52042 0.00169531 8.63986C-0.00592512 8.7593 0.0122057 8.87898 0.0548596 8.9908C0.0975136 9.10262 0.163695 9.20397 0.248924 9.28799C0.334153 9.37201 0.436439 9.43674 0.548858 9.47779C0.661277 9.51884 0.781204 9.53526 0.90052 9.52594C1.01984 9.51661 1.13576 9.48176 1.24043 9.42374C1.34511 9.36572 1.43609 9.28588 1.50723 9.18964L7.56404 3.18296V19.1646C7.56404 19.3861 7.65206 19.5986 7.80873 19.7553C7.9654 19.912 8.1779 20 8.39946 20C8.62103 20 8.83352 19.912 8.9902 19.7553C9.14687 19.5986 9.23488 19.3861 9.23488 19.1646V3.18296L15.2917 9.18964C15.449 9.34585 15.6619 9.43316 15.8836 9.43238C16.1053 9.43159 16.3176 9.34278 16.4738 9.18546C16.63 9.02815 16.7173 8.81523 16.7166 8.59354C16.7158 8.37185 16.627 8.15955 16.4696 8.00334V8.0284Z",
|
||||
}
|
||||
4
src/.figma_internal/svg-k5kyhbfrwc.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export default {
|
||||
p177af00: "M24.7045 12.0426L12.5992 0L0.493934 12.0426C0.35111 12.1514 0.233321 12.2895 0.148542 12.4478C0.0637634 12.606 0.0139736 12.7806 0.00254296 12.9598C-0.00888768 13.1389 0.0183085 13.3185 0.0822895 13.4862C0.14627 13.6539 0.245543 13.806 0.373386 13.932C0.501229 14.058 0.654659 14.1551 0.823287 14.2167C0.991916 14.2783 1.17181 14.3029 1.35078 14.2889C1.52975 14.2749 1.70363 14.2226 1.86065 14.1356C2.01766 14.0486 2.15414 13.9288 2.26085 13.7845L11.3461 4.77444V28.7469C11.3461 29.0792 11.4781 29.398 11.7131 29.633C11.9481 29.868 12.2668 30 12.5992 30C12.9315 30 13.2503 29.868 13.4853 29.633C13.7203 29.398 13.8523 29.0792 13.8523 28.7469V4.77444L22.9375 13.7845C23.1735 14.0188 23.4929 14.1497 23.8254 14.1486C24.158 14.1474 24.4764 14.0142 24.7107 13.7782C24.945 13.5422 25.076 13.2228 25.0748 12.8903C25.0737 12.5578 24.9404 12.2393 24.7045 12.005V12.0426Z",
|
||||
pac4fa00: "M16.4696 8.0284L8.39946 0L0.329289 8.0284C0.234073 8.10091 0.155547 8.19303 0.0990282 8.29852C0.042509 8.40402 0.00931573 8.52042 0.00169531 8.63986C-0.00592512 8.7593 0.0122057 8.87898 0.0548596 8.9908C0.0975136 9.10262 0.163695 9.20397 0.248924 9.28799C0.334153 9.37201 0.436439 9.43674 0.548858 9.47779C0.661277 9.51884 0.781204 9.53526 0.90052 9.52594C1.01984 9.51661 1.13576 9.48176 1.24043 9.42374C1.34511 9.36572 1.43609 9.28588 1.50723 9.18964L7.56404 3.18296V19.1646C7.56404 19.3861 7.65206 19.5986 7.80873 19.7553C7.9654 19.912 8.1779 20 8.39946 20C8.62103 20 8.83352 19.912 8.9902 19.7553C9.14687 19.5986 9.23488 19.3861 9.23488 19.1646V3.18296L15.2917 9.18964C15.449 9.34585 15.6619 9.43316 15.8836 9.43238C16.1053 9.43159 16.3176 9.34278 16.4738 9.18546C16.63 9.02815 16.7173 8.81523 16.7166 8.59354C16.7158 8.37185 16.627 8.15955 16.4696 8.00334V8.0284Z",
|
||||
}
|
||||
4
src/.figma_internal/svg-sphymsws2o.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export default {
|
||||
p177af00: "M24.7045 12.0426L12.5992 0L0.493934 12.0426C0.35111 12.1514 0.233321 12.2895 0.148542 12.4478C0.0637634 12.606 0.0139736 12.7806 0.00254296 12.9598C-0.00888768 13.1389 0.0183085 13.3185 0.0822895 13.4862C0.14627 13.6539 0.245543 13.806 0.373386 13.932C0.501229 14.058 0.654659 14.1551 0.823287 14.2167C0.991916 14.2783 1.17181 14.3029 1.35078 14.2889C1.52975 14.2749 1.70363 14.2226 1.86065 14.1356C2.01766 14.0486 2.15414 13.9288 2.26085 13.7845L11.3461 4.77444V28.7469C11.3461 29.0792 11.4781 29.398 11.7131 29.633C11.9481 29.868 12.2668 30 12.5992 30C12.9315 30 13.2503 29.868 13.4853 29.633C13.7203 29.398 13.8523 29.0792 13.8523 28.7469V4.77444L22.9375 13.7845C23.1735 14.0188 23.4929 14.1497 23.8254 14.1486C24.158 14.1474 24.4764 14.0142 24.7107 13.7782C24.945 13.5422 25.076 13.2228 25.0748 12.8903C25.0737 12.5578 24.9404 12.2393 24.7045 12.005V12.0426Z",
|
||||
pac4fa00: "M16.4696 8.0284L8.39946 0L0.329289 8.0284C0.234073 8.10091 0.155547 8.19303 0.0990282 8.29852C0.042509 8.40402 0.00931573 8.52042 0.00169531 8.63986C-0.00592512 8.7593 0.0122057 8.87898 0.0548596 8.9908C0.0975136 9.10262 0.163695 9.20397 0.248924 9.28799C0.334153 9.37201 0.436439 9.43674 0.548858 9.47779C0.661277 9.51884 0.781204 9.53526 0.90052 9.52594C1.01984 9.51661 1.13576 9.48176 1.24043 9.42374C1.34511 9.36572 1.43609 9.28588 1.50723 9.18964L7.56404 3.18296V19.1646C7.56404 19.3861 7.65206 19.5986 7.80873 19.7553C7.9654 19.912 8.1779 20 8.39946 20C8.62103 20 8.83352 19.912 8.9902 19.7553C9.14687 19.5986 9.23488 19.3861 9.23488 19.1646V3.18296L15.2917 9.18964C15.449 9.34585 15.6619 9.43316 15.8836 9.43238C16.1053 9.43159 16.3176 9.34278 16.4738 9.18546C16.63 9.02815 16.7173 8.81523 16.7166 8.59354C16.7158 8.37185 16.627 8.15955 16.4696 8.00334V8.0284Z",
|
||||
}
|
||||
170
src/App.tsx
Normal file
@@ -0,0 +1,170 @@
|
||||
import React from 'react';
|
||||
import { Routes, Route } from "react-router-dom";
|
||||
import { AuthProvider } from "./components/AuthContext";
|
||||
import { CartProvider } from "./components/CartContext";
|
||||
import { Navigation } from "./components/Navigation";
|
||||
import HeroSection from "./components/HeroSection";
|
||||
import { StatsSection } from "./components/StatsSection";
|
||||
import { LogosSection } from "./components/LogosSection";
|
||||
import { ServicesSection } from "./components/ServicesSection";
|
||||
import { VirtualSpaceSection } from "./components/VirtualSpaceSection";
|
||||
import { TestimonialsSection } from "./components/TestimonialsSection";
|
||||
import { UpcomingWebinarsSection } from "./components/UpcomingWebinarsSection";
|
||||
import { InsightsSection } from "./components/InsightsSection";
|
||||
import { CTABannerSection } from "./components/CTABannerSection";
|
||||
import { WhitepapersSection } from "./components/WhitepapersSection";
|
||||
import { Footer } from "./components/Footer";
|
||||
import { AIChatbot } from "./components/AIChatbot";
|
||||
import { LeadershipJourneyPage } from "./components/LeadershipJourneyPage";
|
||||
import { LeadershipDevelopment } from "./components/services/LeadershipDevelopment";
|
||||
import { Consulting } from "./components/services/Consulting";
|
||||
import { CultureCompetence } from "./components/services/CultureCompetence";
|
||||
import { ExecutiveCoaching } from "./components/services/ExecutiveCoaching";
|
||||
import { ManagementDevelopment } from "./components/services/ManagementDevelopment";
|
||||
import { LearningFacility } from "./components/services/LearningFacility";
|
||||
import { OurVision } from "./components/about/OurVision";
|
||||
import { OurExpertise } from "./components/about/OurExpertise";
|
||||
import { OurImpact } from "./components/about/OurImpact";
|
||||
import { Contact } from "./components/Contact";
|
||||
import { Cart } from "./components/Cart";
|
||||
import { CorporateSignIn } from "./components/CorporateSignIn";
|
||||
import { SelfLearnerSignIn } from "./components/SelfLearnerSignIn";
|
||||
import { CorporateSignUp } from "./components/CorporateSignUp";
|
||||
import { SelfLearnerSignUp } from "./components/SelfLearnerSignUp";
|
||||
import { ProgrammeDetail } from "./components/ProgrammeDetail";
|
||||
import { Articles } from "./components/Articles";
|
||||
import { Blogs } from "./components/Blogs";
|
||||
import { BlogDetail } from "./components/BlogDetail";
|
||||
import { Webinars } from "./components/Webinars";
|
||||
import { WebinarsPage } from "./components/WebinarsPage";
|
||||
import WebinarsListing from "./components/WebinarsListing";
|
||||
import WebinarDetail from "./components/WebinarDetail";
|
||||
import { LearningOnline } from "./components/LearningOnline";
|
||||
import { Terms } from "./components/Terms";
|
||||
// import EnrollPlaceholder from "./components/EnrollPlaceholder";
|
||||
// import ForgotPasswordPlaceholder from "./components/ForgotPasswordPlaceholder";
|
||||
// import DashboardPlaceholder from "./components/DashboardPlaceholder";
|
||||
// import CheckoutPlaceholder from "./components/CheckoutPlaceholder";
|
||||
// import HomePage from "./components/HomePage";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<AuthProvider>
|
||||
<CartProvider>
|
||||
<div
|
||||
className="min-h-screen main-content"
|
||||
style={{ backgroundColor: '#FFFFFF' }}
|
||||
>
|
||||
<Routes>
|
||||
{/* Home Page */}
|
||||
<Route path="/" element={<HomePage />} />
|
||||
|
||||
{/* Leadership Journey */}
|
||||
<Route path="/leadership-journey" element={<LeadershipJourneyPage />} />
|
||||
|
||||
{/* Services Pages */}
|
||||
<Route path="/services/leadership-development" element={<LeadershipDevelopment />} />
|
||||
<Route path="/services/consulting" element={<Consulting />} />
|
||||
<Route path="/services/culture-competence" element={<CultureCompetence />} />
|
||||
<Route path="/services/executive-coaching" element={<ExecutiveCoaching />} />
|
||||
<Route path="/services/management-development" element={<ManagementDevelopment />} />
|
||||
<Route path="/services/learning-facility" element={<LearningFacility />} />
|
||||
|
||||
{/* About Us Pages */}
|
||||
<Route path="/about/our-vision" element={<OurVision />} />
|
||||
<Route path="/about/our-expertise" element={<OurExpertise />} />
|
||||
<Route path="/about/our-impact" element={<OurImpact />} />
|
||||
|
||||
{/* Learning Pages */}
|
||||
<Route path="/learning/articles" element={<Articles />} />
|
||||
<Route path="/learning/blogs" element={<Blogs />} />
|
||||
<Route path="/learning/webcast" element={<Webinars />} />
|
||||
|
||||
{/* Webinars Pages */}
|
||||
<Route path="/webinars" element={<WebinarsPage />} />
|
||||
<Route path="/webinars-legacy" element={<WebinarsListing />} />
|
||||
|
||||
{/* Learning Online */}
|
||||
<Route path="/learning-online" element={<LearningOnline />} />
|
||||
|
||||
{/* Terms & Conditions */}
|
||||
<Route path="/terms" element={<Terms />} />
|
||||
|
||||
{/* Cart */}
|
||||
<Route path="/cart" element={<Cart />} />
|
||||
|
||||
{/* Authentication Pages */}
|
||||
<Route path="/corporate-login" element={<CorporateSignIn />} />
|
||||
<Route path="/self-learner-signin" element={<SelfLearnerSignIn />} />
|
||||
<Route path="/signin/self-learner" element={<SelfLearnerSignIn />} />
|
||||
<Route path="/corporate-signup" element={<CorporateSignUp />} />
|
||||
<Route path="/self-learner-signup" element={<SelfLearnerSignUp />} />
|
||||
|
||||
{/* Contact Page */}
|
||||
<Route path="/contact" element={<Contact />} />
|
||||
|
||||
{/* Dynamic Routes */}
|
||||
<Route path="/learning/articles/:slug" element={<BlogDetail />} />
|
||||
<Route path="/learning/blogs/:slug" element={<BlogDetail />} />
|
||||
{/* <Route path="/learning/webcast/:slug" element={<WebinarDetail />} />
|
||||
<Route path="/webinar/:slug" element={<WebinarDetail />} /> */}
|
||||
<Route path="/course/:slug" element={<ProgrammeDetail />} />
|
||||
<Route path="/programme/:slug" element={<ProgrammeDetail />} />
|
||||
|
||||
{/* Placeholder Pages */}
|
||||
{/* <Route path="/enroll" element={<EnrollPlaceholder />} />
|
||||
<Route path="/forgot-password" element={<ForgotPasswordPlaceholder />} />
|
||||
<Route path="/dashboard" element={<DashboardPlaceholder />} />
|
||||
<Route path="/checkout" element={<CheckoutPlaceholder />} /> */}
|
||||
|
||||
{/* 404 Page */}
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Routes>
|
||||
|
||||
{/* Add AIChatbot to all pages */}
|
||||
<AIChatbot />
|
||||
</div>
|
||||
</CartProvider>
|
||||
</AuthProvider>
|
||||
);
|
||||
}
|
||||
|
||||
// Home Page Component (extracted from your default landing page)
|
||||
function HomePage() {
|
||||
return (
|
||||
<>
|
||||
<Navigation />
|
||||
<HeroSection />
|
||||
<StatsSection />
|
||||
<LogosSection />
|
||||
<ServicesSection />
|
||||
<VirtualSpaceSection />
|
||||
<TestimonialsSection />
|
||||
<UpcomingWebinarsSection />
|
||||
<InsightsSection />
|
||||
<WhitepapersSection />
|
||||
<CTABannerSection />
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// 404 Not Found Component
|
||||
function NotFound() {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="text-center">
|
||||
<h1 className="text-h2 mb-4">Page Not Found</h1>
|
||||
<p className="text-body-lg text-muted mb-8">
|
||||
The page you're looking for doesn't exist.
|
||||
</p>
|
||||
<button
|
||||
onClick={() => window.location.href = '/'}
|
||||
className="brand-button-system"
|
||||
>
|
||||
Back to Home
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
3
src/Attributions.md
Normal file
@@ -0,0 +1,3 @@
|
||||
This Figma Make file includes components from [shadcn/ui](https://ui.shadcn.com/) used under [MIT license](https://github.com/shadcn-ui/ui/blob/main/LICENSE.md).
|
||||
|
||||
This Figma Make file includes photos from [Unsplash](https://unsplash.com) used under [license](https://unsplash.com/license).
|
||||
BIN
src/assets/037c4659b7b0bf15b1dfdcd4868cb42e8257e838.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
src/assets/4833274f0a593cd31fdefe553b70bb016de281af.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/assets/6bdf8056f51bbdc6dd9dab9044a6579a254bd02c.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
src/assets/a28d79dd35b730f689b77dbb30452ca27bd25759.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
src/assets/c57ec1f4466f68e607139a3cd6d52f7e2f372408.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
src/assets/d5bab6ea4f3d8cef3b0425c45cfee7faea19fdbc.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
src/assets/e8fad960112d5eba554c3969d08891ebe4d4b9c7.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
src/assets/e98caa8afd8d11246bbff1dde75bbaae6f6a0894.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
485
src/components/AIChatbot.tsx
Normal file
@@ -0,0 +1,485 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { Button } from './ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from './ui/card';
|
||||
import { Badge } from './ui/badge';
|
||||
import {
|
||||
MessageCircle,
|
||||
X,
|
||||
Send,
|
||||
Bot,
|
||||
User,
|
||||
Minimize2,
|
||||
Maximize2
|
||||
} from 'lucide-react';
|
||||
import { Input } from './ui/input';
|
||||
import { ScrollArea } from './ui/scroll-area';
|
||||
|
||||
interface Message {
|
||||
id: string;
|
||||
type: 'user' | 'bot';
|
||||
content: string;
|
||||
timestamp: Date;
|
||||
suggestions?: string[];
|
||||
}
|
||||
|
||||
const initialMessage: Message = {
|
||||
id: '1',
|
||||
type: 'bot',
|
||||
content: "Hi! I'm here to help you explore KLC's leadership programs and facilities. What are you looking for today?",
|
||||
timestamp: new Date(),
|
||||
suggestions: [
|
||||
"Show me leadership programs",
|
||||
"Book a facility tour",
|
||||
"Upcoming webinars",
|
||||
"Contact information"
|
||||
]
|
||||
};
|
||||
|
||||
const botResponses: Record<string, { content: string; suggestions?: string[] }> = {
|
||||
"programs": {
|
||||
content: "Great! We offer various leadership development programs including Executive Leadership, Strategic Management, and Team Building workshops. Would you like to explore specific programs or see our full catalog?",
|
||||
suggestions: ["View all programs", "Executive programs", "Team building", "Custom corporate training"]
|
||||
},
|
||||
"leadership": {
|
||||
content: "Our leadership programs are designed to transform leaders at every level. We offer Executive Leadership Development, Strategic Leadership, Team Leadership, and Management Excellence programs.",
|
||||
suggestions: ["Executive Leadership", "Strategic Leadership", "Team Leadership", "Management Excellence"]
|
||||
},
|
||||
"facilities": {
|
||||
content: "Our state-of-the-art facilities include modern conference rooms, training halls, and collaboration spaces. You can take a virtual tour or book a facility for your event.",
|
||||
suggestions: ["Virtual tour", "Book conference room", "Training halls", "Facility pricing"]
|
||||
},
|
||||
"facility": {
|
||||
content: "Our learning facility features cutting-edge technology, flexible meeting spaces, and comfortable learning environments. We can accommodate groups from 10 to 500 participants.",
|
||||
suggestions: ["Book now", "View amenities", "Check availability", "Pricing information"]
|
||||
},
|
||||
"tour": {
|
||||
content: "I'd be happy to arrange a facility tour for you! Our tours showcase our modern classrooms, breakout spaces, and technology capabilities. Would you like to schedule one?",
|
||||
suggestions: ["Schedule tour", "Virtual tour", "Facility features", "Contact tour guide"]
|
||||
},
|
||||
"webinars": {
|
||||
content: "We host regular webinars on leadership topics. You can view upcoming sessions, register for live events, or access our library of recorded sessions.",
|
||||
suggestions: ["Upcoming webinars", "Recorded sessions", "Register for webinar", "Webinar schedule"]
|
||||
},
|
||||
"webinar": {
|
||||
content: "Our webinars cover essential leadership topics like Strategic Thinking, Team Management, Change Leadership, and Executive Presence. Most are free to attend!",
|
||||
suggestions: ["View schedule", "Register now", "Past recordings", "Webinar topics"]
|
||||
},
|
||||
"contact": {
|
||||
content: "You can reach us at info@klc.edu.in or call +91 11 4567 8900. Our team is available Monday to Friday, 9 AM to 6 PM IST. You can also schedule a consultation.",
|
||||
suggestions: ["Schedule consultation", "Email us", "Office locations", "Support hours"]
|
||||
},
|
||||
"executive": {
|
||||
content: "Our Executive Leadership programs are designed for senior leaders and C-suite executives. These intensive programs focus on strategic thinking, organizational transformation, and executive presence.",
|
||||
suggestions: ["Program details", "Enrollment", "Schedule", "Executive coaching"]
|
||||
},
|
||||
"strategic": {
|
||||
content: "Strategic Leadership development helps leaders think systematically about complex challenges, make better decisions, and drive organizational change effectively.",
|
||||
suggestions: ["Learn more", "Case studies", "Program schedule", "Apply now"]
|
||||
},
|
||||
"team": {
|
||||
content: "Team Leadership programs focus on building high-performing teams, improving collaboration, and developing emotional intelligence for team leaders.",
|
||||
suggestions: ["Team programs", "Workshop format", "Group discounts", "Custom training"]
|
||||
},
|
||||
"management": {
|
||||
content: "Management Excellence programs cover essential management skills including performance management, delegation, communication, and conflict resolution.",
|
||||
suggestions: ["Management training", "Skills assessment", "Corporate packages", "Schedule consultation"]
|
||||
},
|
||||
"pricing": {
|
||||
content: "Our pricing varies by program type and group size. Individual programs start from ₹15,000, while corporate packages offer significant discounts for bulk enrollments.",
|
||||
suggestions: ["Individual pricing", "Corporate packages", "Group discounts", "Payment options"]
|
||||
},
|
||||
"schedule": {
|
||||
content: "We offer programs throughout the year with flexible scheduling options. Most programs are available in weekend, weekday, and intensive formats.",
|
||||
suggestions: ["View calendar", "Weekend programs", "Weekday options", "Custom scheduling"]
|
||||
},
|
||||
"enrollment": {
|
||||
content: "Enrollment is simple! You can register online, call us directly, or schedule a consultation to discuss the best program for your needs.",
|
||||
suggestions: ["Register online", "Call us", "Schedule consultation", "Application process"]
|
||||
},
|
||||
"corporate": {
|
||||
content: "Our corporate solutions include custom leadership development programs, executive coaching, and organizational development services tailored to your company's needs.",
|
||||
suggestions: ["Custom programs", "Executive coaching", "Organizational development", "Corporate pricing"]
|
||||
},
|
||||
"coaching": {
|
||||
content: "We offer one-on-one executive coaching with certified leadership coaches. Our coaching programs are customized to address specific leadership challenges and goals.",
|
||||
suggestions: ["Coach profiles", "Coaching process", "Pricing", "Schedule session"]
|
||||
},
|
||||
"default": {
|
||||
content: "I can help you with information about our programs, facilities, webinars, and more. What would you like to know?",
|
||||
suggestions: ["Leadership programs", "Facility booking", "Webinars", "Contact us"]
|
||||
}
|
||||
};
|
||||
|
||||
export function AIChatbot() {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [isMinimized, setIsMinimized] = useState(false);
|
||||
const [messages, setMessages] = useState<Message[]>([initialMessage]);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [isTyping, setIsTyping] = useState(false);
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
const inactivityTimerRef = useRef<NodeJS.Timeout>();
|
||||
|
||||
// Show chatbot after user activity
|
||||
useEffect(() => {
|
||||
const resetTimer = () => {
|
||||
if (inactivityTimerRef.current) {
|
||||
clearTimeout(inactivityTimerRef.current);
|
||||
}
|
||||
|
||||
inactivityTimerRef.current = setTimeout(() => {
|
||||
if (!isOpen) {
|
||||
setIsVisible(true);
|
||||
}
|
||||
}, 8000); // Show after 8 seconds of activity
|
||||
};
|
||||
|
||||
const events = ['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart'];
|
||||
|
||||
const addEventListeners = () => {
|
||||
events.forEach(event => {
|
||||
document.addEventListener(event, resetTimer, true);
|
||||
});
|
||||
};
|
||||
|
||||
const removeEventListeners = () => {
|
||||
events.forEach(event => {
|
||||
document.removeEventListener(event, resetTimer, true);
|
||||
});
|
||||
};
|
||||
|
||||
addEventListeners();
|
||||
resetTimer();
|
||||
|
||||
return () => {
|
||||
removeEventListeners();
|
||||
if (inactivityTimerRef.current) {
|
||||
clearTimeout(inactivityTimerRef.current);
|
||||
}
|
||||
};
|
||||
}, [isOpen]);
|
||||
|
||||
// Auto-scroll to bottom of messages
|
||||
useEffect(() => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||
}, [messages]);
|
||||
|
||||
const getBotResponse = (userMessage: string): { content: string; suggestions?: string[] } => {
|
||||
const message = userMessage.toLowerCase();
|
||||
|
||||
// Enhanced keyword matching
|
||||
if (message.includes('leadership') && (message.includes('program') || message.includes('course') || message.includes('training'))) {
|
||||
return botResponses.leadership;
|
||||
} else if (message.includes('executive') || message.includes('senior') || message.includes('c-suite')) {
|
||||
return botResponses.executive;
|
||||
} else if (message.includes('strategic') || message.includes('strategy')) {
|
||||
return botResponses.strategic;
|
||||
} else if (message.includes('team') || message.includes('collaboration')) {
|
||||
return botResponses.team;
|
||||
} else if (message.includes('management') || message.includes('manager')) {
|
||||
return botResponses.management;
|
||||
} else if (message.includes('program') || message.includes('course') || message.includes('training')) {
|
||||
return botResponses.programs;
|
||||
} else if (message.includes('facility') || message.includes('space') || message.includes('room')) {
|
||||
return botResponses.facility;
|
||||
} else if (message.includes('tour') || message.includes('visit')) {
|
||||
return botResponses.tour;
|
||||
} else if (message.includes('book') || message.includes('reserve') || message.includes('schedule')) {
|
||||
return botResponses.facilities;
|
||||
} else if (message.includes('webinar') || message.includes('session') || message.includes('online')) {
|
||||
return botResponses.webinar;
|
||||
} else if (message.includes('contact') || message.includes('phone') || message.includes('email') || message.includes('reach')) {
|
||||
return botResponses.contact;
|
||||
} else if (message.includes('price') || message.includes('cost') || message.includes('fee')) {
|
||||
return botResponses.pricing;
|
||||
} else if (message.includes('schedule') || message.includes('timing') || message.includes('when')) {
|
||||
return botResponses.schedule;
|
||||
} else if (message.includes('enroll') || message.includes('register') || message.includes('apply')) {
|
||||
return botResponses.enrollment;
|
||||
} else if (message.includes('corporate') || message.includes('company') || message.includes('organization')) {
|
||||
return botResponses.corporate;
|
||||
} else if (message.includes('coach') || message.includes('mentor') || message.includes('personal')) {
|
||||
return botResponses.coaching;
|
||||
} else {
|
||||
return botResponses.default;
|
||||
}
|
||||
};
|
||||
|
||||
const handleSendMessage = async (content: string) => {
|
||||
if (!content.trim()) return;
|
||||
|
||||
// Add user message
|
||||
const userMessage: Message = {
|
||||
id: Date.now().toString(),
|
||||
type: 'user',
|
||||
content: content.trim(),
|
||||
timestamp: new Date()
|
||||
};
|
||||
|
||||
setMessages(prev => [...prev, userMessage]);
|
||||
setInputValue('');
|
||||
setIsTyping(true);
|
||||
|
||||
// Simulate typing delay with realistic timing
|
||||
setTimeout(() => {
|
||||
const response = getBotResponse(content);
|
||||
const botMessage: Message = {
|
||||
id: (Date.now() + 1).toString(),
|
||||
type: 'bot',
|
||||
content: response.content,
|
||||
timestamp: new Date(),
|
||||
suggestions: response.suggestions
|
||||
};
|
||||
|
||||
setMessages(prev => [...prev, botMessage]);
|
||||
setIsTyping(false);
|
||||
}, 800 + Math.random() * 600); // 800ms to 1.4s delay
|
||||
};
|
||||
|
||||
const handleSuggestionClick = (suggestion: string) => {
|
||||
handleSendMessage(suggestion);
|
||||
};
|
||||
|
||||
const formatTime = (date: Date) => {
|
||||
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
||||
};
|
||||
|
||||
const handleKeyPress = (e: React.KeyboardEvent) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
handleSendMessage(inputValue);
|
||||
}
|
||||
};
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Chatbot Toggle Button */}
|
||||
{!isOpen && (
|
||||
<Button
|
||||
onClick={() => setIsOpen(true)}
|
||||
className="fixed bottom-6 right-6 w-16 h-16 rounded-full shadow-xl z-50 transition-all duration-300 hover:scale-110 chatbot-button"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-primary)',
|
||||
border: 'none'
|
||||
}}
|
||||
size="lg"
|
||||
aria-label="Open KLC Assistant chat"
|
||||
>
|
||||
<MessageCircle className="w-6 h-6 text-white" />
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* Chat Window */}
|
||||
{isOpen && (
|
||||
<Card className={`fixed bottom-6 right-6 w-80 md:w-96 shadow-2xl z-50 transition-all duration-300 border-0 ${
|
||||
isMinimized ? 'h-16' : 'h-[500px]'
|
||||
}`}>
|
||||
{/* Header */}
|
||||
<CardHeader
|
||||
className="flex flex-row items-center justify-between space-y-0 pb-3 rounded-t-lg"
|
||||
style={{ backgroundColor: 'var(--color-primary)' }}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div
|
||||
className="w-10 h-10 rounded-full flex items-center justify-center"
|
||||
style={{ backgroundColor: 'rgba(255, 255, 255, 0.2)' }}
|
||||
>
|
||||
<Bot className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<CardTitle className="text-base font-medium text-white" style={{ fontFamily: 'var(--font-family-base)' }}>
|
||||
KLC Assistant
|
||||
</CardTitle>
|
||||
<div className="flex items-center gap-2 text-sm text-white opacity-90">
|
||||
<div className="w-2 h-2 bg-green-400 rounded-full animate-pulse"></div>
|
||||
<span style={{ fontFamily: 'var(--font-family-base)' }}>Online & Ready to Help</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setIsMinimized(!isMinimized)}
|
||||
className="w-8 h-8 p-0 hover:bg-white/20 text-white"
|
||||
aria-label={isMinimized ? "Maximize chat" : "Minimize chat"}
|
||||
>
|
||||
{isMinimized ? (
|
||||
<Maximize2 className="w-4 h-4" />
|
||||
) : (
|
||||
<Minimize2 className="w-4 h-4" />
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setIsOpen(false)}
|
||||
className="w-8 h-8 p-0 hover:bg-white/20 text-white"
|
||||
aria-label="Close chat"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
{/* Chat Content */}
|
||||
{!isMinimized && (
|
||||
<CardContent className="p-0 flex flex-col h-[420px]">
|
||||
{/* Messages */}
|
||||
<ScrollArea className="flex-1 p-4">
|
||||
<div className="space-y-4">
|
||||
{messages.map((message) => (
|
||||
<div key={message.id} className={`flex ${message.type === 'user' ? 'justify-end' : 'justify-start'}`}>
|
||||
<div className={`flex items-start gap-3 max-w-[85%] ${message.type === 'user' ? 'flex-row-reverse' : ''}`}>
|
||||
<div className={`w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0 ${
|
||||
message.type === 'user'
|
||||
? 'text-white'
|
||||
: 'bg-gray-100 text-gray-600'
|
||||
}`}
|
||||
style={message.type === 'user' ? { backgroundColor: 'var(--color-primary)' } : {}}
|
||||
>
|
||||
{message.type === 'user' ? (
|
||||
<User className="w-4 h-4" />
|
||||
) : (
|
||||
<Bot className="w-4 h-4" />
|
||||
)}
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className={`p-3 rounded-lg ${
|
||||
message.type === 'user'
|
||||
? 'text-white'
|
||||
: 'bg-gray-50 text-gray-800'
|
||||
}`}
|
||||
style={message.type === 'user' ? { backgroundColor: 'var(--color-primary)' } : {}}
|
||||
>
|
||||
<p
|
||||
className="text-sm leading-relaxed"
|
||||
style={{ fontFamily: 'var(--font-family-base)' }}
|
||||
>
|
||||
{message.content}
|
||||
</p>
|
||||
</div>
|
||||
{message.suggestions && (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{message.suggestions.map((suggestion, index) => (
|
||||
<Badge
|
||||
key={index}
|
||||
variant="outline"
|
||||
className="cursor-pointer transition-colors text-xs py-1 px-3 rounded-full font-normal border-gray-300 text-gray-700 hover:text-white"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
borderColor: 'var(--color-primary)'
|
||||
}}
|
||||
onClick={() => handleSuggestionClick(suggestion)}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.backgroundColor = 'var(--color-primary)';
|
||||
e.currentTarget.style.borderColor = 'var(--color-primary)';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.backgroundColor = 'transparent';
|
||||
e.currentTarget.style.borderColor = 'var(--color-primary)';
|
||||
}}
|
||||
>
|
||||
{suggestion}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<p
|
||||
className="text-xs text-gray-500 px-1"
|
||||
style={{ fontFamily: 'var(--font-family-base)' }}
|
||||
>
|
||||
{formatTime(message.timestamp)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{isTyping && (
|
||||
<div className="flex justify-start">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-8 h-8 bg-gray-100 rounded-full flex items-center justify-center">
|
||||
<Bot className="w-4 h-4 text-gray-600" />
|
||||
</div>
|
||||
<div className="bg-gray-50 p-3 rounded-lg">
|
||||
<div className="flex gap-1">
|
||||
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce"></div>
|
||||
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style={{ animationDelay: '0.1s' }}></div>
|
||||
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style={{ animationDelay: '0.2s' }}></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div ref={messagesEndRef} />
|
||||
</div>
|
||||
</ScrollArea>
|
||||
|
||||
{/* Input */}
|
||||
<div className="border-t p-4 bg-white">
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
handleSendMessage(inputValue);
|
||||
}}
|
||||
className="flex gap-2"
|
||||
role="form"
|
||||
aria-label="Chat message form"
|
||||
>
|
||||
<Input
|
||||
value={inputValue}
|
||||
onChange={(e) => setInputValue(e.target.value)}
|
||||
onKeyPress={handleKeyPress}
|
||||
placeholder="Type your message..."
|
||||
className="flex-1 min-h-[44px] border-gray-200 focus:border-blue-500 focus:ring-blue-500"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
disabled={isTyping}
|
||||
aria-label="Chat message input"
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
size="sm"
|
||||
disabled={!inputValue.trim() || isTyping}
|
||||
className="min-h-[44px] min-w-[44px] text-white transition-all duration-200"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-primary)',
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
if (!e.currentTarget.disabled) {
|
||||
e.currentTarget.style.backgroundColor = '#030359';
|
||||
}
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (!e.currentTarget.disabled) {
|
||||
e.currentTarget.style.backgroundColor = 'var(--color-primary)';
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Send className="w-4 h-4" />
|
||||
<span className="sr-only">Send message</span>
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
{/* Quick Help Text */}
|
||||
<p
|
||||
className="text-xs text-gray-500 mt-2 text-center"
|
||||
style={{ fontFamily: 'var(--font-family-base)' }}
|
||||
>
|
||||
Ask me about programs, facilities, or upcoming events
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
)}
|
||||
</Card>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
244
src/components/AboutSection.tsx
Normal file
@@ -0,0 +1,244 @@
|
||||
export function AboutSection() {
|
||||
return (
|
||||
<section
|
||||
className="py-16 section-margin-x"
|
||||
style={{ backgroundColor: 'var(--color-brand-bg-white)' }}
|
||||
>
|
||||
<div className="max-w-7xl mx-auto">
|
||||
{/* Section Header with Branded Tag */}
|
||||
<div className="text-center mb-16">
|
||||
<div className="flex items-center justify-center gap-2 mb-6">
|
||||
<div
|
||||
className="w-2 h-2 rounded-full"
|
||||
style={{ backgroundColor: 'var(--color-brand-accent)' }}
|
||||
></div>
|
||||
<span
|
||||
className="text-sm font-semibold tracking-wider uppercase"
|
||||
style={{
|
||||
color: 'var(--color-brand-black)',
|
||||
fontFamily: 'var(--font-family-brand)'
|
||||
}}
|
||||
>
|
||||
WHO WE ARE
|
||||
</span>
|
||||
</div>
|
||||
<h2
|
||||
className="text-5xl font-semibold mb-6"
|
||||
style={{
|
||||
color: 'var(--color-brand-black)',
|
||||
fontFamily: 'var(--font-family-brand)',
|
||||
lineHeight: '1.1'
|
||||
}}
|
||||
>
|
||||
About KLC
|
||||
</h2>
|
||||
<p
|
||||
className="text-xl max-w-3xl mx-auto leading-relaxed"
|
||||
style={{
|
||||
color: 'var(--color-brand-gray-muted)',
|
||||
fontFamily: 'var(--font-family-brand)'
|
||||
}}
|
||||
>
|
||||
Transforming businesses through strategic expertise, innovative solutions, and lasting partnerships that drive sustainable growth.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Three Column Layout */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-12 lg:gap-8">
|
||||
{/* Our Vision */}
|
||||
<div className="text-center lg:text-left">
|
||||
<div
|
||||
className="w-16 h-16 mx-auto lg:mx-0 mb-6 rounded-lg flex items-center justify-center"
|
||||
style={{ backgroundColor: 'var(--color-brand-primary)' }}
|
||||
>
|
||||
<svg
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
className="text-white"
|
||||
>
|
||||
<path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"/>
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3
|
||||
className="text-2xl font-bold mb-4"
|
||||
style={{
|
||||
color: 'var(--color-brand-black)',
|
||||
fontFamily: 'var(--font-family-brand)'
|
||||
}}
|
||||
>
|
||||
Our Vision
|
||||
</h3>
|
||||
<p
|
||||
className="text-base leading-relaxed mb-4"
|
||||
style={{
|
||||
color: 'var(--color-brand-gray-muted)',
|
||||
fontFamily: 'var(--font-family-brand)'
|
||||
}}
|
||||
>
|
||||
To be the leading catalyst for business transformation, empowering organizations to unlock their full potential through innovative strategies and cutting-edge solutions.
|
||||
</p>
|
||||
<p
|
||||
className="text-base leading-relaxed"
|
||||
style={{
|
||||
color: 'var(--color-brand-gray-muted)',
|
||||
fontFamily: 'var(--font-family-brand)'
|
||||
}}
|
||||
>
|
||||
We envision a future where every business can thrive in an ever-evolving marketplace through strategic foresight and adaptive excellence.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Our Impact */}
|
||||
<div className="text-center lg:text-left">
|
||||
<div
|
||||
className="w-16 h-16 mx-auto lg:mx-0 mb-6 rounded-lg flex items-center justify-center"
|
||||
style={{ backgroundColor: 'var(--color-brand-primary)' }}
|
||||
>
|
||||
<svg
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
className="text-white"
|
||||
>
|
||||
<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/>
|
||||
<path d="M3 3v5h5"/>
|
||||
<path d="M12 7v5l4 2"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3
|
||||
className="text-2xl font-bold mb-4"
|
||||
style={{
|
||||
color: 'var(--color-brand-black)',
|
||||
fontFamily: 'var(--font-family-brand)'
|
||||
}}
|
||||
>
|
||||
Our Impact
|
||||
</h3>
|
||||
<p
|
||||
className="text-base leading-relaxed mb-4"
|
||||
style={{
|
||||
color: 'var(--color-brand-gray-muted)',
|
||||
fontFamily: 'var(--font-family-brand)'
|
||||
}}
|
||||
>
|
||||
Over two decades of delivering measurable results for Fortune 500 companies and emerging businesses alike, driving over $2.5 billion in combined client value creation.
|
||||
</p>
|
||||
<p
|
||||
className="text-base leading-relaxed"
|
||||
style={{
|
||||
color: 'var(--color-brand-gray-muted)',
|
||||
fontFamily: 'var(--font-family-brand)'
|
||||
}}
|
||||
>
|
||||
Our strategic interventions have transformed operational efficiency by an average of 40% and accelerated growth trajectories across diverse industries.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Our Expertise */}
|
||||
<div className="text-center lg:text-left">
|
||||
<div
|
||||
className="w-16 h-16 mx-auto lg:mx-0 mb-6 rounded-lg flex items-center justify-center"
|
||||
style={{ backgroundColor: 'var(--color-brand-primary)' }}
|
||||
>
|
||||
<svg
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
className="text-white"
|
||||
>
|
||||
<path d="M12 2L2 7l10 5 10-5-10-5z"/>
|
||||
<path d="M2 17l10 5 10-5"/>
|
||||
<path d="M2 12l10 5 10-5"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3
|
||||
className="text-2xl font-bold mb-4"
|
||||
style={{
|
||||
color: 'var(--color-brand-black)',
|
||||
fontFamily: 'var(--font-family-brand)'
|
||||
}}
|
||||
>
|
||||
Our Expertise
|
||||
</h3>
|
||||
<p
|
||||
className="text-base leading-relaxed mb-4"
|
||||
style={{
|
||||
color: 'var(--color-brand-gray-muted)',
|
||||
fontFamily: 'var(--font-family-brand)'
|
||||
}}
|
||||
>
|
||||
Deep domain knowledge spanning strategic consulting, digital transformation, operational excellence, and change management across multiple industry verticals.
|
||||
</p>
|
||||
<p
|
||||
className="text-base leading-relaxed"
|
||||
style={{
|
||||
color: 'var(--color-brand-gray-muted)',
|
||||
fontFamily: 'var(--font-family-brand)'
|
||||
}}
|
||||
>
|
||||
Our multidisciplinary team combines analytical rigor with creative problem-solving to deliver solutions that are both innovative and pragmatic.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CTA Section */}
|
||||
<div className="text-center mt-16">
|
||||
<button
|
||||
className="brand-button"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-brand-accent)',
|
||||
color: 'var(--color-brand-black)',
|
||||
border: 'none',
|
||||
borderRadius: '10px',
|
||||
padding: '1rem 2rem',
|
||||
fontSize: '1.125rem',
|
||||
fontWeight: '600',
|
||||
fontFamily: 'var(--font-family-brand)',
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.3s ease',
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: '0.5rem'
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.backgroundColor = 'var(--color-brand-primary)';
|
||||
e.currentTarget.style.color = 'white';
|
||||
e.currentTarget.style.transform = 'translateY(-2px)';
|
||||
e.currentTarget.style.boxShadow = '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.backgroundColor = 'var(--color-brand-accent)';
|
||||
e.currentTarget.style.color = 'var(--color-brand-black)';
|
||||
e.currentTarget.style.transform = 'translateY(0)';
|
||||
e.currentTarget.style.boxShadow = '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)';
|
||||
}}
|
||||
>
|
||||
Learn More About Our Story
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path d="M5 12h14"/>
|
||||
<path d="M12 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
743
src/components/Articles.tsx
Normal file
@@ -0,0 +1,743 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { Button } from './ui/button';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './ui/card';
|
||||
import { Badge } from './ui/badge';
|
||||
import { Input } from './ui/input';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
|
||||
import { ImageWithFallback } from './figma/ImageWithFallback';
|
||||
import { PrimaryCTAButton } from './PrimaryCTAButton';
|
||||
import { navigateTo } from './Router';
|
||||
import {
|
||||
Search,
|
||||
Calendar,
|
||||
User,
|
||||
ArrowRight,
|
||||
BookOpen,
|
||||
Filter,
|
||||
Grid,
|
||||
List,
|
||||
SortAsc,
|
||||
Clock,
|
||||
Eye,
|
||||
Star,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
X
|
||||
} from 'lucide-react';
|
||||
|
||||
// Mock articles data
|
||||
const articles = [
|
||||
{
|
||||
id: '1',
|
||||
slug: 'future-of-leadership-development',
|
||||
title: 'The Future of Leadership Development: Trends and Innovations',
|
||||
excerpt: 'Explore emerging trends in leadership development and how organizations are adapting to create more effective leaders for tomorrow\'s challenges.',
|
||||
content: 'Leadership development is evolving rapidly in response to changing workplace dynamics, technological advances, and new generations entering the workforce...',
|
||||
author: 'Sarah Johnson',
|
||||
authorTitle: 'Senior Leadership Consultant',
|
||||
authorAvatar: 'https://images.unsplash.com/photo-1494790108755-2616b612b47c?w=150&h=150&fit=crop&crop=face',
|
||||
date: '2024-02-20',
|
||||
readTime: '8 min read',
|
||||
category: 'Leadership Development',
|
||||
tags: ['Future of Work', 'Leadership Trends', 'Innovation', 'Strategy'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1552664730-d307ca884978?w=600&h=400&fit=crop',
|
||||
featured: true,
|
||||
views: '2.4k',
|
||||
likes: 45
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
slug: 'emotional-intelligence-in-leadership',
|
||||
title: 'Emotional Intelligence: The Key to Effective Leadership',
|
||||
excerpt: 'Discover how emotional intelligence impacts leadership effectiveness and learn practical strategies to develop your EQ.',
|
||||
content: 'Emotional intelligence has become increasingly recognized as a critical factor in leadership success...',
|
||||
author: 'Dr. Michael Chen',
|
||||
authorTitle: 'Executive Coach',
|
||||
authorAvatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=150&h=150&fit=crop&crop=face',
|
||||
date: '2024-02-18',
|
||||
readTime: '6 min read',
|
||||
category: 'Personal Development',
|
||||
tags: ['Emotional Intelligence', 'Leadership Skills', 'Communication', 'Self-Awareness'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1559027615-cd4628902d4a?w=600&h=400&fit=crop',
|
||||
featured: true,
|
||||
views: '1.8k',
|
||||
likes: 32
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
slug: 'building-high-performing-teams',
|
||||
title: 'Building High-Performing Teams: A Leader\'s Guide',
|
||||
excerpt: 'Learn the essential strategies for creating and maintaining high-performing teams that deliver exceptional results.',
|
||||
content: 'High-performing teams don\'t happen by accident. They require intentional leadership, clear purpose, and the right environment...',
|
||||
author: 'Lisa Rodriguez',
|
||||
authorTitle: 'Team Development Specialist',
|
||||
authorAvatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=150&h=150&fit=crop&crop=face',
|
||||
date: '2024-02-15',
|
||||
readTime: '10 min read',
|
||||
category: 'Team Development',
|
||||
tags: ['Team Building', 'Performance', 'Collaboration', 'Leadership'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=600&h=400&fit=crop',
|
||||
featured: false,
|
||||
views: '3.1k',
|
||||
likes: 58
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
slug: 'digital-transformation-leadership',
|
||||
title: 'Leading Digital Transformation: A Strategic Approach',
|
||||
excerpt: 'Navigate the complexities of digital transformation with proven leadership strategies and best practices.',
|
||||
content: 'Digital transformation is reshaping industries and organizations worldwide. Successful transformation requires strong leadership...',
|
||||
author: 'David Park',
|
||||
authorTitle: 'Digital Strategy Consultant',
|
||||
authorAvatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=150&h=150&fit=crop&crop=face',
|
||||
date: '2024-02-12',
|
||||
readTime: '12 min read',
|
||||
category: 'Digital Transformation',
|
||||
tags: ['Digital Strategy', 'Change Management', 'Technology', 'Innovation'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1560472355-536de3962603?w=600&h=400&fit=crop',
|
||||
featured: false,
|
||||
views: '2.7k',
|
||||
likes: 41
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
slug: 'crisis-leadership-strategies',
|
||||
title: 'Crisis Leadership: Navigating Uncertainty with Confidence',
|
||||
excerpt: 'Master the art of crisis leadership and learn how to guide your organization through challenging times.',
|
||||
content: 'Crisis situations test the mettle of leaders and reveal the true strength of an organization...',
|
||||
author: 'Jennifer Adams',
|
||||
authorTitle: 'Crisis Management Expert',
|
||||
authorAvatar: 'https://images.unsplash.com/photo-1580489944761-15a19d654956?w=150&h=150&fit=crop&crop=face',
|
||||
date: '2024-02-10',
|
||||
readTime: '9 min read',
|
||||
category: 'Crisis Management',
|
||||
tags: ['Crisis Leadership', 'Risk Management', 'Decision Making', 'Communication'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1584697964358-3e14ca57658b?w=600&h=400&fit=crop',
|
||||
featured: false,
|
||||
views: '1.9k',
|
||||
likes: 35
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
slug: 'sustainable-leadership-practices',
|
||||
title: 'Sustainable Leadership: Building for the Long Term',
|
||||
excerpt: 'Explore sustainable leadership practices that create lasting value for organizations and stakeholders.',
|
||||
content: 'Sustainable leadership goes beyond short-term gains to create lasting value for all stakeholders...',
|
||||
author: 'Robert Kim',
|
||||
authorTitle: 'Sustainability Consultant',
|
||||
authorAvatar: 'https://images.unsplash.com/photo-1560250097-0b93528c311a?w=150&h=150&fit=crop&crop=face',
|
||||
date: '2024-02-08',
|
||||
readTime: '7 min read',
|
||||
category: 'Strategy',
|
||||
tags: ['Sustainability', 'ESG', 'Long-term Thinking', 'Stakeholder Value'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1542601906990-b4d3fb778b09?w=600&h=400&fit=crop',
|
||||
featured: false,
|
||||
views: '1.5k',
|
||||
likes: 28
|
||||
}
|
||||
];
|
||||
|
||||
export function Articles() {
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [selectedCategory, setSelectedCategory] = useState('All Categories');
|
||||
const [selectedAuthor, setSelectedAuthor] = useState('All Authors');
|
||||
const [selectedReadTime, setSelectedReadTime] = useState('All Read Times');
|
||||
const [selectedDateRange, setSelectedDateRange] = useState('All Time');
|
||||
const [selectedTopic, setSelectedTopic] = useState('All Topics');
|
||||
const [sortBy, setSortBy] = useState('Most Recent');
|
||||
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid');
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const articlesPerPage = 6;
|
||||
|
||||
// Get unique values for filters
|
||||
const categories = ['All Categories', ...Array.from(new Set(articles.map(article => article.category)))];
|
||||
const authors = ['All Authors', ...Array.from(new Set(articles.map(article => article.author)))];
|
||||
const readTimes = ['All Read Times', 'Under 5 min', '5-10 min', 'Over 10 min'];
|
||||
const dateRanges = ['All Time', 'Last 7 days', 'Last 30 days', 'Last 3 months'];
|
||||
const allTags = Array.from(new Set(articles.flatMap(article => article.tags)));
|
||||
const sortOptions = [
|
||||
{ value: 'Most Recent', label: 'Most Recent' },
|
||||
{ value: 'oldest', label: 'Oldest First' },
|
||||
{ value: 'title', label: 'Title A-Z' },
|
||||
{ value: 'readTime', label: 'Read Time' },
|
||||
{ value: 'popular', label: 'Most Popular' }
|
||||
];
|
||||
|
||||
// Filter and sort articles
|
||||
const filteredArticles = articles.filter(article => {
|
||||
const matchesSearch = article.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
article.excerpt.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
article.author.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
article.tags.some(tag => tag.toLowerCase().includes(searchTerm.toLowerCase()));
|
||||
const matchesCategory = selectedCategory === 'All Categories' || article.category === selectedCategory;
|
||||
const matchesAuthor = selectedAuthor === 'All Authors' || article.author === selectedAuthor;
|
||||
|
||||
// Read time filter
|
||||
const readTimeMinutes = parseInt(article.readTime);
|
||||
const matchesReadTime = selectedReadTime === 'All Read Times' ||
|
||||
(selectedReadTime === 'Under 5 min' && readTimeMinutes < 5) ||
|
||||
(selectedReadTime === '5-10 min' && readTimeMinutes >= 5 && readTimeMinutes <= 10) ||
|
||||
(selectedReadTime === 'Over 10 min' && readTimeMinutes > 10);
|
||||
|
||||
// Date range filter
|
||||
const articleDate = new Date(article.date);
|
||||
const now = new Date();
|
||||
const matchesDateRange = selectedDateRange === 'All Time' ||
|
||||
(selectedDateRange === 'Last 7 days' && (now.getTime() - articleDate.getTime()) <= 7 * 24 * 60 * 60 * 1000) ||
|
||||
(selectedDateRange === 'Last 30 days' && (now.getTime() - articleDate.getTime()) <= 30 * 24 * 60 * 60 * 1000) ||
|
||||
(selectedDateRange === 'Last 3 months' && (now.getTime() - articleDate.getTime()) <= 90 * 24 * 60 * 60 * 1000);
|
||||
|
||||
// Topic filter
|
||||
const matchesTopic = selectedTopic === 'All Topics' ||
|
||||
article.tags.includes(selectedTopic);
|
||||
|
||||
return matchesSearch && matchesCategory && matchesAuthor && matchesReadTime && matchesDateRange && matchesTopic;
|
||||
}).sort((a, b) => {
|
||||
switch (sortBy) {
|
||||
case 'Most Recent':
|
||||
return new Date(b.date).getTime() - new Date(a.date).getTime();
|
||||
case 'oldest':
|
||||
return new Date(a.date).getTime() - new Date(b.date).getTime();
|
||||
case 'title':
|
||||
return a.title.localeCompare(b.title);
|
||||
case 'readTime':
|
||||
return parseInt(a.readTime) - parseInt(b.readTime);
|
||||
case 'popular':
|
||||
return parseInt(b.views) - parseInt(a.views);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
// Paginate results
|
||||
const totalPages = Math.ceil(filteredArticles.length / articlesPerPage);
|
||||
const startIndex = (currentPage - 1) * articlesPerPage;
|
||||
const currentArticles = filteredArticles.slice(startIndex, startIndex + articlesPerPage);
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
return new Date(dateString).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
});
|
||||
};
|
||||
|
||||
const clearAllFilters = () => {
|
||||
setSearchTerm('');
|
||||
setSelectedCategory('All Categories');
|
||||
setSelectedAuthor('All Authors');
|
||||
setSelectedReadTime('All Read Times');
|
||||
setSelectedDateRange('All Time');
|
||||
setSelectedTopic('All Topics');
|
||||
setSortBy('Most Recent');
|
||||
};
|
||||
|
||||
|
||||
|
||||
const hasActiveFilters = searchTerm ||
|
||||
selectedCategory !== 'All Categories' ||
|
||||
selectedAuthor !== 'All Authors' ||
|
||||
selectedReadTime !== 'All Read Times' ||
|
||||
selectedDateRange !== 'All Time' ||
|
||||
selectedTopic !== 'All Topics';
|
||||
|
||||
return (
|
||||
<div style={{ backgroundColor: '#FFFFFF' }}>
|
||||
{/* Hero Section */}
|
||||
<section className="relative py-28 overflow-hidden">
|
||||
<div className="absolute inset-0">
|
||||
<ImageWithFallback
|
||||
src="https://images.unsplash.com/photo-1481627834876-b7833e8f5570?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxhcnRpY2xlJTIwYmxvZyUyMGNvbnRlbnQlMjBrbm93bGVkZ2V8ZW58MXx8fHwxNzU1ODU0Mjg2fDA&ixlib=rb-4.1.0&q=80&w=1080"
|
||||
alt="Knowledge and insights through articles and research"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-black/60" />
|
||||
</div>
|
||||
|
||||
<div className="relative section-margin-x">
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<div className="branded-tag-system-white mb-6 justify-center">
|
||||
<div className="dot"></div>
|
||||
<span className="text">INSIGHTS & KNOWLEDGE</span>
|
||||
</div>
|
||||
|
||||
<h1 className="text-h1-white mb-6">
|
||||
Articles & Research
|
||||
</h1>
|
||||
|
||||
<p className="text-body-lg-white mb-8 max-w-2xl mx-auto">
|
||||
Discover cutting-edge insights, research findings, and expert perspectives on leadership development, management strategies, and organizational excellence.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Statistics Strip */}
|
||||
<div className="absolute bottom-0 left-0 right-0">
|
||||
<div className="bg-black/80 backdrop-blur-sm px-8 py-6">
|
||||
<div className="section-margin-x">
|
||||
<div className="grid grid-cols-3 gap-8 text-center">
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">{articles.length}+</div>
|
||||
<div className="text-small-white">Expert Articles</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">{categories.length - 1}</div>
|
||||
<div className="text-small-white">Categories</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">25,400</div>
|
||||
<div className="text-small-white">Total Reads</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Search and Controls Section */}
|
||||
<section className="py-8" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="section-margin-x">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 mb-6">
|
||||
{/* Search Bar */}
|
||||
<div className="relative max-w-md flex-1">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Search articles, authors, topics..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="pl-10 pr-4 py-3 text-body rounded-lg border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 transition-all duration-200 w-full bg-gray-50"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
height: '48px'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* View Toggle and Sort */}
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center border border-gray-300 rounded-lg overflow-hidden">
|
||||
<button
|
||||
onClick={() => setViewMode('grid')}
|
||||
className={`p-2 transition-colors ${
|
||||
viewMode === 'grid'
|
||||
? 'text-white'
|
||||
: 'bg-white text-gray-600 hover:bg-gray-50'
|
||||
}`}
|
||||
style={{
|
||||
backgroundColor: viewMode === 'grid' ? 'var(--color-primary)' : undefined
|
||||
}}
|
||||
aria-label="Grid view"
|
||||
>
|
||||
<Grid className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setViewMode('list')}
|
||||
className={`p-2 transition-colors ${
|
||||
viewMode === 'list'
|
||||
? 'text-white'
|
||||
: 'bg-white text-gray-600 hover:bg-gray-50'
|
||||
}`}
|
||||
style={{
|
||||
backgroundColor: viewMode === 'list' ? 'var(--color-primary)' : undefined
|
||||
}}
|
||||
aria-label="List view"
|
||||
>
|
||||
<List className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Select value={sortBy} onValueChange={setSortBy}>
|
||||
<SelectTrigger className="w-40 text-body">
|
||||
<SelectValue placeholder="Sort by" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{sortOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Main Content Section with Sidebar */}
|
||||
<section className="pb-16" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="section-margin-x">
|
||||
<div className="grid grid-cols-12 gap-8">
|
||||
{/* Left Sidebar - Sticky Filters */}
|
||||
<div className="col-span-12 lg:col-span-3">
|
||||
<div className="sticky top-4">
|
||||
<Card className="bg-white border border-gray-200 rounded-lg shadow-md overflow-hidden">
|
||||
{/* Filter Header */}
|
||||
<div className="bg-gray-50 px-4 py-3 border-b border-gray-200">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="p-1.5 rounded-md" style={{ backgroundColor: 'rgba(4, 4, 91, 0.1)' }}>
|
||||
<Filter className="w-3.5 h-3.5" style={{ color: 'var(--color-primary)' }} />
|
||||
</div>
|
||||
<h3 className="text-body font-semibold text-gray-800">
|
||||
Filters
|
||||
</h3>
|
||||
</div>
|
||||
{hasActiveFilters && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={clearAllFilters}
|
||||
className="text-xs px-2 py-1 rounded-md transition-colors filter-clear-btn"
|
||||
>
|
||||
<X className="w-3 h-3 mr-1" />
|
||||
Clear
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Filter Content */}
|
||||
<div className="p-4">
|
||||
<div className="space-y-4">
|
||||
{/* Category Filter */}
|
||||
<div className="filter-section">
|
||||
<label className="block text-small mb-2 font-medium text-gray-700">
|
||||
Category
|
||||
</label>
|
||||
<Select value={selectedCategory} onValueChange={setSelectedCategory}>
|
||||
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors" style={{ '&:focus': { borderColor: 'var(--color-primary)' } }}>
|
||||
<SelectValue placeholder="All Categories" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{categories.map((category) => (
|
||||
<SelectItem key={category} value={category} className="text-small">
|
||||
{category}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Author Filter */}
|
||||
<div className="filter-section">
|
||||
<label className="block text-small mb-2 font-medium text-gray-700">
|
||||
Author
|
||||
</label>
|
||||
<Select value={selectedAuthor} onValueChange={setSelectedAuthor}>
|
||||
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors" style={{ '&:focus': { borderColor: 'var(--color-primary)' } }}>
|
||||
<SelectValue placeholder="All Authors" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{authors.map((author) => (
|
||||
<SelectItem key={author} value={author} className="text-small">
|
||||
{author}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Read Time Filter */}
|
||||
<div className="filter-section">
|
||||
<label className="block text-small mb-2 font-medium text-gray-700">
|
||||
Read Time
|
||||
</label>
|
||||
<Select value={selectedReadTime} onValueChange={setSelectedReadTime}>
|
||||
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors" style={{ '&:focus': { borderColor: 'var(--color-primary)' } }}>
|
||||
<SelectValue placeholder="All Read Times" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{readTimes.map((readTime) => (
|
||||
<SelectItem key={readTime} value={readTime} className="text-small">
|
||||
{readTime}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Date Range Filter */}
|
||||
<div className="filter-section">
|
||||
<label className="block text-small mb-2 font-medium text-gray-700">
|
||||
Date Range
|
||||
</label>
|
||||
<Select value={selectedDateRange} onValueChange={setSelectedDateRange}>
|
||||
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors" style={{ '&:focus': { borderColor: 'var(--color-primary)' } }}>
|
||||
<SelectValue placeholder="All Time" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{dateRanges.map((dateRange) => (
|
||||
<SelectItem key={dateRange} value={dateRange} className="text-small">
|
||||
{dateRange}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Topics Filter */}
|
||||
<div className="filter-section">
|
||||
<label className="block text-small mb-2 font-medium text-gray-700">
|
||||
Topic
|
||||
</label>
|
||||
<Select value={selectedTopic} onValueChange={setSelectedTopic}>
|
||||
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors" style={{ '&:focus': { borderColor: 'var(--color-primary)' } }}>
|
||||
<SelectValue placeholder="All Topics" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="All Topics" className="text-small">
|
||||
All Topics
|
||||
</SelectItem>
|
||||
{allTags.map((tag) => (
|
||||
<SelectItem key={tag} value={tag} className="text-small">
|
||||
{tag}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Content Area - Scrollable Articles */}
|
||||
<div className="col-span-12 lg:col-span-9">
|
||||
<div className="mb-4 text-small text-muted">
|
||||
Showing {currentArticles.length} of {filteredArticles.length} articles
|
||||
</div>
|
||||
|
||||
{/* Articles Results */}
|
||||
{currentArticles.length === 0 ? (
|
||||
<div className="text-center py-12">
|
||||
<p className="text-body-lg text-muted">
|
||||
No articles found matching your criteria.
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{/* Grid View */}
|
||||
{viewMode === 'grid' && (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{currentArticles.map((article) => (
|
||||
<Card
|
||||
key={article.id}
|
||||
className="overflow-hidden hover:shadow-lg transition-all duration-300 cursor-pointer group"
|
||||
onClick={() => navigateTo(`/learning/articles/${article.slug}`)}
|
||||
>
|
||||
<div className="aspect-video w-full bg-gray-100 overflow-hidden relative">
|
||||
<ImageWithFallback
|
||||
src={article.thumbnail}
|
||||
alt={article.title}
|
||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
||||
/>
|
||||
{article.featured && (
|
||||
<div className="absolute top-4 right-4">
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="bg-yellow-100 text-yellow-800 border-yellow-200"
|
||||
>
|
||||
Featured
|
||||
</Badge>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<Badge variant="outline" className="text-small">
|
||||
{article.category}
|
||||
</Badge>
|
||||
<span className="text-small text-muted">{article.readTime}</span>
|
||||
</div>
|
||||
|
||||
<h3 className="text-h4 mb-3 group-hover:text-blue-600 transition-colors line-clamp-2">
|
||||
{article.title}
|
||||
</h3>
|
||||
|
||||
<p className="text-small text-muted mb-4 line-clamp-3">
|
||||
{article.excerpt}
|
||||
</p>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<ImageWithFallback
|
||||
src={article.authorAvatar}
|
||||
alt={article.author}
|
||||
className="w-6 h-6 rounded-full object-cover"
|
||||
/>
|
||||
<span className="text-small">{article.author}</span>
|
||||
</div>
|
||||
<div className="text-small text-muted">
|
||||
{formatDate(article.date)}
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* List View */}
|
||||
{viewMode === 'list' && (
|
||||
<div className="space-y-6">
|
||||
{currentArticles.map((article) => (
|
||||
<Card
|
||||
key={article.id}
|
||||
className="overflow-hidden hover:shadow-lg transition-all duration-300 cursor-pointer group"
|
||||
onClick={() => navigateTo(`/learning/articles/${article.slug}`)}
|
||||
>
|
||||
<div className="flex flex-col md:flex-row">
|
||||
<div className="md:w-80 h-48 md:h-auto bg-gray-100 overflow-hidden relative flex-shrink-0">
|
||||
<ImageWithFallback
|
||||
src={article.thumbnail}
|
||||
alt={article.title}
|
||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
||||
/>
|
||||
{article.featured && (
|
||||
<div className="absolute top-2 right-2">
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="bg-yellow-100 text-yellow-800 border-yellow-200 text-xs"
|
||||
>
|
||||
Featured
|
||||
</Badge>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex-1 p-6">
|
||||
<div className="flex justify-between items-start">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Badge variant="outline" className="text-small">
|
||||
{article.category}
|
||||
</Badge>
|
||||
<span className="text-small text-muted">{article.readTime}</span>
|
||||
<div className="flex items-center gap-1 text-small text-muted">
|
||||
<Eye className="w-3 h-3" />
|
||||
<span>{article.views}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 className="text-h4 mb-2 group-hover:text-blue-600 transition-colors">
|
||||
{article.title}
|
||||
</h3>
|
||||
|
||||
<p className="text-body text-muted mb-3">
|
||||
{article.excerpt}
|
||||
</p>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<ImageWithFallback
|
||||
src={article.authorAvatar}
|
||||
alt={article.author}
|
||||
className="w-6 h-6 rounded-full object-cover"
|
||||
/>
|
||||
<div>
|
||||
<span className="text-small font-medium">{article.author}</span>
|
||||
<span className="text-small text-muted ml-1">• {article.authorTitle}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-small text-muted">
|
||||
{formatDate(article.date)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Pagination */}
|
||||
{totalPages > 1 && (
|
||||
<div className="flex items-center justify-center gap-4 mt-12">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setCurrentPage(prev => Math.max(prev - 1, 1))}
|
||||
disabled={currentPage === 1}
|
||||
className="text-body"
|
||||
>
|
||||
<ChevronLeft className="w-4 h-4 mr-2" />
|
||||
Previous
|
||||
</Button>
|
||||
|
||||
<span className="text-body">
|
||||
Page {currentPage} of {totalPages}
|
||||
</span>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setCurrentPage(prev => Math.min(prev + 1, totalPages))}
|
||||
disabled={currentPage === totalPages}
|
||||
className="text-body"
|
||||
>
|
||||
Next
|
||||
<ChevronRight className="w-4 h-4 ml-2" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA Banner Section */}
|
||||
<section className="relative h-[700px] overflow-hidden">
|
||||
<div className="absolute inset-0">
|
||||
<ImageWithFallback
|
||||
src="https://images.unsplash.com/photo-1753613648191-4771cf76f034?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxvbmxpbmUlMjBsZWFybmluZyUyMGRpZ2l0YWwlMjBlZHVjYXRpb258ZW58MXx8fHwxNzU1ODU0Mjc1fDA&ixlib=rb-4.1.0&q=80&w=1080"
|
||||
alt="Online learning and digital education environment"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-black/30" />
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-black/20 via-transparent to-black/60" />
|
||||
</div>
|
||||
|
||||
<div className="relative h-full flex items-center justify-end section-margin-x">
|
||||
<div
|
||||
className="bg-opacity-95 backdrop-blur-sm rounded-lg p-16 max-w-2xl"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-brand-primary)'
|
||||
}}
|
||||
>
|
||||
<div className="branded-tag-system-next-steps mb-6 justify-start">
|
||||
<div className="dot"></div>
|
||||
<span className="text">NEXT STEPS</span>
|
||||
</div>
|
||||
|
||||
<h2 className="text-h2-white mb-8">
|
||||
Ready to explore more insights?
|
||||
<span
|
||||
className="italic"
|
||||
style={{ color: 'var(--color-brand-accent)' }}
|
||||
>
|
||||
{" "}Discover{" "}
|
||||
</span>
|
||||
our complete library of leadership resources.
|
||||
</h2>
|
||||
|
||||
<PrimaryCTAButton
|
||||
text="Browse All Resources"
|
||||
onClick={() => navigateTo('/learning/articles')}
|
||||
ariaLabel="Browse all leadership articles and resources"
|
||||
className="cta-banner-yellow mb-6"
|
||||
/>
|
||||
|
||||
<p className="text-body-white opacity-90">
|
||||
Access cutting-edge research, expert insights, and practical guidance to accelerate your leadership journey and organizational success.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
54
src/components/AuthContext.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import React, { createContext, useContext, useState, ReactNode } from 'react';
|
||||
|
||||
interface User {
|
||||
name: string;
|
||||
email: string;
|
||||
corporateName: string;
|
||||
avatar?: string;
|
||||
}
|
||||
|
||||
interface AuthContextType {
|
||||
user: User | null;
|
||||
isAuthenticated: boolean;
|
||||
signIn: (user: User) => void;
|
||||
signOut: () => void;
|
||||
}
|
||||
|
||||
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
||||
|
||||
interface AuthProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export function AuthProvider({ children }: AuthProviderProps) {
|
||||
const [user, setUser] = useState<User | null>(null);
|
||||
|
||||
const signIn = (userData: User) => {
|
||||
setUser(userData);
|
||||
};
|
||||
|
||||
const signOut = () => {
|
||||
setUser(null);
|
||||
};
|
||||
|
||||
const value: AuthContextType = {
|
||||
user,
|
||||
isAuthenticated: !!user,
|
||||
signIn,
|
||||
signOut,
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={value}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useAuth() {
|
||||
const context = useContext(AuthContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useAuth must be used within an AuthProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
815
src/components/BlogDetail.tsx
Normal file
@@ -0,0 +1,815 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Button } from './ui/button';
|
||||
import { Card, CardContent } from './ui/card';
|
||||
import { Badge } from './ui/badge';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from './ui/avatar';
|
||||
import { navigateTo } from './Router';
|
||||
import { ImageWithFallback } from './figma/ImageWithFallback';
|
||||
import { CTABannerSection } from './CTABannerSection';
|
||||
import { useCart } from './CartContext';
|
||||
import {
|
||||
Calendar,
|
||||
Clock,
|
||||
ArrowRight,
|
||||
ChevronUp,
|
||||
Share2,
|
||||
Bookmark,
|
||||
Twitter,
|
||||
Facebook,
|
||||
Linkedin,
|
||||
Link,
|
||||
Heart,
|
||||
Eye,
|
||||
BookOpen,
|
||||
ArrowLeft,
|
||||
Star,
|
||||
MessageCircle,
|
||||
Users,
|
||||
TrendingUp,
|
||||
Award
|
||||
} from 'lucide-react';
|
||||
|
||||
interface BlogPost {
|
||||
id: string;
|
||||
title: string;
|
||||
slug: string;
|
||||
excerpt: string;
|
||||
content: string;
|
||||
author: string;
|
||||
authorBio: string;
|
||||
authorImage: string;
|
||||
publishedDate: string;
|
||||
updatedDate?: string;
|
||||
readTime: string;
|
||||
views: number;
|
||||
likes: number;
|
||||
category: string;
|
||||
tags: string[];
|
||||
image: string;
|
||||
featured: boolean;
|
||||
}
|
||||
|
||||
interface RelatedPost {
|
||||
id: string;
|
||||
title: string;
|
||||
slug: string;
|
||||
excerpt: string;
|
||||
author: string;
|
||||
publishedDate: string;
|
||||
readTime: string;
|
||||
category: string;
|
||||
image: string;
|
||||
}
|
||||
|
||||
interface BlogDetailProps {
|
||||
params?: {
|
||||
slug?: string;
|
||||
};
|
||||
}
|
||||
|
||||
// Articles data synced with Articles component for consistency
|
||||
const articlesData = [
|
||||
{
|
||||
id: 1,
|
||||
title: "The Future of Leadership Development: Trends and Innovations",
|
||||
slug: "future-of-leadership-development",
|
||||
excerpt: "Explore emerging trends in leadership development and how organizations are adapting to create more effective leaders for tomorrow's challenges.",
|
||||
content: `
|
||||
<p>Leadership development is evolving rapidly in response to changing workplace dynamics, technological advances, and new generations entering the workforce. Organizations worldwide are reimagining their approach to cultivating leadership talent.</p>
|
||||
|
||||
<h2>Emerging Trends in Leadership Development</h2>
|
||||
<p>Several key trends are reshaping how we develop leaders:</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>Personalized Learning Paths:</strong> Customized development programs based on individual strengths, weaknesses, and career aspirations.</li>
|
||||
<li><strong>Digital-First Delivery:</strong> Virtual reality simulations, AI-powered coaching, and mobile learning platforms.</li>
|
||||
<li><strong>Continuous Feedback Culture:</strong> Real-time performance insights and ongoing mentorship relationships.</li>
|
||||
<li><strong>Cross-Functional Exposure:</strong> Leaders developing broader business acumen through diverse role experiences.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Innovation in Leadership Development</h2>
|
||||
<p>Organizations are experimenting with new methodologies and technologies to create more effective development experiences.</p>
|
||||
|
||||
<blockquote>
|
||||
<p>"The future of leadership development lies in creating adaptive, resilient leaders who can thrive in uncertainty while driving innovation and human connection."</p>
|
||||
<cite>— Sarah Johnson, Senior Leadership Consultant</cite>
|
||||
</blockquote>
|
||||
|
||||
<h3>Technology-Enhanced Learning</h3>
|
||||
<p>From virtual reality leadership scenarios to AI-powered coaching platforms, technology is creating immersive development experiences that accelerate learning and skill application.</p>
|
||||
|
||||
<p>The future belongs to leaders who embrace continuous learning, demonstrate emotional intelligence, and can navigate complexity with confidence and clarity.</p>
|
||||
`,
|
||||
author: "Sarah Johnson",
|
||||
authorTitle: "Senior Leadership Consultant",
|
||||
authorBio: "Sarah Johnson is a Senior Leadership Consultant with over 15 years of experience in organizational development and talent management. She specializes in designing innovative leadership programs for Fortune 500 companies.",
|
||||
authorImage: "https://images.unsplash.com/photo-1494790108755-2616b612b47c?w=150&h=150&fit=crop",
|
||||
publishedDate: "2024-02-20",
|
||||
readTime: "8 min read",
|
||||
category: "Leadership Development",
|
||||
tags: ["Future of Work", "Leadership Trends", "Innovation", "Strategy"],
|
||||
image: "https://images.unsplash.com/photo-1552664730-d307ca884978?w=1200&h=600&fit=crop",
|
||||
featured: true,
|
||||
views: 2400,
|
||||
likes: 45
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Emotional Intelligence: The Key to Effective Leadership",
|
||||
slug: "emotional-intelligence-in-leadership",
|
||||
excerpt: "Discover how emotional intelligence impacts leadership effectiveness and learn practical strategies to develop your EQ.",
|
||||
content: `
|
||||
<p>Emotional intelligence has become increasingly recognized as a critical factor in leadership success. Research consistently shows that leaders with high emotional intelligence create more engaged teams, drive better business results, and build more resilient organizations.</p>
|
||||
|
||||
<h2>The Four Domains of Emotional Intelligence</h2>
|
||||
<p>Daniel Goleman's framework identifies four key domains:</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>Self-Awareness:</strong> Understanding your emotions, strengths, and limitations.</li>
|
||||
<li><strong>Self-Management:</strong> Effectively managing your emotions and behaviors.</li>
|
||||
<li><strong>Social Awareness:</strong> Reading others' emotions and understanding group dynamics.</li>
|
||||
<li><strong>Relationship Management:</strong> Influencing and managing relationships effectively.</li>
|
||||
</ul>
|
||||
|
||||
<blockquote>
|
||||
<p>"IQ gets you hired, but EQ gets you promoted. In leadership roles, emotional intelligence becomes the differentiator between good managers and great leaders."</p>
|
||||
<cite>— Dr. Michael Chen, Executive Coach</cite>
|
||||
</blockquote>
|
||||
|
||||
<h2>Developing Your Emotional Intelligence</h2>
|
||||
<p>Unlike IQ, emotional intelligence can be developed throughout your career through deliberate practice, feedback, and reflection.</p>
|
||||
|
||||
<h3>Practical Strategies</h3>
|
||||
<p>Start with mindfulness practices, seek feedback from trusted colleagues, and practice active listening in all your interactions. The investment in developing emotional intelligence pays dividends in leadership effectiveness.</p>
|
||||
`,
|
||||
author: "Dr. Michael Chen",
|
||||
authorTitle: "Executive Coach",
|
||||
authorBio: "Dr. Michael Chen is an Executive Coach and organizational psychologist with expertise in emotional intelligence and leadership development. He has coached hundreds of senior executives across various industries.",
|
||||
authorImage: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=150&h=150&fit=crop",
|
||||
publishedDate: "2024-02-18",
|
||||
readTime: "6 min read",
|
||||
category: "Personal Development",
|
||||
tags: ["Emotional Intelligence", "Leadership Skills", "Communication", "Self-Awareness"],
|
||||
image: "https://images.unsplash.com/photo-1559027615-cd4628902d4a?w=1200&h=600&fit=crop",
|
||||
featured: true,
|
||||
views: 1800,
|
||||
likes: 32
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Building High-Performing Teams: A Leader's Guide",
|
||||
slug: "building-high-performing-teams",
|
||||
excerpt: "Learn the essential strategies for creating and maintaining high-performing teams that deliver exceptional results.",
|
||||
content: `
|
||||
<p>High-performing teams don't happen by accident. They require intentional leadership, clear purpose, and the right environment to thrive. Understanding the key elements that drive team performance is essential for any leader.</p>
|
||||
|
||||
<h2>Characteristics of High-Performing Teams</h2>
|
||||
<p>Research identifies several common traits among exceptional teams:</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>Shared Purpose:</strong> Clear understanding of goals and how individual contributions matter.</li>
|
||||
<li><strong>Psychological Safety:</strong> Environment where team members feel safe to take risks and share ideas.</li>
|
||||
<li><strong>Diverse Perspectives:</strong> Variety of backgrounds, skills, and thinking styles.</li>
|
||||
<li><strong>Effective Communication:</strong> Open, honest, and constructive dialogue.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Building Team Performance</h2>
|
||||
<p>Creating high-performing teams requires deliberate effort in team formation, skill development, and culture building.</p>
|
||||
|
||||
<blockquote>
|
||||
<p>"Great teams are not created by chance. They are the result of intentional leadership that focuses on both individual development and collective excellence."</p>
|
||||
<cite>— Lisa Rodriguez, Team Development Specialist</cite>
|
||||
</blockquote>
|
||||
|
||||
<p>The journey to high performance requires patience, consistency, and a commitment to continuous improvement at both individual and team levels.</p>
|
||||
`,
|
||||
author: "Lisa Rodriguez",
|
||||
authorTitle: "Team Development Specialist",
|
||||
authorBio: "Lisa Rodriguez is a Team Development Specialist with over 12 years of experience helping organizations build high-performing teams. She specializes in team dynamics, collaboration, and performance optimization.",
|
||||
authorImage: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=150&h=150&fit=crop",
|
||||
publishedDate: "2024-02-15",
|
||||
readTime: "10 min read",
|
||||
category: "Team Development",
|
||||
tags: ["Team Building", "Performance", "Collaboration", "Leadership"],
|
||||
image: "https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=1200&h=600&fit=crop",
|
||||
featured: false,
|
||||
views: 3100,
|
||||
likes: 58
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "Leading Digital Transformation: A Strategic Approach",
|
||||
slug: "digital-transformation-leadership",
|
||||
excerpt: "Navigate the complexities of digital transformation with proven leadership strategies and best practices.",
|
||||
content: `
|
||||
<p>Digital transformation is reshaping industries and organizations worldwide. Successful transformation requires strong leadership that can balance technological innovation with human-centered change management.</p>
|
||||
|
||||
<h2>The Leadership Challenge</h2>
|
||||
<p>Digital transformation is as much about people and culture as it is about technology. Leaders must navigate resistance, build new capabilities, and maintain business performance during the transition.</p>
|
||||
|
||||
<h3>Key Success Factors</h3>
|
||||
<ul>
|
||||
<li><strong>Vision Clarity:</strong> Articulating a compelling future state that motivates change.</li>
|
||||
<li><strong>Change Management:</strong> Supporting people through the transformation journey.</li>
|
||||
<li><strong>Technology Integration:</strong> Selecting and implementing the right solutions.</li>
|
||||
<li><strong>Cultural Evolution:</strong> Building a culture that embraces digital innovation.</li>
|
||||
</ul>
|
||||
|
||||
<blockquote>
|
||||
<p>"Digital transformation succeeds when leaders focus on transforming people and processes, not just implementing technology."</p>
|
||||
<cite>— David Park, Digital Strategy Consultant</cite>
|
||||
</blockquote>
|
||||
|
||||
<p>The most successful digital transformations are led by leaders who understand that technology enables transformation, but people make it happen.</p>
|
||||
`,
|
||||
author: "David Park",
|
||||
authorTitle: "Digital Strategy Consultant",
|
||||
authorBio: "David Park is a Digital Strategy Consultant with extensive experience in leading large-scale digital transformations across multiple industries. He specializes in technology strategy and organizational change.",
|
||||
authorImage: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=150&h=150&fit=crop",
|
||||
publishedDate: "2024-02-12",
|
||||
readTime: "12 min read",
|
||||
category: "Digital Transformation",
|
||||
tags: ["Digital Strategy", "Change Management", "Technology", "Innovation"],
|
||||
image: "https://images.unsplash.com/photo-1560472355-536de3962603?w=1200&h=600&fit=crop",
|
||||
featured: false,
|
||||
views: 2700,
|
||||
likes: 41
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: "Crisis Leadership: Navigating Uncertainty with Confidence",
|
||||
slug: "crisis-leadership-strategies",
|
||||
excerpt: "Master the art of crisis leadership and learn how to guide your organization through challenging times.",
|
||||
content: `
|
||||
<p>Crisis situations test the mettle of leaders and reveal the true strength of an organization. Effective crisis leadership requires a unique combination of decisive action, clear communication, and emotional resilience.</p>
|
||||
|
||||
<h2>Principles of Crisis Leadership</h2>
|
||||
<p>Successful crisis leaders follow key principles that help them navigate uncertainty:</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>Stay Calm Under Pressure:</strong> Maintaining composure to make clear-headed decisions.</li>
|
||||
<li><strong>Communicate Transparently:</strong> Providing honest, frequent updates to all stakeholders.</li>
|
||||
<li><strong>Act Decisively:</strong> Making tough decisions quickly with available information.</li>
|
||||
<li><strong>Show Empathy:</strong> Understanding and addressing the human impact of the crisis.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Building Crisis Resilience</h2>
|
||||
<p>The best time to prepare for a crisis is before it happens. Organizations need to build crisis response capabilities and leadership resilience proactively.</p>
|
||||
|
||||
<blockquote>
|
||||
<p>"Crisis leadership is not about having all the answers—it's about maintaining trust and direction when everything seems uncertain."</p>
|
||||
<cite>— Jennifer Adams, Crisis Management Expert</cite>
|
||||
</blockquote>
|
||||
|
||||
<p>Leaders who excel in crisis situations combine strategic thinking with human compassion, creating stability in the midst of chaos.</p>
|
||||
`,
|
||||
author: "Jennifer Adams",
|
||||
authorTitle: "Crisis Management Expert",
|
||||
authorBio: "Jennifer Adams is a Crisis Management Expert with over 20 years of experience helping organizations navigate complex crises. She has advised government agencies and Fortune 500 companies on crisis preparedness and response.",
|
||||
authorImage: "https://images.unsplash.com/photo-1580489944761-15a19d654956?w=150&h=150&fit=crop",
|
||||
publishedDate: "2024-02-10",
|
||||
readTime: "9 min read",
|
||||
category: "Crisis Management",
|
||||
tags: ["Crisis Leadership", "Risk Management", "Decision Making", "Communication"],
|
||||
image: "https://images.unsplash.com/photo-1584697964358-3e14ca57658b?w=1200&h=600&fit=crop",
|
||||
featured: false,
|
||||
views: 1900,
|
||||
likes: 35
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: "Sustainable Leadership: Building for the Long Term",
|
||||
slug: "sustainable-leadership-practices",
|
||||
excerpt: "Explore sustainable leadership practices that create lasting value for organizations and stakeholders.",
|
||||
content: `
|
||||
<p>Sustainable leadership goes beyond short-term gains to create lasting value for all stakeholders. It requires a long-term perspective that balances profit with purpose, growth with responsibility.</p>
|
||||
|
||||
<h2>The Sustainability Imperative</h2>
|
||||
<p>Modern leaders must navigate increasing expectations for environmental and social responsibility while maintaining business performance.</p>
|
||||
|
||||
<h3>Key Elements of Sustainable Leadership</h3>
|
||||
<ul>
|
||||
<li><strong>Stakeholder Focus:</strong> Considering the needs of all stakeholders, not just shareholders.</li>
|
||||
<li><strong>Environmental Stewardship:</strong> Making decisions that consider environmental impact.</li>
|
||||
<li><strong>Social Responsibility:</strong> Contributing positively to communities and society.</li>
|
||||
<li><strong>Economic Viability:</strong> Ensuring long-term business sustainability.</li>
|
||||
</ul>
|
||||
|
||||
<blockquote>
|
||||
<p>"Sustainable leadership is about creating value that endures beyond any individual leader's tenure, building organizations that thrive for generations."</p>
|
||||
<cite>— Robert Kim, Sustainability Consultant</cite>
|
||||
</blockquote>
|
||||
|
||||
<p>The future belongs to leaders who can balance multiple bottom lines—profit, people, and planet—while driving innovation and growth.</p>
|
||||
`,
|
||||
author: "Robert Kim",
|
||||
authorTitle: "Sustainability Consultant",
|
||||
authorBio: "Robert Kim is a Sustainability Consultant specializing in ESG strategy and sustainable business practices. He has helped numerous organizations integrate sustainability into their core business strategy.",
|
||||
authorImage: "https://images.unsplash.com/photo-1560250097-0b93528c311a?w=150&h=150&fit=crop",
|
||||
publishedDate: "2024-02-08",
|
||||
readTime: "7 min read",
|
||||
category: "Strategy",
|
||||
tags: ["Sustainability", "ESG", "Long-term Thinking", "Stakeholder Value"],
|
||||
image: "https://images.unsplash.com/photo-1542601906990-b4d3fb778b09?w=1200&h=600&fit=crop",
|
||||
featured: false,
|
||||
views: 1500,
|
||||
likes: 28
|
||||
}
|
||||
];
|
||||
|
||||
// Function to get blog post by slug
|
||||
const getBlogPostBySlug = (slug: string): BlogPost => {
|
||||
const article = articlesData.find(article => article.slug === slug);
|
||||
|
||||
if (!article) {
|
||||
return {
|
||||
id: '1',
|
||||
title: 'Article Not Found',
|
||||
slug: 'not-found',
|
||||
excerpt: `The requested article "${slug}" could not be found.`,
|
||||
content: `
|
||||
<p>We're sorry, but the article you're looking for could not be found. It may have been moved or removed.</p>
|
||||
<p>Here are some available articles you can read:</p>
|
||||
<ul>
|
||||
<li><a href="/learning/articles/future-of-leadership-development">The Future of Leadership Development: Trends and Innovations</a></li>
|
||||
<li><a href="/learning/articles/emotional-intelligence-in-leadership">Emotional Intelligence: The Key to Effective Leadership</a></li>
|
||||
<li><a href="/learning/articles/building-high-performing-teams">Building High-Performing Teams: A Leader's Guide</a></li>
|
||||
<li><a href="/learning/articles/digital-transformation-leadership">Leading Digital Transformation: A Strategic Approach</a></li>
|
||||
<li><a href="/learning/articles/crisis-leadership-strategies">Crisis Leadership: Navigating Uncertainty with Confidence</a></li>
|
||||
<li><a href="/learning/articles/sustainable-leadership-practices">Sustainable Leadership: Building for the Long Term</a></li>
|
||||
</ul>
|
||||
<p><a href="/learning/articles">Return to all articles</a></p>
|
||||
`,
|
||||
author: 'KLC Team',
|
||||
authorBio: 'The Knowledge Leadership Centre team is dedicated to providing excellent leadership development resources.',
|
||||
authorImage: 'https://images.unsplash.com/photo-1494790108755-2616b612b47c?w=150&h=150&fit=crop',
|
||||
publishedDate: '2024-01-01',
|
||||
updatedDate: '2024-01-01',
|
||||
readTime: '1 min read',
|
||||
views: 0,
|
||||
likes: 0,
|
||||
category: 'General',
|
||||
tags: [],
|
||||
image: 'https://images.unsplash.com/photo-1552664730-d307ca884978?w=1200&h=600&fit=crop',
|
||||
featured: false
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
id: article.id.toString(),
|
||||
title: article.title,
|
||||
slug: article.slug,
|
||||
excerpt: article.excerpt,
|
||||
content: article.content,
|
||||
author: article.author,
|
||||
authorBio: article.authorBio,
|
||||
authorImage: article.authorImage,
|
||||
publishedDate: article.publishedDate,
|
||||
updatedDate: article.publishedDate,
|
||||
readTime: article.readTime,
|
||||
views: article.views,
|
||||
likes: article.likes,
|
||||
category: article.category,
|
||||
tags: article.tags,
|
||||
image: article.image,
|
||||
featured: article.featured
|
||||
};
|
||||
};
|
||||
|
||||
const relatedPosts: RelatedPost[] = [
|
||||
{
|
||||
id: '2',
|
||||
title: 'Emotional Intelligence: The Key to Effective Leadership',
|
||||
slug: 'emotional-intelligence-in-leadership',
|
||||
excerpt: 'Discover how emotional intelligence impacts leadership effectiveness and learn practical strategies to develop your EQ.',
|
||||
author: 'Dr. Michael Chen',
|
||||
publishedDate: '2024-02-18',
|
||||
readTime: '6 min read',
|
||||
category: 'Personal Development',
|
||||
image: 'https://images.unsplash.com/photo-1559027615-cd4628902d4a?w=400&h=300&fit=crop'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: 'Building High-Performing Teams: A Leader\'s Guide',
|
||||
slug: 'building-high-performing-teams',
|
||||
excerpt: 'Learn the essential strategies for creating and maintaining high-performing teams that deliver exceptional results.',
|
||||
author: 'Lisa Rodriguez',
|
||||
publishedDate: '2024-02-15',
|
||||
readTime: '10 min read',
|
||||
category: 'Team Development',
|
||||
image: 'https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=400&h=300&fit=crop'
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
title: 'Leading Digital Transformation: A Strategic Approach',
|
||||
slug: 'digital-transformation-leadership',
|
||||
excerpt: 'Navigate the complexities of digital transformation with proven leadership strategies and best practices.',
|
||||
author: 'David Park',
|
||||
publishedDate: '2024-02-12',
|
||||
readTime: '12 min read',
|
||||
category: 'Digital Transformation',
|
||||
image: 'https://images.unsplash.com/photo-1560472355-536de3962603?w=400&h=300&fit=crop'
|
||||
}
|
||||
];
|
||||
|
||||
export function BlogDetail({ params }: BlogDetailProps) {
|
||||
const slug = params?.slug || '';
|
||||
const [scrollProgress, setScrollProgress] = useState(0);
|
||||
const [showBackToTop, setShowBackToTop] = useState(false);
|
||||
const [isLiked, setIsLiked] = useState(false);
|
||||
const [isBookmarked, setIsBookmarked] = useState(false);
|
||||
const { addToCart } = useCart();
|
||||
|
||||
// Get blog post data based on slug
|
||||
const blogPost = getBlogPostBySlug(slug);
|
||||
|
||||
useEffect(() => {
|
||||
document.title = `${blogPost.title} | KLC Blog`;
|
||||
window.scrollTo(0, 0);
|
||||
|
||||
const handleScroll = () => {
|
||||
const winScroll = document.body.scrollTop || document.documentElement.scrollTop;
|
||||
const height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
|
||||
const scrolled = (winScroll / height) * 100;
|
||||
|
||||
setScrollProgress(scrolled);
|
||||
setShowBackToTop(winScroll > 300);
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
return () => window.removeEventListener('scroll', handleScroll);
|
||||
}, [blogPost.title]);
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
return new Date(dateString).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
});
|
||||
};
|
||||
|
||||
const handleShare = (platform: string) => {
|
||||
const url = window.location.href;
|
||||
const title = blogPost.title;
|
||||
|
||||
const shareUrls = {
|
||||
twitter: `https://twitter.com/intent/tweet?text=${encodeURIComponent(title)}&url=${encodeURIComponent(url)}`,
|
||||
facebook: `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(url)}`,
|
||||
linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(url)}`,
|
||||
copy: url
|
||||
};
|
||||
|
||||
if (platform === 'copy') {
|
||||
navigator.clipboard.writeText(url);
|
||||
alert('Link copied to clipboard!');
|
||||
} else {
|
||||
window.open(shareUrls[platform as keyof typeof shareUrls], '_blank', 'width=600,height=400');
|
||||
}
|
||||
};
|
||||
|
||||
const scrollToTop = () => {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
{/* Scroll Progress Bar */}
|
||||
<div className="fixed top-0 left-0 w-full h-1 z-50" style={{ backgroundColor: 'rgba(0, 0, 0, 0.1)' }}>
|
||||
<div
|
||||
className="h-full transition-all duration-150"
|
||||
style={{
|
||||
width: `${scrollProgress}%`,
|
||||
backgroundColor: '#04045B'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Back to Top Button */}
|
||||
{showBackToTop && (
|
||||
<Button
|
||||
onClick={scrollToTop}
|
||||
size="icon"
|
||||
className="fixed bottom-8 right-8 z-40 rounded-full shadow-lg"
|
||||
style={{
|
||||
backgroundColor: '#04045B',
|
||||
color: 'white'
|
||||
}}
|
||||
>
|
||||
<ChevronUp className="w-4 h-4" />
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<main className="pt-20">
|
||||
{/* Consistent Side Gutters - Breadcrumb Navigation */}
|
||||
<div className="section-margin-x mb-8">
|
||||
<div className="flex items-center gap-3 text-small" style={{ color: '#6F6F6F' }}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => navigateTo('/learning/articles')}
|
||||
className="p-0 h-auto font-medium hover:bg-transparent transition-colors"
|
||||
style={{ color: '#6F6F6F' }}
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
||||
Back to Articles
|
||||
</Button>
|
||||
<span className="text-[#E5E7EB]">•</span>
|
||||
<span>{blogPost.category}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Content Container with Consistent Gutters */}
|
||||
<div className="section-margin-x">
|
||||
{/* Reading Width Constraint for Better Readability */}
|
||||
<div className="max-w-4xl mx-auto">
|
||||
{/* Hero Header - Improved Spacing */}
|
||||
<header className="mb-16">
|
||||
{/* Category Badge */}
|
||||
<div className="mb-8">
|
||||
<Badge
|
||||
className="mb-6 text-small px-4 py-2 font-medium border-none"
|
||||
style={{
|
||||
backgroundColor: 'rgba(4, 4, 91, 0.1)',
|
||||
color: '#04045B'
|
||||
}}
|
||||
>
|
||||
{blogPost.category}
|
||||
</Badge>
|
||||
|
||||
{/* Improved Typography Hierarchy */}
|
||||
<h1 className="text-h1 mb-6 leading-tight" style={{ color: '#26231A' }}>
|
||||
{blogPost.title}
|
||||
</h1>
|
||||
|
||||
{/* Constrained Width Excerpt for Better Readability */}
|
||||
<div className="max-w-3xl">
|
||||
<p className="text-body-lg leading-relaxed" style={{ color: '#6F6F6F' }}>
|
||||
{blogPost.excerpt}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Enhanced Meta Bar with Cleaner Spacing */}
|
||||
<div
|
||||
className="flex flex-col lg:flex-row items-start lg:items-center justify-between gap-6 p-6 rounded-xl border"
|
||||
style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)', borderColor: 'rgba(0, 0, 0, 0.08)' }}
|
||||
>
|
||||
{/* Author Info with Improved Layout */}
|
||||
<div className="flex items-center gap-4">
|
||||
<Avatar className="w-14 h-14 ring-2 ring-white shadow-md">
|
||||
<AvatarImage src={blogPost.authorImage} alt={blogPost.author} />
|
||||
<AvatarFallback className="text-subhead font-medium">{blogPost.author.split(' ').map(n => n[0]).join('')}</AvatarFallback>
|
||||
</Avatar>
|
||||
<div>
|
||||
<div className="text-subhead font-medium mb-1" style={{ color: '#26231A' }}>
|
||||
{blogPost.author}
|
||||
</div>
|
||||
|
||||
{/* Cleaner Meta Information with Subtle Dividers */}
|
||||
<div className="flex items-center gap-4 text-small" style={{ color: '#6F6F6F' }}>
|
||||
<span className="flex items-center gap-1.5">
|
||||
<Calendar className="w-4 h-4" />
|
||||
{formatDate(blogPost.publishedDate)}
|
||||
</span>
|
||||
|
||||
<div className="w-1 h-1 rounded-full" style={{ backgroundColor: '#E5E7EB' }}></div>
|
||||
|
||||
<span className="flex items-center gap-1.5">
|
||||
<Clock className="w-4 h-4" />
|
||||
{blogPost.readTime}
|
||||
</span>
|
||||
|
||||
<div className="w-1 h-1 rounded-full" style={{ backgroundColor: '#E5E7EB' }}></div>
|
||||
|
||||
<span className="flex items-center gap-1.5">
|
||||
<Eye className="w-4 h-4" />
|
||||
{blogPost.views.toLocaleString()}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Action Buttons with Better Spacing */}
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setIsLiked(!isLiked)}
|
||||
className={`transition-colors ${isLiked ? 'text-red-500' : 'text-[#6F6F6F]'}`}
|
||||
>
|
||||
<Heart className={`w-4 h-4 mr-2 ${isLiked ? 'fill-current' : ''}`} />
|
||||
{blogPost.likes + (isLiked ? 1 : 0)}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setIsBookmarked(!isBookmarked)}
|
||||
className={`transition-colors ${isBookmarked ? 'text-[#04045B]' : 'text-[#6F6F6F]'}`}
|
||||
>
|
||||
<Bookmark className={`w-4 h-4 ${isBookmarked ? 'fill-current' : ''}`} />
|
||||
</Button>
|
||||
|
||||
{/* Share Options */}
|
||||
<div className="flex items-center gap-1 ml-2 pl-2 border-l border-[#E5E7EB]">
|
||||
<Button variant="ghost" size="sm" onClick={() => handleShare('twitter')} className="text-[#6F6F6F] hover:text-[#04045B]">
|
||||
<Twitter className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" onClick={() => handleShare('linkedin')} className="text-[#6F6F6F] hover:text-[#04045B]">
|
||||
<Linkedin className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" onClick={() => handleShare('copy')} className="text-[#6F6F6F] hover:text-[#04045B]">
|
||||
<Link className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Featured Image with Better Aspect Ratio */}
|
||||
<div className="aspect-[16/9] rounded-xl overflow-hidden mt-8 shadow-lg">
|
||||
<ImageWithFallback
|
||||
src={blogPost.image}
|
||||
alt={blogPost.title}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Article Body with Enhanced Typography - Full Width */}
|
||||
<article className="mb-20">
|
||||
{/* Full Width Container - Uses complete available width */}
|
||||
<div className="w-full">
|
||||
<div
|
||||
className="prose prose-xl max-w-none blog-article-content w-full"
|
||||
style={{
|
||||
/* Enhanced Typography Hierarchy using Design System */
|
||||
'--tw-prose-body': '#26231A',
|
||||
'--tw-prose-headings': '#26231A',
|
||||
'--tw-prose-lead': '#26231A',
|
||||
'--tw-prose-links': '#04045B',
|
||||
'--tw-prose-bold': '#26231A',
|
||||
'--tw-prose-counters': '#6F6F6F',
|
||||
'--tw-prose-bullets': '#6F6F6F',
|
||||
'--tw-prose-hr': 'rgba(0, 0, 0, 0.1)',
|
||||
'--tw-prose-quotes': '#04045B',
|
||||
'--tw-prose-quote-borders': '#04045B',
|
||||
'--tw-prose-captions': '#6F6F6F',
|
||||
'--tw-prose-code': '#04045B',
|
||||
'--tw-prose-pre-code': '#26231A',
|
||||
'--tw-prose-pre-bg': 'rgba(0, 0, 0, 0.05)',
|
||||
'--tw-prose-th-borders': 'rgba(0, 0, 0, 0.15)',
|
||||
'--tw-prose-td-borders': 'rgba(0, 0, 0, 0.1)',
|
||||
|
||||
/* Typography Scale using Design System Tokens */
|
||||
fontSize: 'var(--font-body-lg)',
|
||||
lineHeight: '1.75',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: '#26231A',
|
||||
width: '100%'
|
||||
}}
|
||||
dangerouslySetInnerHTML={{ __html: blogPost.content }}
|
||||
/>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
{/* Enhanced Tag Pills with Hover States */}
|
||||
<div className="mb-16">
|
||||
<h3 className="text-subhead mb-6 font-medium" style={{ color: '#26231A' }}>
|
||||
Topics covered in this article
|
||||
</h3>
|
||||
<div className="flex flex-wrap gap-3">
|
||||
{blogPost.tags.map((tag) => (
|
||||
<Badge
|
||||
key={tag}
|
||||
className="transition-all duration-200 text-body px-4 py-2 font-medium"
|
||||
style={{
|
||||
backgroundColor: 'rgba(4, 4, 91, 0.08)',
|
||||
color: '#04045B',
|
||||
border: '1px solid rgba(4, 4, 91, 0.15)'
|
||||
}}
|
||||
>
|
||||
{tag}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Enhanced Author Bio Card */}
|
||||
<Card className="mb-16 shadow-md border-0" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<CardContent className="p-8">
|
||||
<div className="flex items-start gap-6">
|
||||
<Avatar className="w-20 h-20 ring-4 ring-white shadow-lg">
|
||||
<AvatarImage src={blogPost.authorImage} alt={blogPost.author} />
|
||||
<AvatarFallback className="text-lg font-medium">{blogPost.author.split(' ').map(n => n[0]).join('')}</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="flex-1">
|
||||
<h4 className="text-h4 mb-3 font-semibold" style={{ color: '#26231A' }}>
|
||||
About {blogPost.author}
|
||||
</h4>
|
||||
<p className="text-body leading-relaxed mb-6" style={{ color: '#6F6F6F' }}>
|
||||
{blogPost.authorBio}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Related Articles Section with Balanced Grid Layout */}
|
||||
<section className="py-20" style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}>
|
||||
<div className="section-margin-x">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<div className="text-center mb-16">
|
||||
<div className="branded-tag-system mb-6">
|
||||
<div className="dot"></div>
|
||||
<span className="text">Continue Learning</span>
|
||||
</div>
|
||||
<h2 className="text-h2 mb-6 font-bold" style={{ color: '#26231A' }}>
|
||||
Explore More Leadership Insights
|
||||
</h2>
|
||||
<p className="text-body-lg max-w-2xl mx-auto" style={{ color: '#6F6F6F' }}>
|
||||
Discover additional strategies and frameworks to enhance your leadership effectiveness
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Balanced Card Grid with Equal Spacing */}
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{relatedPosts.map((post) => (
|
||||
<Card
|
||||
key={post.id}
|
||||
className="overflow-hidden hover:shadow-xl transition-all duration-300 cursor-pointer group border-0"
|
||||
onClick={() => navigateTo(`/learning/articles/${post.slug}`)}
|
||||
style={{ backgroundColor: '#FFFFFF' }}
|
||||
>
|
||||
<div className="aspect-[16/10] w-full bg-gray-100 overflow-hidden relative">
|
||||
<ImageWithFallback
|
||||
src={post.image}
|
||||
alt={post.title}
|
||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
||||
/>
|
||||
</div>
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="text-small border-none"
|
||||
style={{
|
||||
backgroundColor: 'rgba(4, 4, 91, 0.1)',
|
||||
color: '#04045B'
|
||||
}}
|
||||
>
|
||||
{post.category}
|
||||
</Badge>
|
||||
<span className="text-small" style={{ color: '#6F6F6F' }}>
|
||||
{post.readTime}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h3
|
||||
className="mb-3 group-hover:text-blue-600 transition-colors line-clamp-2"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
lineHeight: '1.3',
|
||||
color: '#26231A',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{post.title}
|
||||
</h3>
|
||||
|
||||
<p
|
||||
className="mb-4 line-clamp-3"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
lineHeight: '1.5',
|
||||
color: '#6F6F6F',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{post.excerpt}
|
||||
</p>
|
||||
|
||||
<div className="flex items-center justify-between pt-4 border-t" style={{ borderColor: 'rgba(0, 0, 0, 0.05)' }}>
|
||||
<span className="text-small" style={{ color: '#6F6F6F' }}>
|
||||
{post.author}
|
||||
</span>
|
||||
<span className="text-small" style={{ color: '#6F6F6F' }}>
|
||||
{formatDate(post.publishedDate)}
|
||||
</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA Section */}
|
||||
<CTABannerSection />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
483
src/components/Blogs.tsx
Normal file
@@ -0,0 +1,483 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Button } from './ui/button';
|
||||
import { Badge } from './ui/badge';
|
||||
import { Calendar, Clock, User, ChevronLeft, ChevronRight, Search, Filter } from 'lucide-react';
|
||||
import { Input } from './ui/input';
|
||||
import { navigateTo } from './Router';
|
||||
import { ImageWithFallback } from './figma/ImageWithFallback';
|
||||
|
||||
// Mock blog data
|
||||
const blogPosts = [
|
||||
{
|
||||
id: '1',
|
||||
slug: 'future-of-leadership-development',
|
||||
title: 'The Future of Leadership Development: Trends and Innovations',
|
||||
excerpt: 'Explore emerging trends in leadership development, from AI-powered coaching to virtual reality training experiences that are reshaping how leaders learn and grow.',
|
||||
content: 'Full blog content would go here...',
|
||||
author: 'Dr. Sarah Mitchell',
|
||||
authorRole: 'Chief Learning Officer',
|
||||
publishDate: '2024-02-15',
|
||||
readTime: '8 min read',
|
||||
category: 'Leadership',
|
||||
tags: ['Leadership Development', 'Innovation', 'Future Trends', 'Technology'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1552664730-d307ca884978?w=600&h=400&fit=crop',
|
||||
featured: true
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
slug: 'building-emotional-intelligence',
|
||||
title: 'Building Emotional Intelligence: A Leader\'s Guide to Self-Awareness',
|
||||
excerpt: 'Discover practical strategies for developing emotional intelligence and how self-aware leaders create more engaged and productive teams.',
|
||||
content: 'Full blog content would go here...',
|
||||
author: 'Marcus Rodriguez',
|
||||
authorRole: 'Leadership Coach',
|
||||
publishDate: '2024-02-10',
|
||||
readTime: '6 min read',
|
||||
category: 'Personal Development',
|
||||
tags: ['Emotional Intelligence', 'Self-Awareness', 'Team Building', 'Communication'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1559027615-cd4628902d4a?w=600&h=400&fit=crop',
|
||||
featured: false
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
slug: 'remote-leadership-strategies',
|
||||
title: 'Remote Leadership Strategies: Managing Distributed Teams Effectively',
|
||||
excerpt: 'Learn proven strategies for leading remote teams, maintaining culture, and driving performance in a distributed work environment.',
|
||||
content: 'Full blog content would go here...',
|
||||
author: 'Dr. Emily Chen',
|
||||
authorRole: 'Remote Work Expert',
|
||||
publishDate: '2024-02-05',
|
||||
readTime: '10 min read',
|
||||
category: 'Remote Work',
|
||||
tags: ['Remote Leadership', 'Virtual Teams', 'Digital Communication', 'Performance Management'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1600880292203-757bb62b4baf?w=600&h=400&fit=crop',
|
||||
featured: true
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
slug: 'diversity-inclusion-leadership',
|
||||
title: 'Diversity and Inclusion: Leading Change in Your Organization',
|
||||
excerpt: 'A comprehensive guide to implementing meaningful diversity and inclusion initiatives that drive real organizational change.',
|
||||
content: 'Full blog content would go here...',
|
||||
author: 'Jennifer Adams',
|
||||
authorRole: 'D&I Consultant',
|
||||
publishDate: '2024-01-28',
|
||||
readTime: '12 min read',
|
||||
category: 'Diversity & Inclusion',
|
||||
tags: ['Diversity', 'Inclusion', 'Organizational Change', 'Culture'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?w=600&h=400&fit=crop',
|
||||
featured: false
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
slug: 'agile-leadership-mindset',
|
||||
title: 'Developing an Agile Leadership Mindset for Rapid Change',
|
||||
excerpt: 'Explore how agile principles can transform your leadership approach and help organizations navigate uncertainty and rapid change.',
|
||||
content: 'Full blog content would go here...',
|
||||
author: 'David Park',
|
||||
authorRole: 'Agile Coach',
|
||||
publishDate: '2024-01-20',
|
||||
readTime: '7 min read',
|
||||
category: 'Agile Leadership',
|
||||
tags: ['Agile', 'Change Management', 'Adaptability', 'Innovation'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1552664730-d307ca884978?w=600&h=400&fit=crop',
|
||||
featured: false
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
slug: 'coaching-vs-mentoring',
|
||||
title: 'Coaching vs. Mentoring: Understanding the Difference and When to Use Each',
|
||||
excerpt: 'Learn the key differences between coaching and mentoring, and discover when each approach is most effective for leadership development.',
|
||||
content: 'Full blog content would go here...',
|
||||
author: 'Lisa Thompson',
|
||||
authorRole: 'Executive Coach',
|
||||
publishDate: '2024-01-15',
|
||||
readTime: '9 min read',
|
||||
category: 'Coaching',
|
||||
tags: ['Coaching', 'Mentoring', 'Leadership Development', 'Professional Growth'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=600&h=400&fit=crop',
|
||||
featured: false
|
||||
}
|
||||
];
|
||||
|
||||
export function Blogs() {
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [selectedCategory, setSelectedCategory] = useState('All');
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const postsPerPage = 6;
|
||||
|
||||
// Get unique categories
|
||||
const categories = ['All', ...Array.from(new Set(blogPosts.map(post => post.category)))];
|
||||
|
||||
// Filter posts based on search and category
|
||||
const filteredPosts = blogPosts.filter(post => {
|
||||
const matchesSearch = post.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
post.excerpt.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
post.author.toLowerCase().includes(searchTerm.toLowerCase());
|
||||
const matchesCategory = selectedCategory === 'All' || post.category === selectedCategory;
|
||||
return matchesSearch && matchesCategory;
|
||||
});
|
||||
|
||||
// Paginate results
|
||||
const totalPages = Math.ceil(filteredPosts.length / postsPerPage);
|
||||
const currentPosts = filteredPosts.slice((currentPage - 1) * postsPerPage, currentPage * postsPerPage);
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
return new Date(dateString).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ backgroundColor: 'var(--color-bg-white)' }}>
|
||||
{/* Header Section */}
|
||||
<div className="py-12">
|
||||
<div className="hero-margin-x">
|
||||
<div className="text-center mb-12">
|
||||
<div className="branded-tag-system mb-6">
|
||||
<div className="dot"></div>
|
||||
<span className="text">INSIGHTS & EXPERTISE</span>
|
||||
</div>
|
||||
|
||||
<h1
|
||||
className="mb-6"
|
||||
style={{
|
||||
fontSize: 'var(--font-h1)',
|
||||
fontWeight: 'var(--font-weight-h1)',
|
||||
lineHeight: 'var(--line-height-h1)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Leadership Insights Blog
|
||||
</h1>
|
||||
|
||||
<p
|
||||
className="max-w-3xl mx-auto"
|
||||
style={{
|
||||
fontSize: 'var(--font-body-lg)',
|
||||
lineHeight: 'var(--line-height-body-lg)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Discover thought-provoking articles, expert insights, and practical strategies from leading voices in organizational development and leadership excellence.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Search and Filter Section */}
|
||||
<div className="mb-12">
|
||||
<div className="flex flex-col lg:flex-row gap-6 items-center justify-between">
|
||||
{/* Search Bar */}
|
||||
<div className="relative flex-1 max-w-md">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Search articles..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="pl-10"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Category Filter */}
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{categories.map((category) => (
|
||||
<Button
|
||||
key={category}
|
||||
variant={selectedCategory === category ? "default" : "outline"}
|
||||
onClick={() => {
|
||||
setSelectedCategory(category);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
backgroundColor: selectedCategory === category ? 'var(--color-primary)' : 'transparent',
|
||||
borderColor: 'var(--color-primary)',
|
||||
color: selectedCategory === category ? 'white' : 'var(--color-primary)'
|
||||
}}
|
||||
>
|
||||
{category}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Featured Posts Section */}
|
||||
{searchTerm === '' && selectedCategory === 'All' && (
|
||||
<div className="mb-16">
|
||||
<h2
|
||||
className="mb-8"
|
||||
style={{
|
||||
fontSize: 'var(--font-h2)',
|
||||
fontWeight: 'var(--font-weight-h2)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Featured Articles
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
{blogPosts.filter(post => post.featured).slice(0, 2).map((post) => (
|
||||
<article
|
||||
key={post.id}
|
||||
className="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden hover:shadow-lg transition-all duration-300 cursor-pointer"
|
||||
onClick={() => navigateTo(`/learning/blogs/${post.slug}`)}
|
||||
>
|
||||
<div className="aspect-video w-full bg-gray-100">
|
||||
<ImageWithFallback
|
||||
src={post.thumbnail}
|
||||
alt={post.title}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<Badge
|
||||
variant="secondary"
|
||||
style={{
|
||||
backgroundColor: 'rgba(248, 195, 1, 0.1)',
|
||||
color: 'var(--color-black)',
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{post.category}
|
||||
</Badge>
|
||||
<Badge
|
||||
variant="outline"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
borderColor: 'var(--color-primary)',
|
||||
color: 'var(--color-primary)'
|
||||
}}
|
||||
>
|
||||
Featured
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<h3
|
||||
className="mb-3 hover:text-blue-600 transition-colors"
|
||||
style={{
|
||||
fontSize: 'var(--font-h3)',
|
||||
fontWeight: 'var(--font-weight-h3)',
|
||||
lineHeight: 'var(--line-height-h3)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{post.title}
|
||||
</h3>
|
||||
|
||||
<p
|
||||
className="mb-4"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
lineHeight: 'var(--line-height-body)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{post.excerpt}
|
||||
</p>
|
||||
|
||||
<div className="flex items-center justify-between text-sm text-gray-500">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-1">
|
||||
<User className="w-4 h-4" />
|
||||
<span style={{ fontSize: 'var(--font-small)', fontFamily: 'var(--font-family-base)' }}>
|
||||
{post.author}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Calendar className="w-4 h-4" />
|
||||
<span style={{ fontSize: 'var(--font-small)', fontFamily: 'var(--font-family-base)' }}>
|
||||
{formatDate(post.publishDate)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Clock className="w-4 h-4" />
|
||||
<span style={{ fontSize: 'var(--font-small)', fontFamily: 'var(--font-family-base)' }}>
|
||||
{post.readTime}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* All Posts Grid */}
|
||||
<div className="mb-12">
|
||||
<h2
|
||||
className="mb-8"
|
||||
style={{
|
||||
fontSize: 'var(--font-h2)',
|
||||
fontWeight: 'var(--font-weight-h2)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{searchTerm || selectedCategory !== 'All' ? 'Search Results' : 'All Articles'}
|
||||
</h2>
|
||||
|
||||
{currentPosts.length === 0 ? (
|
||||
<div className="text-center py-12">
|
||||
<p
|
||||
style={{
|
||||
fontSize: 'var(--font-body-lg)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
No articles found matching your criteria.
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{currentPosts.map((post) => (
|
||||
<article
|
||||
key={post.id}
|
||||
className="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden hover:shadow-lg transition-all duration-300 cursor-pointer group"
|
||||
onClick={() => navigateTo(`/learning/blogs/${post.slug}`)}
|
||||
>
|
||||
<div className="aspect-video w-full bg-gray-100 overflow-hidden">
|
||||
<ImageWithFallback
|
||||
src={post.thumbnail}
|
||||
alt={post.title}
|
||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
||||
/>
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<Badge
|
||||
variant="secondary"
|
||||
style={{
|
||||
backgroundColor: 'rgba(248, 195, 1, 0.1)',
|
||||
color: 'var(--color-black)',
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{post.category}
|
||||
</Badge>
|
||||
{post.featured && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
borderColor: 'var(--color-primary)',
|
||||
color: 'var(--color-primary)'
|
||||
}}
|
||||
>
|
||||
Featured
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<h3
|
||||
className="mb-3 group-hover:text-blue-600 transition-colors"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
lineHeight: 'var(--line-height-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{post.title}
|
||||
</h3>
|
||||
|
||||
<p
|
||||
className="mb-4"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
lineHeight: 'var(--line-height-small)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{post.excerpt}
|
||||
</p>
|
||||
|
||||
<div className="flex items-center justify-between text-sm text-gray-500">
|
||||
<div className="flex items-center gap-1">
|
||||
<User className="w-4 h-4" />
|
||||
<span style={{ fontSize: 'var(--font-small)', fontFamily: 'var(--font-family-base)' }}>
|
||||
{post.author}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Clock className="w-4 h-4" />
|
||||
<span style={{ fontSize: 'var(--font-small)', fontFamily: 'var(--font-family-base)' }}>
|
||||
{post.readTime}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Pagination */}
|
||||
{totalPages > 1 && (
|
||||
<div className="flex items-center justify-center gap-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setCurrentPage(prev => Math.max(prev - 1, 1))}
|
||||
disabled={currentPage === 1}
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
borderColor: 'var(--color-primary)'
|
||||
}}
|
||||
>
|
||||
<ChevronLeft className="w-4 h-4 mr-2" />
|
||||
Previous
|
||||
</Button>
|
||||
|
||||
<span
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-black)'
|
||||
}}
|
||||
>
|
||||
Page {currentPage} of {totalPages}
|
||||
</span>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setCurrentPage(prev => Math.min(prev + 1, totalPages))}
|
||||
disabled={currentPage === totalPages}
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
borderColor: 'var(--color-primary)'
|
||||
}}
|
||||
>
|
||||
Next
|
||||
<ChevronRight className="w-4 h-4 ml-2" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
991
src/components/BookFacility.tsx
Normal file
@@ -0,0 +1,991 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { navigateTo } from './Router';
|
||||
import { Button } from './ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from './ui/card';
|
||||
import { Badge } from './ui/badge';
|
||||
import { Input } from './ui/input';
|
||||
import { Label } from './ui/label';
|
||||
import { Textarea } from './ui/textarea';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
|
||||
import { Calendar } from './ui/calendar';
|
||||
import { Alert, AlertDescription } from './ui/alert';
|
||||
import { Separator } from './ui/separator';
|
||||
import { ImageWithFallback } from './figma/ImageWithFallback';
|
||||
|
||||
import {
|
||||
Users,
|
||||
MapPin,
|
||||
Clock,
|
||||
CheckCircle,
|
||||
CreditCard,
|
||||
Calendar as CalendarIcon,
|
||||
Building,
|
||||
Phone,
|
||||
Mail,
|
||||
User,
|
||||
ArrowLeft,
|
||||
ArrowRight,
|
||||
Wifi,
|
||||
Coffee,
|
||||
Car,
|
||||
Utensils,
|
||||
Copy,
|
||||
Download,
|
||||
AlertCircle,
|
||||
XCircle,
|
||||
Zap,
|
||||
Award,
|
||||
Shield
|
||||
} from 'lucide-react';
|
||||
|
||||
interface BookingFormData {
|
||||
companyName: string;
|
||||
contactName: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
role: string;
|
||||
teamSize: string;
|
||||
facilityZone: string;
|
||||
additionalRequirements: string;
|
||||
}
|
||||
|
||||
interface TimeSlot {
|
||||
time: string;
|
||||
available: boolean;
|
||||
price: number;
|
||||
}
|
||||
|
||||
interface BookingConfirmation {
|
||||
referenceId: string;
|
||||
facilityName: string;
|
||||
date: string;
|
||||
timeSlot: string;
|
||||
totalAmount: number;
|
||||
companyName: string;
|
||||
contactName: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
}
|
||||
|
||||
const facilityData = {
|
||||
id: 'executive-boardroom',
|
||||
name: 'Executive Boardroom',
|
||||
description: 'Premium boardroom designed for high-level meetings and strategic discussions with state-of-the-art technology.',
|
||||
capacity: 20,
|
||||
pricePerHour: 2500,
|
||||
image: 'https://images.unsplash.com/photo-1560472354-b33ff0c44a43?w=800&h=600&fit=crop',
|
||||
features: [
|
||||
'4K video conferencing system',
|
||||
'Premium leather seating',
|
||||
'Climate controlled environment',
|
||||
'Wireless presentation system',
|
||||
'Premium catering options',
|
||||
'Dedicated technical support'
|
||||
],
|
||||
amenities: [
|
||||
{ icon: Wifi, name: 'High-Speed WiFi' },
|
||||
{ icon: Coffee, name: 'Refreshment Station' },
|
||||
{ icon: Car, name: 'Valet Parking' },
|
||||
{ icon: Utensils, name: 'Premium Catering' }
|
||||
]
|
||||
};
|
||||
|
||||
const generateTimeSlots = (date: Date): TimeSlot[] => {
|
||||
const slots: TimeSlot[] = [];
|
||||
const isWeekend = date.getDay() === 0 || date.getDay() === 6;
|
||||
|
||||
if (isWeekend) {
|
||||
return []; // No slots available on weekends
|
||||
}
|
||||
|
||||
const startHour = 9;
|
||||
const endHour = 18;
|
||||
|
||||
for (let hour = startHour; hour < endHour; hour++) {
|
||||
const timeString = `${hour.toString().padStart(2, '0')}:00`;
|
||||
const available = Math.random() > 0.3; // Random availability for demo
|
||||
slots.push({
|
||||
time: timeString,
|
||||
available,
|
||||
price: facilityData.pricePerHour
|
||||
});
|
||||
}
|
||||
|
||||
return slots;
|
||||
};
|
||||
|
||||
const generateReferenceId = (): string => {
|
||||
return 'KLC-' + Date.now().toString().slice(-8).toUpperCase();
|
||||
};
|
||||
|
||||
export function BookFacility() {
|
||||
const [currentStep, setCurrentStep] = useState<'booking' | 'confirmation' | 'payment-success' | 'payment-failed'>('booking');
|
||||
const [selectedDate, setSelectedDate] = useState<Date | undefined>(new Date());
|
||||
const [selectedTimeSlot, setSelectedTimeSlot] = useState<string>('');
|
||||
const [timeSlots, setTimeSlots] = useState<TimeSlot[]>([]);
|
||||
const [bookingForm, setBookingForm] = useState<BookingFormData>({
|
||||
companyName: '',
|
||||
contactName: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
role: '',
|
||||
teamSize: '',
|
||||
facilityZone: 'executive-wing',
|
||||
additionalRequirements: ''
|
||||
});
|
||||
const [bookingConfirmation, setBookingConfirmation] = useState<BookingConfirmation | null>(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
document.title = 'Book Facility - Kautilya Leadership Centre';
|
||||
window.scrollTo(0, 0);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedDate) {
|
||||
setTimeSlots(generateTimeSlots(selectedDate));
|
||||
setSelectedTimeSlot(''); // Reset time slot when date changes
|
||||
}
|
||||
}, [selectedDate]);
|
||||
|
||||
const formatPrice = (price: number) => {
|
||||
return new Intl.NumberFormat('en-IN', {
|
||||
style: 'currency',
|
||||
currency: 'INR',
|
||||
maximumFractionDigits: 0
|
||||
}).format(price);
|
||||
};
|
||||
|
||||
const formatDate = (date: Date) => {
|
||||
return date.toLocaleDateString('en-IN', {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmitBooking = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!selectedDate || !selectedTimeSlot) return;
|
||||
|
||||
setIsSubmitting(true);
|
||||
|
||||
// Simulate API call
|
||||
setTimeout(() => {
|
||||
const selectedSlot = timeSlots.find(slot => slot.time === selectedTimeSlot);
|
||||
const confirmation: BookingConfirmation = {
|
||||
referenceId: generateReferenceId(),
|
||||
facilityName: facilityData.name,
|
||||
date: formatDate(selectedDate),
|
||||
timeSlot: selectedTimeSlot,
|
||||
totalAmount: selectedSlot?.price || facilityData.pricePerHour,
|
||||
companyName: bookingForm.companyName,
|
||||
contactName: bookingForm.contactName,
|
||||
email: bookingForm.email,
|
||||
phone: bookingForm.phone
|
||||
};
|
||||
|
||||
setBookingConfirmation(confirmation);
|
||||
setCurrentStep('confirmation');
|
||||
setIsSubmitting(false);
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
const handlePayNow = () => {
|
||||
// Simulate payment processing
|
||||
setIsSubmitting(true);
|
||||
setTimeout(() => {
|
||||
const success = Math.random() > 0.2; // 80% success rate for demo
|
||||
setCurrentStep(success ? 'payment-success' : 'payment-failed');
|
||||
setIsSubmitting(false);
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
const copyReferenceId = () => {
|
||||
if (bookingConfirmation) {
|
||||
navigator.clipboard.writeText(bookingConfirmation.referenceId);
|
||||
}
|
||||
};
|
||||
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
bookingForm.companyName &&
|
||||
bookingForm.contactName &&
|
||||
bookingForm.email &&
|
||||
bookingForm.phone &&
|
||||
bookingForm.role &&
|
||||
bookingForm.teamSize &&
|
||||
selectedDate &&
|
||||
selectedTimeSlot
|
||||
);
|
||||
};
|
||||
|
||||
const availableTimeSlots = timeSlots.filter(slot => slot.available);
|
||||
|
||||
if (currentStep === 'payment-success') {
|
||||
return (
|
||||
<div className="min-h-screen" style={{ backgroundColor: '#F7F7FD' }}>
|
||||
<div className="pt-40 pb-16">
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="max-w-2xl mx-auto text-center">
|
||||
<div
|
||||
className="w-20 h-20 rounded-full flex items-center justify-center mx-auto mb-6"
|
||||
style={{ backgroundColor: 'rgba(34, 197, 94, 0.1)' }}
|
||||
>
|
||||
<CheckCircle className="w-10 h-10 text-green-600" />
|
||||
</div>
|
||||
|
||||
<h1 className="text-h2 mb-4">
|
||||
Payment Successful!
|
||||
</h1>
|
||||
|
||||
<p className="text-body-lg text-muted mb-8">
|
||||
Your facility booking has been confirmed and payment processed successfully.
|
||||
You'll receive a confirmation email shortly.
|
||||
</p>
|
||||
|
||||
{bookingConfirmation && (
|
||||
<Card className="mb-8 text-left" style={{ backgroundColor: 'var(--color-bg-white)' }}>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-subhead">Booking Confirmed</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="grid grid-cols-2 gap-4 text-body">
|
||||
<div>
|
||||
<span className="text-muted">Reference ID:</span>
|
||||
<div className="text-subhead">{bookingConfirmation.referenceId}</div>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-muted">Amount Paid:</span>
|
||||
<div className="text-subhead text-green-600">
|
||||
{formatPrice(bookingConfirmation.totalAmount)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<button
|
||||
className="brand-button-system"
|
||||
onClick={() => navigateTo('/')}
|
||||
>
|
||||
<Download className="w-5 h-5" />
|
||||
Download Receipt
|
||||
</button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="lg"
|
||||
onClick={() => navigateTo('/')}
|
||||
className="text-body"
|
||||
>
|
||||
Return to Homepage
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (currentStep === 'payment-failed') {
|
||||
return (
|
||||
<div className="min-h-screen" style={{ backgroundColor: '#F7F7FD' }}>
|
||||
<div className="pt-40 pb-16">
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="max-w-2xl mx-auto text-center">
|
||||
<div
|
||||
className="w-20 h-20 rounded-full flex items-center justify-center mx-auto mb-6"
|
||||
style={{ backgroundColor: 'rgba(239, 68, 68, 0.1)' }}
|
||||
>
|
||||
<XCircle className="w-10 h-10 text-red-600" />
|
||||
</div>
|
||||
|
||||
<h1 className="text-h2 mb-4">
|
||||
Payment Failed
|
||||
</h1>
|
||||
|
||||
<p className="text-body-lg text-muted mb-8">
|
||||
We encountered an issue processing your payment. Your booking is still reserved
|
||||
for the next 30 minutes. Please try again or contact our support team.
|
||||
</p>
|
||||
|
||||
<Alert className="mb-8 text-left">
|
||||
<AlertCircle className="h-4 w-4" />
|
||||
<AlertDescription className="text-body">
|
||||
Your booking reference <strong>{bookingConfirmation?.referenceId}</strong> is
|
||||
temporarily held. Please complete payment within 30 minutes to confirm your booking.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<Button
|
||||
size="lg"
|
||||
onClick={handlePayNow}
|
||||
disabled={isSubmitting}
|
||||
className="text-body"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-primary)',
|
||||
color: 'white'
|
||||
}}
|
||||
>
|
||||
<CreditCard className="w-5 h-5 mr-2" />
|
||||
{isSubmitting ? 'Processing...' : 'Try Payment Again'}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="lg"
|
||||
onClick={() => navigateTo('/contact')}
|
||||
className="text-body"
|
||||
>
|
||||
Contact Support
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (currentStep === 'confirmation') {
|
||||
return (
|
||||
<div className="min-h-screen" style={{ backgroundColor: '#F7F7FD' }}>
|
||||
<div className="pt-40 pb-16">
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
{/* Confirmation Header */}
|
||||
<div className="text-center mb-8">
|
||||
<div
|
||||
className="w-20 h-20 rounded-full flex items-center justify-center mx-auto mb-6"
|
||||
style={{ backgroundColor: 'rgba(34, 197, 94, 0.1)' }}
|
||||
>
|
||||
<CheckCircle className="w-10 h-10 text-green-600" />
|
||||
</div>
|
||||
<h1 className="text-h2 mb-4">
|
||||
Booking Request Submitted
|
||||
</h1>
|
||||
<p className="text-body-lg text-muted">
|
||||
Your facility booking request has been received and is being processed.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Confirmation Panel */}
|
||||
{bookingConfirmation && (
|
||||
<Card className="mb-8" style={{ backgroundColor: 'var(--color-bg-white)' }}>
|
||||
<CardHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle className="text-subhead">Booking Confirmation</CardTitle>
|
||||
<Badge variant="outline" className="text-body px-4 py-2">
|
||||
Pending Confirmation
|
||||
</Badge>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
{/* Reference ID */}
|
||||
<div
|
||||
className="rounded-lg p-4"
|
||||
style={{ backgroundColor: 'rgba(0, 0, 0, 0.03)' }}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<span className="text-body text-muted">Reference ID</span>
|
||||
<div className="text-subhead text-primary">
|
||||
{bookingConfirmation.referenceId}
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={copyReferenceId}
|
||||
className="text-body"
|
||||
>
|
||||
<Copy className="w-4 h-4 mr-2" />
|
||||
Copy
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Booking Details */}
|
||||
<div className="grid md:grid-cols-2 gap-6">
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-subhead">Facility Details</h3>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<Building className="w-5 h-5 text-muted" />
|
||||
<div>
|
||||
<div className="text-body">{bookingConfirmation.facilityName}</div>
|
||||
<div className="text-small text-muted">Executive Wing</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<CalendarIcon className="w-5 h-5 text-muted" />
|
||||
<div>
|
||||
<div className="text-body">{bookingConfirmation.date}</div>
|
||||
<div className="text-small text-muted">{bookingConfirmation.timeSlot}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Users className="w-5 h-5 text-muted" />
|
||||
<div>
|
||||
<div className="text-body">Up to {facilityData.capacity} people</div>
|
||||
<div className="text-small text-muted">Maximum capacity</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-subhead">Contact Information</h3>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<Building className="w-5 h-5 text-muted" />
|
||||
<div>
|
||||
<div className="text-body">{bookingConfirmation.companyName}</div>
|
||||
<div className="text-small text-muted">Company</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<User className="w-5 h-5 text-muted" />
|
||||
<div>
|
||||
<div className="text-body">{bookingConfirmation.contactName}</div>
|
||||
<div className="text-small text-muted">Contact Person</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Mail className="w-5 h-5 text-muted" />
|
||||
<div>
|
||||
<div className="text-body">{bookingConfirmation.email}</div>
|
||||
<div className="text-small text-muted">Email</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
{/* Pricing */}
|
||||
<div className="flex items-center justify-between text-subhead">
|
||||
<span>Total Amount</span>
|
||||
<span className="text-primary">
|
||||
{formatPrice(bookingConfirmation.totalAmount)}
|
||||
</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<Button
|
||||
size="lg"
|
||||
onClick={handlePayNow}
|
||||
disabled={isSubmitting}
|
||||
className="text-body-lg"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-primary)',
|
||||
color: 'white'
|
||||
}}
|
||||
>
|
||||
<CreditCard className="w-5 h-5 mr-2" />
|
||||
{isSubmitting ? 'Processing Payment...' : 'Pay Now'}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="lg"
|
||||
onClick={() => navigateTo('/')}
|
||||
className="text-body-lg"
|
||||
>
|
||||
Continue Browsing
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen" style={{ backgroundColor: '#F7F7FD' }}>
|
||||
{/* Hero Section */}
|
||||
<section className="relative h-[80vh] lg:h-[90vh] overflow-hidden pt-20" style={{ backgroundColor: 'var(--color-bg-white)' }}>
|
||||
<div className="h-full flex items-center">
|
||||
{/* Left Side - Content (60%) */}
|
||||
<div className="relative w-full lg:w-[60%] h-full flex items-center justify-center p-8 lg:p-16 py-12 lg:py-16">
|
||||
<div className="max-w-2xl space-y-8">
|
||||
{/* Price Badge */}
|
||||
<div className="space-y-2">
|
||||
<Badge
|
||||
className="text-body px-4 py-2"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-primary)',
|
||||
color: 'white',
|
||||
border: 'none'
|
||||
}}
|
||||
>
|
||||
₹2,500/hour
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
{/* Main Headline */}
|
||||
<div className="space-y-6">
|
||||
<h1 className="text-h1 text-primary leading-tight">
|
||||
Book Executive
|
||||
<br />
|
||||
<span className="text-primary">Boardroom</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<div className="space-y-6">
|
||||
<p className="text-body-lg text-muted leading-relaxed max-w-xl">
|
||||
Premium boardroom designed for high-level meetings and strategic discussions with
|
||||
state-of-the-art technology.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Facility Details */}
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<Users className="w-5 h-5 text-muted" />
|
||||
<span className="text-body">20 people</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<MapPin className="w-5 h-5 text-muted" />
|
||||
<span className="text-body">Executive Wing</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock className="w-5 h-5 text-muted" />
|
||||
<span className="text-body">9 AM - 6 PM</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<CheckCircle className="w-5 h-5 text-green-600" />
|
||||
<span className="text-body">Available</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Amenities */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<Wifi className="w-5 h-5 text-primary flex-shrink-0" />
|
||||
<span className="text-body">High-Speed WiFi</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Coffee className="w-5 h-5 text-primary flex-shrink-0" />
|
||||
<span className="text-body">Refreshment Station</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Car className="w-5 h-5 text-primary flex-shrink-0" />
|
||||
<span className="text-body">Valet Parking</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Utensils className="w-5 h-5 text-primary flex-shrink-0" />
|
||||
<span className="text-body">Premium Catering</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Zap className="w-5 h-5 text-primary flex-shrink-0" />
|
||||
<span className="text-body">4K Video System</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Award className="w-5 h-5 text-primary flex-shrink-0" />
|
||||
<span className="text-body">Premium Seating</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CTA Button */}
|
||||
<div className="pt-4">
|
||||
<Button
|
||||
onClick={() => {
|
||||
const bookingSection = document.getElementById('booking-section');
|
||||
if (bookingSection) {
|
||||
bookingSection.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
}}
|
||||
className="group shadow-xl"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-primary)',
|
||||
color: 'white',
|
||||
borderRadius: '9999px',
|
||||
padding: '1rem 2rem'
|
||||
}}
|
||||
>
|
||||
<span>Book This Boardroom</span>
|
||||
<ArrowRight className="w-5 h-5 ml-2 group-hover:translate-x-1 transition-transform" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Side - Luxury Interior Image (40%) */}
|
||||
<div className="hidden lg:block relative w-[40%] h-full">
|
||||
<div className="absolute inset-0">
|
||||
<ImageWithFallback
|
||||
src="https://images.unsplash.com/photo-1577791464704-3bbd2da56803?w=1080&h=800&fit=crop"
|
||||
alt="Luxury executive boardroom with modern design"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
{/* Subtle overlay for depth */}
|
||||
<div className="absolute inset-0 bg-gradient-to-l from-transparent via-transparent to-white/20"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div id="booking-section" className="py-8 lg:py-12">
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
{/* Back Button */}
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => navigateTo('/learning-facility/virtual-tour')}
|
||||
className="mb-6 text-body"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
||||
Back to Virtual Tour
|
||||
</Button>
|
||||
|
||||
{/* Facility Header Card */}
|
||||
<Card className="mb-8" style={{ backgroundColor: 'var(--color-bg-white)' }}>
|
||||
<CardContent className="p-6">
|
||||
<div className="grid lg:grid-cols-3 gap-6">
|
||||
<div className="lg:col-span-1">
|
||||
<div className="aspect-video rounded-lg overflow-hidden">
|
||||
<ImageWithFallback
|
||||
src={facilityData.image}
|
||||
alt={facilityData.name}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="lg:col-span-2">
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div>
|
||||
<h1 className="text-h3 mb-2">
|
||||
Book {facilityData.name}
|
||||
</h1>
|
||||
<p className="text-body text-muted mb-4">
|
||||
{facilityData.description}
|
||||
</p>
|
||||
</div>
|
||||
<Badge variant="secondary" className="text-body px-4 py-2">
|
||||
{formatPrice(facilityData.pricePerHour)}/hour
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<Users className="w-5 h-5 text-muted" />
|
||||
<span className="text-body">{facilityData.capacity} people</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<MapPin className="w-5 h-5 text-muted" />
|
||||
<span className="text-body">Executive Wing</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock className="w-5 h-5 text-muted" />
|
||||
<span className="text-body">9 AM - 6 PM</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<CheckCircle className="w-5 h-5 text-green-600" />
|
||||
<span className="text-body">Available</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{facilityData.amenities.map((amenity, index) => (
|
||||
<Badge key={index} variant="outline" className="text-small px-3 py-1">
|
||||
<amenity.icon className="w-3 h-3 mr-1" />
|
||||
{amenity.name}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<div className="grid lg:grid-cols-3 gap-8">
|
||||
{/* Booking Form */}
|
||||
<div className="lg:col-span-2">
|
||||
<form onSubmit={handleSubmitBooking} className="space-y-8">
|
||||
{/* Company Information */}
|
||||
<Card style={{ backgroundColor: 'var(--color-bg-white)' }}>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-subhead">Company Information</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label htmlFor="companyName" className="text-body">Company Name *</Label>
|
||||
<Input
|
||||
id="companyName"
|
||||
value={bookingForm.companyName}
|
||||
onChange={(e) => setBookingForm({...bookingForm, companyName: e.target.value})}
|
||||
required
|
||||
className="text-body min-h-[44px]"
|
||||
placeholder="Enter company name"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="contactName" className="text-body">Contact Person *</Label>
|
||||
<Input
|
||||
id="contactName"
|
||||
value={bookingForm.contactName}
|
||||
onChange={(e) => setBookingForm({...bookingForm, contactName: e.target.value})}
|
||||
required
|
||||
className="text-body min-h-[44px]"
|
||||
placeholder="Enter contact name"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label htmlFor="email" className="text-body">Email Address *</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
value={bookingForm.email}
|
||||
onChange={(e) => setBookingForm({...bookingForm, email: e.target.value})}
|
||||
required
|
||||
className="text-body min-h-[44px]"
|
||||
placeholder="Enter email address"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="phone" className="text-body">Phone Number *</Label>
|
||||
<Input
|
||||
id="phone"
|
||||
value={bookingForm.phone}
|
||||
onChange={(e) => setBookingForm({...bookingForm, phone: e.target.value})}
|
||||
required
|
||||
className="text-body min-h-[44px]"
|
||||
placeholder="Enter phone number"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label htmlFor="role" className="text-body">Your Role *</Label>
|
||||
<Select value={bookingForm.role} onValueChange={(value) => setBookingForm({...bookingForm, role: value})}>
|
||||
<SelectTrigger className="text-body min-h-[44px]">
|
||||
<SelectValue placeholder="Select your role" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="hr-director">HR Director</SelectItem>
|
||||
<SelectItem value="learning-development">L&D Manager</SelectItem>
|
||||
<SelectItem value="executive-assistant">Executive Assistant</SelectItem>
|
||||
<SelectItem value="ceo-founder">CEO/Founder</SelectItem>
|
||||
<SelectItem value="other">Other</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="teamSize" className="text-body">Expected Team Size *</Label>
|
||||
<Select value={bookingForm.teamSize} onValueChange={(value) => setBookingForm({...bookingForm, teamSize: value})}>
|
||||
<SelectTrigger className="text-body min-h-[44px]">
|
||||
<SelectValue placeholder="Select team size" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="1-5">1-5 people</SelectItem>
|
||||
<SelectItem value="6-10">6-10 people</SelectItem>
|
||||
<SelectItem value="11-15">11-15 people</SelectItem>
|
||||
<SelectItem value="16-20">16-20 people</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="additionalRequirements" className="text-body">Additional Requirements</Label>
|
||||
<Textarea
|
||||
id="additionalRequirements"
|
||||
value={bookingForm.additionalRequirements}
|
||||
onChange={(e) => setBookingForm({...bookingForm, additionalRequirements: e.target.value})}
|
||||
placeholder="Catering, special AV setup, accessibility needs..."
|
||||
className="text-body min-h-[100px]"
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Date and Time Selection */}
|
||||
<Card style={{ backgroundColor: 'var(--color-bg-white)' }}>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-subhead">Select Date & Time</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
{/* Calendar Widget */}
|
||||
<div>
|
||||
<Label className="text-body mb-4 block">Select Date *</Label>
|
||||
<div className="border rounded-lg p-4">
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={selectedDate}
|
||||
onSelect={setSelectedDate}
|
||||
disabled={(date) =>
|
||||
date < new Date() ||
|
||||
date.getDay() === 0 ||
|
||||
date.getDay() === 6
|
||||
}
|
||||
className="rounded-md"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-small text-muted mt-2">
|
||||
Bookings available Monday to Friday only
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Time Slots */}
|
||||
{selectedDate && (
|
||||
<div>
|
||||
<Label className="text-body mb-4 block">Available Time Slots *</Label>
|
||||
{availableTimeSlots.length > 0 ? (
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
|
||||
{availableTimeSlots.map((slot) => (
|
||||
<Button
|
||||
key={slot.time}
|
||||
type="button"
|
||||
variant={selectedTimeSlot === slot.time ? "default" : "outline"}
|
||||
className="text-body justify-between p-4 h-auto"
|
||||
onClick={() => setSelectedTimeSlot(slot.time)}
|
||||
style={{
|
||||
backgroundColor: selectedTimeSlot === slot.time ? 'var(--color-primary)' : 'transparent',
|
||||
color: selectedTimeSlot === slot.time ? 'white' : 'var(--color-black)'
|
||||
}}
|
||||
>
|
||||
<span>{slot.time}</span>
|
||||
<span className="text-small">{formatPrice(slot.price)}</span>
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<Alert>
|
||||
<AlertCircle className="h-4 w-4" />
|
||||
<AlertDescription className="text-body">
|
||||
No time slots available for the selected date. Please choose a different date.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Submit Button */}
|
||||
<Card style={{ backgroundColor: 'var(--color-bg-white)' }}>
|
||||
<CardContent className="p-6">
|
||||
<Button
|
||||
type="submit"
|
||||
size="lg"
|
||||
className="w-full text-body-lg"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
backgroundColor: 'var(--color-primary)',
|
||||
color: 'white'
|
||||
}}
|
||||
>
|
||||
{isSubmitting ? (
|
||||
'Processing Booking...'
|
||||
) : (
|
||||
<>
|
||||
Submit Booking Request
|
||||
<ArrowRight className="w-5 h-5 ml-2" />
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
<p className="text-small text-muted text-center mt-4">
|
||||
By submitting, you agree to our booking terms and conditions
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{/* Booking Summary Sidebar */}
|
||||
<div className="lg:col-span-1">
|
||||
<Card className="sticky top-24" style={{ backgroundColor: 'var(--color-bg-white)' }}>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-subhead">Booking Summary</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between text-body">
|
||||
<span>Facility:</span>
|
||||
<span className="text-subhead">{facilityData.name}</span>
|
||||
</div>
|
||||
|
||||
{selectedDate && (
|
||||
<div className="flex justify-between text-body">
|
||||
<span>Date:</span>
|
||||
<span className="text-subhead">
|
||||
{selectedDate.toLocaleDateString('en-IN', {
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedTimeSlot && (
|
||||
<div className="flex justify-between text-body">
|
||||
<span>Time:</span>
|
||||
<span className="text-subhead">{selectedTimeSlot}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex justify-between text-body">
|
||||
<span>Duration:</span>
|
||||
<span className="text-subhead">1 hour</span>
|
||||
</div>
|
||||
|
||||
{bookingForm.teamSize && (
|
||||
<div className="flex justify-between text-body">
|
||||
<span>Team Size:</span>
|
||||
<span className="text-subhead">{bookingForm.teamSize}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between text-body">
|
||||
<span>Base Rate:</span>
|
||||
<span>{formatPrice(facilityData.pricePerHour)}</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-body">
|
||||
<span>Service Charge:</span>
|
||||
<span>Included</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="flex justify-between text-subhead">
|
||||
<span>Total:</span>
|
||||
<span className="text-primary">{formatPrice(facilityData.pricePerHour)}</span>
|
||||
</div>
|
||||
|
||||
<div className="text-small text-muted">
|
||||
<p>• Payment due upon confirmation</p>
|
||||
<p>• Free cancellation up to 24 hours</p>
|
||||
<p>• Technical support included</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
69
src/components/CTABannerSection.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import { ArrowRight } from "lucide-react";
|
||||
import { ImageWithFallback } from "./figma/ImageWithFallback";
|
||||
import { BrandedTag } from "./about/BrandedTag";
|
||||
import { PrimaryCTAButton } from "./PrimaryCTAButton";
|
||||
import { navigateTo } from "./Router";
|
||||
|
||||
export function CTABannerSection() {
|
||||
return (
|
||||
<section className="relative h-[700px] overflow-hidden">
|
||||
{/* Background Image */}
|
||||
<div className="absolute inset-0">
|
||||
<ImageWithFallback
|
||||
src="https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2940&q=80"
|
||||
alt="Professional team collaborating in modern office"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
|
||||
{/* Subtle dark overlay for overall image */}
|
||||
<div className="absolute inset-0 bg-black/30" />
|
||||
|
||||
{/* Gradient overlay for better text readability */}
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-black/20 via-transparent to-black/60" />
|
||||
</div>
|
||||
|
||||
{/* Content Container */}
|
||||
<div className="relative h-full flex items-center justify-end section-margin-x">
|
||||
{/* CTA Content Block */}
|
||||
<div
|
||||
className="bg-opacity-95 backdrop-blur-sm rounded-lg p-16 max-w-2xl"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-brand-primary)'
|
||||
}}
|
||||
>
|
||||
{/* Branded Tag */}
|
||||
<BrandedTag text="Next Steps" variant="white" />
|
||||
|
||||
{/* Main Headline */}
|
||||
<h2
|
||||
className="text-h2-white mb-8"
|
||||
>
|
||||
Ready to transform your leadership?
|
||||
<span
|
||||
className="italic"
|
||||
style={{ color: 'var(--color-brand-accent)' }}
|
||||
>
|
||||
{" "}Get in touch{" "}
|
||||
</span>
|
||||
to start your development journey now.
|
||||
</h2>
|
||||
|
||||
{/* CTA Button - Updated to redirect to contact page */}
|
||||
<PrimaryCTAButton
|
||||
text="Schedule a Consultation"
|
||||
onClick={() => navigateTo('/contact?topic=consulting')}
|
||||
ariaLabel="Schedule a consultation with our leadership experts"
|
||||
className="cta-banner-yellow"
|
||||
/>
|
||||
|
||||
{/* Supporting Text */}
|
||||
<p
|
||||
className="text-body-white mt-6 opacity-90"
|
||||
>
|
||||
Connect with our leadership experts to discuss your organization's specific development needs.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
213
src/components/CTAPopupModal.tsx
Normal file
@@ -0,0 +1,213 @@
|
||||
import React from 'react';
|
||||
import { Button } from './ui/button';
|
||||
import { Eye, Calendar, ArrowRight, Building } from 'lucide-react';
|
||||
import { navigateTo } from './Router';
|
||||
|
||||
interface CTAPopupModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export function CTAPopupModal({ isOpen, onClose }: CTAPopupModalProps) {
|
||||
console.log('CTAPopupModal render - isOpen:', isOpen); // Debug log
|
||||
|
||||
const handleVirtualTour = () => {
|
||||
navigateTo('/services/learning-facility');
|
||||
onClose();
|
||||
// Add a small delay to ensure navigation completes before triggering tour
|
||||
setTimeout(() => {
|
||||
// Trigger virtual tour start if on facility page
|
||||
const startTourBtn = document.querySelector('[data-tour-trigger]') as HTMLButtonElement;
|
||||
if (startTourBtn) {
|
||||
startTourBtn.click();
|
||||
}
|
||||
}, 100);
|
||||
};
|
||||
|
||||
const handleBooking = () => {
|
||||
console.log('Booking button clicked in modal'); // Debug log
|
||||
|
||||
// Use URL parameter approach for more reliable booking modal triggering
|
||||
navigateTo('/services/learning-facility?autoBooking=true');
|
||||
onClose();
|
||||
|
||||
// Also try the element-based approach as backup
|
||||
const attemptBookingTrigger = (attempt = 1, maxAttempts = 5) => {
|
||||
console.log(`Booking trigger attempt #${attempt}`); // Debug log
|
||||
|
||||
// Look for the booking trigger element
|
||||
const bookingBtn = document.querySelector('[data-booking-trigger]') as HTMLButtonElement;
|
||||
console.log('Found booking trigger:', bookingBtn); // Debug log
|
||||
|
||||
if (bookingBtn) {
|
||||
console.log('Clicking booking trigger...'); // Debug log
|
||||
bookingBtn.click();
|
||||
return;
|
||||
}
|
||||
|
||||
// If not found and we haven't reached max attempts, try again
|
||||
if (attempt < maxAttempts) {
|
||||
setTimeout(() => attemptBookingTrigger(attempt + 1, maxAttempts), 200);
|
||||
} else {
|
||||
console.log('Element-based approach failed, relying on URL parameter approach'); // Debug log
|
||||
}
|
||||
};
|
||||
|
||||
// Start the booking trigger attempts after initial navigation delay
|
||||
setTimeout(() => attemptBookingTrigger(), 500);
|
||||
};
|
||||
|
||||
const handleContactUs = () => {
|
||||
navigateTo('/contact');
|
||||
onClose();
|
||||
};
|
||||
|
||||
// Simple modal implementation that bypasses Dialog component issues
|
||||
if (!isOpen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
background: 'rgba(0, 0, 0, 0.5)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
zIndex: 9999
|
||||
}}
|
||||
onClick={onClose}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
background: 'white',
|
||||
padding: '2rem',
|
||||
borderRadius: '8px',
|
||||
maxWidth: '500px',
|
||||
width: '90%',
|
||||
maxHeight: '80vh',
|
||||
overflow: 'auto'
|
||||
}}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="text-center space-y-4 mb-6">
|
||||
<div
|
||||
className="w-20 h-20 rounded-2xl flex items-center justify-center mx-auto"
|
||||
style={{ backgroundColor: 'rgba(4, 4, 91, 0.1)' }}
|
||||
>
|
||||
<Building className="w-10 h-10 text-primary" />
|
||||
</div>
|
||||
<h2 className="text-h3">
|
||||
Explore Our Learning Facility
|
||||
</h2>
|
||||
<p className="text-body text-muted">
|
||||
Discover our world-class learning environment. Take a virtual tour to see our facilities or book your space for an upcoming event.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="py-6 space-y-4">
|
||||
{/* Test Message */}
|
||||
<div className="text-center p-4 bg-green-100 rounded-lg">
|
||||
<p className="text-body text-green-800">
|
||||
🎉 Simple Modal is working! This popup opened successfully.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Virtual Tour Option */}
|
||||
<div className="rounded-lg border p-6 space-y-4 hover:shadow-md transition-shadow">
|
||||
<div className="flex items-center gap-3">
|
||||
<div
|
||||
className="w-12 h-12 rounded-lg flex items-center justify-center"
|
||||
style={{ backgroundColor: 'rgba(4, 4, 91, 0.1)' }}
|
||||
>
|
||||
<Eye className="w-6 h-6 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-h4 mb-1">Virtual Tour</h4>
|
||||
<p className="text-body text-muted">
|
||||
Explore our four distinct learning environments
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
onClick={handleVirtualTour}
|
||||
className="w-full"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-primary)',
|
||||
color: 'white'
|
||||
}}
|
||||
>
|
||||
<Eye className="w-5 h-5 mr-2" />
|
||||
Start Virtual Tour
|
||||
<ArrowRight className="w-5 h-5 ml-2" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Booking Option */}
|
||||
<div className="rounded-lg border p-6 space-y-4 hover:shadow-md transition-shadow">
|
||||
<div className="flex items-center gap-3">
|
||||
<div
|
||||
className="w-12 h-12 rounded-lg flex items-center justify-center"
|
||||
style={{ backgroundColor: 'rgba(248, 195, 1, 0.1)' }}
|
||||
>
|
||||
<Calendar className="w-6 h-6" style={{ color: '#F8C301' }} />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-h4 mb-1">Book Facility</h4>
|
||||
<p className="text-body text-muted">
|
||||
Reserve our learning spaces for your next event
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
onClick={handleBooking}
|
||||
variant="outline"
|
||||
className="w-full"
|
||||
style={{
|
||||
borderColor: 'var(--color-primary)',
|
||||
color: 'var(--color-primary)'
|
||||
}}
|
||||
>
|
||||
<Calendar className="w-5 h-5 mr-2" />
|
||||
Book Now
|
||||
<ArrowRight className="w-5 h-5 ml-2" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Contact Us Option */}
|
||||
<div className="pt-4 border-t">
|
||||
<p className="text-center text-small text-muted mb-3">
|
||||
Need more information or have specific requirements?
|
||||
</p>
|
||||
<Button
|
||||
onClick={handleContactUs}
|
||||
variant="ghost"
|
||||
className="w-full text-body"
|
||||
style={{ color: 'var(--color-primary)' }}
|
||||
>
|
||||
Contact Us
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Close Button */}
|
||||
<div className="pt-4">
|
||||
<Button
|
||||
onClick={onClose}
|
||||
variant="outline"
|
||||
className="w-full"
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
493
src/components/Cart.tsx
Normal file
@@ -0,0 +1,493 @@
|
||||
import React from 'react';
|
||||
import { Button } from './ui/button';
|
||||
import { Card, CardContent } from './ui/card';
|
||||
import { Badge } from './ui/badge';
|
||||
import { Separator } from './ui/separator';
|
||||
import {
|
||||
ShoppingBag,
|
||||
Trash2,
|
||||
Plus,
|
||||
ArrowRight,
|
||||
Shield,
|
||||
CheckCircle,
|
||||
Home,
|
||||
ChevronRight
|
||||
} from 'lucide-react';
|
||||
import { motion } from 'motion/react';
|
||||
import { navigateTo } from './Router';
|
||||
import { ImageWithFallback } from './figma/ImageWithFallback';
|
||||
import { useCart } from './CartContext';
|
||||
|
||||
export function Cart() {
|
||||
const { cartItems, removeFromCart, getCartTotal } = useCart();
|
||||
|
||||
// Calculate pricing
|
||||
const subtotal = getCartTotal();
|
||||
const gstRate = 0.18; // 18% GST
|
||||
const gstAmount = subtotal * gstRate;
|
||||
const total = subtotal + gstAmount;
|
||||
|
||||
const handleProceedToPayment = () => {
|
||||
// Navigate to checkout/payment page
|
||||
navigateTo('/checkout');
|
||||
};
|
||||
|
||||
const handleAddMoreProgrammes = () => {
|
||||
navigateTo('/learning-online');
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ backgroundColor: '#FFFFFF' }}>
|
||||
{/* Breadcrumb Navigation */}
|
||||
<div className="section-margin-x py-6 border-b border-gray-200">
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<button
|
||||
onClick={() => navigateTo('/')}
|
||||
className="flex items-center gap-1 text-gray-600 hover:text-gray-900 transition-colors"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
<Home className="w-4 h-4" />
|
||||
Home
|
||||
</button>
|
||||
<ChevronRight className="w-4 h-4 text-gray-400" />
|
||||
<span
|
||||
className="text-gray-900 font-medium"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Cart
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Cart Content */}
|
||||
<div className="section-margin-x py-8">
|
||||
{/* Page Header */}
|
||||
<div className="flex items-center gap-3 mb-8">
|
||||
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center">
|
||||
<ShoppingBag className="w-5 h-5 text-gray-700" />
|
||||
</div>
|
||||
<div>
|
||||
<h1
|
||||
style={{
|
||||
fontSize: 'var(--font-h2)',
|
||||
fontWeight: 'var(--font-weight-h2)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-h2)'
|
||||
}}
|
||||
>
|
||||
Shopping Cart
|
||||
</h1>
|
||||
<p
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
marginTop: '0.25rem'
|
||||
}}
|
||||
>
|
||||
Review your selected programmes before proceeding to payment
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{cartItems.length === 0 ? (
|
||||
/* Empty Cart State */
|
||||
<div className="text-center py-16">
|
||||
<div className="w-24 h-24 mx-auto mb-6 rounded-full bg-gray-100 flex items-center justify-center">
|
||||
<ShoppingBag className="w-10 h-10 text-gray-400" />
|
||||
</div>
|
||||
<h2
|
||||
className="mb-4"
|
||||
style={{
|
||||
fontSize: 'var(--font-h3)',
|
||||
fontWeight: 'var(--font-weight-h3)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Your cart is empty
|
||||
</h2>
|
||||
<p
|
||||
className="mb-8 max-w-md mx-auto"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-body)'
|
||||
}}
|
||||
>
|
||||
Browse our comprehensive leadership development programmes and add them to your cart.
|
||||
</p>
|
||||
<Button
|
||||
onClick={handleAddMoreProgrammes}
|
||||
className="inline-flex items-center gap-2 h-12 px-6 rounded-lg font-medium"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-primary)',
|
||||
color: 'white',
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontWeight: '500'
|
||||
}}
|
||||
>
|
||||
Browse Programmes
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
/* Cart with Items */
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
|
||||
{/* Left Column - Cart Items */}
|
||||
<div className="lg:col-span-2">
|
||||
{/* Cart Items Header */}
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div className="flex items-center gap-3">
|
||||
<h2
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Cart Items ({cartItems.length})
|
||||
</h2>
|
||||
<Badge
|
||||
className="px-3 py-1 rounded-full"
|
||||
style={{
|
||||
backgroundColor: 'rgba(248, 195, 1, 0.15)',
|
||||
color: 'var(--color-black)',
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{cartItems.length} programme{cartItems.length !== 1 ? 's' : ''}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Cart Items List */}
|
||||
<div className="space-y-6">
|
||||
{cartItems.map((item, index) => (
|
||||
<motion.div
|
||||
key={item.id}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: index * 0.1 }}
|
||||
>
|
||||
<Card className="overflow-hidden hover:shadow-md transition-all duration-300">
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-start gap-6">
|
||||
|
||||
{/* Programme Thumbnail */}
|
||||
<div className="w-24 h-16 rounded-lg overflow-hidden flex-shrink-0 bg-gray-100">
|
||||
<ImageWithFallback
|
||||
src={item.thumbnail}
|
||||
alt={item.title}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Programme Details */}
|
||||
<div className="flex-1 min-w-0">
|
||||
|
||||
{/* Programme Title */}
|
||||
<h3
|
||||
className="mb-3 line-clamp-2"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-h4)'
|
||||
}}
|
||||
>
|
||||
{item.title}
|
||||
</h3>
|
||||
|
||||
{/* Course Details Row */}
|
||||
<div className="flex flex-wrap items-center gap-4 mb-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<span
|
||||
className="text-xs px-3 py-1 bg-gray-100 rounded-full"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Course ID: {item.category.toUpperCase().slice(0, 3)}-{item.id}
|
||||
</span>
|
||||
<span
|
||||
className="text-xs px-3 py-1 rounded-full"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-primary)',
|
||||
backgroundColor: 'rgba(4, 4, 91, 0.1)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{item.level}
|
||||
</span>
|
||||
<span
|
||||
className="text-xs px-3 py-1 bg-blue-50 text-blue-700 rounded-full"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Advanced
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Pricing Row */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<span
|
||||
className="font-bold"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
color: 'var(--color-primary)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{item.price}
|
||||
</span>
|
||||
{item.originalPrice && (
|
||||
<span
|
||||
className="line-through"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{item.originalPrice}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Remove Button */}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => removeFromCart(item.id)}
|
||||
className="text-red-500 hover:text-red-700 hover:bg-red-50 p-2"
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Add More Programmes Button */}
|
||||
<div className="mt-8 pt-6 border-t border-gray-200">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleAddMoreProgrammes}
|
||||
className="flex items-center gap-2 h-12 px-6 rounded-lg border-2 border-dashed border-gray-300 hover:border-gray-400 transition-all duration-300 w-full"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontWeight: '500'
|
||||
}}
|
||||
>
|
||||
<Plus className="w-5 h-5" />
|
||||
Add More Programmes
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Column - Order Summary */}
|
||||
<div className="lg:col-span-1">
|
||||
<Card className="sticky top-4">
|
||||
<CardContent className="p-6">
|
||||
|
||||
{/* Order Summary Header */}
|
||||
<h3
|
||||
className="mb-6"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Order Summary
|
||||
</h3>
|
||||
|
||||
{/* Pricing Breakdown */}
|
||||
<div className="space-y-4 mb-6">
|
||||
|
||||
{/* Subtotal */}
|
||||
<div className="flex justify-between">
|
||||
<span
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Subtotal
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontWeight: '500'
|
||||
}}
|
||||
>
|
||||
₹{subtotal.toLocaleString('en-IN')}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* GST */}
|
||||
<div className="flex justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<span
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
GST (18%)
|
||||
</span>
|
||||
<div className="w-4 h-4 rounded-full bg-gray-200 flex items-center justify-center cursor-help">
|
||||
<span className="text-xs text-gray-600">i</span>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontWeight: '500'
|
||||
}}
|
||||
>
|
||||
₹{gstAmount.toLocaleString('en-IN')}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
{/* Total */}
|
||||
<div className="flex justify-between">
|
||||
<span
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Total
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-primary)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
₹{total.toLocaleString('en-IN')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Proceed to Payment Button */}
|
||||
<Button
|
||||
onClick={handleProceedToPayment}
|
||||
className="w-full flex items-center justify-center gap-2 h-14 rounded-lg font-semibold mb-6"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-primary)',
|
||||
color: 'white',
|
||||
fontSize: 'var(--font-body-lg)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Proceed to Payment
|
||||
<ArrowRight className="w-5 h-5" />
|
||||
</Button>
|
||||
|
||||
{/* Security Features */}
|
||||
<div className="space-y-3 pt-4 border-t border-gray-200">
|
||||
<div className="flex items-center gap-3 text-sm">
|
||||
<CheckCircle className="w-4 h-4 text-green-600 flex-shrink-0" />
|
||||
<span
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Secure payment processing
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 text-sm">
|
||||
<CheckCircle className="w-4 h-4 text-green-600 flex-shrink-0" />
|
||||
<span
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Instant course access
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 text-sm">
|
||||
<CheckCircle className="w-4 h-4 text-green-600 flex-shrink-0" />
|
||||
<span
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Certificate upon completion
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Security Note */}
|
||||
<div className="flex items-start gap-2 mt-6 p-4 bg-gray-50 rounded-lg">
|
||||
<Shield className="w-4 h-4 text-gray-600 mt-0.5 flex-shrink-0" />
|
||||
<div>
|
||||
<p
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-small)'
|
||||
}}
|
||||
>
|
||||
<strong>Need help with payment?</strong><br />
|
||||
Our AI assistant can guide you through the checkout process and answer questions about course enrollment. Click the chat icon to get started.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
99
src/components/CartContext.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||||
import { CartItem } from './CartPopup';
|
||||
|
||||
interface CartContextType {
|
||||
cartItems: CartItem[];
|
||||
cartCount: number;
|
||||
addToCart: (item: CartItem) => void;
|
||||
removeFromCart: (itemId: string) => void;
|
||||
clearCart: () => void;
|
||||
getCartTotal: () => number;
|
||||
}
|
||||
|
||||
const CartContext = createContext<CartContextType | undefined>(undefined);
|
||||
|
||||
export function CartProvider({ children }: { children: React.ReactNode }) {
|
||||
const [cartItems, setCartItems] = useState<CartItem[]>([]);
|
||||
|
||||
// Load cart from localStorage on mount
|
||||
useEffect(() => {
|
||||
const savedCart = localStorage.getItem('klc-cart');
|
||||
if (savedCart) {
|
||||
try {
|
||||
const parsedCart = JSON.parse(savedCart);
|
||||
if (Array.isArray(parsedCart)) {
|
||||
setCartItems(parsedCart);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading cart from localStorage:', error);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Save cart to localStorage whenever it changes
|
||||
useEffect(() => {
|
||||
localStorage.setItem('klc-cart', JSON.stringify(cartItems));
|
||||
}, [cartItems]);
|
||||
|
||||
// Parse price helper function
|
||||
const parsePrice = (priceStr: string) => {
|
||||
const cleanPrice = priceStr.replace(/[₹$,]/g, '');
|
||||
const numValue = parseFloat(cleanPrice);
|
||||
return isNaN(numValue) ? 0 : numValue;
|
||||
};
|
||||
|
||||
const addToCart = (item: CartItem) => {
|
||||
setCartItems(prevItems => {
|
||||
// Check if item already exists
|
||||
const existingItem = prevItems.find(cartItem => cartItem.id === item.id);
|
||||
|
||||
if (existingItem) {
|
||||
// Item already in cart, don't add duplicate
|
||||
return prevItems;
|
||||
}
|
||||
|
||||
// Add new item
|
||||
return [...prevItems, item];
|
||||
});
|
||||
};
|
||||
|
||||
const removeFromCart = (itemId: string) => {
|
||||
setCartItems(prevItems => prevItems.filter(item => item.id !== itemId));
|
||||
};
|
||||
|
||||
const clearCart = () => {
|
||||
setCartItems([]);
|
||||
};
|
||||
|
||||
const getCartTotal = () => {
|
||||
return cartItems.reduce((total, item) => {
|
||||
const price = parsePrice(item.price);
|
||||
return total + price;
|
||||
}, 0);
|
||||
};
|
||||
|
||||
const cartCount = cartItems.length;
|
||||
|
||||
const value: CartContextType = {
|
||||
cartItems,
|
||||
cartCount,
|
||||
addToCart,
|
||||
removeFromCart,
|
||||
clearCart,
|
||||
getCartTotal
|
||||
};
|
||||
|
||||
return (
|
||||
<CartContext.Provider value={value}>
|
||||
{children}
|
||||
</CartContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useCart() {
|
||||
const context = useContext(CartContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useCart must be used within a CartProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
574
src/components/CartPopup.tsx
Normal file
@@ -0,0 +1,574 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { X, CheckCircle, ShoppingCart, Trash2, Shield, ArrowRight } from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'motion/react';
|
||||
import { Button } from './ui/button';
|
||||
import { ImageWithFallback } from './figma/ImageWithFallback';
|
||||
import { navigateTo } from './Router';
|
||||
import { useCart } from './CartContext';
|
||||
|
||||
export interface CartItem {
|
||||
id: string;
|
||||
title: string;
|
||||
thumbnail: string;
|
||||
price: string;
|
||||
originalPrice?: string;
|
||||
category: string;
|
||||
level: string;
|
||||
}
|
||||
|
||||
interface CartPopupProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
cartItems: CartItem[]; // Legacy prop - no longer used but kept for backward compatibility
|
||||
onRemoveItem: (itemId: string) => void; // Legacy prop - no longer used but kept for backward compatibility
|
||||
recentlyAddedItem?: CartItem | null;
|
||||
}
|
||||
|
||||
export function CartPopup({
|
||||
isOpen,
|
||||
onClose,
|
||||
cartItems: legacyCartItems, // Renamed to avoid confusion
|
||||
onRemoveItem: legacyOnRemoveItem, // Renamed to avoid confusion
|
||||
recentlyAddedItem
|
||||
}: CartPopupProps) {
|
||||
const [showSuccess, setShowSuccess] = useState(false);
|
||||
|
||||
// Use global cart context instead of props
|
||||
const { cartItems, removeFromCart, getCartTotal } = useCart();
|
||||
|
||||
useEffect(() => {
|
||||
if (recentlyAddedItem && isOpen) {
|
||||
setShowSuccess(true);
|
||||
const timer = setTimeout(() => setShowSuccess(false), 3000);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [recentlyAddedItem, isOpen]);
|
||||
|
||||
// Prevent background scrolling when popup is open
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
// Save current scroll position
|
||||
const scrollY = window.scrollY;
|
||||
|
||||
// Prevent scrolling
|
||||
document.body.style.overflow = 'hidden';
|
||||
document.body.style.position = 'fixed';
|
||||
document.body.style.top = `-${scrollY}px`;
|
||||
document.body.style.width = '100%';
|
||||
|
||||
return () => {
|
||||
// Restore scrolling
|
||||
document.body.style.overflow = '';
|
||||
document.body.style.position = '';
|
||||
document.body.style.top = '';
|
||||
document.body.style.width = '';
|
||||
|
||||
// Restore scroll position
|
||||
window.scrollTo(0, scrollY);
|
||||
};
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
// Fixed price parsing function - handles both ₹ and $ formats
|
||||
const parsePrice = (priceStr: string) => {
|
||||
// Remove currency symbols and commas, then parse
|
||||
const cleanPrice = priceStr.replace(/[₹$,]/g, '');
|
||||
const numValue = parseFloat(cleanPrice);
|
||||
return isNaN(numValue) ? 0 : numValue;
|
||||
};
|
||||
|
||||
// Convert to rupees - improved handling
|
||||
const convertToRupees = (priceStr: string) => {
|
||||
const numValue = parsePrice(priceStr);
|
||||
|
||||
// If already in rupees, return as is
|
||||
if (priceStr.includes('₹')) {
|
||||
return `₹${numValue.toLocaleString('en-IN')}`;
|
||||
}
|
||||
|
||||
// Convert from USD to INR
|
||||
return `₹${(numValue * 83).toLocaleString('en-IN')}`;
|
||||
};
|
||||
|
||||
// Use global cart total instead of calculating locally
|
||||
const subtotal = getCartTotal();
|
||||
const estimatedTotal = subtotal; // No taxes for now
|
||||
|
||||
const handleProceedToCheckout = () => {
|
||||
// Navigate to checkout page (placeholder)
|
||||
navigateTo('/checkout');
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleContinueShopping = () => {
|
||||
onClose();
|
||||
};
|
||||
|
||||
// Handle backdrop click - close popup only if clicking on backdrop
|
||||
const handleBackdropClick = (e: React.MouseEvent) => {
|
||||
if (e.target === e.currentTarget) {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{isOpen && (
|
||||
<>
|
||||
{/* Backdrop with higher z-index than navbar */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="fixed inset-0 bg-black/50 backdrop-blur-sm"
|
||||
style={{ zIndex: 10001 }} // Higher than navbar (navbar typically uses z-50 which is 50)
|
||||
onClick={handleBackdropClick}
|
||||
/>
|
||||
|
||||
{/* Cart Popup with highest z-index */}
|
||||
<motion.div
|
||||
initial={{ x: '100%' }}
|
||||
animate={{ x: 0 }}
|
||||
exit={{ x: '100%' }}
|
||||
transition={{ type: 'spring', damping: 30, stiffness: 300 }}
|
||||
className="fixed top-0 right-0 h-full w-full max-w-lg bg-white shadow-2xl overflow-hidden flex flex-col"
|
||||
style={{
|
||||
zIndex: 10002, // Highest z-index to be above everything including navbar
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-6 border-b border-gray-200 bg-white">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-8 h-8 rounded-full bg-green-100 flex items-center justify-center">
|
||||
<CheckCircle className="w-5 h-5 text-green-600" />
|
||||
</div>
|
||||
<div>
|
||||
<h2
|
||||
className="font-semibold"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Added to Cart
|
||||
</h2>
|
||||
<p
|
||||
className="text-muted"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{cartItems.length} programme{cartItems.length !== 1 ? 's' : ''} in your cart
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={onClose}
|
||||
className="p-2 hover:bg-gray-100 rounded-full"
|
||||
>
|
||||
<X className="w-5 h-5" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Success Message */}
|
||||
<AnimatePresence>
|
||||
{showSuccess && recentlyAddedItem && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -20 }}
|
||||
className="mx-6 mt-4 p-4 bg-green-50 border border-green-200 rounded-lg"
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<CheckCircle className="w-5 h-5 text-green-600 mt-0.5 flex-shrink-0" />
|
||||
<div className="flex-1">
|
||||
<p
|
||||
className="font-medium text-green-800"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
"{recentlyAddedItem.title}" added successfully!
|
||||
</p>
|
||||
<p
|
||||
className="text-green-600 mt-1"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
You can continue browsing or proceed to checkout.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
{/* Cart Content - Scrollable */}
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
{/* Cart Items */}
|
||||
<div className="p-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Your Cart ({cartItems.length})
|
||||
</h3>
|
||||
<p
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontWeight: '500'
|
||||
}}
|
||||
>
|
||||
Total: ₹{subtotal.toLocaleString('en-IN')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Cart Items List */}
|
||||
<div className="space-y-4">
|
||||
{cartItems.map((item, index) => (
|
||||
<motion.div
|
||||
key={item.id}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: index * 0.1 }}
|
||||
className="flex items-start gap-4 p-4 border border-gray-200 rounded-lg hover:border-gray-300 transition-colors"
|
||||
>
|
||||
{/* Course Thumbnail */}
|
||||
<div className="w-20 h-14 rounded-lg overflow-hidden flex-shrink-0">
|
||||
<ImageWithFallback
|
||||
src={item.thumbnail}
|
||||
alt={item.title}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Course Details */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<h4
|
||||
className="font-medium line-clamp-2 mb-1"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontWeight: '500',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{item.title}
|
||||
</h4>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<span
|
||||
className="text-xs px-2 py-1 bg-gray-100 rounded-md"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Programme
|
||||
</span>
|
||||
<span
|
||||
className="text-xs px-2 py-1 rounded-md"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-primary)',
|
||||
backgroundColor: 'rgba(4, 4, 91, 0.1)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{item.level}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<span
|
||||
className="font-semibold"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-primary)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{convertToRupees(item.price)}
|
||||
</span>
|
||||
{item.originalPrice && (
|
||||
<span
|
||||
className="line-through"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{convertToRupees(item.originalPrice)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => removeFromCart(item.id)}
|
||||
className="p-2 text-red-500 hover:text-red-700 hover:bg-red-50"
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Cart Summary */}
|
||||
{cartItems.length > 0 && (
|
||||
<div className="mt-6 p-4 bg-gray-50 rounded-lg border">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<ShoppingCart className="w-5 h-5 text-gray-600" />
|
||||
<h4
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontWeight: '600',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Cart Summary
|
||||
</h4>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between">
|
||||
<span
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Total Items:
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontWeight: '500'
|
||||
}}
|
||||
>
|
||||
{cartItems.length} programme{cartItems.length !== 1 ? 's' : ''}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Subtotal:
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontWeight: '600'
|
||||
}}
|
||||
>
|
||||
₹{subtotal.toLocaleString('en-IN')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between font-semibold pt-2 border-t border-gray-200">
|
||||
<span
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontWeight: '600'
|
||||
}}
|
||||
>
|
||||
Estimated Total:
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-primary)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontWeight: '700'
|
||||
}}
|
||||
>
|
||||
₹{estimatedTotal.toLocaleString('en-IN')}
|
||||
</span>
|
||||
</div>
|
||||
<p
|
||||
className="text-center mt-2"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Final total calculated at checkout
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Total Value Highlight Box */}
|
||||
{cartItems.length > 0 && (
|
||||
<div
|
||||
className="mt-6 p-6 rounded-lg text-center"
|
||||
style={{ backgroundColor: 'rgba(4, 4, 91, 0.05)' }}
|
||||
>
|
||||
<h4
|
||||
className="mb-2"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontWeight: '600',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Total Value
|
||||
</h4>
|
||||
<div
|
||||
className="mb-2"
|
||||
style={{
|
||||
fontSize: '2rem',
|
||||
fontWeight: '700',
|
||||
color: 'var(--color-primary)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
₹{estimatedTotal.toLocaleString('en-IN')}
|
||||
</div>
|
||||
<p
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-primary)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontWeight: '500'
|
||||
}}
|
||||
>
|
||||
{cartItems.length} programme{cartItems.length !== 1 ? 's' : ''} selected
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Empty Cart Message */}
|
||||
{cartItems.length === 0 && (
|
||||
<div className="text-center py-8">
|
||||
<ShoppingCart className="w-16 h-16 mx-auto text-gray-300 mb-4" />
|
||||
<h3
|
||||
className="mb-2"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Your cart is empty
|
||||
</h3>
|
||||
<p
|
||||
className="mb-6"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Browse our courses and add programmes to get started.
|
||||
</p>
|
||||
<Button
|
||||
onClick={handleContinueShopping}
|
||||
className="bg-primary text-white hover:bg-primary/90"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-primary)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Browse Courses
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Footer Actions */}
|
||||
{cartItems.length > 0 && (
|
||||
<div className="border-t border-gray-200 p-6 bg-white">
|
||||
{/* CTA Buttons */}
|
||||
<div className="space-y-3 mb-4">
|
||||
{/* Proceed to Checkout - Primary */}
|
||||
<Button
|
||||
onClick={handleProceedToCheckout}
|
||||
className="w-full flex items-center justify-center gap-2 h-12 rounded-lg font-medium"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-primary)',
|
||||
color: 'white',
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontWeight: '600'
|
||||
}}
|
||||
>
|
||||
<ShoppingCart className="w-5 h-5" />
|
||||
Proceed to Checkout
|
||||
</Button>
|
||||
|
||||
{/* Continue Shopping - Secondary */}
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleContinueShopping}
|
||||
className="w-full flex items-center justify-center gap-2 h-12 rounded-lg font-medium"
|
||||
style={{
|
||||
borderColor: 'var(--color-primary)',
|
||||
color: 'var(--color-primary)',
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontWeight: '500',
|
||||
borderWidth: '2px'
|
||||
}}
|
||||
>
|
||||
Continue Shopping
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Security Footer */}
|
||||
<div className="flex items-center justify-center gap-2 pt-3 border-t border-gray-100">
|
||||
<Shield className="w-4 h-4 text-green-600" />
|
||||
<p
|
||||
className="text-center"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Secure checkout • 30-day money-back guarantee
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</motion.div>
|
||||
</>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
);
|
||||
}
|
||||
250
src/components/Chatbot.tsx
Normal file
@@ -0,0 +1,250 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { MessageCircle, X, Send, User, Bot, Minimize2 } from 'lucide-react';
|
||||
import { Button } from './ui/button';
|
||||
import exampleImage from 'figma:asset/a28d79dd35b730f689b77dbb30452ca27bd25759.png';
|
||||
|
||||
interface Message {
|
||||
id: string;
|
||||
text: string;
|
||||
sender: 'user' | 'bot';
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
export function Chatbot() {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [messages, setMessages] = useState<Message[]>([
|
||||
{
|
||||
id: '1',
|
||||
text: 'Hello! I\'m here to help you learn more about Kautilya Leadership Centre. How can I assist you today?',
|
||||
sender: 'bot',
|
||||
timestamp: new Date()
|
||||
}
|
||||
]);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [isTyping, setIsTyping] = useState(false);
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const scrollToBottom = () => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
scrollToBottom();
|
||||
}, [messages]);
|
||||
|
||||
const predefinedResponses: { [key: string]: string } = {
|
||||
'hello': 'Hello! Welcome to Kautilya Leadership Centre. I\'m here to help you with information about our programs, services, and how we can support your leadership development journey.',
|
||||
'programs': 'We offer comprehensive leadership development programs including executive coaching, team development, strategic leadership training, and virtual learning experiences. Would you like to know more about any specific program?',
|
||||
'services': 'Our services include Leadership Development, Executive Coaching, Team Building, Strategic Planning, Organizational Development, and Virtual Learning Platforms. Each is designed to enhance leadership capabilities.',
|
||||
'contact': 'You can reach us through our contact form on the website, or feel free to schedule a consultation. We\'d be happy to discuss how we can support your leadership development goals.',
|
||||
'virtual': 'Our virtual learning platform offers flexible, interactive leadership development experiences. You can access courses, participate in live sessions, and connect with other leaders from anywhere.',
|
||||
'coaching': 'Our executive coaching program provides personalized one-on-one guidance to help leaders develop their skills, overcome challenges, and achieve their professional goals.',
|
||||
'team': 'Our team development services focus on building high-performing teams through collaborative workshops, communication training, and strategic alignment exercises.',
|
||||
'webinar': 'Join our upcoming leadership webinars! We regularly host sessions covering various leadership topics. Check our webinars section for schedules and registration.',
|
||||
'default': 'That\'s a great question! For detailed information about our specific programs and services, I\'d recommend exploring our website or contacting our team directly. Is there anything specific about leadership development I can help you with?'
|
||||
};
|
||||
|
||||
const getBotResponse = (userMessage: string): string => {
|
||||
const lowerMessage = userMessage.toLowerCase();
|
||||
|
||||
for (const [key, response] of Object.entries(predefinedResponses)) {
|
||||
if (key !== 'default' && lowerMessage.includes(key)) {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
return predefinedResponses.default;
|
||||
};
|
||||
|
||||
const handleSendMessage = async () => {
|
||||
if (!inputValue.trim()) return;
|
||||
|
||||
const userMessage: Message = {
|
||||
id: Date.now().toString(),
|
||||
text: inputValue,
|
||||
sender: 'user',
|
||||
timestamp: new Date()
|
||||
};
|
||||
|
||||
setMessages(prev => [...prev, userMessage]);
|
||||
setInputValue('');
|
||||
setIsTyping(true);
|
||||
|
||||
// Simulate typing delay
|
||||
setTimeout(() => {
|
||||
const botMessage: Message = {
|
||||
id: (Date.now() + 1).toString(),
|
||||
text: getBotResponse(inputValue),
|
||||
sender: 'bot',
|
||||
timestamp: new Date()
|
||||
};
|
||||
|
||||
setMessages(prev => [...prev, botMessage]);
|
||||
setIsTyping(false);
|
||||
}, 1500);
|
||||
};
|
||||
|
||||
const handleKeyPress = (e: React.KeyboardEvent) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
handleSendMessage();
|
||||
}
|
||||
};
|
||||
|
||||
const formatTime = (date: Date) => {
|
||||
return date.toLocaleTimeString('en-US', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: true
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Chatbot Toggle Button */}
|
||||
<div className="fixed bottom-6 right-6 z-50">
|
||||
<button
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className="w-16 h-16 rounded-full shadow-xl transition-all duration-300 hover:scale-105 focus:outline-none focus:ring-4 focus:ring-blue-300 chatbot-button"
|
||||
style={{
|
||||
backgroundColor: '#04045B',
|
||||
boxShadow: '0 8px 32px rgba(4, 4, 91, 0.3)'
|
||||
}}
|
||||
aria-label="Open chat"
|
||||
>
|
||||
{isOpen ? (
|
||||
<X className="w-7 h-7 text-white mx-auto" />
|
||||
) : (
|
||||
<MessageCircle className="w-7 h-7 text-white mx-auto" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Chatbot Window */}
|
||||
{isOpen && (
|
||||
<div
|
||||
className="fixed bottom-24 right-6 w-80 bg-white rounded-2xl shadow-2xl border-0 z-50 flex flex-col overflow-hidden"
|
||||
style={{
|
||||
height: '500px',
|
||||
boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(255, 255, 255, 0.1)'
|
||||
}}
|
||||
>
|
||||
{/* Header with gradient background */}
|
||||
<div
|
||||
className="p-6 flex items-center justify-between relative"
|
||||
style={{
|
||||
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
||||
borderRadius: '16px 16px 0 0'
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="w-10 h-10 bg-white/20 backdrop-blur-sm rounded-full flex items-center justify-center">
|
||||
<Bot className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-white font-semibold text-lg">Welcome to KLC</h3>
|
||||
<p className="text-white/80 text-sm">How can I help you today?</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<button
|
||||
onClick={() => setIsOpen(false)}
|
||||
className="text-white/70 hover:text-white transition-colors p-1"
|
||||
>
|
||||
<Minimize2 className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setIsOpen(false)}
|
||||
className="text-white/70 hover:text-white transition-colors p-1"
|
||||
>
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Messages Container */}
|
||||
<div className="flex-1 p-5 overflow-y-auto space-y-4" style={{ backgroundColor: '#f8fafc' }}>
|
||||
{messages.map((message) => (
|
||||
<div
|
||||
key={message.id}
|
||||
className={`flex ${message.sender === 'user' ? 'justify-end' : 'justify-start'}`}
|
||||
>
|
||||
<div className={`flex items-start space-x-3 max-w-[85%] ${message.sender === 'user' ? 'flex-row-reverse space-x-reverse' : ''}`}>
|
||||
<div className={`w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0 ${
|
||||
message.sender === 'user'
|
||||
? 'bg-gradient-to-r from-blue-500 to-purple-600'
|
||||
: 'bg-gradient-to-r from-purple-500 to-pink-500'
|
||||
}`}>
|
||||
{message.sender === 'user' ? (
|
||||
<User className="w-4 h-4 text-white" />
|
||||
) : (
|
||||
<Bot className="w-4 h-4 text-white" />
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<div className={`p-4 rounded-2xl ${
|
||||
message.sender === 'user'
|
||||
? 'bg-gradient-to-r from-blue-500 to-purple-600 text-white'
|
||||
: 'bg-white text-gray-800 shadow-sm border border-gray-100'
|
||||
}`}>
|
||||
<p className="text-sm leading-relaxed">{message.text}</p>
|
||||
</div>
|
||||
<p className="text-xs text-gray-400 mt-2 px-2">
|
||||
{formatTime(message.timestamp)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Typing Indicator */}
|
||||
{isTyping && (
|
||||
<div className="flex justify-start">
|
||||
<div className="flex items-start space-x-3">
|
||||
<div className="w-8 h-8 rounded-full bg-gradient-to-r from-purple-500 to-pink-500 flex items-center justify-center">
|
||||
<Bot className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<div className="bg-white p-4 rounded-2xl shadow-sm border border-gray-100">
|
||||
<div className="flex space-x-1">
|
||||
<div className="w-2 h-2 bg-purple-400 rounded-full animate-bounce"></div>
|
||||
<div className="w-2 h-2 bg-purple-400 rounded-full animate-bounce" style={{ animationDelay: '0.1s' }}></div>
|
||||
<div className="w-2 h-2 bg-purple-400 rounded-full animate-bounce" style={{ animationDelay: '0.2s' }}></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div ref={messagesEndRef} />
|
||||
</div>
|
||||
|
||||
{/* Input Area */}
|
||||
<div className="p-5 bg-white border-t border-gray-100">
|
||||
<div className="flex space-x-3">
|
||||
<input
|
||||
type="text"
|
||||
value={inputValue}
|
||||
onChange={(e) => setInputValue(e.target.value)}
|
||||
onKeyPress={handleKeyPress}
|
||||
placeholder="Type your message..."
|
||||
className="flex-1 px-4 py-3 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent text-sm bg-gray-50"
|
||||
style={{ fontFamily: 'var(--font-family-brand)' }}
|
||||
/>
|
||||
<button
|
||||
onClick={handleSendMessage}
|
||||
disabled={!inputValue.trim()}
|
||||
className="px-4 py-3 rounded-xl text-white transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed focus:outline-none focus:ring-2 focus:ring-purple-500"
|
||||
style={{
|
||||
background: inputValue.trim()
|
||||
? 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'
|
||||
: '#e5e7eb'
|
||||
}}
|
||||
>
|
||||
<Send className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
558
src/components/Contact.tsx
Normal file
@@ -0,0 +1,558 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { MapPin, Phone, Mail, Clock, CheckCircle2, ArrowRight, Send, Calendar, Users } from 'lucide-react';
|
||||
import { Button } from './ui/button';
|
||||
import { Input } from './ui/input';
|
||||
import { Textarea } from './ui/textarea';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
|
||||
import { BrandedTag } from './about/BrandedTag';
|
||||
import { PrimaryCTAButton } from './PrimaryCTAButton';
|
||||
import { ImageWithFallback } from './figma/ImageWithFallback';
|
||||
import { navigateTo } from './Router';
|
||||
|
||||
interface ContactProps {
|
||||
topic?: string;
|
||||
}
|
||||
|
||||
export function Contact({ topic }: ContactProps) {
|
||||
const [formData, setFormData] = useState({
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
company: '',
|
||||
jobTitle: '',
|
||||
subject: topic || '',
|
||||
message: '',
|
||||
source: ''
|
||||
});
|
||||
|
||||
const [isSubmitted, setIsSubmitted] = useState(false);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('Contact component mounted with topic:', topic);
|
||||
// Set default subject based on topic parameter
|
||||
if (topic) {
|
||||
const subject = getTopicSubject(topic);
|
||||
console.log('Setting form subject to:', subject);
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
subject: subject
|
||||
}));
|
||||
}
|
||||
}, [topic]);
|
||||
|
||||
const getTopicSubject = (topicParam: string) => {
|
||||
switch (topicParam) {
|
||||
case 'leadership-pipeline':
|
||||
return 'Leadership Pipeline Development';
|
||||
case 'consulting':
|
||||
return 'Consulting Services';
|
||||
case 'executive-coaching':
|
||||
return 'Executive Coaching';
|
||||
case 'learning-facility':
|
||||
return 'Learning Facility';
|
||||
default:
|
||||
return topicParam.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
|
||||
}
|
||||
};
|
||||
|
||||
const handleInputChange = (field: string, value: string) => {
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
[field]: value
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setIsSubmitting(true);
|
||||
|
||||
// Simulate form submission
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
setIsSubmitted(true);
|
||||
setIsSubmitting(false);
|
||||
};
|
||||
|
||||
if (isSubmitted) {
|
||||
return (
|
||||
<div className="min-h-screen" style={{ backgroundColor: '#F7F7FD' }}>
|
||||
<div className="section-margin-x py-24">
|
||||
<div className="max-w-2xl mx-auto text-center">
|
||||
<div className="w-20 h-20 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-6">
|
||||
<CheckCircle2 className="w-10 h-10 text-green-600" />
|
||||
</div>
|
||||
<h1 className="text-h2 mb-4">Thank You!</h1>
|
||||
<p className="text-body-lg text-muted mb-8">
|
||||
We've received your message and will get back to you within 24 hours.
|
||||
Our leadership development experts are excited to help transform your organization.
|
||||
</p>
|
||||
<PrimaryCTAButton
|
||||
text="Return to Home"
|
||||
onClick={() => navigateTo('/')}
|
||||
ariaLabel="Return to homepage"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen" style={{ backgroundColor: '#F7F7FD' }}>
|
||||
{/* Hero Section with CTA Banner Styling */}
|
||||
<section className="relative h-[600px] overflow-hidden">
|
||||
{/* Background Image */}
|
||||
<div className="absolute inset-0">
|
||||
<ImageWithFallback
|
||||
src="https://images.unsplash.com/photo-1600880292203-757bb62b4baf?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2940&q=80"
|
||||
alt="Professional business meeting and consultation"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
|
||||
{/* 40% black overlay for better text readability */}
|
||||
<div className="absolute inset-0 bg-black/70" />
|
||||
</div>
|
||||
|
||||
{/* Content Container */}
|
||||
<div className="relative h-full flex items-center justify-center section-margin-x">
|
||||
{/* Hero Content Block */}
|
||||
<div className="text-center max-w-4xl mx-auto">
|
||||
{/* Branded Tag */}
|
||||
<BrandedTag text="Get In Touch" variant="white" />
|
||||
|
||||
{/* Main Headline */}
|
||||
<h1 className="text-h1-white mb-6">
|
||||
Ready to transform your leadership?
|
||||
<span
|
||||
className="italic"
|
||||
style={{ color: 'var(--color-brand-accent)' }}
|
||||
>
|
||||
{" "}Let's connect{" "}
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
{/* Supporting Text */}
|
||||
<p className="text-body-lg-white opacity-90 max-w-3xl mx-auto">
|
||||
To start your development journey.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Main Content Section */}
|
||||
<div className="section-margin-x py-16">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-12">
|
||||
{/* Contact Information - Enhanced Design */}
|
||||
<div className="lg:col-span-1">
|
||||
<div className="bg-white rounded-xl shadow-lg border border-gray-100 h-fit overflow-hidden">
|
||||
{/* Contact Info Header */}
|
||||
<div
|
||||
className="p-8 text-white"
|
||||
style={{ backgroundColor: 'var(--color-brand-primary)' }}
|
||||
>
|
||||
<h2 className="text-h3-white mb-2">Contact Information</h2>
|
||||
<p className="text-body-white opacity-90">
|
||||
We're here to help you on your leadership journey
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Contact Details */}
|
||||
<div className="p-8 space-y-8">
|
||||
{/* Office Location */}
|
||||
<div className="flex items-start gap-4">
|
||||
<div
|
||||
className="w-12 h-12 rounded-lg flex items-center justify-center flex-shrink-0"
|
||||
style={{ backgroundColor: 'rgba(248, 195, 1, 0.1)' }}
|
||||
>
|
||||
<MapPin className="w-6 h-6" style={{ color: 'var(--color-brand-primary)' }} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-h3 mb-2">Office Location</h3>
|
||||
<p className="text-body text-muted">
|
||||
123 Leadership Avenue<br />
|
||||
Business District, New Delhi<br />
|
||||
110001, India
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Phone */}
|
||||
<div className="flex items-start gap-4">
|
||||
<div
|
||||
className="w-12 h-12 rounded-lg flex items-center justify-center flex-shrink-0"
|
||||
style={{ backgroundColor: 'rgba(248, 195, 1, 0.1)' }}
|
||||
>
|
||||
<Phone className="w-6 h-6" style={{ color: 'var(--color-brand-primary)' }} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-h3 mb-2">Phone</h3>
|
||||
<p className="text-body text-muted">
|
||||
+91 11 4567 8900<br />
|
||||
+91 98765 43210
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Email */}
|
||||
<div className="flex items-start gap-4">
|
||||
<div
|
||||
className="w-12 h-12 rounded-lg flex items-center justify-center flex-shrink-0"
|
||||
style={{ backgroundColor: 'rgba(248, 195, 1, 0.1)' }}
|
||||
>
|
||||
<Mail className="w-6 h-6" style={{ color: 'var(--color-brand-primary)' }} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-h3 mb-2">Email</h3>
|
||||
<p className="text-body text-muted">
|
||||
info@klc.edu<br />
|
||||
leadership@klc.edu
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Business Hours */}
|
||||
<div className="flex items-start gap-4">
|
||||
<div
|
||||
className="w-12 h-12 rounded-lg flex items-center justify-center flex-shrink-0"
|
||||
style={{ backgroundColor: 'rgba(248, 195, 1, 0.1)' }}
|
||||
>
|
||||
<Clock className="w-6 h-6" style={{ color: 'var(--color-brand-primary)' }} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-h3 mb-2">Business Hours</h3>
|
||||
<p className="text-body text-muted">
|
||||
Monday - Friday: 9:00 AM - 6:00 PM<br />
|
||||
Saturday: 10:00 AM - 4:00 PM<br />
|
||||
Sunday: Closed
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quick Actions */}
|
||||
<div
|
||||
className="p-8 border-t"
|
||||
style={{ borderColor: 'var(--color-border)' }}
|
||||
>
|
||||
<h3 className="text-h3 mb-4">Quick Actions</h3>
|
||||
<div className="space-y-3">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full justify-start hover:bg-blue-50 hover:border-blue-200 transition-all duration-300"
|
||||
onClick={() => navigateTo('/services/learning-facility')}
|
||||
style={{
|
||||
borderColor: 'var(--color-brand-primary)',
|
||||
color: 'var(--color-brand-primary)'
|
||||
}}
|
||||
>
|
||||
<Calendar className="w-4 h-4 mr-2" />
|
||||
Schedule Facility Tour
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full justify-start hover:bg-blue-50 hover:border-blue-200 transition-all duration-300"
|
||||
onClick={() => navigateTo('/leadership-journey')}
|
||||
style={{
|
||||
borderColor: 'var(--color-brand-primary)',
|
||||
color: 'var(--color-brand-primary)'
|
||||
}}
|
||||
>
|
||||
<Users className="w-4 h-4 mr-2" />
|
||||
Start Leadership Assessment
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Contact Form - Enhanced Design */}
|
||||
<div className="lg:col-span-2">
|
||||
<div className="bg-white rounded-xl shadow-lg border border-gray-100">
|
||||
{/* Form Header */}
|
||||
<div className="p-8 border-b" style={{ borderColor: 'var(--color-border)' }}>
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div
|
||||
className="w-10 h-10 rounded-lg flex items-center justify-center"
|
||||
style={{ backgroundColor: 'rgba(248, 195, 1, 0.1)' }}
|
||||
>
|
||||
<Send className="w-5 h-5" style={{ color: 'var(--color-brand-primary)' }} />
|
||||
</div>
|
||||
<h2 className="text-h3">Send us a Message</h2>
|
||||
</div>
|
||||
<p className="text-body text-muted">
|
||||
Fill out the form below and we'll get back to you within 24 hours.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Form Content */}
|
||||
<div className="p-8">
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
|
||||
First Name *
|
||||
</label>
|
||||
<Input
|
||||
type="text"
|
||||
required
|
||||
value={formData.firstName}
|
||||
onChange={(e) => handleInputChange('firstName', e.target.value)}
|
||||
placeholder="John"
|
||||
className="w-full h-12 border-gray-200 focus:border-blue-500 focus:ring-blue-500"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontSize: 'var(--font-body)'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
|
||||
Last Name *
|
||||
</label>
|
||||
<Input
|
||||
type="text"
|
||||
required
|
||||
value={formData.lastName}
|
||||
onChange={(e) => handleInputChange('lastName', e.target.value)}
|
||||
placeholder="Doe"
|
||||
className="w-full h-12 border-gray-200 focus:border-blue-500 focus:ring-blue-500"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontSize: 'var(--font-body)'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
|
||||
Email Address *
|
||||
</label>
|
||||
<Input
|
||||
type="email"
|
||||
required
|
||||
value={formData.email}
|
||||
onChange={(e) => handleInputChange('email', e.target.value)}
|
||||
placeholder="john.doe@company.com"
|
||||
className="w-full h-12 border-gray-200 focus:border-blue-500 focus:ring-blue-500"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontSize: 'var(--font-body)'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
|
||||
Phone Number
|
||||
</label>
|
||||
<Input
|
||||
type="tel"
|
||||
value={formData.phone}
|
||||
onChange={(e) => handleInputChange('phone', e.target.value)}
|
||||
placeholder="+91 98765 43210"
|
||||
className="w-full h-12 border-gray-200 focus:border-blue-500 focus:ring-blue-500"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontSize: 'var(--font-body)'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
|
||||
Company/Organization
|
||||
</label>
|
||||
<Input
|
||||
type="text"
|
||||
value={formData.company}
|
||||
onChange={(e) => handleInputChange('company', e.target.value)}
|
||||
placeholder="Company Name"
|
||||
className="w-full h-12 border-gray-200 focus:border-blue-500 focus:ring-blue-500"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontSize: 'var(--font-body)'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
|
||||
Job Title
|
||||
</label>
|
||||
<Input
|
||||
type="text"
|
||||
value={formData.jobTitle}
|
||||
onChange={(e) => handleInputChange('jobTitle', e.target.value)}
|
||||
placeholder="CEO, HR Director, etc."
|
||||
className="w-full h-12 border-gray-200 focus:border-blue-500 focus:ring-blue-500"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontSize: 'var(--font-body)'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
|
||||
Subject/Interest Area *
|
||||
</label>
|
||||
<Select
|
||||
value={formData.subject}
|
||||
onValueChange={(value) => handleInputChange('subject', value)}
|
||||
>
|
||||
<SelectTrigger
|
||||
className="w-full h-12 border-gray-200 focus:border-blue-500 focus:ring-blue-500"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontSize: 'var(--font-body)'
|
||||
}}
|
||||
>
|
||||
<SelectValue placeholder="Select a subject" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="Leadership Pipeline Development">Leadership Pipeline Development</SelectItem>
|
||||
<SelectItem value="Executive Coaching">Executive Coaching</SelectItem>
|
||||
<SelectItem value="Management Development">Management Development</SelectItem>
|
||||
<SelectItem value="Culture Competence">Culture Competence</SelectItem>
|
||||
<SelectItem value="Consulting Services">Consulting Services</SelectItem>
|
||||
<SelectItem value="Learning Facility">Learning Facility Tour</SelectItem>
|
||||
<SelectItem value="Online Courses">Online Learning Programs</SelectItem>
|
||||
<SelectItem value="General Inquiry">General Inquiry</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
|
||||
How did you hear about us?
|
||||
</label>
|
||||
<Select
|
||||
value={formData.source}
|
||||
onValueChange={(value) => handleInputChange('source', value)}
|
||||
>
|
||||
<SelectTrigger
|
||||
className="w-full h-12 border-gray-200 focus:border-blue-500 focus:ring-blue-500"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontSize: 'var(--font-body)'
|
||||
}}
|
||||
>
|
||||
<SelectValue placeholder="Select an option" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="Website">Website</SelectItem>
|
||||
<SelectItem value="Google Search">Google Search</SelectItem>
|
||||
<SelectItem value="Social Media">Social Media</SelectItem>
|
||||
<SelectItem value="Referral">Referral</SelectItem>
|
||||
<SelectItem value="Event/Conference">Event/Conference</SelectItem>
|
||||
<SelectItem value="LinkedIn">LinkedIn</SelectItem>
|
||||
<SelectItem value="Email Marketing">Email Marketing</SelectItem>
|
||||
<SelectItem value="Other">Other</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
|
||||
Message *
|
||||
</label>
|
||||
<Textarea
|
||||
required
|
||||
value={formData.message}
|
||||
onChange={(e) => handleInputChange('message', e.target.value)}
|
||||
placeholder="Tell us about your leadership development needs, goals, or any specific questions you have..."
|
||||
rows={6}
|
||||
className="w-full border-gray-200 focus:border-blue-500 focus:ring-blue-500 resize-none"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontSize: 'var(--font-body)'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="pt-6 border-t" style={{ borderColor: 'var(--color-border)' }}>
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-between items-start sm:items-center">
|
||||
<div className="text-small text-muted">
|
||||
* Required fields. We'll respond within 24 hours.
|
||||
</div>
|
||||
<PrimaryCTAButton
|
||||
text={isSubmitting ? "Sending Message..." : "Send Message"}
|
||||
onClick={() => {}}
|
||||
ariaLabel="Send contact message"
|
||||
disabled={isSubmitting}
|
||||
className="w-full sm:w-auto"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Enhanced CTA Section - Matching Landing Page Style */}
|
||||
<section className="relative h-[800px] overflow-hidden">
|
||||
{/* Background Image */}
|
||||
<div className="absolute inset-0">
|
||||
<ImageWithFallback
|
||||
src="https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2940&q=80"
|
||||
alt="Professional team collaborating in modern office"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
|
||||
{/* Subtle dark overlay for overall image */}
|
||||
<div className="absolute inset-0 bg-black/30" />
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{/* Content Container */}
|
||||
<div className="relative h-full flex items-center justify-end section-margin-x">
|
||||
{/* CTA Content Block */}
|
||||
<div
|
||||
className="bg-opacity-95 backdrop-blur-sm rounded-lg p-16 max-w-2xl"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-brand-primary)'
|
||||
}}
|
||||
>
|
||||
{/* Branded Tag */}
|
||||
<BrandedTag text="Next Steps" variant="white" />
|
||||
|
||||
{/* Main Headline */}
|
||||
<h2 className="text-h2-white mb-8">
|
||||
Ready to transform your leadership?
|
||||
<span
|
||||
className="italic"
|
||||
style={{ color: 'var(--color-brand-accent)' }}
|
||||
>
|
||||
{" "}Get in touch{" "}
|
||||
</span>
|
||||
to start your development journey now.
|
||||
</h2>
|
||||
|
||||
<PrimaryCTAButton
|
||||
text="Schedule a Consultation"
|
||||
onClick={() => navigateTo('/contact?topic=consulting')}
|
||||
ariaLabel="Schedule a consultation with our leadership experts"
|
||||
className="cta-banner-yellow"
|
||||
/>
|
||||
|
||||
{/* Supporting Text */}
|
||||
<p className="text-body-white opacity-90">
|
||||
Connect with our leadership experts to discuss your organization's specific development needs.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
527
src/components/CorporateSignIn.tsx
Normal file
@@ -0,0 +1,527 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Button } from './ui/button';
|
||||
import { Input } from './ui/input';
|
||||
import { Label } from './ui/label';
|
||||
import { Card, CardContent } from './ui/card';
|
||||
import { Checkbox } from './ui/checkbox';
|
||||
import {
|
||||
Users,
|
||||
BarChart3,
|
||||
Settings,
|
||||
ArrowRight,
|
||||
Eye,
|
||||
EyeOff,
|
||||
Building2,
|
||||
BookOpen,
|
||||
Target,
|
||||
Award,
|
||||
User
|
||||
} from 'lucide-react';
|
||||
import { navigateTo } from './Router';
|
||||
|
||||
export function CorporateSignIn() {
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [rememberMe, setRememberMe] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const handleSignIn = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setIsLoading(true);
|
||||
|
||||
// Simulate authentication
|
||||
setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
// Navigate to dashboard or success page
|
||||
navigateTo('/dashboard');
|
||||
}, 1500);
|
||||
};
|
||||
|
||||
const transformationFeatures = [
|
||||
{
|
||||
icon: BookOpen,
|
||||
title: 'World-Class Content',
|
||||
description: 'Access premium leadership courses from industry experts'
|
||||
},
|
||||
{
|
||||
icon: Users,
|
||||
title: 'Global Community',
|
||||
description: 'Join 25,000+ leaders from top organizations worldwide'
|
||||
},
|
||||
{
|
||||
icon: Award,
|
||||
title: 'Recognized Credentials',
|
||||
description: 'Earn certificates valued by Fortune 500 companies'
|
||||
},
|
||||
{
|
||||
icon: Target,
|
||||
title: 'Personalized Path',
|
||||
description: 'Get customized learning recommendations based on your goals'
|
||||
}
|
||||
];
|
||||
|
||||
const corporateFeatures = [
|
||||
{
|
||||
icon: Users,
|
||||
title: 'Bulk Enrollment',
|
||||
description: 'Enroll entire teams with volume discounts up to 30%'
|
||||
},
|
||||
{
|
||||
icon: BarChart3,
|
||||
title: 'Progress Analytics',
|
||||
description: 'Real-time dashboards to track team learning outcomes'
|
||||
},
|
||||
{
|
||||
icon: Settings,
|
||||
title: 'Custom Programs',
|
||||
description: 'Tailored leadership development aligned with your goals'
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={{ backgroundColor: '#FFFFFF' }}>
|
||||
{/* Main Content */}
|
||||
<div className="section-margin-x py-16">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
|
||||
{/* Header Section */}
|
||||
<div className="text-center mb-12">
|
||||
<div className="mb-6">
|
||||
<span
|
||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-full text-sm font-medium"
|
||||
style={{
|
||||
backgroundColor: 'rgba(248, 195, 1, 0.15)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
<Building2 className="w-4 h-4" />
|
||||
Corporate Solutions
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h1
|
||||
className="mb-6"
|
||||
style={{
|
||||
fontSize: 'var(--font-h2)',
|
||||
fontWeight: 'var(--font-weight-h2)',
|
||||
lineHeight: 'var(--line-height-h2)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Welcome Back, KLC Partner
|
||||
</h1>
|
||||
|
||||
<p
|
||||
className="mb-12 max-w-2xl mx-auto"
|
||||
style={{
|
||||
fontSize: 'var(--font-body-lg)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-body-lg)'
|
||||
}}
|
||||
>
|
||||
Access your corporate dashboard to manage team enrollments, track learning
|
||||
progress, and continue building your organization's leadership capabilities.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Two Column Layout */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-start">
|
||||
|
||||
{/* Left Column - Content */}
|
||||
<div>
|
||||
{/* Leadership Transformation Section */}
|
||||
<div className="mb-12">
|
||||
<div className="mb-6">
|
||||
<span
|
||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-full text-sm font-medium"
|
||||
style={{
|
||||
backgroundColor: 'rgba(248, 195, 1, 0.15)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
⭐ Join KLC Community
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h2
|
||||
className="mb-4"
|
||||
style={{
|
||||
fontSize: 'var(--font-h3)',
|
||||
fontWeight: 'var(--font-weight-h3)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Start Your Leadership Transformation
|
||||
</h2>
|
||||
|
||||
<p
|
||||
className="mb-8"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-body)'
|
||||
}}
|
||||
>
|
||||
Join thousands of professionals who have accelerated their careers through KLC's
|
||||
proven leadership development programs. Your journey to becoming an exceptional leader starts here.
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{transformationFeatures.map((feature, index) => (
|
||||
<div key={index} className="flex items-start gap-3">
|
||||
<div
|
||||
className="w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0"
|
||||
style={{ backgroundColor: 'var(--color-primary)' }}
|
||||
>
|
||||
<feature.icon className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3
|
||||
className="font-medium mb-1"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{feature.title}
|
||||
</h3>
|
||||
<p
|
||||
style={{
|
||||
fontSize: '0.75rem',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: '1.3'
|
||||
}}
|
||||
>
|
||||
{feature.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Corporate Dashboard Features */}
|
||||
<div>
|
||||
<h2
|
||||
className="mb-8"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Corporate Dashboard Features
|
||||
</h2>
|
||||
|
||||
<div className="space-y-6">
|
||||
{corporateFeatures.map((feature, index) => (
|
||||
<div key={index} className="flex items-start gap-4">
|
||||
<div
|
||||
className="w-12 h-12 rounded-lg flex items-center justify-center flex-shrink-0"
|
||||
style={{ backgroundColor: 'var(--color-primary)' }}
|
||||
>
|
||||
<feature.icon className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3
|
||||
className="font-medium mb-2"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{feature.title}
|
||||
</h3>
|
||||
<p
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-small)'
|
||||
}}
|
||||
>
|
||||
{feature.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Existing Customer Note */}
|
||||
<div
|
||||
className="mt-8 p-4 rounded-lg border"
|
||||
style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}
|
||||
>
|
||||
<p
|
||||
className="mb-2"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontWeight: '500',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Already have a corporate account?
|
||||
</p>
|
||||
<button
|
||||
onClick={() => navigateTo('/corporate-login')}
|
||||
className="text-blue-600 hover:text-blue-700 transition-colors"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Sign in to your dashboard
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Column - Sign In Form */}
|
||||
<div>
|
||||
<Card className="shadow-lg border-0">
|
||||
<CardContent className="p-8">
|
||||
|
||||
{/* Form Header */}
|
||||
<div className="text-center mb-8">
|
||||
<h2
|
||||
className="mb-2"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Sign In to Your Dashboard
|
||||
</h2>
|
||||
<p
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Manage your corporate learning programs
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Sign In Form */}
|
||||
<form onSubmit={handleSignIn} className="space-y-6">
|
||||
|
||||
{/* Email Field */}
|
||||
<div>
|
||||
<Label
|
||||
htmlFor="email"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontWeight: '500',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Work Email *
|
||||
</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="your.work@company.com"
|
||||
required
|
||||
className="mt-2 h-12"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Password Field */}
|
||||
<div>
|
||||
<Label
|
||||
htmlFor="password"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontWeight: '500',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Password *
|
||||
</Label>
|
||||
<div className="relative mt-2">
|
||||
<Input
|
||||
id="password"
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder="Enter your password"
|
||||
required
|
||||
className="h-12 pr-12"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-500 hover:text-gray-700"
|
||||
>
|
||||
{showPassword ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Remember Me */}
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="remember"
|
||||
checked={rememberMe}
|
||||
onCheckedChange={setRememberMe}
|
||||
/>
|
||||
<Label
|
||||
htmlFor="remember"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Remember me
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
{/* Sign In Button */}
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={isLoading}
|
||||
className="w-full h-14 text-white font-medium flex items-center justify-center gap-2"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-primary)',
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
borderRadius: '10px'
|
||||
}}
|
||||
>
|
||||
{isLoading ? (
|
||||
<div className="w-5 h-5 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
||||
) : (
|
||||
<>
|
||||
Sign In
|
||||
<ArrowRight className="w-5 h-5" />
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
{/* Demo Credentials */}
|
||||
<div
|
||||
className="mt-8 p-4 rounded-lg border"
|
||||
style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}
|
||||
>
|
||||
<h4
|
||||
className="mb-2 flex items-center gap-2"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontWeight: '500',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
<div className="w-4 h-4 rounded-full bg-gray-300 flex items-center justify-center">
|
||||
<span className="text-xs text-gray-600">i</span>
|
||||
</div>
|
||||
Demo credentials:
|
||||
</h4>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-small)'
|
||||
}}
|
||||
>
|
||||
<p>Email: demo@company.com | Password: demo123</p>
|
||||
<p>MFA Demo: mfa@company.com | Password: demo123 (Code: 123456)</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sign Up Link */}
|
||||
<div className="text-center mt-8">
|
||||
<button
|
||||
onClick={() => navigateTo('/corporate-signup')}
|
||||
className="text-blue-600 hover:text-blue-700 transition-colors"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
New to KLC? Sign up your company
|
||||
</button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Need Help Section - Separate Section Below */}
|
||||
<div
|
||||
className="section-margin-x py-12"
|
||||
style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}
|
||||
>
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<h3
|
||||
className="mb-4"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Need Help with Onboarding?
|
||||
</h3>
|
||||
<p
|
||||
className="mb-6"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-body)'
|
||||
}}
|
||||
>
|
||||
Our team is ready to help you set up your corporate learning program and answer any questions about our enterprise solutions.
|
||||
</p>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="border-2 border-blue-600 text-blue-600 hover:bg-blue-600 hover:text-white"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontWeight: '500',
|
||||
padding: '0.75rem 2rem',
|
||||
borderRadius: '10px'
|
||||
}}
|
||||
>
|
||||
Contact Sales Team
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
655
src/components/CorporateSignUp.tsx
Normal file
@@ -0,0 +1,655 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Button } from './ui/button';
|
||||
import { Input } from './ui/input';
|
||||
import { Label } from './ui/label';
|
||||
import { Card, CardContent } from './ui/card';
|
||||
import { Checkbox } from './ui/checkbox';
|
||||
import {
|
||||
Users,
|
||||
BarChart3,
|
||||
Settings,
|
||||
ArrowRight,
|
||||
Eye,
|
||||
EyeOff,
|
||||
Building2,
|
||||
Globe,
|
||||
User,
|
||||
Mail,
|
||||
Lock,
|
||||
Phone,
|
||||
BookOpen,
|
||||
Target,
|
||||
Award
|
||||
} from 'lucide-react';
|
||||
import { navigateTo } from './Router';
|
||||
|
||||
export function CorporateSignUp() {
|
||||
const [formData, setFormData] = useState({
|
||||
organisationName: '',
|
||||
officialDomain: '',
|
||||
contactName: '',
|
||||
workEmail: '',
|
||||
password: '',
|
||||
phoneNumber: ''
|
||||
});
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const handleInputChange = (field: string, value: string) => {
|
||||
setFormData(prev => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
const handleSignUp = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setIsLoading(true);
|
||||
|
||||
// Simulate registration
|
||||
setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
// Navigate to dashboard or success page
|
||||
navigateTo('/dashboard');
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
const transformationFeatures = [
|
||||
{
|
||||
icon: BookOpen,
|
||||
title: 'World-Class Content',
|
||||
description: 'Access premium leadership courses from industry experts'
|
||||
},
|
||||
{
|
||||
icon: Users,
|
||||
title: 'Global Community',
|
||||
description: 'Join 25,000+ leaders from top organizations worldwide'
|
||||
},
|
||||
{
|
||||
icon: Award,
|
||||
title: 'Recognized Credentials',
|
||||
description: 'Earn certificates valued by Fortune 500 companies'
|
||||
},
|
||||
{
|
||||
icon: Target,
|
||||
title: 'Personalized Path',
|
||||
description: 'Get customized learning recommendations based on your goals'
|
||||
}
|
||||
];
|
||||
|
||||
const corporateFeatures = [
|
||||
{
|
||||
icon: Users,
|
||||
title: 'Team Management',
|
||||
description: 'Centralized dashboard to manage multiple learner accounts and progress tracking'
|
||||
},
|
||||
{
|
||||
icon: BarChart3,
|
||||
title: 'Advanced Analytics',
|
||||
description: 'Comprehensive reporting and insights on team learning outcomes and ROI'
|
||||
},
|
||||
{
|
||||
icon: Settings,
|
||||
title: 'Custom Solutions',
|
||||
description: 'Tailored programs designed specifically for your organization\'s goals'
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={{ backgroundColor: '#FFFFFF' }}>
|
||||
{/* Main Content */}
|
||||
<div className="section-margin-x py-16">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
|
||||
{/* Header Section */}
|
||||
<div className="text-center mb-12">
|
||||
<div className="mb-6">
|
||||
<span
|
||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-full text-sm font-medium"
|
||||
style={{
|
||||
backgroundColor: 'rgba(248, 195, 1, 0.15)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
<Building2 className="w-4 h-4" />
|
||||
Corporate Registration
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h1
|
||||
className="mb-6"
|
||||
style={{
|
||||
fontSize: 'var(--font-h2)',
|
||||
fontWeight: 'var(--font-weight-h2)',
|
||||
lineHeight: 'var(--line-height-h2)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Transform Your Organization's Leadership
|
||||
</h1>
|
||||
|
||||
<p
|
||||
className="mb-12 max-w-2xl mx-auto"
|
||||
style={{
|
||||
fontSize: 'var(--font-body-lg)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-body-lg)'
|
||||
}}
|
||||
>
|
||||
Join thousands of organizations worldwide who trust KLC to develop their leaders.
|
||||
Get started with enterprise-grade learning solutions and dedicated support.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Two Column Layout */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-start">
|
||||
|
||||
{/* Left Column - Content */}
|
||||
<div>
|
||||
{/* Leadership Transformation Section */}
|
||||
<div className="mb-12">
|
||||
<div className="mb-6">
|
||||
<span
|
||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-full text-sm font-medium"
|
||||
style={{
|
||||
backgroundColor: 'rgba(248, 195, 1, 0.15)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
⭐ Join KLC Community
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h2
|
||||
className="mb-4"
|
||||
style={{
|
||||
fontSize: 'var(--font-h3)',
|
||||
fontWeight: 'var(--font-weight-h3)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Start Your Leadership Transformation
|
||||
</h2>
|
||||
|
||||
<p
|
||||
className="mb-8"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-body)'
|
||||
}}
|
||||
>
|
||||
Join thousands of professionals who have accelerated their careers through KLC's
|
||||
proven leadership development programs. Your journey to becoming an exceptional leader starts here.
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{transformationFeatures.map((feature, index) => (
|
||||
<div key={index} className="flex items-start gap-3">
|
||||
<div
|
||||
className="w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0"
|
||||
style={{ backgroundColor: 'var(--color-primary)' }}
|
||||
>
|
||||
<feature.icon className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3
|
||||
className="font-medium mb-1"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{feature.title}
|
||||
</h3>
|
||||
<p
|
||||
style={{
|
||||
fontSize: '0.75rem',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: '1.3'
|
||||
}}
|
||||
>
|
||||
{feature.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* What You'll Get with Corporate Access */}
|
||||
<div>
|
||||
<h2
|
||||
className="mb-8"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
What You'll Get with Corporate Access
|
||||
</h2>
|
||||
|
||||
<div className="space-y-6">
|
||||
{corporateFeatures.map((feature, index) => (
|
||||
<div key={index} className="flex items-start gap-4">
|
||||
<div
|
||||
className="w-12 h-12 rounded-lg flex items-center justify-center flex-shrink-0"
|
||||
style={{ backgroundColor: 'var(--color-primary)' }}
|
||||
>
|
||||
<feature.icon className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3
|
||||
className="font-medium mb-2"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{feature.title}
|
||||
</h3>
|
||||
<p
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-small)'
|
||||
}}
|
||||
>
|
||||
{feature.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Existing Customer Note */}
|
||||
<div
|
||||
className="mt-8 p-4 rounded-lg border"
|
||||
style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}
|
||||
>
|
||||
<p
|
||||
className="mb-2"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontWeight: '500',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Already have a corporate account?
|
||||
</p>
|
||||
<button
|
||||
onClick={() => navigateTo('/corporate-login')}
|
||||
className="text-blue-600 hover:text-blue-700 transition-colors"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Sign in to your dashboard
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Column - Sign Up Form */}
|
||||
<div>
|
||||
<Card className="shadow-lg border-0">
|
||||
<CardContent className="p-8">
|
||||
|
||||
{/* Form Header */}
|
||||
<div className="text-center mb-8">
|
||||
<h2
|
||||
className="mb-2"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Create Your KLC HR Account
|
||||
</h2>
|
||||
<p
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Get started with KLC's corporate leadership development programs
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Sign Up Form */}
|
||||
<form onSubmit={handleSignUp} className="space-y-6">
|
||||
|
||||
{/* Organisation Name */}
|
||||
<div>
|
||||
<Label
|
||||
htmlFor="organisationName"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontWeight: '500',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Organisation Name *
|
||||
</Label>
|
||||
<div className="relative mt-2">
|
||||
<div className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500">
|
||||
<Building2 className="w-5 h-5" />
|
||||
</div>
|
||||
<Input
|
||||
id="organisationName"
|
||||
type="text"
|
||||
value={formData.organisationName}
|
||||
onChange={(e) => handleInputChange('organisationName', e.target.value)}
|
||||
placeholder="Your Company Name"
|
||||
required
|
||||
className="h-12 pl-12"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
backgroundColor: '#F3F3F5'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Official Domain */}
|
||||
<div>
|
||||
<Label
|
||||
htmlFor="officialDomain"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontWeight: '500',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Official Domain *
|
||||
</Label>
|
||||
<div className="relative mt-2">
|
||||
<div className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500">
|
||||
<Globe className="w-5 h-5" />
|
||||
</div>
|
||||
<Input
|
||||
id="officialDomain"
|
||||
type="text"
|
||||
value={formData.officialDomain}
|
||||
onChange={(e) => handleInputChange('officialDomain', e.target.value)}
|
||||
placeholder="company.com"
|
||||
required
|
||||
className="h-12 pl-12"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
backgroundColor: '#F3F3F5'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Contact Name */}
|
||||
<div>
|
||||
<Label
|
||||
htmlFor="contactName"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontWeight: '500',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Contact Name *
|
||||
</Label>
|
||||
<div className="relative mt-2">
|
||||
<div className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500">
|
||||
<User className="w-5 h-5" />
|
||||
</div>
|
||||
<Input
|
||||
id="contactName"
|
||||
type="text"
|
||||
value={formData.contactName}
|
||||
onChange={(e) => handleInputChange('contactName', e.target.value)}
|
||||
placeholder="Your full name"
|
||||
required
|
||||
className="h-12 pl-12"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
backgroundColor: '#F3F3F5'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Work Email */}
|
||||
<div>
|
||||
<Label
|
||||
htmlFor="workEmail"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontWeight: '500',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Work Email *
|
||||
</Label>
|
||||
<div className="relative mt-2">
|
||||
<div className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500">
|
||||
<Mail className="w-5 h-5" />
|
||||
</div>
|
||||
<Input
|
||||
id="workEmail"
|
||||
type="email"
|
||||
value={formData.workEmail}
|
||||
onChange={(e) => handleInputChange('workEmail', e.target.value)}
|
||||
placeholder="you@company.com"
|
||||
required
|
||||
className="h-12 pl-12"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
backgroundColor: '#F3F3F5'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Password */}
|
||||
<div>
|
||||
<Label
|
||||
htmlFor="password"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontWeight: '500',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Password *
|
||||
</Label>
|
||||
<div className="relative mt-2">
|
||||
<div className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500">
|
||||
<Lock className="w-5 h-5" />
|
||||
</div>
|
||||
<Input
|
||||
id="password"
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
value={formData.password}
|
||||
onChange={(e) => handleInputChange('password', e.target.value)}
|
||||
placeholder="Create a strong password"
|
||||
required
|
||||
className="h-12 pl-12 pr-12"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
backgroundColor: '#F3F3F5'
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-500 hover:text-gray-700"
|
||||
>
|
||||
{showPassword ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Phone Number (Optional) */}
|
||||
<div>
|
||||
<Label
|
||||
htmlFor="phoneNumber"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontWeight: '500',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Phone Number (Optional)
|
||||
</Label>
|
||||
<div className="relative mt-2">
|
||||
<div className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500">
|
||||
<Phone className="w-5 h-5" />
|
||||
</div>
|
||||
<Input
|
||||
id="phoneNumber"
|
||||
type="tel"
|
||||
value={formData.phoneNumber}
|
||||
onChange={(e) => handleInputChange('phoneNumber', e.target.value)}
|
||||
placeholder="+1 234 567 8900"
|
||||
className="h-12 pl-12"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
backgroundColor: '#F3F3F5'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Create Account Button */}
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={isLoading}
|
||||
className="w-full h-14 text-white font-medium flex items-center justify-center gap-2"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-primary)',
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
borderRadius: '10px'
|
||||
}}
|
||||
>
|
||||
{isLoading ? (
|
||||
<div className="w-5 h-5 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
||||
) : (
|
||||
<>
|
||||
Create Account
|
||||
<ArrowRight className="w-5 h-5" />
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
{/* Terms Text */}
|
||||
<div className="text-center mt-6">
|
||||
<p
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-small)'
|
||||
}}
|
||||
>
|
||||
By creating an account, you agree to our{' '}
|
||||
<span className="text-blue-600 hover:text-blue-700 cursor-pointer">
|
||||
Corporate Terms
|
||||
</span>{' '}
|
||||
and{' '}
|
||||
<span className="text-blue-600 hover:text-blue-700 cursor-pointer">
|
||||
Privacy Policy
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Sign In Link */}
|
||||
<div className="text-center mt-6">
|
||||
<button
|
||||
onClick={() => navigateTo('/corporate-login')}
|
||||
className="text-blue-600 hover:text-blue-700 transition-colors"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Already have an account? Sign in instead
|
||||
</button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Need Help Section - Separate Section Below */}
|
||||
<div
|
||||
className="section-margin-x py-12"
|
||||
style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}
|
||||
>
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<h3
|
||||
className="mb-4"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Need Help Getting Started?
|
||||
</h3>
|
||||
<p
|
||||
className="mb-6"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-body)'
|
||||
}}
|
||||
>
|
||||
Our team is ready to help you set up your corporate learning program and answer any questions about our enterprise solutions.
|
||||
</p>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="border-2 border-blue-600 text-blue-600 hover:bg-blue-600 hover:text-white"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontWeight: '500',
|
||||
padding: '0.75rem 2rem',
|
||||
borderRadius: '10px'
|
||||
}}
|
||||
>
|
||||
Contact Sales Team
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
280
src/components/CourseCard.tsx
Normal file
@@ -0,0 +1,280 @@
|
||||
import React from 'react';
|
||||
import { Button } from './ui/button';
|
||||
import { Badge } from './ui/badge';
|
||||
import {
|
||||
Users,
|
||||
Clock,
|
||||
Star,
|
||||
ArrowRight,
|
||||
ShoppingCart
|
||||
} from 'lucide-react';
|
||||
import { motion } from 'motion/react';
|
||||
import { navigateTo } from './Router';
|
||||
import { ImageWithFallback } from './figma/ImageWithFallback';
|
||||
import type { CartItem } from './CartPopup';
|
||||
|
||||
// Course type interface
|
||||
export interface Course {
|
||||
id: string;
|
||||
title: string;
|
||||
thumbnail: string;
|
||||
duration: string;
|
||||
level: string;
|
||||
format: string;
|
||||
rating: number;
|
||||
participants: string;
|
||||
category: string;
|
||||
description: string;
|
||||
price: string;
|
||||
originalPrice?: string; // For showing discounted prices
|
||||
}
|
||||
|
||||
interface CourseCardProps {
|
||||
course: Course;
|
||||
onClick?: () => void;
|
||||
className?: string;
|
||||
onAddToCart?: (item: CartItem) => void;
|
||||
}
|
||||
|
||||
export function CourseCard({ course, onClick, className, onAddToCart }: CourseCardProps) {
|
||||
const handleClick = () => {
|
||||
if (onClick) {
|
||||
onClick();
|
||||
} else {
|
||||
navigateTo(`/course/${course.id}`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddToCart = (e: React.MouseEvent) => {
|
||||
e.stopPropagation(); // Prevent card click when clicking Add to Cart
|
||||
|
||||
if (onAddToCart) {
|
||||
const cartItem: CartItem = {
|
||||
id: course.id,
|
||||
title: course.title,
|
||||
thumbnail: course.thumbnail,
|
||||
price: course.price,
|
||||
originalPrice: course.originalPrice,
|
||||
category: course.category,
|
||||
level: course.level
|
||||
};
|
||||
onAddToCart(cartItem);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
className={`flex-shrink-0 w-96 h-[560px] bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden hover:shadow-xl transition-all duration-300 cursor-pointer group flex flex-col ${className || ''}`}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{/* Course Image with Category Badge */}
|
||||
<div className="relative aspect-[16/9] overflow-hidden flex-shrink-0">
|
||||
<ImageWithFallback
|
||||
src={course.thumbnail}
|
||||
alt={course.title}
|
||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
||||
/>
|
||||
<div className="absolute top-4 left-4">
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="px-3 py-1 font-medium"
|
||||
style={{
|
||||
backgroundColor: 'rgba(248, 195, 1, 0.95)',
|
||||
color: 'var(--color-black)',
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
backdropFilter: 'blur(4px)'
|
||||
}}
|
||||
>
|
||||
{course.category}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="absolute top-4 right-4">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="px-3 py-1 font-medium bg-white/90 backdrop-blur-sm"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
borderColor: 'var(--color-primary)',
|
||||
color: 'var(--color-primary)'
|
||||
}}
|
||||
>
|
||||
{course.level}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Card Content - Reduced horizontal padding */}
|
||||
<div className="p-5 flex flex-col flex-1">
|
||||
{/* Course Title */}
|
||||
<h3
|
||||
className="mb-3 group-hover:text-blue-600 transition-colors leading-snug"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
lineHeight: '1.3',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{course.title}
|
||||
</h3>
|
||||
|
||||
{/* Course Description - Limited to 2 lines with ellipsis */}
|
||||
<p
|
||||
className="mb-5 line-clamp-2 leading-relaxed"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
lineHeight: '1.5',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
display: '-webkit-box',
|
||||
WebkitLineClamp: 2,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis'
|
||||
}}
|
||||
>
|
||||
{course.description}
|
||||
</p>
|
||||
|
||||
{/* Course Meta Information - Reduced bottom margin */}
|
||||
<div className="flex items-center justify-between mb-5 pt-3 border-t border-gray-100">
|
||||
<div className="flex items-center gap-5">
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock className="w-4 h-4 text-gray-400" />
|
||||
<span style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontWeight: '500'
|
||||
}}>
|
||||
{course.duration}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Users className="w-4 h-4 text-gray-400" />
|
||||
<span style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontWeight: '500'
|
||||
}}>
|
||||
{course.participants}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1">
|
||||
<Star className="w-4 h-4 fill-current text-yellow-400" />
|
||||
<span
|
||||
className="font-semibold"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-black)'
|
||||
}}
|
||||
>
|
||||
{course.rating}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Pricing Section - Reduced bottom margin */}
|
||||
<div className="mb-5">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-baseline gap-3">
|
||||
<span
|
||||
className="font-bold"
|
||||
style={{
|
||||
fontSize: '1.75rem',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: '#04045B'
|
||||
}}
|
||||
>
|
||||
{course.price}
|
||||
</span>
|
||||
{course.originalPrice && (
|
||||
<span
|
||||
className="line-through"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-gray-muted)'
|
||||
}}
|
||||
>
|
||||
{course.originalPrice}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{course.originalPrice && (
|
||||
<div className="text-right">
|
||||
<span
|
||||
className="text-green-600 font-semibold text-sm"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Save {Math.round(((parseFloat(course.originalPrice.replace('$', '')) - parseFloat(course.price.replace('$', ''))) / parseFloat(course.originalPrice.replace('$', ''))) * 100)}%
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Action Buttons - Horizontal Layout with reduced gap */}
|
||||
<div className="flex flex-row gap-2 mt-auto">
|
||||
{/* Add to Cart Button - Outline Blue */}
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleAddToCart}
|
||||
className="flex-1 flex items-center justify-center gap-2 h-11 rounded-lg transition-all duration-200 font-medium"
|
||||
style={{
|
||||
borderColor: '#04045B',
|
||||
color: '#04045B',
|
||||
backgroundColor: 'transparent',
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontWeight: '500',
|
||||
borderWidth: '2px'
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.backgroundColor = '#04045B';
|
||||
e.currentTarget.style.color = 'white';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.backgroundColor = 'transparent';
|
||||
e.currentTarget.style.color = '#04045B';
|
||||
}}
|
||||
>
|
||||
<ShoppingCart className="w-4 h-4" />
|
||||
Add to Cart
|
||||
</Button>
|
||||
|
||||
{/* Learn More Button - Solid Blue */}
|
||||
<Button
|
||||
className="flex-1 flex items-center justify-center gap-2 h-11 rounded-lg transition-all duration-200 font-medium"
|
||||
style={{
|
||||
backgroundColor: '#04045B',
|
||||
color: 'white',
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontWeight: '500',
|
||||
border: 'none'
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.backgroundColor = '#030359';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.backgroundColor = '#04045B';
|
||||
}}
|
||||
>
|
||||
Learn More
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
235
src/components/ExpertiseSections.tsx
Normal file
@@ -0,0 +1,235 @@
|
||||
import React from 'react';
|
||||
import { Badge } from './ui/badge';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from './ui/card';
|
||||
import { Button } from './ui/button';
|
||||
import { ImageWithFallback } from './figma/ImageWithFallback';
|
||||
import { navigateTo } from './Router';
|
||||
import { CheckCircle, ArrowRight, Clock, Users, Star } from 'lucide-react';
|
||||
|
||||
// Service Card Component
|
||||
export function ServiceCard({ service }) {
|
||||
const IconComponent = service.icon;
|
||||
|
||||
return (
|
||||
<Card className="border hover:shadow-lg transition-all duration-300 group h-full" style={{ backgroundColor: 'var(--color-bg-white)', borderColor: 'var(--color-border)' }}>
|
||||
<CardHeader className="pb-4">
|
||||
<div className="w-16 h-16 rounded-xl flex items-center justify-center mb-4 group-hover:scale-110 transition-transform" style={{ backgroundColor: 'var(--color-primary)' }}>
|
||||
<IconComponent className="w-8 h-8 text-white" />
|
||||
</div>
|
||||
<CardTitle className="text-h4 mb-3">{service.title}</CardTitle>
|
||||
<p className="text-body text-muted leading-relaxed">{service.description}</p>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="pt-0">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h4 className="text-subhead mb-3">Key Features:</h4>
|
||||
<div className="space-y-2">
|
||||
{service.features.map((feature, index) => (
|
||||
<div key={index} className="flex items-start gap-2">
|
||||
<CheckCircle className="w-4 h-4 text-green-600 mt-0.5 flex-shrink-0" />
|
||||
<span className="text-small text-muted">{feature}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="pt-4 border-t" style={{ borderColor: 'var(--color-border)' }}>
|
||||
<p className="text-small text-primary">
|
||||
<strong>Outcome:</strong> {service.outcome}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
// Theme Card Component
|
||||
export function ThemeCard({ theme }) {
|
||||
const IconComponent = theme.icon;
|
||||
|
||||
return (
|
||||
<Card className="border hover:shadow-md transition-all duration-300 text-center group" style={{ backgroundColor: 'var(--color-bg-white)', borderColor: 'var(--color-border)' }}>
|
||||
<CardContent className="p-6">
|
||||
<div className={`w-16 h-16 rounded-xl bg-gradient-to-r ${theme.color} flex items-center justify-center mx-auto mb-4 group-hover:scale-110 transition-transform`}>
|
||||
<IconComponent className="w-8 h-8 text-white" />
|
||||
</div>
|
||||
<h3 className="text-subhead mb-2">{theme.title}</h3>
|
||||
<p className="text-small text-muted">{theme.description}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
// Program Card Component
|
||||
export function ProgramCard({ program }) {
|
||||
return (
|
||||
<Card className="border hover:shadow-lg transition-all duration-300 h-full" style={{ backgroundColor: 'var(--color-bg-white)', borderColor: 'var(--color-border)' }}>
|
||||
<CardHeader className="pb-4">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<Badge className="text-small px-3 py-1" style={{ backgroundColor: 'var(--color-primary)', color: 'white' }}>
|
||||
{program.level}
|
||||
</Badge>
|
||||
<Badge variant="outline" className="text-small px-3 py-1">
|
||||
{program.format}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<CardTitle className="text-h4 mb-3">{program.title}</CardTitle>
|
||||
<p className="text-body text-muted leading-relaxed">{program.description}</p>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="pt-0">
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-2 gap-4 text-small">
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock className="w-4 h-4 text-primary" />
|
||||
<span>{program.duration}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Users className="w-4 h-4 text-primary" />
|
||||
<span>{program.participants}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 className="text-subhead mb-3">Program Highlights:</h4>
|
||||
<div className="space-y-2">
|
||||
{program.highlights.map((highlight, index) => (
|
||||
<div key={index} className="flex items-start gap-2">
|
||||
<CheckCircle className="w-4 h-4 text-green-600 mt-0.5 flex-shrink-0" />
|
||||
<span className="text-small text-muted">{highlight}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
// Enhanced Feature Card Component for Platform Features
|
||||
export function FeatureCard({ feature }) {
|
||||
const IconComponent = feature.icon;
|
||||
|
||||
return (
|
||||
<Card className="border border-gray-200 hover:border-primary/30 transition-all duration-300 shadow-sm hover:shadow-lg group h-full" style={{ backgroundColor: 'var(--color-bg-white)' }}>
|
||||
<CardContent className="p-8">
|
||||
{/* Feature Header */}
|
||||
<div className="flex items-start gap-4 mb-6">
|
||||
<div
|
||||
className="w-14 h-14 rounded-xl flex items-center justify-center group-hover:scale-110 transition-transform duration-300 flex-shrink-0"
|
||||
style={{ backgroundColor: 'rgba(4, 4, 91, 0.08)' }}
|
||||
>
|
||||
<IconComponent className="w-7 h-7 text-primary" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-h4 mb-3 text-black">{feature.title}</h3>
|
||||
<p className="text-body text-muted leading-relaxed">{feature.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Feature List */}
|
||||
<div className="space-y-3">
|
||||
{feature.features.map((item, itemIndex) => (
|
||||
<div key={itemIndex} className="flex items-center gap-3">
|
||||
<div
|
||||
className="w-5 h-5 rounded-full flex items-center justify-center flex-shrink-0"
|
||||
style={{ backgroundColor: 'var(--color-accent)' }}
|
||||
>
|
||||
<CheckCircle className="w-3 h-3 text-white" />
|
||||
</div>
|
||||
<span className="text-body text-muted">{item}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
// Case Study Card Component
|
||||
export function CaseStudyCard({ caseStudy }) {
|
||||
return (
|
||||
<Card className="border hover:shadow-lg transition-all duration-300 overflow-hidden group" style={{ backgroundColor: 'var(--color-bg-white)', borderColor: 'var(--color-border)' }}>
|
||||
<div className="relative h-48">
|
||||
<ImageWithFallback
|
||||
src={caseStudy.image}
|
||||
alt={caseStudy.title}
|
||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
||||
/>
|
||||
<div className="absolute top-4 left-4">
|
||||
<Badge className="text-small px-3 py-1" style={{ backgroundColor: 'var(--color-accent)', color: 'var(--color-black)' }}>
|
||||
{caseStudy.industry}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CardContent className="p-6">
|
||||
<h3 className="text-h4 mb-3">{caseStudy.title}</h3>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h4 className="text-subhead mb-2">Challenge:</h4>
|
||||
<p className="text-small text-muted">{caseStudy.challenge}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 className="text-subhead mb-2">Solution:</h4>
|
||||
<p className="text-small text-muted">{caseStudy.solution}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 className="text-subhead mb-2">Results:</h4>
|
||||
<div className="space-y-2">
|
||||
{caseStudy.results.map((result, index) => (
|
||||
<div key={index} className="flex items-start gap-2">
|
||||
<Star className="w-4 h-4 text-yellow-500 mt-0.5 flex-shrink-0" />
|
||||
<span className="text-small text-muted">{result}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 pt-2 text-small">
|
||||
<span className="flex items-center gap-1">
|
||||
<Clock className="w-4 h-4 text-primary" />
|
||||
{caseStudy.duration}
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Users className="w-4 h-4 text-primary" />
|
||||
{caseStudy.participants} participants
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
// Sticky Tabs Component
|
||||
export function StickyTabs({ pillars, activeTab, onTabClick, isSticky }) {
|
||||
return (
|
||||
<div className={`bg-white border-b transition-all duration-300 ${isSticky ? 'fixed top-20 left-0 right-0 z-40 shadow-md' : 'relative'}`} style={{ borderColor: 'var(--color-border)' }}>
|
||||
<nav className="section-margin-x mx-auto" aria-label="Section navigation">
|
||||
{/* Mobile: compact dropdown */}
|
||||
<div className="md:hidden py-3">
|
||||
<label htmlFor="section-select" className="sr-only">Select section</label>
|
||||
<select
|
||||
id="section-select"
|
||||
className="w-full border rounded-lg p-3 bg-white text-body focus-ring"
|
||||
value={activeTab}
|
||||
onChange={(e) => onTabClick(e.target.value)}
|
||||
aria-controls="page-sections"
|
||||
>
|
||||
{pillars.map((pillar) => (
|
||||
<option key={pillar.id} value={pillar.id}>{pillar.title}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
327
src/components/Footer.tsx
Normal file
@@ -0,0 +1,327 @@
|
||||
import { Facebook, X, Linkedin, Instagram, User } from "lucide-react";
|
||||
import { ImageWithFallback } from './figma/ImageWithFallback';
|
||||
import { navigateTo } from './Router';
|
||||
import klcLogo from 'figma:asset/e98caa8afd8d11246bbff1dde75bbaae6f6a0894.png';
|
||||
|
||||
export function Footer() {
|
||||
return (
|
||||
<footer
|
||||
className="relative py-16"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-brand-primary)',
|
||||
color: 'white'
|
||||
}}
|
||||
>
|
||||
{/* Main Footer Content */}
|
||||
<div className="max-w-7xl mx-auto section-margin-x">
|
||||
<div className="flex flex-col lg:flex-row lg:justify-between lg:items-start gap-12 lg:gap-8">
|
||||
|
||||
{/* Logo and Description Column */}
|
||||
<div className="lg:flex-shrink-0 lg:w-80">
|
||||
{/* Logo */}
|
||||
<div className="flex items-center mb-6">
|
||||
<img
|
||||
src={klcLogo}
|
||||
alt="Kautilya Leadership Centre"
|
||||
className="h-16 w-auto object-contain"
|
||||
style={{ filter: 'brightness(0) invert(1)' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Company Description */}
|
||||
<p
|
||||
className="text-sm leading-relaxed mb-8"
|
||||
style={{ color: 'rgba(255, 255, 255, 0.85)' }}
|
||||
>
|
||||
Empowering leaders with transformative development opportunities for achieving excellence, growth, and lasting impact.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
{/* Social Media Icons */}
|
||||
<div className="flex space-x-4">
|
||||
{[
|
||||
{ icon: X, label: 'X' },
|
||||
{ icon: Facebook, label: 'Facebook' },
|
||||
{ icon: Linkedin, label: 'LinkedIn' },
|
||||
{ icon: Instagram, label: 'Instagram' }
|
||||
].map(({ icon: Icon, label }) => (
|
||||
<a
|
||||
key={label}
|
||||
href="#"
|
||||
className="w-8 h-8 rounded-full flex items-center justify-center transition-all duration-300 group"
|
||||
style={{ backgroundColor: 'rgba(192, 192, 192, 0.1)' }}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.backgroundColor = 'var(--color-brand-accent)';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.backgroundColor = 'rgba(192, 192, 192, 0.1)';
|
||||
}}
|
||||
aria-label={label}
|
||||
>
|
||||
<Icon
|
||||
className="w-4 h-4 transition-colors duration-300"
|
||||
style={{ color: 'rgba(255, 255, 255, 0.85)' }}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.color = 'var(--color-brand-black)';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.color = 'rgba(255, 255, 255, 0.85)';
|
||||
}}
|
||||
/>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Services */}
|
||||
<div className="lg:w-auto lg:flex-shrink-0">
|
||||
<h4
|
||||
className="font-semibold mb-6 text-sm"
|
||||
style={{ color: 'white' }}
|
||||
>
|
||||
Services
|
||||
</h4>
|
||||
<div className="space-y-3">
|
||||
{[
|
||||
{ text: 'Leadership Development', href: '/services/leadership-development', badge: null },
|
||||
{ text: 'Executive Coaching', href: '/services/executive-coaching', badge: null },
|
||||
{ text: 'Management Development', href: '/services/management-development', badge: null },
|
||||
{ text: 'Consulting Services', href: '/services/consulting', badge: null },
|
||||
{ text: 'Culture & Competence', href: '/services/culture-competence', badge: null },
|
||||
{ text: 'Learning Facility', href: '/services/learning-facility', badge: 'Popular' }
|
||||
].map((item) => (
|
||||
<div key={item.text} className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={() => navigateTo(item.href)}
|
||||
className="text-sm transition-colors duration-300 cursor-pointer text-left"
|
||||
style={{ color: 'rgba(255, 255, 255, 0.85)' }}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.color = 'white';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.color = 'rgba(255, 255, 255, 0.85)';
|
||||
}}
|
||||
>
|
||||
{item.text}
|
||||
</button>
|
||||
{item.badge && (
|
||||
<span
|
||||
className="px-2 py-0.5 text-xs rounded"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-brand-accent)',
|
||||
color: 'var(--color-brand-black)'
|
||||
}}
|
||||
>
|
||||
{item.badge}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Company */}
|
||||
<div className="lg:w-auto lg:flex-shrink-0">
|
||||
<h4
|
||||
className="font-semibold mb-6 text-sm"
|
||||
style={{ color: 'white' }}
|
||||
>
|
||||
About Us
|
||||
</h4>
|
||||
<div className="space-y-3">
|
||||
{[
|
||||
{ text: 'Our vision', href: '/about/our-vision' },
|
||||
{ text: 'Our Impact', href: '/about/our-impact' },
|
||||
{ text: 'Our Expertise', href: '/about/our-expertise' }
|
||||
].map((link) => (
|
||||
<button
|
||||
key={link.text}
|
||||
onClick={() => navigateTo(link.href)}
|
||||
className="block text-sm transition-colors duration-300 cursor-pointer text-left"
|
||||
style={{ color: 'rgba(255, 255, 255, 0.85)' }}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.color = 'white';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.color = 'rgba(255, 255, 255, 0.85)';
|
||||
}}
|
||||
>
|
||||
{link.text}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Explore */}
|
||||
<div className="lg:w-auto lg:flex-shrink-0">
|
||||
<h4
|
||||
className="font-semibold mb-6 text-sm"
|
||||
style={{ color: 'white' }}
|
||||
>
|
||||
Explore
|
||||
</h4>
|
||||
<div className="space-y-3">
|
||||
{[
|
||||
{ text: 'Learning facility', href: '/services/learning-facility' },
|
||||
{ text: 'Online Courses', href: '/learning-online' },
|
||||
{ text: 'Contact Us', href: '/contact' }
|
||||
].map((link) => (
|
||||
<button
|
||||
key={link.text}
|
||||
onClick={() => navigateTo(link.href)}
|
||||
className="block text-sm transition-colors duration-300 cursor-pointer text-left"
|
||||
style={{ color: 'rgba(255, 255, 255, 0.85)' }}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.color = 'white';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.color = 'rgba(255, 255, 255, 0.85)';
|
||||
}}
|
||||
>
|
||||
{link.text}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Resources */}
|
||||
<div className="lg:w-auto lg:flex-shrink-0">
|
||||
<h4
|
||||
className="font-semibold mb-6 text-sm"
|
||||
style={{ color: 'white' }}
|
||||
>
|
||||
Resources
|
||||
</h4>
|
||||
<div className="space-y-3">
|
||||
{[
|
||||
{ text: 'Blog article', href: '/learning/articles' },
|
||||
{ text: 'Webcast', href: '/learning/webcast' }
|
||||
].map((link) => (
|
||||
<button
|
||||
key={link.text}
|
||||
onClick={() => navigateTo(link.href)}
|
||||
className="block text-sm transition-colors duration-300 cursor-pointer text-left"
|
||||
style={{ color: 'rgba(255, 255, 255, 0.85)' }}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.color = 'white';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.color = 'rgba(255, 255, 255, 0.85)';
|
||||
}}
|
||||
>
|
||||
{link.text}
|
||||
</button>
|
||||
))}
|
||||
|
||||
{/* Back To Top Button - Kept in Resources section */}
|
||||
<div className="pt-12">
|
||||
<button
|
||||
onClick={() => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}}
|
||||
className="flex items-center gap-2 text-sm transition-all duration-300 group"
|
||||
style={{ color: 'rgba(255, 255, 255, 0.85)' }}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.color = 'var(--color-brand-accent)';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.color = 'rgba(255, 255, 255, 0.85)';
|
||||
}}
|
||||
aria-label="Back to top"
|
||||
>
|
||||
<svg
|
||||
className="w-4 h-4 transition-transform duration-300 group-hover:-translate-y-1"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M5 10l7-7m0 0l7 7m-7-7v18"
|
||||
/>
|
||||
</svg>
|
||||
<span className="transition-colors duration-300">
|
||||
Back To Top
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{/* Divider */}
|
||||
<div
|
||||
className="w-full h-px my-12"
|
||||
style={{ backgroundColor: 'rgba(192, 192, 192, 0.2)' }}
|
||||
/>
|
||||
|
||||
{/* Bottom Footer */}
|
||||
<div className="flex flex-col lg:flex-row justify-between items-start lg:items-center space-y-6 lg:space-y-0">
|
||||
|
||||
{/* Copyright and Legal Links */}
|
||||
<div className="flex flex-col lg:flex-row lg:items-center space-y-4 lg:space-y-0 lg:space-x-8">
|
||||
<p
|
||||
className="text-sm"
|
||||
style={{ color: 'rgba(255, 255, 255, 0.85)' }}
|
||||
>
|
||||
Copyright 2025 Kautilya Leadership Centre
|
||||
</p>
|
||||
<div className="flex space-x-6">
|
||||
<a
|
||||
href="#"
|
||||
className="text-sm transition-colors duration-300"
|
||||
style={{ color: 'rgba(255, 255, 255, 0.85)' }}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.color = 'var(--color-brand-accent)';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.color = 'rgba(255, 255, 255, 0.85)';
|
||||
}}
|
||||
>
|
||||
Privacy and Cookie Policy
|
||||
</a>
|
||||
<button
|
||||
onClick={() => navigateTo('/terms')}
|
||||
className="text-sm transition-colors duration-300 cursor-pointer text-left"
|
||||
style={{ color: 'rgba(255, 255, 255, 0.85)' }}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.color = 'var(--color-brand-accent)';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.color = 'rgba(255, 255, 255, 0.85)';
|
||||
}}
|
||||
>
|
||||
Terms of Service
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Contact Info */}
|
||||
<div className="text-right">
|
||||
<p
|
||||
className="text-sm font-medium"
|
||||
style={{ color: 'rgba(255, 255, 255, 0.85)' }}
|
||||
>
|
||||
Follow Our Leadership Journey
|
||||
</p>
|
||||
<p
|
||||
className="text-xs"
|
||||
style={{ color: 'rgba(255, 255, 255, 0.7)' }}
|
||||
>
|
||||
Discover leadership excellence with KLC
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
304
src/components/HeroSection.tsx
Normal file
@@ -0,0 +1,304 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
import { navigateTo } from './Router';
|
||||
import svgPaths from "../imports/svg-i1joeov37f";
|
||||
|
||||
interface SlideData {
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
backgroundImage: string;
|
||||
shortTitle: string;
|
||||
}
|
||||
|
||||
export default function HeroSection() {
|
||||
const [currentSlide, setCurrentSlide] = useState(0);
|
||||
const [isAutoPlaying, setIsAutoPlaying] = useState(true);
|
||||
const [progressValues, setProgressValues] = useState([0, 0, 0]);
|
||||
|
||||
const slides: SlideData[] = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Empowering Future-Ready\nLeaders",
|
||||
description: "Build confidence, agility, and clarity for today's complex challenges.",
|
||||
backgroundImage: "https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1920&q=80",
|
||||
shortTitle: "Leadership Is Learned. We Teach It Right."
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Turn Managers into\nImpactful Leaders",
|
||||
description: "Transform your management team into visionary leaders who inspire teams, drive innovation, and achieve exceptional business outcomes.",
|
||||
backgroundImage: "https://images.unsplash.com/photo-1600880292203-757bb62b4baf?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1920&q=80",
|
||||
shortTitle: "Turn Managers into Impactful Leaders"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Struggling with\nManagerial Gaps?",
|
||||
description: "Bridge the leadership gap in your organization with our proven methodologies that develop confident, capable leaders at every level.",
|
||||
backgroundImage: "https://images.unsplash.com/photo-1542744173-8e7e53415bb0?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1920&q=80",
|
||||
shortTitle: "Struggling with Managerial Gaps?"
|
||||
}
|
||||
];
|
||||
|
||||
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];
|
||||
newProgress[currentSlide] = Math.min(newProgress[currentSlide] + (100 / (slideDuration / 100)), 100);
|
||||
// Reset progress when slide changes
|
||||
if (newProgress[currentSlide] >= 100) {
|
||||
newProgress[currentSlide] = 0;
|
||||
// Reset other slides
|
||||
newProgress.forEach((_, index) => {
|
||||
if (index !== currentSlide) {
|
||||
newProgress[index] = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
return newProgress;
|
||||
});
|
||||
}, 100);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [currentSlide, isAutoPlaying]);
|
||||
|
||||
// Reset progress when manually changing slides
|
||||
useEffect(() => {
|
||||
setProgressValues(prev => {
|
||||
const newProgress = [0, 0, 0];
|
||||
newProgress[currentSlide] = 0;
|
||||
return newProgress;
|
||||
});
|
||||
}, [currentSlide]);
|
||||
|
||||
const goToSlide = useCallback((slideIndex: number) => {
|
||||
if (slideIndex !== currentSlide) {
|
||||
setCurrentSlide(slideIndex);
|
||||
setIsAutoPlaying(false);
|
||||
// Resume auto-play after manual interaction
|
||||
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
|
||||
className="hero-section"
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
{/* Background Slides */}
|
||||
{slides.map((slide, index) => (
|
||||
<div
|
||||
key={slide.id}
|
||||
className={`hero-slide ${index === currentSlide ? 'active' : ''}`}
|
||||
style={{
|
||||
backgroundImage: `url('${slide.backgroundImage}')`
|
||||
}}
|
||||
>
|
||||
<div className="hero-overlay" />
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Hero Content */}
|
||||
<div className="hero-content">
|
||||
<div className="hero-text-section">
|
||||
{/* Title */}
|
||||
<h1 className="hero-title" style={{ whiteSpace: 'pre-line' }}>
|
||||
{slides[currentSlide].title}
|
||||
</h1>
|
||||
{/* Description */}
|
||||
<p className="hero-description">
|
||||
{slides[currentSlide].description}
|
||||
</p>
|
||||
|
||||
{/* Build Your Leadership Pipeline 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('/contact?topic=leadership-pipeline');
|
||||
}}
|
||||
aria-label="Build Your Leadership Pipeline"
|
||||
>
|
||||
{/* 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'
|
||||
}}
|
||||
>
|
||||
Build Your Leadership Pipeline
|
||||
</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'
|
||||
}}
|
||||
>
|
||||
Build Your Leadership Pipeline
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom Navigation */}
|
||||
<div className="hero-navigation">
|
||||
{/* Progress Section */}
|
||||
<div className="hero-progress-container">
|
||||
{slides.map((slide, index) => (
|
||||
<div
|
||||
key={slide.id}
|
||||
className="hero-progress-item"
|
||||
onClick={() => goToSlide(index)}
|
||||
>
|
||||
{/* 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 Text */}
|
||||
<div className={`hero-progress-text ${index === currentSlide ? 'active' : ''}`}>
|
||||
{slide.shortTitle}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Navigation Arrows */}
|
||||
<div className="hero-controls">
|
||||
<button
|
||||
className="hero-nav-button"
|
||||
onClick={prevSlide}
|
||||
aria-label="Previous slide"
|
||||
>
|
||||
<ChevronLeft className="w-5 h-5" />
|
||||
</button>
|
||||
<button
|
||||
className="hero-nav-button"
|
||||
onClick={nextSlide}
|
||||
aria-label="Next slide"
|
||||
>
|
||||
<ChevronRight className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
235
src/components/InsightsSection.tsx
Normal file
@@ -0,0 +1,235 @@
|
||||
import { ArrowUpRight } from "lucide-react";
|
||||
import { ImageWithFallback } from "./figma/ImageWithFallback";
|
||||
import { BrandedTag } from "./about/BrandedTag";
|
||||
import { PrimaryCTAButton } from "./PrimaryCTAButton";
|
||||
import { navigateTo } from "./Router";
|
||||
|
||||
interface InsightCard {
|
||||
id: number;
|
||||
title: string;
|
||||
description?: string;
|
||||
date: string;
|
||||
tags: string[];
|
||||
image: string;
|
||||
}
|
||||
|
||||
const insightCards: InsightCard[] = [
|
||||
{
|
||||
id: 1,
|
||||
title: "The Evolving DNA of a Modern Leader",
|
||||
description: "Explore how leadership traits are shifting in the age of AI, remote work, and hyper-connectivity.",
|
||||
date: "25-05-2025",
|
||||
tags: ["Adaptability", "Strategic foresight"],
|
||||
image: "https://images.unsplash.com/photo-1522202176988-66273c2fd55f?auto=format&fit=crop&w=800&q=80"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Strategic Leadership in Uncertain Times",
|
||||
date: "25-05-2025",
|
||||
tags: ["Strategy", "Decision Making"],
|
||||
image: "https://images.unsplash.com/photo-1553877522-43269d4ea984?auto=format&fit=crop&w=800&q=80"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Building High-Performance Teams",
|
||||
date: "25-05-2025",
|
||||
tags: ["Team Building", "Performance"],
|
||||
image: "https://images.unsplash.com/photo-1559136555-9303baea8ebd?auto=format&fit=crop&w=800&q=80"
|
||||
}
|
||||
];
|
||||
|
||||
// Insight Tag Component
|
||||
function InsightTag({ text }: { text: string }) {
|
||||
return (
|
||||
<span
|
||||
className="px-3 py-1 text-sm font-medium rounded-full"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-brand-accent)',
|
||||
color: 'var(--color-brand-black)'
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
// Arrow Icon Component
|
||||
function ArrowIcon() {
|
||||
return (
|
||||
<div className="p-2 rounded-full transition-all duration-300 group-hover:bg-gray-100"
|
||||
style={{ backgroundColor: 'rgba(0, 0, 0, 0.05)' }}>
|
||||
<ArrowUpRight className="w-4 h-4" style={{ color: 'var(--color-brand-black)' }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Calendar Icon Component
|
||||
function CalendarIcon() {
|
||||
return (
|
||||
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z" clipRule="evenodd" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
// Explore All Button Component - Updated to redirect to articles page
|
||||
function ExploreAllButton() {
|
||||
return (
|
||||
<PrimaryCTAButton
|
||||
text="Explore All"
|
||||
onClick={() => navigateTo('/learning/articles')}
|
||||
ariaLabel="Explore all leadership insights and ideas"
|
||||
className="explore-all-cta-override"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Large Insight Card Component
|
||||
function LargeInsightCard({ card }: { card: InsightCard }) {
|
||||
return (
|
||||
<div className="relative h-[500px] rounded-xl overflow-hidden group cursor-pointer">
|
||||
{/* Background Image */}
|
||||
<div className="absolute inset-0 transition-transform duration-300 group-hover:scale-105">
|
||||
<ImageWithFallback
|
||||
src={card.image}
|
||||
alt={card.title}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* White Content Box */}
|
||||
<div className="insight-card-white-box absolute bottom-6 left-6 right-6 bg-white rounded-xl p-6 shadow-lg transition-all duration-300 group-hover:shadow-xl">
|
||||
{/* Top section with tags and arrow */}
|
||||
<div className="insight-card-header flex items-start justify-between mb-4">
|
||||
<div className="insight-card-tags flex gap-2">
|
||||
{card.tags.map((tag, index) => (
|
||||
<InsightTag key={index} text={tag} />
|
||||
))}
|
||||
</div>
|
||||
<ArrowIcon />
|
||||
</div>
|
||||
|
||||
{/* Bottom section with content and date */}
|
||||
<div className="insight-card-footer">
|
||||
<div className="insight-card-text-content mb-4">
|
||||
<h3 className="insight-card-title text-xl font-bold mb-3 leading-tight"
|
||||
style={{ color: 'var(--color-brand-black)' }}>
|
||||
{card.title}
|
||||
</h3>
|
||||
{card.description && (
|
||||
<p className="insight-card-description text-gray-600 text-sm leading-relaxed">
|
||||
{card.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="insight-card-date flex items-center gap-2 text-gray-500">
|
||||
<CalendarIcon />
|
||||
<span className="insight-card-date-text text-sm font-medium">{card.date}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Small Insight Card Component
|
||||
function SmallInsightCard({ card }: { card: InsightCard }) {
|
||||
return (
|
||||
<div className="relative h-[235px] rounded-xl overflow-hidden group cursor-pointer">
|
||||
{/* Background Image */}
|
||||
<div className="absolute inset-0 transition-transform duration-300 group-hover:scale-105">
|
||||
<ImageWithFallback
|
||||
src={card.image}
|
||||
alt={card.title}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* White Content Box */}
|
||||
<div className="insight-card-white-box absolute bottom-4 left-4 right-4 bg-white rounded-xl p-4 shadow-lg transition-all duration-300 group-hover:shadow-xl">
|
||||
{/* Top section with tags and arrow */}
|
||||
<div className="insight-card-header flex items-start justify-between mb-3">
|
||||
<div className="insight-card-tags flex gap-2">
|
||||
{card.tags.map((tag, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="px-2.5 py-1 text-xs font-medium rounded-full"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-brand-accent)',
|
||||
color: 'var(--color-brand-black)'
|
||||
}}
|
||||
>
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="p-1.5 rounded-full transition-all duration-300 group-hover:bg-gray-100"
|
||||
style={{ backgroundColor: 'rgba(0, 0, 0, 0.05)' }}>
|
||||
<ArrowUpRight className="w-3.5 h-3.5" style={{ color: 'var(--color-brand-black)' }} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom section with content and date */}
|
||||
<div className="insight-card-footer">
|
||||
<div className="insight-card-text-content mb-3">
|
||||
<h3 className="insight-card-title text-base font-bold leading-tight"
|
||||
style={{ color: 'var(--color-brand-black)' }}>
|
||||
{card.title}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="insight-card-date flex items-center gap-2 text-gray-500">
|
||||
<CalendarIcon />
|
||||
<span className="insight-card-date-text text-sm font-medium">{card.date}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function InsightsSection() {
|
||||
return (
|
||||
<section
|
||||
className="py-24"
|
||||
style={{
|
||||
backgroundColor: '#F7F7FD',
|
||||
paddingTop: '8.75rem',
|
||||
paddingBottom: '8.75rem'
|
||||
}}
|
||||
>
|
||||
<div className="max-w-7xl mx-auto section-margin-x">
|
||||
{/* Branded Tag */}
|
||||
<BrandedTag text="Leadership Insights" />
|
||||
|
||||
<div className="insights-container">
|
||||
{/* Header */}
|
||||
<div className="insights-header flex flex-col lg:flex-row lg:items-center lg:justify-between mb-16 gap-8 text-center lg:text-left">
|
||||
<h2
|
||||
className="insights-title text-3xl md:text-4xl lg:text-5xl font-bold leading-tight"
|
||||
style={{ color: 'var(--color-brand-black)' }}
|
||||
>
|
||||
Leadership Insights & Ideas
|
||||
</h2>
|
||||
<ExploreAllButton />
|
||||
</div>
|
||||
|
||||
{/* Main Grid Layout */}
|
||||
<div className="insights-grid grid grid-cols-1 lg:grid-cols-2 gap-4 md:gap-6 lg:gap-8">
|
||||
{/* Large Card - Takes full height on left */}
|
||||
<div className="lg:row-span-2">
|
||||
<LargeInsightCard card={insightCards[0]} />
|
||||
</div>
|
||||
|
||||
{/* Small Cards - Stack on right */}
|
||||
<div className="flex flex-col gap-4 md:gap-6 lg:gap-8">
|
||||
<SmallInsightCard card={insightCards[1]} />
|
||||
<SmallInsightCard card={insightCards[2]} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
188
src/components/LeadershipJourneyPage.tsx
Normal file
@@ -0,0 +1,188 @@
|
||||
import React, { useState } from 'react';
|
||||
import { motion } from 'motion/react';
|
||||
import { Building2, User, ArrowRight, ChevronLeft, Shield, Users, Target, BookOpen } from 'lucide-react';
|
||||
import { Button } from './ui/button';
|
||||
import { navigateTo } from './Router';
|
||||
import { BrandedTag } from './about/BrandedTag';
|
||||
|
||||
interface JourneyOption {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
icon: React.ReactNode;
|
||||
benefits: string[];
|
||||
buttonText: string;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
export function LeadershipJourneyPage() {
|
||||
const [hoveredOption, setHoveredOption] = useState<string | null>(null);
|
||||
|
||||
const journeyOptions: JourneyOption[] = [
|
||||
{
|
||||
id: 'corporate',
|
||||
title: 'Corporate Login',
|
||||
description: 'Access your organization\'s leadership development programs and track team progress.',
|
||||
icon: <Building2 className="w-8 h-8" />,
|
||||
benefits: [
|
||||
'Team management and tracking',
|
||||
'Enterprise-grade security',
|
||||
'Customized learning paths',
|
||||
'Advanced analytics and reporting'
|
||||
],
|
||||
buttonText: 'Access Corporate Portal',
|
||||
onClick: () => navigateTo('/corporate-login')
|
||||
},
|
||||
{
|
||||
id: 'self-learner',
|
||||
title: 'Self Learner',
|
||||
description: 'Begin your personal leadership journey with our comprehensive individual programs.',
|
||||
icon: <User className="w-8 h-8" />,
|
||||
benefits: [
|
||||
'Personalized learning experience',
|
||||
'Self-paced development',
|
||||
'Individual progress tracking',
|
||||
'Direct access to resources'
|
||||
],
|
||||
buttonText: 'Start Personal Journey',
|
||||
onClick: () => navigateTo('/self-learner-signup')
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen" style={{ backgroundColor: '#F7F7FD' }}>
|
||||
{/* Header Section */}
|
||||
<section className="py-20" style={{ backgroundColor: 'var(--color-brand-bg-white)' }}>
|
||||
<div className="max-w-7xl mx-auto section-margin-x">
|
||||
|
||||
|
||||
{/* Branded Tag */}
|
||||
<motion.div
|
||||
className="mb-6 text-center"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
<BrandedTag text="Choose Your Path" />
|
||||
</motion.div>
|
||||
|
||||
{/* Main Heading */}
|
||||
<motion.div
|
||||
className="text-center mb-6"
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.7, delay: 0.2 }}
|
||||
>
|
||||
<h1
|
||||
className="text-5xl font-bold leading-tight max-lg:text-4xl max-md:text-3xl mb-6"
|
||||
style={{ color: 'var(--color-brand-black)' }}
|
||||
>
|
||||
Start Your Leadership Journey
|
||||
</h1>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Journey Options Section */}
|
||||
<section className="py-20">
|
||||
<div className="max-w-7xl mx-auto section-margin-x">
|
||||
<div className="grid md:grid-cols-2 gap-12">
|
||||
{journeyOptions.map((option, index) => (
|
||||
<motion.div
|
||||
key={option.id}
|
||||
className="relative"
|
||||
initial={{ opacity: 0, y: 40 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.7, delay: 0.6 + index * 0.2 }}
|
||||
onHoverStart={() => setHoveredOption(option.id)}
|
||||
onHoverEnd={() => setHoveredOption(null)}
|
||||
>
|
||||
<div
|
||||
className="bg-white rounded-2xl p-8 h-full shadow-lg border border-gray-100 transition-all duration-300 hover:shadow-2xl hover:-translate-y-2"
|
||||
style={{
|
||||
transform: hoveredOption === option.id ? 'translateY(-8px)' : 'translateY(0)',
|
||||
boxShadow: hoveredOption === option.id
|
||||
? '0 25px 50px -12px rgba(0, 0, 0, 0.15)'
|
||||
: '0 10px 25px -5px rgba(0, 0, 0, 0.1)'
|
||||
}}
|
||||
>
|
||||
{/* Icon Container */}
|
||||
<div
|
||||
className="w-16 h-16 rounded-xl flex items-center justify-center mb-6 transition-all duration-300"
|
||||
style={{
|
||||
backgroundColor: hoveredOption === option.id
|
||||
? 'var(--color-brand-accent)'
|
||||
: 'var(--color-brand-primary)'
|
||||
}}
|
||||
>
|
||||
<div style={{ color: hoveredOption === option.id ? 'var(--color-brand-black)' : 'white' }}>
|
||||
{option.icon}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<h3
|
||||
className="text-2xl font-bold mb-4"
|
||||
style={{ color: 'var(--color-brand-black)' }}
|
||||
>
|
||||
{option.title}
|
||||
</h3>
|
||||
|
||||
<p className="text-gray-600 text-lg leading-relaxed mb-6">
|
||||
{option.description}
|
||||
</p>
|
||||
|
||||
{/* Benefits List */}
|
||||
<div className="mb-8">
|
||||
<h4 className="text-sm font-semibold text-gray-800 mb-4 uppercase tracking-wider">
|
||||
Key Benefits
|
||||
</h4>
|
||||
<ul className="space-y-3">
|
||||
{option.benefits.map((benefit, benefitIndex) => (
|
||||
<motion.li
|
||||
key={benefitIndex}
|
||||
className="flex items-start gap-3"
|
||||
initial={{ opacity: 0, x: -10 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.8 + index * 0.2 + benefitIndex * 0.1 }}
|
||||
>
|
||||
<div
|
||||
className="w-2 h-2 rounded-full mt-2 flex-shrink-0"
|
||||
style={{ backgroundColor: 'var(--color-brand-accent)' }}
|
||||
/>
|
||||
<span className="text-gray-700 leading-relaxed">{benefit}</span>
|
||||
</motion.li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* CTA Button */}
|
||||
<motion.button
|
||||
className="w-full flex items-center justify-center gap-3 px-8 py-4 font-semibold text-lg transition-all duration-300 hover:shadow-lg hover:-translate-y-1"
|
||||
style={{
|
||||
borderRadius: '10px',
|
||||
backgroundColor: hoveredOption === option.id
|
||||
? 'var(--color-brand-primary)'
|
||||
: 'var(--color-brand-accent)',
|
||||
color: hoveredOption === option.id
|
||||
? 'white'
|
||||
: 'var(--color-brand-black)'
|
||||
}}
|
||||
onClick={option.onClick}
|
||||
whileHover={{ scale: 1.02 }}
|
||||
whileTap={{ scale: 0.98 }}
|
||||
>
|
||||
<span>{option.buttonText}</span>
|
||||
<ArrowRight className="w-5 h-5" />
|
||||
</motion.button>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
1149
src/components/LearningOnline.tsx
Normal file
12
src/components/LogoTicker.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
// This component has been consolidated into LogosSection.tsx
|
||||
// All logo ticker functionality is now integrated into the main LogosSection component
|
||||
// with enhanced features including:
|
||||
// - Actual Figma logo assets
|
||||
// - Enhanced scrolling animations
|
||||
// - Responsive design
|
||||
// - Brand guidelines compliance
|
||||
// - Accessibility features
|
||||
|
||||
export default function LogoTicker() {
|
||||
return null;
|
||||
}
|
||||
167
src/components/LogosSection.tsx
Normal file
@@ -0,0 +1,167 @@
|
||||
import React from 'react';
|
||||
import imgImage36 from "figma:asset/6bdf8056f51bbdc6dd9dab9044a6579a254bd02c.png";
|
||||
import imgImage39 from "figma:asset/037c4659b7b0bf15b1dfdcd4868cb42e8257e838.png";
|
||||
import imgImage43 from "figma:asset/c57ec1f4466f68e607139a3cd6d52f7e2f372408.png";
|
||||
import imgImage45 from "figma:asset/4833274f0a593cd31fdefe553b70bb016de281af.png";
|
||||
import imgImage38 from "figma:asset/d5bab6ea4f3d8cef3b0425c45cfee7faea19fdbc.png";
|
||||
import imgImage47 from "figma:asset/e8fad960112d5eba554c3969d08891ebe4d4b9c7.png";
|
||||
import Frame1597884933 from "../imports/Frame1597884933-44-374";
|
||||
|
||||
// Logo data using Frame1597884944 logos with proper dimensions
|
||||
const logoData = [
|
||||
{ src: imgImage36, name: 'CANMOOR', width: 302, height: 54 }, // CANMOOR
|
||||
{ src: imgImage45, name: 'BlackRock', width: 210, height: 54 }, // BlackRock
|
||||
{ src: imgImage38, name: 'Royal London', width: 145, height: 54 }, // Royal London
|
||||
{ src: imgImage39, name: 'Abstract', width: 172, height: 54 }, // Abstract
|
||||
{ src: imgImage47, name: 'ARES', width: 163, height: 54 }, // ARES
|
||||
{ src: imgImage43, name: 'KADANS', width: 206, height: 54 }, // KADANS
|
||||
// Repeat logos for more variety in scrolling
|
||||
{ src: imgImage36, name: 'CANMOOR', width: 302, height: 54 }, // CANMOOR (repeat)
|
||||
{ src: imgImage45, name: 'BlackRock', width: 210, height: 54 }, // BlackRock (repeat)
|
||||
{ src: imgImage38, name: 'Royal London', width: 145, height: 54 }, // Royal London (repeat)
|
||||
{ src: imgImage39, name: 'Abstract', width: 172, height: 54 }, // Abstract (repeat)
|
||||
{ src: imgImage47, name: 'ARES', width: 163, height: 54 }, // ARES (repeat)
|
||||
{ src: imgImage43, name: 'KADANS', width: 206, height: 54 }, // KADANS (repeat)
|
||||
];
|
||||
|
||||
// Top row logos - Original 6 unique logos
|
||||
const topRowLogos = logoData.slice(0, 6);
|
||||
|
||||
// Bottom row logos - Repeated 6 logos for variety
|
||||
const bottomRowLogos = logoData.slice(6, 12);
|
||||
|
||||
interface LogoItemProps {
|
||||
logo: typeof logoData[0];
|
||||
index: number;
|
||||
duplicate?: boolean;
|
||||
}
|
||||
|
||||
function LogoItem({ logo, index, duplicate = false }: LogoItemProps) {
|
||||
// Scale down larger logos while maintaining aspect ratio
|
||||
const scaledWidth = logo.width > 250 ? Math.round(logo.width * 0.8) : logo.width;
|
||||
const displayWidth = Math.max(120, Math.min(scaledWidth, 250)); // Min 120px, Max 250px
|
||||
|
||||
return (
|
||||
<div
|
||||
className="logo-ticker-item flex-shrink-0 flex items-center justify-center mx-8"
|
||||
style={{
|
||||
width: `${displayWidth}px`,
|
||||
height: '54px',
|
||||
minWidth: `${displayWidth}px`,
|
||||
}}
|
||||
aria-label={logo.name}
|
||||
role="listitem"
|
||||
tabIndex={duplicate ? -1 : 0}
|
||||
>
|
||||
<img
|
||||
src={logo.src}
|
||||
alt={`${logo.name} logo`}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: 'auto',
|
||||
maxHeight: '54px',
|
||||
objectFit: 'contain',
|
||||
}}
|
||||
className="logo-ticker-item-image"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function LogosSection() {
|
||||
return (
|
||||
<section
|
||||
className="py-16 overflow-hidden"
|
||||
style={{
|
||||
backgroundColor: '#F7F7FD', // Same as app background
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
{/* Reference Frame Component (Hidden - maintains import) */}
|
||||
<div className="hidden" aria-hidden="true">
|
||||
<Frame1597884933 />
|
||||
</div>
|
||||
|
||||
{/* Logo Ticker - Two Rows */}
|
||||
<div
|
||||
className="w-full relative"
|
||||
role="region"
|
||||
aria-label="Client logos showcase"
|
||||
>
|
||||
{/* Top Row - Scrolling Left to Right */}
|
||||
<div
|
||||
className="relative h-[54px] mb-16 overflow-hidden"
|
||||
role="list"
|
||||
aria-label="Client logos row 1"
|
||||
>
|
||||
<div className="flex items-center h-full will-change-transform">
|
||||
<div
|
||||
className="scroll-left flex items-center h-full"
|
||||
style={{
|
||||
width: '400%',
|
||||
gap: '80px',
|
||||
paddingLeft: '40px',
|
||||
paddingRight: '40px'
|
||||
}}
|
||||
>
|
||||
{/* Create multiple sets for seamless infinite scroll */}
|
||||
{[1, 2, 3, 4].map((setNumber) => (
|
||||
<div
|
||||
key={`top-set-${setNumber}`}
|
||||
className="flex items-center h-full"
|
||||
style={{ gap: '80px' }}
|
||||
>
|
||||
{topRowLogos.map((logo, index) => (
|
||||
<LogoItem
|
||||
key={`top-${setNumber}-${index}`}
|
||||
logo={logo}
|
||||
index={index}
|
||||
duplicate={setNumber > 1}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom Row - Scrolling Right to Left */}
|
||||
<div
|
||||
className="relative h-[54px] overflow-hidden"
|
||||
role="list"
|
||||
aria-label="Client logos row 2"
|
||||
>
|
||||
<div className="flex items-center h-full will-change-transform">
|
||||
<div
|
||||
className="scroll-right flex items-center h-full"
|
||||
style={{
|
||||
width: '400%',
|
||||
gap: '80px',
|
||||
paddingLeft: '40px',
|
||||
paddingRight: '40px'
|
||||
}}
|
||||
>
|
||||
{/* Create multiple sets for seamless infinite scroll */}
|
||||
{[1, 2, 3, 4].map((setNumber) => (
|
||||
<div
|
||||
key={`bottom-set-${setNumber}`}
|
||||
className="flex items-center h-full"
|
||||
style={{ gap: '80px' }}
|
||||
>
|
||||
{bottomRowLogos.map((logo, index) => (
|
||||
<LogoItem
|
||||
key={`bottom-${setNumber}-${index}`}
|
||||
logo={logo}
|
||||
index={index}
|
||||
duplicate={setNumber > 1}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
747
src/components/Navigation.tsx
Normal file
@@ -0,0 +1,747 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Button } from './ui/button';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from './ui/avatar';
|
||||
import { Badge } from './ui/badge';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuLabel,
|
||||
} from './ui/dropdown-menu';
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
SheetTrigger,
|
||||
} from './ui/sheet';
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from './ui/collapsible';
|
||||
import { navigateTo } from './Router';
|
||||
import { useAuth } from './AuthContext';
|
||||
import { useCart } from './CartContext';
|
||||
import { ImageWithFallback } from './figma/ImageWithFallback';
|
||||
import { PrimaryCTAButton } from './PrimaryCTAButton';
|
||||
import { StandardCTAButton } from './StandardCTAButton';
|
||||
import klcLogo from 'figma:asset/e98caa8afd8d11246bbff1dde75bbaae6f6a0894.png';
|
||||
import {
|
||||
Menu,
|
||||
ChevronDown,
|
||||
ChevronRight,
|
||||
Building2,
|
||||
User,
|
||||
Settings,
|
||||
LogOut,
|
||||
LayoutDashboard,
|
||||
Users,
|
||||
Target,
|
||||
Award,
|
||||
Lightbulb,
|
||||
GraduationCap,
|
||||
BookOpen,
|
||||
Video,
|
||||
FileText,
|
||||
Eye,
|
||||
Heart,
|
||||
MapPin,
|
||||
Calendar,
|
||||
Play,
|
||||
Home,
|
||||
Monitor,
|
||||
Headphones,
|
||||
Globe,
|
||||
Download,
|
||||
Clock,
|
||||
TrendingUp,
|
||||
Star,
|
||||
BookMarked,
|
||||
Podcast,
|
||||
Building,
|
||||
ShoppingCart,
|
||||
ArrowRight
|
||||
} from 'lucide-react';
|
||||
|
||||
interface NavigationProps {
|
||||
currentPage?: string;
|
||||
}
|
||||
|
||||
interface NavLink {
|
||||
label: string;
|
||||
href?: string;
|
||||
items?: { label: string; href: string; description?: string; icon?: any }[];
|
||||
icon?: any;
|
||||
}
|
||||
|
||||
const navigationItems: NavLink[] = [
|
||||
{
|
||||
label: 'Services',
|
||||
items: [
|
||||
{
|
||||
label: 'Leadership Development',
|
||||
href: '/services/leadership-development',
|
||||
description: 'Executive and senior leadership programs',
|
||||
icon: Target
|
||||
},
|
||||
{
|
||||
label: 'Management Development',
|
||||
href: '/services/management-development',
|
||||
description: 'Middle management and team leader training',
|
||||
icon: Users
|
||||
},
|
||||
{
|
||||
label: 'Culture Competence',
|
||||
href: '/services/culture-competence',
|
||||
description: 'Building inclusive and high-performance cultures',
|
||||
icon: Heart
|
||||
},
|
||||
{
|
||||
label: 'Consulting',
|
||||
href: '/services/consulting',
|
||||
description: 'Strategic organizational transformation',
|
||||
icon: Lightbulb
|
||||
},
|
||||
{
|
||||
label: 'Executive Coaching',
|
||||
href: '/services/executive-coaching',
|
||||
description: 'Personalized leadership development',
|
||||
icon: GraduationCap
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'About Us',
|
||||
items: [
|
||||
{
|
||||
label: 'Our Vision',
|
||||
href: '/about/our-vision',
|
||||
description: 'Our mission to transform leadership globally',
|
||||
icon: Eye
|
||||
},
|
||||
{
|
||||
label: 'Our Impact',
|
||||
href: '/about/our-impact',
|
||||
description: 'Real results and measurable outcomes',
|
||||
icon: TrendingUp
|
||||
},
|
||||
{
|
||||
label: 'Our Expertise',
|
||||
href: '/about/our-expertise',
|
||||
description: 'Industry-leading knowledge and experience',
|
||||
icon: Star
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Learning Facility',
|
||||
href: '/services/learning-facility'
|
||||
},
|
||||
{
|
||||
label: 'Resources',
|
||||
items: [
|
||||
{
|
||||
label: 'Article Blog',
|
||||
href: '/learning/articles',
|
||||
description: 'Latest insights and thought leadership',
|
||||
icon: BookMarked
|
||||
},
|
||||
{
|
||||
label: 'Webcast',
|
||||
href: '/learning/webcast',
|
||||
description: 'Live and recorded leadership sessions',
|
||||
icon: Podcast
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Online Courses',
|
||||
href: '/learning-online'
|
||||
},
|
||||
{
|
||||
label: 'Contact Us',
|
||||
href: '/contact'
|
||||
}
|
||||
];
|
||||
|
||||
const signInOptions = [
|
||||
{
|
||||
label: 'Corporate HR',
|
||||
href: '/signin/corporate-hr',
|
||||
description: 'HR dashboard, team management, and bulk enrollment tools'
|
||||
},
|
||||
{
|
||||
label: 'Corporate Learner',
|
||||
href: '/signin/corporate-learner',
|
||||
description: 'Employee learning dashboard and assigned programs'
|
||||
},
|
||||
{
|
||||
label: 'Self-Learner',
|
||||
href: '/signin/self-learner',
|
||||
description: 'Individual professional development access'
|
||||
}
|
||||
];
|
||||
|
||||
function NavLink({ item, isMobile = false }: { item: NavLink; isMobile?: boolean }) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
if (item.href) {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => navigateTo(item.href!)}
|
||||
className={isMobile ? "w-full justify-start min-h-[44px]" : "min-h-[44px]"}
|
||||
style={{
|
||||
fontSize: '14px', // Reduced from var(--font-body) by 2px
|
||||
fontWeight: 'normal',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-black)'
|
||||
}}
|
||||
>
|
||||
{item.icon && <item.icon className="w-4 h-4 mr-2" />}
|
||||
{item.label}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
if (isMobile) {
|
||||
return (
|
||||
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
|
||||
<CollapsibleTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full justify-between min-h-[44px]"
|
||||
style={{
|
||||
fontSize: '14px', // Reduced from var(--font-body) by 2px
|
||||
fontWeight: 'normal',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-black)'
|
||||
}}
|
||||
>
|
||||
<span className="flex items-center">
|
||||
{item.icon && <item.icon className="w-4 h-4 mr-2" />}
|
||||
{item.label}
|
||||
</span>
|
||||
<ChevronRight className={`w-4 h-4 transition-transform ${isOpen ? 'rotate-90' : ''}`} />
|
||||
</Button>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent className="pl-4 space-y-1">
|
||||
{item.items?.map((subItem) => (
|
||||
<Button
|
||||
key={subItem.href}
|
||||
variant="ghost"
|
||||
onClick={() => navigateTo(subItem.href)}
|
||||
className="w-full justify-start min-h-[44px] pl-6"
|
||||
style={{
|
||||
fontSize: '14px', // Reduced from var(--font-body) by 2px
|
||||
fontWeight: 'normal',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-black)'
|
||||
}}
|
||||
>
|
||||
{subItem.icon && <subItem.icon className="w-4 h-4 mr-2" />}
|
||||
{subItem.label}
|
||||
</Button>
|
||||
))}
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="flex items-center gap-1 min-h-[44px] transition-all duration-300 hover:transform hover:-translate-y-1"
|
||||
style={{
|
||||
fontSize: '14px', // Reduced from var(--font-body) by 2px
|
||||
fontWeight: 'normal',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-black)'
|
||||
}}
|
||||
>
|
||||
{item.icon && <item.icon className="w-4 h-4 mr-2" />}
|
||||
{item.label}
|
||||
<ChevronDown className="w-4 h-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
align="start"
|
||||
className="w-80"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-bg-white)',
|
||||
border: `1px solid var(--color-border)`,
|
||||
borderRadius: '12px',
|
||||
boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{item.items?.map((subItem) => (
|
||||
<DropdownMenuItem
|
||||
key={subItem.href}
|
||||
onClick={() => navigateTo(subItem.href)}
|
||||
className="flex items-start gap-3 p-4 cursor-pointer transition-all duration-300 hover:transform hover:-translate-y-1"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{subItem.icon && (
|
||||
<div
|
||||
className="w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0 mt-1"
|
||||
style={{
|
||||
backgroundColor: 'rgba(4, 4, 91, 0.1)',
|
||||
color: 'var(--color-brand-primary)'
|
||||
}}
|
||||
>
|
||||
<subItem.icon className="w-4 h-4" />
|
||||
</div>
|
||||
)}
|
||||
<div className="flex-1">
|
||||
<div
|
||||
className="font-medium"
|
||||
style={{
|
||||
fontSize: '14px', // Reduced from var(--font-body) by 2px
|
||||
fontWeight: '400',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{subItem.label}
|
||||
</div>
|
||||
{subItem.description && (
|
||||
<div
|
||||
className="mt-1"
|
||||
style={{
|
||||
fontSize: '12px', // Reduced from var(--font-small) by 2px
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-small)'
|
||||
}}
|
||||
>
|
||||
{subItem.description}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
||||
function ProfileDropdown({ user }: { user: any }) {
|
||||
const { signOut } = useAuth();
|
||||
|
||||
const handleSignOut = () => {
|
||||
signOut();
|
||||
navigateTo('/');
|
||||
};
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="flex items-center gap-3 px-3 py-2 h-auto min-h-[44px]"
|
||||
style={{
|
||||
fontSize: '14px', // Reduced from var(--font-body) by 2px
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
<Avatar className="h-8 w-8">
|
||||
<AvatarImage src={user.avatar} alt={user.name} />
|
||||
<AvatarFallback
|
||||
className="text-sm"
|
||||
style={{ fontFamily: 'var(--font-family-base)' }}
|
||||
>
|
||||
{user.name.split(' ').map((n: string) => n[0]).join('')}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="text-left">
|
||||
<div
|
||||
className="font-medium"
|
||||
style={{
|
||||
fontSize: '14px', // Reduced from var(--font-body) by 2px
|
||||
fontWeight: '400',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{user.corporateName}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '12px', // Reduced from var(--font-small) by 2px
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{user.name}
|
||||
</div>
|
||||
</div>
|
||||
<ChevronDown className="w-4 h-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
align="end"
|
||||
className="w-64"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-bg-white)',
|
||||
border: `1px solid var(--color-border)`,
|
||||
borderRadius: '12px',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
<DropdownMenuLabel
|
||||
style={{
|
||||
fontSize: '14px', // Reduced from var(--font-body) by 2px
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="font-medium"
|
||||
style={{ fontWeight: '400' }}
|
||||
>
|
||||
{user.corporateName}
|
||||
</div>
|
||||
<div
|
||||
className="font-normal"
|
||||
style={{
|
||||
fontSize: '12px', // Reduced from var(--font-small) by 2px
|
||||
color: 'var(--color-gray-muted)'
|
||||
}}
|
||||
>
|
||||
{user.email}
|
||||
</div>
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
onClick={() => navigateTo('/dashboard')}
|
||||
className="min-h-[44px]"
|
||||
style={{
|
||||
fontSize: '14px', // Reduced from var(--font-body) by 2px
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
<LayoutDashboard className="w-4 h-4 mr-2" />
|
||||
Dashboard
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => navigateTo('/team')}
|
||||
className="min-h-[44px]"
|
||||
style={{
|
||||
fontSize: '14px', // Reduced from var(--font-body) by 2px
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
<Users className="w-4 h-4 mr-2" />
|
||||
Team Management
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => navigateTo('/profile')}
|
||||
className="min-h-[44px]"
|
||||
style={{
|
||||
fontSize: '14px', // Reduced from var(--font-body) by 2px
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
<User className="w-4 h-4 mr-2" />
|
||||
Profile
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => navigateTo('/settings')}
|
||||
className="min-h-[44px]"
|
||||
style={{
|
||||
fontSize: '14px', // Reduced from var(--font-body) by 2px
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
<Settings className="w-4 h-4 mr-2" />
|
||||
Settings
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
onClick={handleSignOut}
|
||||
className="min-h-[44px]"
|
||||
style={{
|
||||
fontSize: '14px', // Reduced from var(--font-body) by 2px
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--destructive)'
|
||||
}}
|
||||
>
|
||||
<LogOut className="w-4 h-4 mr-2" />
|
||||
Sign Out
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
||||
function CartIcon() {
|
||||
const { cartCount } = useCart();
|
||||
|
||||
const handleCartClick = () => {
|
||||
navigateTo('/cart');
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={handleCartClick}
|
||||
className="relative min-h-[44px] min-w-[44px] hover:bg-gray-100 transition-all duration-300"
|
||||
aria-label={`Shopping cart with ${cartCount} items`}
|
||||
>
|
||||
<ShoppingCart className="w-5 h-5 text-gray-600" />
|
||||
|
||||
{/* Cart Count Badge */}
|
||||
{cartCount > 0 && (
|
||||
<div
|
||||
className="absolute -top-1 -right-1 min-w-[18px] h-[18px] flex items-center justify-center rounded-full text-xs font-medium text-white animate-in fade-in zoom-in duration-300"
|
||||
style={{
|
||||
backgroundColor: '#dc2626', // Red background for notification
|
||||
fontSize: '11px',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: '1'
|
||||
}}
|
||||
>
|
||||
{cartCount > 9 ? '9+' : cartCount}
|
||||
</div>
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
export function Navigation({ currentPage }: NavigationProps) {
|
||||
const [isScrolled, setIsScrolled] = useState(false);
|
||||
const { user, isAuthenticated, signOut } = useAuth();
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
setIsScrolled(window.scrollY > 10);
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScroll);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleMobileSignOut = () => {
|
||||
signOut();
|
||||
navigateTo('/');
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Top notification bar - PRESERVED */}
|
||||
<div className="notification-strip">
|
||||
<span className="notification-text">
|
||||
Join Our Upcoming Leadership Webinars - Transform Your Leadership Journey
|
||||
</span>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => navigateTo('/enroll')}
|
||||
className="notification-button"
|
||||
>
|
||||
Enroll Now
|
||||
<ChevronRight className="w-4 h-4 ml-1" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Main navigation header - ENHANCED STICKY */}
|
||||
<header
|
||||
className={`w-full border-b transition-all duration-300 navbar-sticky ${isScrolled ? 'shadow-lg scrolled' : 'shadow-sm'}`}
|
||||
style={{
|
||||
borderColor: 'var(--color-border)'
|
||||
}}
|
||||
>
|
||||
<nav className="container mx-auto px-4 lg:px-8">
|
||||
<div className="flex items-center justify-between h-20">
|
||||
{/* Logo */}
|
||||
<div className="flex items-center">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="p-2 hover:bg-transparent"
|
||||
onClick={() => navigateTo('/')}
|
||||
>
|
||||
<img
|
||||
src={klcLogo}
|
||||
alt="Kautilya Leadership Centre"
|
||||
className="h-12 w-auto object-contain"
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Desktop Navigation */}
|
||||
<div className="hidden lg:flex items-center gap-2">
|
||||
{navigationItems.map((item) => (
|
||||
<NavLink key={item.label} item={item} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Right Side Actions */}
|
||||
<div className="flex items-center gap-4">
|
||||
|
||||
{/* Cart Icon - Always visible */}
|
||||
<CartIcon />
|
||||
|
||||
{/* Authentication Button/Profile */}
|
||||
{isAuthenticated && user ? (
|
||||
<div className="hidden md:flex">
|
||||
<ProfileDropdown user={user} />
|
||||
</div>
|
||||
) : (
|
||||
<div className="hidden md:flex items-center">
|
||||
<Button
|
||||
onClick={() => navigateTo('/leadership-journey')}
|
||||
className="management-dev-glassmorphic-btn text-body px-8 py-4 min-h-[52px] border transition-all duration-300 group"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: '#04045B'
|
||||
}}
|
||||
>
|
||||
Start Leadership Journey
|
||||
<ArrowRight
|
||||
className="w-5 h-5 ml-2 transition-transform duration-300 group-hover:translate-x-1"
|
||||
style={{ color: '#04045B' }}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Mobile Menu */}
|
||||
<Sheet>
|
||||
<SheetTrigger asChild>
|
||||
<Button variant="ghost" size="icon" className="lg:hidden min-h-[44px] min-w-[44px]">
|
||||
<Menu className="w-6 h-6" />
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent
|
||||
side="right"
|
||||
className="w-80"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-bg-white)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
zIndex: 10000
|
||||
}}
|
||||
>
|
||||
<SheetHeader>
|
||||
<SheetTitle
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-black)'
|
||||
}}
|
||||
>
|
||||
Navigation
|
||||
</SheetTitle>
|
||||
</SheetHeader>
|
||||
<div className="mt-6 space-y-4">
|
||||
{navigationItems.map((item) => (
|
||||
<NavLink key={item.label} item={item} isMobile />
|
||||
))}
|
||||
|
||||
<div className="pt-4 border-t space-y-3">
|
||||
|
||||
{isAuthenticated && user ? (
|
||||
<div className="space-y-3">
|
||||
<div
|
||||
className="flex items-center gap-3 p-3 rounded-lg"
|
||||
style={{ backgroundColor: 'rgba(0, 0, 0, 0.05)' }}
|
||||
>
|
||||
<Avatar className="h-10 w-10">
|
||||
<AvatarImage src={user.avatar} alt={user.name} />
|
||||
<AvatarFallback style={{ fontFamily: 'var(--font-family-base)' }}>
|
||||
{user.name.split(' ').map((n: string) => n[0]).join('')}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div>
|
||||
<div
|
||||
className="font-medium"
|
||||
style={{
|
||||
fontSize: '14px', // Reduced from var(--font-body) by 2px
|
||||
fontWeight: '400',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{user.corporateName}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '12px', // Reduced from var(--font-small) by 2px
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{user.name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full justify-start min-h-[44px]"
|
||||
onClick={() => navigateTo('/dashboard')}
|
||||
style={{
|
||||
fontSize: '14px', // Reduced from var(--font-body) by 2px
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
<LayoutDashboard className="w-4 h-4 mr-2" />
|
||||
Dashboard
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full justify-start min-h-[44px]"
|
||||
onClick={() => navigateTo('/profile')}
|
||||
style={{
|
||||
fontSize: '14px', // Reduced from var(--font-body) by 2px
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
<User className="w-4 h-4 mr-2" />
|
||||
Profile
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full justify-start min-h-[44px]"
|
||||
onClick={handleMobileSignOut}
|
||||
style={{
|
||||
fontSize: '14px', // Reduced from var(--font-body) by 2px
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--destructive)'
|
||||
}}
|
||||
>
|
||||
<LogOut className="w-4 h-4 mr-2" />
|
||||
Sign Out
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
<Button
|
||||
onClick={() => navigateTo('/leadership-journey')}
|
||||
className="management-dev-glassmorphic-btn text-body px-8 py-4 min-h-[52px] border transition-all duration-300 w-full group"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: '#04045B'
|
||||
}}
|
||||
>
|
||||
Start Leadership Journey
|
||||
<ArrowRight
|
||||
className="w-5 h-5 ml-2 transition-transform duration-300 group-hover:translate-x-1"
|
||||
style={{ color: '#04045B' }}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
</>
|
||||
);
|
||||
}
|
||||
1
src/components/OurExpertisePage.tsx
Normal file
@@ -0,0 +1 @@
|
||||
// This file has been removed
|
||||
1
src/components/OurImpactPage.tsx
Normal file
@@ -0,0 +1 @@
|
||||
// This file has been removed
|
||||
1
src/components/OurVisionPage.tsx
Normal file
@@ -0,0 +1 @@
|
||||
// This file has been removed
|
||||
146
src/components/PrimaryCTAButton.tsx
Normal file
@@ -0,0 +1,146 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import svgPaths from "../imports/svg-i1joeov37f";
|
||||
import { navigateTo } from './Router';
|
||||
|
||||
interface PrimaryCTAButtonProps {
|
||||
text: string;
|
||||
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
className?: string;
|
||||
ariaLabel?: string;
|
||||
debugId?: string; // Add debug identifier
|
||||
}
|
||||
|
||||
export const PrimaryCTAButton: React.FC<PrimaryCTAButtonProps> = ({
|
||||
text,
|
||||
onClick,
|
||||
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');
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
}}
|
||||
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>
|
||||
);
|
||||
};
|
||||
33
src/components/PrimaryCTAButtonBlue.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import { PrimaryCTAButton, PrimaryCTAButtonProps } from './PrimaryCTAButton';
|
||||
|
||||
/**
|
||||
* Blue Primary CTA Button Component
|
||||
*
|
||||
* A variant of the Primary CTA Button with blue background (#04045B) and black text (#26231A).
|
||||
* Preserves all sophisticated animations and functionality from the main component.
|
||||
*
|
||||
* Features:
|
||||
* - Blue background (#04045B) instead of yellow
|
||||
* - Black text (#26231A) for contrast
|
||||
* - All sophisticated slide animations preserved
|
||||
* - Consistent with brand guidelines
|
||||
*
|
||||
* Usage:
|
||||
* ```tsx
|
||||
* <PrimaryCTAButtonBlue
|
||||
* text="Get Started"
|
||||
* onClick={() => handleClick()}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export const PrimaryCTAButtonBlue: React.FC<PrimaryCTAButtonProps> = (props) => {
|
||||
return (
|
||||
<PrimaryCTAButton
|
||||
{...props}
|
||||
className={`primary-cta-button-blue ${props.className || ''}`}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default PrimaryCTAButtonBlue;
|
||||
90
src/components/PrimaryCTAButtonVariant.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
import React from 'react';
|
||||
import { PrimaryCTAButton, PrimaryCTAButtonProps } from './PrimaryCTAButton';
|
||||
|
||||
export type CTAButtonVariant = 'yellow' | 'blue' | 'custom';
|
||||
|
||||
export interface PrimaryCTAButtonVariantProps extends PrimaryCTAButtonProps {
|
||||
variant?: CTAButtonVariant;
|
||||
customBackgroundColor?: string;
|
||||
customTextColor?: string;
|
||||
customHoverBackgroundColor?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary CTA Button Variant Component
|
||||
*
|
||||
* A flexible variant of the Primary CTA Button that supports multiple color schemes
|
||||
* while preserving all sophisticated animations and functionality.
|
||||
*
|
||||
* Variants:
|
||||
* - 'yellow': Original yellow background (#F8C301) with white text (default)
|
||||
* - 'blue': Blue background (#04045B) with black text (#26231A)
|
||||
* - 'custom': Custom colors using customBackgroundColor and customTextColor props
|
||||
*
|
||||
* Features:
|
||||
* - Multiple pre-defined color variants
|
||||
* - Custom color support
|
||||
* - All sophisticated slide animations preserved
|
||||
* - Consistent with brand guidelines
|
||||
*
|
||||
* Usage:
|
||||
* ```tsx
|
||||
* // Yellow variant (default)
|
||||
* <PrimaryCTAButtonVariant text="Learn More" onClick={handleClick} />
|
||||
*
|
||||
* // Blue variant
|
||||
* <PrimaryCTAButtonVariant variant="blue" text="Get Started" onClick={handleClick} />
|
||||
*
|
||||
* // Custom colors
|
||||
* <PrimaryCTAButtonVariant
|
||||
* variant="custom"
|
||||
* customBackgroundColor="#FF6B6B"
|
||||
* customTextColor="#FFFFFF"
|
||||
* customHoverBackgroundColor="#FF5252"
|
||||
* text="Custom Button"
|
||||
* onClick={handleClick}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export const PrimaryCTAButtonVariant: React.FC<PrimaryCTAButtonVariantProps> = ({
|
||||
variant = 'yellow',
|
||||
customBackgroundColor,
|
||||
customTextColor,
|
||||
customHoverBackgroundColor,
|
||||
className,
|
||||
...props
|
||||
}) => {
|
||||
// Generate variant-specific className
|
||||
const getVariantClassName = () => {
|
||||
switch (variant) {
|
||||
case 'blue':
|
||||
return 'primary-cta-button-blue';
|
||||
case 'yellow':
|
||||
return 'primary-cta-button-yellow';
|
||||
case 'custom':
|
||||
return 'primary-cta-button-custom';
|
||||
default:
|
||||
return 'primary-cta-button-yellow';
|
||||
}
|
||||
};
|
||||
|
||||
// Custom CSS variables for custom variant
|
||||
const customStyles = variant === 'custom' ? {
|
||||
'--custom-bg-color': customBackgroundColor || '#F8C301',
|
||||
'--custom-text-color': customTextColor || '#FFFFFF',
|
||||
'--custom-hover-bg-color': customHoverBackgroundColor || customBackgroundColor || '#F8C301',
|
||||
} as React.CSSProperties : {};
|
||||
|
||||
const combinedClassName = `${getVariantClassName()} ${className || ''}`;
|
||||
|
||||
return (
|
||||
<div style={customStyles}>
|
||||
<PrimaryCTAButton
|
||||
{...props}
|
||||
className={combinedClassName}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PrimaryCTAButtonVariant;
|
||||
33
src/components/PrimaryCTAButtonYellow.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import { PrimaryCTAButton, PrimaryCTAButtonProps } from './PrimaryCTAButton';
|
||||
|
||||
/**
|
||||
* Yellow Primary CTA Button Component
|
||||
*
|
||||
* A variant of the Primary CTA Button that maintains the original yellow background (#F8C301) with white text.
|
||||
* This is the default styling variant that preserves the original design.
|
||||
*
|
||||
* Features:
|
||||
* - Original yellow background (#F8C301)
|
||||
* - White text for contrast
|
||||
* - All sophisticated slide animations preserved
|
||||
* - Default brand styling
|
||||
*
|
||||
* Usage:
|
||||
* ```tsx
|
||||
* <PrimaryCTAButtonYellow
|
||||
* text="Learn More"
|
||||
* onClick={() => handleClick()}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export const PrimaryCTAButtonYellow: React.FC<PrimaryCTAButtonProps> = (props) => {
|
||||
return (
|
||||
<PrimaryCTAButton
|
||||
{...props}
|
||||
className={`primary-cta-button-yellow ${props.className || ''}`}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default PrimaryCTAButtonYellow;
|
||||
1698
src/components/ProgrammeDetail.tsx
Normal file
197
src/components/README-CTA-Button-Spacing-Guidelines.md
Normal file
@@ -0,0 +1,197 @@
|
||||
# CTA Button Spacing Optimization Guidelines
|
||||
|
||||
## 🎯 No Extra Horizontal Spacing Policy
|
||||
|
||||
This document establishes the **zero extra spacing** standard for all CTA buttons throughout the KLC website to ensure perfect, tight layouts with no unnecessary horizontal space.
|
||||
|
||||
## ✅ Primary CTA Button (Reference Implementation)
|
||||
|
||||
The `PrimaryCTAButton` component serves as the **gold standard** for spacing optimization:
|
||||
|
||||
```tsx
|
||||
// ✅ CORRECT: Perfect width with no extra space
|
||||
style={{
|
||||
width: 'fit-content' // Button sizes exactly to content
|
||||
}}
|
||||
|
||||
// ❌ AVOID: Fixed widths that create extra space
|
||||
style={{
|
||||
width: '324px' // Creates unnecessary horizontal padding
|
||||
}}
|
||||
|
||||
// ❌ AVOID: Calculated widths that may be imprecise
|
||||
style={{
|
||||
width: `${estimatedWidth + padding}px` // Can create extra space
|
||||
}}
|
||||
```
|
||||
|
||||
## 🔧 Implementation Standards
|
||||
|
||||
### 1. Button Container Sizing
|
||||
```tsx
|
||||
// Use fit-content for perfect sizing
|
||||
button: {
|
||||
width: 'fit-content',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '10px' // Consistent gap between elements
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Text Container Optimization
|
||||
```tsx
|
||||
// Text containers should also use fit-content
|
||||
textContainer: {
|
||||
width: 'fit-content',
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden'
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Icon Container Standards
|
||||
```tsx
|
||||
// Fixed icon sizes with no extra space
|
||||
iconContainer: {
|
||||
width: '50px',
|
||||
height: '50px',
|
||||
flexShrink: 0 // Prevent compression
|
||||
}
|
||||
```
|
||||
|
||||
## 📏 Components to Update
|
||||
|
||||
Apply these principles to ALL CTA buttons throughout the website:
|
||||
|
||||
### ✅ Already Optimized
|
||||
- `PrimaryCTAButton.tsx` - ✅ Fully optimized
|
||||
- `Navigation.tsx` CTA button - ✅ Now uses PrimaryCTAButton
|
||||
|
||||
### 🔄 Needs Review & Optimization
|
||||
|
||||
#### Hero Section
|
||||
- Hero CTA buttons in `HeroSection.tsx`
|
||||
- Any custom hero buttons
|
||||
|
||||
#### Services Section
|
||||
- Service CTA buttons in `ServicesSection.tsx`
|
||||
- Service card action buttons
|
||||
- "Learn More" buttons
|
||||
|
||||
#### CTA Banner Section
|
||||
- Main CTA buttons in `CTABannerSection.tsx`
|
||||
- Secondary action buttons
|
||||
|
||||
#### Footer
|
||||
- Newsletter signup buttons in `Footer.tsx`
|
||||
- Footer CTA buttons
|
||||
|
||||
#### Page-Specific CTAs
|
||||
- Leadership Journey page buttons
|
||||
- Service detail page CTAs
|
||||
- About page action buttons
|
||||
- Learning page CTAs
|
||||
|
||||
#### Modal & Dialog CTAs
|
||||
- Popup action buttons
|
||||
- Form submission buttons
|
||||
- Modal confirmation buttons
|
||||
|
||||
## 🎨 Styling Principles
|
||||
|
||||
### Width Calculation Method
|
||||
```tsx
|
||||
// ✅ PREFERRED: CSS fit-content
|
||||
width: 'fit-content'
|
||||
|
||||
// ✅ ACCEPTABLE: Flexbox natural sizing
|
||||
display: 'flex'
|
||||
// No explicit width = natural sizing
|
||||
|
||||
// ❌ AVOID: Manual calculations
|
||||
width: `${textWidth + iconWidth + gaps + padding}px`
|
||||
```
|
||||
|
||||
### Responsive Considerations
|
||||
```tsx
|
||||
// Ensure responsiveness is maintained
|
||||
@media (max-width: 768px) {
|
||||
button: {
|
||||
width: 'fit-content', // Still use fit-content
|
||||
fontSize: '16px', // Adjust text size
|
||||
gap: '8px' // Adjust spacing
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🔍 Quality Assurance Checklist
|
||||
|
||||
For each CTA button, verify:
|
||||
|
||||
- [ ] **No Extra Width**: Button width exactly matches content needs
|
||||
- [ ] **No Horizontal Padding**: Only necessary internal spacing
|
||||
- [ ] **Consistent Gap**: Proper spacing between icon and text (10px)
|
||||
- [ ] **Text Fit**: Text container width matches text content exactly
|
||||
- [ ] **Icon Precision**: Icon container has exact dimensions (50px × 50px)
|
||||
- [ ] **Responsive Behavior**: Maintains fit-content approach across breakpoints
|
||||
- [ ] **Animation Compatibility**: Width optimization doesn't break hover/click animations
|
||||
|
||||
## 🚀 Implementation Process
|
||||
|
||||
### Step 1: Identify Current CTA Buttons
|
||||
1. Audit all components containing CTA buttons
|
||||
2. Document current width calculation methods
|
||||
3. Identify components with excessive horizontal spacing
|
||||
|
||||
### Step 2: Apply Optimization
|
||||
1. Replace fixed widths with `width: 'fit-content'`
|
||||
2. Remove manual width calculations
|
||||
3. Ensure text containers use `width: 'fit-content'`
|
||||
4. Test responsive behavior
|
||||
|
||||
### Step 3: Validation
|
||||
1. Visual inspection for extra spacing
|
||||
2. Test across different text lengths
|
||||
3. Verify responsive behavior
|
||||
4. Confirm animation functionality
|
||||
|
||||
## 📱 Responsive Standards
|
||||
|
||||
```tsx
|
||||
// Consistent approach across breakpoints
|
||||
.cta-button {
|
||||
width: fit-content;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
width: fit-content; // Maintain principle
|
||||
gap: 8px; // Adjust spacing only
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
width: fit-content; // Maintain principle
|
||||
gap: 6px; // Adjust spacing only
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 💡 Benefits Achieved
|
||||
|
||||
1. **Perfect Visual Alignment**: No unnecessary gaps or spacing
|
||||
2. **Dynamic Adaptation**: Buttons automatically adjust to content
|
||||
3. **Responsive Excellence**: Consistent behavior across devices
|
||||
4. **Maintenance Simplicity**: No complex width calculations to maintain
|
||||
5. **Design System Consistency**: Uniform spacing approach across all CTAs
|
||||
6. **Performance Optimization**: Reduced layout calculations
|
||||
|
||||
## 🔗 Related Components
|
||||
|
||||
When implementing, also consider:
|
||||
- Button hover states
|
||||
- Focus indicators
|
||||
- Loading states
|
||||
- Disabled states
|
||||
- Icon animations
|
||||
- Text animations
|
||||
|
||||
---
|
||||
|
||||
**Next Steps**: Apply this optimization approach to all identified CTA buttons throughout the website to achieve perfect, space-efficient button layouts across the entire KLC platform.
|
||||
381
src/components/README-Independent-Primary-CTA-Buttons.md
Normal file
@@ -0,0 +1,381 @@
|
||||
# Independent Primary CTA Button System
|
||||
|
||||
## Overview
|
||||
|
||||
The KLC Primary CTA Button system has been updated to provide **complete independence** between button instances. Each Primary CTA Button can now have unique styling without affecting any other buttons on the website, while preserving all sophisticated animations and functionality.
|
||||
|
||||
## 🎯 Key Benefits
|
||||
|
||||
### ✅ **Complete Independence**
|
||||
- Each button instance is completely separate
|
||||
- Changes to one button will NEVER affect others
|
||||
- No global CSS overrides that impact all buttons
|
||||
|
||||
### ✅ **Animation Preservation**
|
||||
- All sophisticated slide animations maintained
|
||||
- Text and icon transitions work perfectly
|
||||
- 300ms ease-in-out timing preserved
|
||||
|
||||
### ✅ **Component Protection**
|
||||
- Main `/components/PrimaryCTAButton.tsx` never modified
|
||||
- Core functionality remains intact
|
||||
- Sophisticated animation structure protected
|
||||
|
||||
### ✅ **Unlimited Flexibility**
|
||||
- Support for any color combination
|
||||
- Custom colors via CSS classes or variant props
|
||||
- Pre-built variants for common use cases
|
||||
|
||||
## 🛠️ Implementation Methods
|
||||
|
||||
### Method 1: Variant Components (Recommended)
|
||||
|
||||
Use pre-built variant components for common color schemes:
|
||||
|
||||
```tsx
|
||||
import { PrimaryCTAButton } from './components/PrimaryCTAButton';
|
||||
import { PrimaryCTAButtonYellow } from './components/PrimaryCTAButtonYellow';
|
||||
import { PrimaryCTAButtonVariant } from './components/PrimaryCTAButtonVariant';
|
||||
|
||||
// Default blue variant - NEW DEFAULT with brand blue and black text
|
||||
<PrimaryCTAButton
|
||||
text="Get Started"
|
||||
onClick={handleGetStarted}
|
||||
ariaLabel="Get started with KLC services"
|
||||
/>
|
||||
|
||||
// Yellow variant - Former default styling
|
||||
<PrimaryCTAButtonYellow
|
||||
text="Learn More"
|
||||
onClick={handleLearnMore}
|
||||
ariaLabel="Learn more about our programs"
|
||||
/>
|
||||
|
||||
// Custom variant with any colors
|
||||
<PrimaryCTAButtonVariant
|
||||
variant="custom"
|
||||
customBackgroundColor="#28A745"
|
||||
customTextColor="#FFFFFF"
|
||||
customHoverBackgroundColor="#1E7E34"
|
||||
text="Custom Action"
|
||||
onClick={handleCustomAction}
|
||||
/>
|
||||
```
|
||||
|
||||
### Method 2: Unique CSS Classes
|
||||
|
||||
Add unique CSS class modifiers to any Primary CTA Button:
|
||||
|
||||
```tsx
|
||||
import { PrimaryCTAButton } from './components/PrimaryCTAButton';
|
||||
|
||||
// Default blue button (no class needed)
|
||||
<PrimaryCTAButton
|
||||
text="Blue Action"
|
||||
onClick={handleBlueAction}
|
||||
/>
|
||||
|
||||
// Yellow button with unique CSS class
|
||||
<PrimaryCTAButton
|
||||
text="Yellow Action"
|
||||
onClick={handleYellowAction}
|
||||
className="cta-custom-yellow"
|
||||
/>
|
||||
|
||||
// Green button with unique CSS class
|
||||
<PrimaryCTAButton
|
||||
text="Green Action"
|
||||
onClick={handleGreenAction}
|
||||
className="cta-custom-green"
|
||||
/>
|
||||
|
||||
// Red button with unique CSS class
|
||||
<PrimaryCTAButton
|
||||
text="Red Action"
|
||||
onClick={handleRedAction}
|
||||
className="cta-custom-red"
|
||||
/>
|
||||
```
|
||||
|
||||
## 🎨 Available CSS Class Modifiers
|
||||
|
||||
### Pre-defined Classes
|
||||
|
||||
**Default (no class needed):** Primary CTA Button
|
||||
- Background: `#04045B` (Brand Blue) - **NEW DEFAULT**
|
||||
- Text: `#26231A` (Brand Black)
|
||||
- Hover Background: `#030359` (Darker Blue)
|
||||
|
||||
**Yellow Variant:** `.cta-custom-yellow`
|
||||
- Background: `#F8C301` (Yellow) - **Former Default**
|
||||
- Text: `#FFFFFF` (White)
|
||||
- Hover Background: `#E6AF01` (Darker Yellow)
|
||||
|
||||
**Green Variant:** `.cta-custom-green`
|
||||
- Background: `#28A745` (Green)
|
||||
- Text: `#FFFFFF` (White)
|
||||
- Hover Background: `#1E7E34` (Darker Green)
|
||||
|
||||
### Creating Custom Classes
|
||||
|
||||
Add new color combinations in `/styles/globals.css`:
|
||||
|
||||
```css
|
||||
/* Custom Red Button */
|
||||
.primary-cta-button.cta-custom-red .bg-\[#F8C301\] {
|
||||
background-color: #DC3545 !important; /* Red background */
|
||||
}
|
||||
|
||||
.primary-cta-button.cta-custom-red div[class*="bg-[#F8C301]"] {
|
||||
background-color: #DC3545 !important;
|
||||
}
|
||||
|
||||
.primary-cta-button.cta-custom-red .text-layer,
|
||||
.primary-cta-button.cta-custom-red .text-element,
|
||||
.primary-cta-button.cta-custom-red .text-layer *,
|
||||
.primary-cta-button.cta-custom-red .text-element * {
|
||||
color: #FFFFFF !important; /* White text */
|
||||
}
|
||||
|
||||
/* Red hover state */
|
||||
.primary-cta-button.cta-custom-red:hover .bg-\[#F8C301\] {
|
||||
background-color: #C82333 !important; /* Darker red on hover */
|
||||
}
|
||||
|
||||
.primary-cta-button.cta-custom-red:hover div[class*="bg-[#F8C301]"] {
|
||||
background-color: #C82333 !important;
|
||||
}
|
||||
```
|
||||
|
||||
## 📋 Component Reference
|
||||
|
||||
### PrimaryCTAButtonBlue
|
||||
|
||||
**Purpose:** Blue variant for brand-consistent primary actions
|
||||
**Colors:** Blue background (#04045B), Black text (#26231A)
|
||||
|
||||
```tsx
|
||||
interface PrimaryCTAButtonBlueProps extends PrimaryCTAButtonProps {
|
||||
// Inherits all PrimaryCTAButton props
|
||||
}
|
||||
```
|
||||
|
||||
### PrimaryCTAButtonYellow
|
||||
|
||||
**Purpose:** Original yellow variant (default styling)
|
||||
**Colors:** Yellow background (#F8C301), White text (#FFFFFF)
|
||||
|
||||
```tsx
|
||||
interface PrimaryCTAButtonYellowProps extends PrimaryCTAButtonProps {
|
||||
// Inherits all PrimaryCTAButton props
|
||||
}
|
||||
```
|
||||
|
||||
### PrimaryCTAButtonVariant
|
||||
|
||||
**Purpose:** Flexible component with variant and custom color support
|
||||
|
||||
```tsx
|
||||
interface PrimaryCTAButtonVariantProps extends PrimaryCTAButtonProps {
|
||||
variant?: 'yellow' | 'blue' | 'custom';
|
||||
customBackgroundColor?: string;
|
||||
customTextColor?: string;
|
||||
customHoverBackgroundColor?: string;
|
||||
}
|
||||
|
||||
// Usage examples
|
||||
<PrimaryCTAButtonVariant variant="blue" text="Blue Button" />
|
||||
<PrimaryCTAButtonVariant
|
||||
variant="custom"
|
||||
customBackgroundColor="#FF6B6B"
|
||||
customTextColor="#FFFFFF"
|
||||
text="Custom Button"
|
||||
/>
|
||||
```
|
||||
|
||||
## 🔧 Advanced Usage Examples
|
||||
|
||||
### Multiple Independent Buttons on Same Page
|
||||
|
||||
```tsx
|
||||
function HomePage() {
|
||||
return (
|
||||
<div>
|
||||
{/* Hero section - Blue button */}
|
||||
<section className="hero">
|
||||
<PrimaryCTAButtonBlue
|
||||
text="Get Started Today"
|
||||
onClick={handleGetStarted}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Services section - Yellow button */}
|
||||
<section className="services">
|
||||
<PrimaryCTAButtonYellow
|
||||
text="Explore Services"
|
||||
onClick={handleExploreServices}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Contact section - Custom green button */}
|
||||
<section className="contact">
|
||||
<PrimaryCTAButtonVariant
|
||||
variant="custom"
|
||||
customBackgroundColor="#28A745"
|
||||
customTextColor="#FFFFFF"
|
||||
text="Contact Us"
|
||||
onClick={handleContact}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Footer - Custom purple button */}
|
||||
<footer>
|
||||
<PrimaryCTAButton
|
||||
text="Subscribe"
|
||||
onClick={handleSubscribe}
|
||||
className="cta-custom-purple"
|
||||
/>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Dynamic Button Colors
|
||||
|
||||
```tsx
|
||||
function DynamicButton({ buttonType }: { buttonType: 'primary' | 'secondary' | 'danger' }) {
|
||||
const getVariantProps = () => {
|
||||
switch (buttonType) {
|
||||
case 'primary':
|
||||
return {
|
||||
variant: 'blue' as const,
|
||||
text: 'Primary Action'
|
||||
};
|
||||
case 'secondary':
|
||||
return {
|
||||
variant: 'yellow' as const,
|
||||
text: 'Secondary Action'
|
||||
};
|
||||
case 'danger':
|
||||
return {
|
||||
variant: 'custom' as const,
|
||||
customBackgroundColor: '#DC3545',
|
||||
customTextColor: '#FFFFFF',
|
||||
text: 'Danger Action'
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<PrimaryCTAButtonVariant
|
||||
{...getVariantProps()}
|
||||
onClick={handleDynamicAction}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## 🚨 Important Guidelines
|
||||
|
||||
### ✅ DO
|
||||
- Use variant components for common color schemes
|
||||
- Create unique CSS classes for custom colors
|
||||
- Test hover states for proper contrast
|
||||
- Maintain accessibility with proper ARIA labels
|
||||
- Follow brand guidelines for color choices
|
||||
|
||||
### ❌ DON'T
|
||||
- Modify `/components/PrimaryCTAButton.tsx` directly
|
||||
- Use global CSS that affects all buttons
|
||||
- Break the animation structure
|
||||
- Forget to test accessibility
|
||||
- Use colors that don't meet contrast requirements
|
||||
|
||||
## 🎨 Color Guidelines
|
||||
|
||||
### Brand-Approved Colors
|
||||
|
||||
**Primary Blue:** `#04045B`
|
||||
- Use for main call-to-action buttons
|
||||
- Pair with black text (`#26231A`) for contrast
|
||||
|
||||
**Accent Yellow:** `#F8C301`
|
||||
- Original brand color
|
||||
- Pair with white text (`#FFFFFF`)
|
||||
|
||||
**Secondary Colors:** Use sparingly
|
||||
- Green: `#28A745` for success actions
|
||||
- Red: `#DC3545` for warning/danger actions
|
||||
- Purple: `#6F42C1` for special campaigns
|
||||
|
||||
### Accessibility Requirements
|
||||
|
||||
- Ensure minimum 4.5:1 contrast ratio
|
||||
- Test with screen readers
|
||||
- Provide meaningful ARIA labels
|
||||
- Support keyboard navigation
|
||||
|
||||
## 🔄 Migration Guide
|
||||
|
||||
### From Global CSS Overrides
|
||||
|
||||
**Old approach:**
|
||||
```css
|
||||
/* This affected ALL buttons globally */
|
||||
.primary-cta-button .bg-[#F8C301] {
|
||||
background-color: #04045B !important;
|
||||
}
|
||||
```
|
||||
|
||||
**New approach:**
|
||||
```tsx
|
||||
// Use specific variant components
|
||||
<PrimaryCTAButtonBlue text="Get Started" onClick={handleClick} />
|
||||
|
||||
// Or unique CSS classes
|
||||
<PrimaryCTAButton text="Custom" onClick={handleClick} className="cta-custom-blue" />
|
||||
```
|
||||
|
||||
### From Direct Component Modification
|
||||
|
||||
**Old approach:**
|
||||
```tsx
|
||||
// Modifying main component (❌ PROHIBITED)
|
||||
// Never do this!
|
||||
```
|
||||
|
||||
**New approach:**
|
||||
```tsx
|
||||
// Use independent styling methods
|
||||
<PrimaryCTAButtonVariant
|
||||
variant="custom"
|
||||
customBackgroundColor="#your-color"
|
||||
text="Your Text"
|
||||
onClick={handleClick}
|
||||
/>
|
||||
```
|
||||
|
||||
## 🧪 Testing Checklist
|
||||
|
||||
When implementing independent Primary CTA buttons:
|
||||
|
||||
- [ ] Animations work correctly
|
||||
- [ ] Hover states display properly
|
||||
- [ ] Text contrast meets accessibility standards
|
||||
- [ ] Multiple buttons on same page don't interfere
|
||||
- [ ] Responsive behavior is maintained
|
||||
- [ ] Keyboard navigation works
|
||||
- [ ] Screen reader compatibility
|
||||
- [ ] Focus indicators are visible
|
||||
|
||||
## 📞 Support
|
||||
|
||||
For questions about the independent Primary CTA Button system:
|
||||
|
||||
1. Check this documentation first
|
||||
2. Review `/components/README-PrimaryCTAButton-Variants.md`
|
||||
3. Examine existing implementations in the codebase
|
||||
4. Follow component protection guidelines strictly
|
||||
|
||||
Remember: The main `/components/PrimaryCTAButton.tsx` file must NEVER be modified for styling changes. Always use the independent styling methods documented here.
|
||||
173
src/components/README-PrimaryCTAButton-Variants.md
Normal file
@@ -0,0 +1,173 @@
|
||||
# Primary CTA Button Variants
|
||||
|
||||
This document explains how to use the different Primary CTA Button variants available in the KLC website design system.
|
||||
|
||||
## Overview
|
||||
|
||||
To provide individual color control while preserving the sophisticated animations, we've created separate Primary CTA Button variant components. Each variant maintains all the original functionality and animations while allowing different color schemes.
|
||||
|
||||
## Available Components
|
||||
|
||||
### 1. Original PrimaryCTAButton (Protected)
|
||||
- **File**: `/components/PrimaryCTAButton.tsx`
|
||||
- **Status**: 🔒 PROTECTED - Never modify directly
|
||||
- **Default**: Yellow background (#F8C301) with white text
|
||||
|
||||
### 2. PrimaryCTAButtonYellow
|
||||
- **File**: `/components/PrimaryCTAButtonYellow.tsx`
|
||||
- **Purpose**: Maintains original yellow styling
|
||||
- **Colors**: Yellow background (#F8C301), White text (#FFFFFF)
|
||||
|
||||
### 3. PrimaryCTAButtonBlue
|
||||
- **File**: `/components/PrimaryCTAButtonBlue.tsx`
|
||||
- **Purpose**: Blue variant for brand consistency
|
||||
- **Colors**: Blue background (#04045B), Black text (#26231A)
|
||||
|
||||
### 4. PrimaryCTAButtonVariant
|
||||
- **File**: `/components/PrimaryCTAButtonVariant.tsx`
|
||||
- **Purpose**: Flexible component with variant and custom color support
|
||||
- **Variants**: 'yellow', 'blue', 'custom'
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Yellow Variant (Default Styling)
|
||||
```tsx
|
||||
import { PrimaryCTAButtonYellow } from './components/PrimaryCTAButtonYellow';
|
||||
|
||||
<PrimaryCTAButtonYellow
|
||||
text="Learn More"
|
||||
onClick={() => handleClick()}
|
||||
ariaLabel="Learn more about our services"
|
||||
/>
|
||||
```
|
||||
|
||||
### Blue Variant (Brand Blue)
|
||||
```tsx
|
||||
import { PrimaryCTAButtonBlue } from './components/PrimaryCTAButtonBlue';
|
||||
|
||||
<PrimaryCTAButtonBlue
|
||||
text="Get Started"
|
||||
onClick={() => handleClick()}
|
||||
ariaLabel="Get started with KLC"
|
||||
/>
|
||||
```
|
||||
|
||||
### Flexible Variant Component
|
||||
```tsx
|
||||
import { PrimaryCTAButtonVariant } from './components/PrimaryCTAButtonVariant';
|
||||
|
||||
// Yellow variant
|
||||
<PrimaryCTAButtonVariant
|
||||
variant="yellow"
|
||||
text="Default Yellow"
|
||||
onClick={handleClick}
|
||||
/>
|
||||
|
||||
// Blue variant
|
||||
<PrimaryCTAButtonVariant
|
||||
variant="blue"
|
||||
text="Brand Blue"
|
||||
onClick={handleClick}
|
||||
/>
|
||||
|
||||
// Custom colors
|
||||
<PrimaryCTAButtonVariant
|
||||
variant="custom"
|
||||
customBackgroundColor="#FF6B6B"
|
||||
customTextColor="#FFFFFF"
|
||||
customHoverBackgroundColor="#FF5252"
|
||||
text="Custom Red Button"
|
||||
onClick={handleClick}
|
||||
/>
|
||||
```
|
||||
|
||||
## Component Protection Policy
|
||||
|
||||
### 🔒 CRITICAL RULES
|
||||
|
||||
1. **NEVER modify `/components/PrimaryCTAButton.tsx`** - This is the protected base component
|
||||
2. **All variants preserve animations** - Sophisticated slide animations are maintained
|
||||
3. **Use variants for color changes** - Instead of modifying the main component
|
||||
4. **CSS overrides are component-specific** - Each variant has its own CSS classes
|
||||
|
||||
### ✅ APPROVED MODIFICATIONS
|
||||
|
||||
- ✅ Create new variant components
|
||||
- ✅ Use CSS classes for variant-specific styling
|
||||
- ✅ Extend functionality through wrapper components
|
||||
- ✅ Add custom color support via props
|
||||
|
||||
### ❌ PROHIBITED MODIFICATIONS
|
||||
|
||||
- ❌ Direct changes to main PrimaryCTAButton.tsx
|
||||
- ❌ Global CSS overrides affecting all buttons
|
||||
- ❌ Modifying animation structure or timing
|
||||
- ❌ Breaking accessibility features
|
||||
|
||||
## CSS Classes Used
|
||||
|
||||
### Blue Variant
|
||||
- `.primary-cta-button-blue` - Main blue variant class
|
||||
- Background: #04045B (Brand Blue)
|
||||
- Text: #26231A (Brand Black)
|
||||
- Hover Background: #030359 (Darker Blue)
|
||||
|
||||
### Yellow Variant
|
||||
- `.primary-cta-button-yellow` - Main yellow variant class
|
||||
- Preserves original component styling
|
||||
|
||||
### Custom Variant
|
||||
- `.primary-cta-button-custom` - Custom variant class
|
||||
- Uses CSS custom properties:
|
||||
- `--custom-bg-color`
|
||||
- `--custom-text-color`
|
||||
- `--custom-hover-bg-color`
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### Existing Code
|
||||
```tsx
|
||||
// Old approach - affects all buttons globally
|
||||
<PrimaryCTAButton text="Click Me" onClick={handleClick} />
|
||||
```
|
||||
|
||||
### New Approach
|
||||
```tsx
|
||||
// New approach - specific variant control
|
||||
<PrimaryCTAButtonBlue text="Click Me" onClick={handleClick} />
|
||||
// or
|
||||
<PrimaryCTAButtonYellow text="Click Me" onClick={handleClick} />
|
||||
// or
|
||||
<PrimaryCTAButtonVariant variant="blue" text="Click Me" onClick={handleClick} />
|
||||
```
|
||||
|
||||
## Benefits
|
||||
|
||||
1. **Individual Control**: Each button can have different colors
|
||||
2. **Animation Preservation**: All sophisticated animations maintained
|
||||
3. **Component Protection**: Main component remains untouched
|
||||
4. **Flexibility**: Support for custom colors when needed
|
||||
5. **Brand Consistency**: Pre-defined variants follow brand guidelines
|
||||
6. **Type Safety**: Full TypeScript support with proper prop types
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use specific variants** when you know the color scheme
|
||||
2. **Use PrimaryCTAButtonVariant** for dynamic or custom colors
|
||||
3. **Always provide meaningful ariaLabel** for accessibility
|
||||
4. **Test hover states** to ensure proper color contrast
|
||||
5. **Follow brand guidelines** when using custom colors
|
||||
|
||||
## Accessibility
|
||||
|
||||
All variants maintain the original accessibility features:
|
||||
- Proper ARIA labels
|
||||
- Keyboard navigation support
|
||||
- Focus indicators
|
||||
- Color contrast compliance (when using approved color combinations)
|
||||
|
||||
## Performance
|
||||
|
||||
- **No performance impact**: Variants are lightweight wrappers
|
||||
- **CSS optimization**: Specific classes prevent global overrides
|
||||
- **Animation performance**: All GPU-accelerated animations preserved
|
||||
223
src/components/README-PrimaryCTAButton.md
Normal file
@@ -0,0 +1,223 @@
|
||||
# Primary CTA Button - Design System Component
|
||||
|
||||
## Overview
|
||||
The `PrimaryCTAButton` is a sophisticated, reusable component that provides the exact same slide animations, design, and behavior as the hero section CTA button. It features advanced text and icon animations with dynamic width adjustment.
|
||||
|
||||
## Features
|
||||
|
||||
### ✨ Sophisticated Animations
|
||||
- **Text Slide Animation**: Current text slides up and fades out while identical text slides in from below (300ms ease-in-out)
|
||||
- **Arrow Icon Animation**: Arrow slides diagonally up-right and fades out while new arrow enters from bottom-left
|
||||
- **Hardware Accelerated**: Optimized with proper transforms and will-change properties
|
||||
|
||||
### 🎨 Design Specifications
|
||||
- **Icon**: Blue background (#04045B) with white arrow SVG, 50x50px container
|
||||
- **Text**: White Inter font, 20px size, 400 weight, with text shadows for visibility
|
||||
- **Responsive**: Automatically scales for mobile (48px icon) and mobile small (46px icon)
|
||||
- **Dynamic Width**: Button width adjusts automatically based on text length
|
||||
|
||||
### ⚙️ Technical Implementation
|
||||
- Uses the same SVG paths as the hero button
|
||||
- Maintains brand consistency with design system colors
|
||||
- Proper accessibility with ARIA labels and focus states
|
||||
- Cross-browser compatible animations
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Example
|
||||
```tsx
|
||||
import { PrimaryCTAButton } from './components/PrimaryCTAButton';
|
||||
|
||||
// Simple usage
|
||||
<PrimaryCTAButton
|
||||
text="Get Started Today"
|
||||
onClick={() => console.log('CTA clicked')}
|
||||
/>
|
||||
```
|
||||
|
||||
### With Custom aria-label
|
||||
```tsx
|
||||
<PrimaryCTAButton
|
||||
text="Learn More"
|
||||
onClick={() => navigate('/about')}
|
||||
ariaLabel="Learn more about our services"
|
||||
/>
|
||||
```
|
||||
|
||||
### With Additional Classes
|
||||
```tsx
|
||||
<PrimaryCTAButton
|
||||
text="Contact Us Now"
|
||||
onClick={() => window.location.href = '/contact'}
|
||||
className="mt-8 mx-auto"
|
||||
/>
|
||||
```
|
||||
|
||||
## Props Interface
|
||||
|
||||
```tsx
|
||||
interface PrimaryCTAButtonProps {
|
||||
text: string; // Button text content (required)
|
||||
onClick: () => void; // Click handler (required)
|
||||
ariaLabel?: string; // Custom aria-label (optional, defaults to text)
|
||||
className?: string; // Additional CSS classes (optional)
|
||||
}
|
||||
```
|
||||
|
||||
## Dynamic Width Calculation
|
||||
|
||||
The component automatically calculates the optimal width based on text length:
|
||||
- **Minimum Width**: 280px (ensures usability on mobile)
|
||||
- **Dynamic Calculation**: Icon width (50px) + gap (10px) + padding (32px) + estimated text width
|
||||
- **Text Width Estimation**: ~12px per character for 20px Inter font
|
||||
|
||||
### Width Examples:
|
||||
- `"Get Started"` (11 chars) → ~324px
|
||||
- `"Learn More About Us"` (18 chars) → ~372px
|
||||
- `"Build Your Leadership Pipeline"` (31 chars) → ~464px
|
||||
- `"Contact Our Team Today"` (21 chars) → ~384px
|
||||
|
||||
## Responsive Behavior
|
||||
|
||||
### Desktop (Default)
|
||||
- Icon: 50x50px
|
||||
- Text: 20px font size, 28px line height
|
||||
- Gap: 10px between icon and text
|
||||
|
||||
### Tablet (≤768px)
|
||||
- Icon: 48x48px
|
||||
- Text: 18px font size, 24px line height
|
||||
- Gap: 8px between icon and text
|
||||
|
||||
### Mobile (≤480px)
|
||||
- Icon: 46x46px
|
||||
- Text: 16px font size, 22px line height
|
||||
- Gap: 6px between icon and text
|
||||
|
||||
## Animation Details
|
||||
|
||||
### Text Animation
|
||||
1. **Initial State**: Primary text visible, secondary text positioned below (translateY(100%))
|
||||
2. **Hover State**: Primary text slides up (translateY(-100%)) and fades out
|
||||
3. **Simultaneous**: Secondary text slides to center (translateY(0)) and fades in
|
||||
4. **Duration**: 300ms with ease-in-out timing
|
||||
5. **Reverse**: Perfect reverse animation on mouse leave
|
||||
|
||||
### Icon Animation
|
||||
1. **Initial State**: Primary arrow visible, secondary arrow positioned bottom-left
|
||||
2. **Hover State**: Primary arrow slides up-right diagonally (translateX(24px), translateY(-24px)) and fades out
|
||||
3. **Simultaneous**: Secondary arrow slides to center from bottom-left and fades in
|
||||
4. **Duration**: 300ms with ease-in-out timing
|
||||
5. **Background**: Consistent blue background throughout (no color change)
|
||||
|
||||
## Design System Integration
|
||||
|
||||
### Colors Used
|
||||
- **Icon Background**: `#04045B` (Brand Primary Blue)
|
||||
- **Icon/Arrow**: `#FFFFFF` (White)
|
||||
- **Text**: `#FFFFFF` (White with shadow)
|
||||
- **Text Shadow**: `0 1px 2px rgba(0, 0, 0, 0.4)`
|
||||
|
||||
### Typography
|
||||
- **Font**: Inter (design system font)
|
||||
- **Size**: 20px (desktop), 18px (tablet), 16px (mobile)
|
||||
- **Weight**: 400 (Regular)
|
||||
- **Line Height**: 28px (desktop), 24px (tablet), 22px (mobile)
|
||||
|
||||
### Accessibility
|
||||
- **Focus Ring**: 3px rgba(248, 195, 1, 0.3) outline
|
||||
- **ARIA Support**: Proper aria-label attributes
|
||||
- **Keyboard Navigation**: Full keyboard accessibility
|
||||
- **Screen Readers**: Semantic button element with descriptive labels
|
||||
|
||||
## Example Scenarios
|
||||
|
||||
### Hero Sections
|
||||
```tsx
|
||||
<PrimaryCTAButton
|
||||
text="Transform Your Leadership"
|
||||
onClick={() => navigate('/leadership-program')}
|
||||
ariaLabel="Start your leadership transformation journey"
|
||||
/>
|
||||
```
|
||||
|
||||
### Service Pages
|
||||
```tsx
|
||||
<PrimaryCTAButton
|
||||
text="Book a Consultation"
|
||||
onClick={() => setShowBookingModal(true)}
|
||||
/>
|
||||
```
|
||||
|
||||
### Landing Pages
|
||||
```tsx
|
||||
<PrimaryCTAButton
|
||||
text="Download Free Guide"
|
||||
onClick={() => triggerDownload()}
|
||||
className="mt-6"
|
||||
/>
|
||||
```
|
||||
|
||||
### Contact Forms
|
||||
```tsx
|
||||
<PrimaryCTAButton
|
||||
text="Send Message"
|
||||
onClick={handleSubmit}
|
||||
ariaLabel="Send your message to our team"
|
||||
/>
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Text Content
|
||||
- **Length**: Keep text concise (2-4 words ideal)
|
||||
- **Action-Oriented**: Use verbs ("Get", "Start", "Learn", "Download")
|
||||
- **Clear Value**: Communicate what happens when clicked
|
||||
- **Consistent Tone**: Match your brand voice
|
||||
|
||||
### Implementation
|
||||
- **Single CTA**: Use only one Primary CTA per section for maximum impact
|
||||
- **Strategic Placement**: Position at natural conversion points
|
||||
- **Sufficient Spacing**: Provide adequate whitespace around button
|
||||
- **Context Relevant**: Ensure CTA matches the surrounding content
|
||||
|
||||
### Accessibility
|
||||
- **Descriptive Labels**: Use clear, descriptive aria-labels when needed
|
||||
- **Keyboard Focus**: Ensure button is reachable via keyboard navigation
|
||||
- **Color Contrast**: White text on blue background meets WCAG AA standards
|
||||
- **Touch Targets**: Minimum 44px height maintained on mobile
|
||||
|
||||
## CSS Classes Reference
|
||||
|
||||
### Component Classes
|
||||
- `.primary-cta-button` - Main button container
|
||||
- `.icon-layer` - Icon animation container
|
||||
- `.text-layer` - Text animation container
|
||||
- `.text-element` - Individual text instances
|
||||
|
||||
### State Classes
|
||||
- `.group` - Enables group-hover functionality
|
||||
- `.group-hover:*` - Triggered animation states
|
||||
|
||||
### Responsive Classes
|
||||
- Media queries handle automatic responsive scaling
|
||||
- No additional classes needed for responsive behavior
|
||||
|
||||
## Integration Notes
|
||||
|
||||
When implementing "Update as per Primary CTA Button":
|
||||
|
||||
1. **Replace existing button** with `<PrimaryCTAButton>`
|
||||
2. **Update text prop** with new button content
|
||||
3. **Adjust onClick handler** for new functionality
|
||||
4. **Add custom aria-label** if button text isn't descriptive enough
|
||||
5. **Apply additional classes** if positioning adjustments needed
|
||||
|
||||
The component will automatically handle:
|
||||
- ✅ Width adjustment for new text
|
||||
- ✅ Responsive scaling
|
||||
- ✅ Animation consistency
|
||||
- ✅ Design system compliance
|
||||
- ✅ Accessibility requirements
|
||||
|
||||
This ensures perfect consistency across all Primary CTA buttons while maintaining the sophisticated slide animations that enhance user engagement.
|
||||
337
src/components/README-ReusableCarousel.md
Normal file
@@ -0,0 +1,337 @@
|
||||
# Reusable Carousel Component Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
The `ReusableCarousel` component provides a consistent, sophisticated carousel design that can be used across different sections of the website. It maintains the same design pattern as the "Our Services" section but allows for customizable content, styling, and functionality.
|
||||
|
||||
## Key Features
|
||||
|
||||
- **Consistent Design Language**: Maintains the same layout, spacing, and visual hierarchy
|
||||
- **Flexible Content**: Accepts any data structure with customizable card rendering
|
||||
- **Responsive Navigation**: Built-in carousel controls with proper accessibility
|
||||
- **Customizable Styling**: Adjustable card dimensions, spacing, and layout
|
||||
- **CTA Integration**: Built-in call-to-action button with custom configuration
|
||||
- **Pre-built Variants**: Ready-to-use components for common use cases
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### 1. Import the Component
|
||||
|
||||
```tsx
|
||||
import { ReusableCarousel, CarouselItem, CarouselConfig } from '../ReusableCarousel';
|
||||
```
|
||||
|
||||
### 2. Prepare Your Data
|
||||
|
||||
```tsx
|
||||
const myItems: CarouselItem[] = [
|
||||
{
|
||||
title: "Item Title",
|
||||
description: "Item description text",
|
||||
icon: MyIcon, // Lucide React icon component
|
||||
features: ["Feature 1", "Feature 2", "Feature 3"],
|
||||
outcome: "Expected outcome description"
|
||||
},
|
||||
// ... more items
|
||||
];
|
||||
```
|
||||
|
||||
### 3. Configure the Carousel
|
||||
|
||||
```tsx
|
||||
const config: CarouselConfig = {
|
||||
eyebrowText: "SECTION CATEGORY",
|
||||
title: "Section Title",
|
||||
description: "Section description that appears below the title",
|
||||
ctaText: "Call to Action Text",
|
||||
ctaAction: () => navigateTo('/target-page'),
|
||||
ctaAriaLabel: "Accessible description for the CTA button"
|
||||
};
|
||||
```
|
||||
|
||||
### 4. Render the Carousel
|
||||
|
||||
```tsx
|
||||
<ReusableCarousel
|
||||
items={myItems}
|
||||
config={config}
|
||||
/>
|
||||
```
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
### Custom Card Rendering
|
||||
|
||||
For specialized card layouts, provide a custom renderer:
|
||||
|
||||
```tsx
|
||||
const customRenderer = (item: CarouselItem, index: number) => (
|
||||
<Card className="custom-card-styles">
|
||||
<CardContent className="p-6">
|
||||
{/* Your custom card content */}
|
||||
<h3>{item.title}</h3>
|
||||
<p>{item.description}</p>
|
||||
{/* Add any custom fields from your CarouselItem */}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
|
||||
<ReusableCarousel
|
||||
items={myItems}
|
||||
config={config}
|
||||
customCardRenderer={customRenderer}
|
||||
/>
|
||||
```
|
||||
|
||||
### Styling Customization
|
||||
|
||||
```tsx
|
||||
<ReusableCarousel
|
||||
items={myItems}
|
||||
config={config}
|
||||
className="my-custom-section" // Additional CSS classes
|
||||
cardWidth="80%" // Adjust card width (default: "70%")
|
||||
cardSpacing="mr-8" // Adjust spacing between cards (default: "mr-7")
|
||||
featuresTitle="Custom Features Title:" // Override features section title
|
||||
outcomesTitle="Custom Outcomes Title:" // Override outcomes section title
|
||||
/>
|
||||
```
|
||||
|
||||
## Pre-built Variants
|
||||
|
||||
For common use cases, use the pre-configured variants:
|
||||
|
||||
### Consulting Services Carousel
|
||||
|
||||
```tsx
|
||||
import { ConsultingServicesCarousel } from '../ReusableCarousel';
|
||||
|
||||
<ConsultingServicesCarousel consultingServices={consultingData} />
|
||||
```
|
||||
|
||||
### Leadership Programs Carousel
|
||||
|
||||
```tsx
|
||||
import { LeadershipProgramsCarousel } from '../ReusableCarousel';
|
||||
|
||||
<LeadershipProgramsCarousel leadershipPrograms={programsData} />
|
||||
```
|
||||
|
||||
### Expert Services Carousel
|
||||
|
||||
```tsx
|
||||
import { ExpertServicesCarousel } from '../ReusableCarousel';
|
||||
|
||||
<ExpertServicesCarousel expertServices={servicesData} />
|
||||
```
|
||||
|
||||
### Platform Features Carousel
|
||||
|
||||
```tsx
|
||||
import { PlatformFeaturesCarousel } from '../ReusableCarousel';
|
||||
|
||||
<PlatformFeaturesCarousel platformFeatures={featuresData} />
|
||||
```
|
||||
|
||||
## Data Structure Requirements
|
||||
|
||||
### CarouselItem Interface
|
||||
|
||||
```tsx
|
||||
interface CarouselItem {
|
||||
title: string; // Required: Item title
|
||||
description: string; // Required: Item description
|
||||
icon: React.ComponentType<any>; // Required: Lucide React icon
|
||||
features?: string[]; // Optional: Array of feature bullets
|
||||
outcome?: string; // Optional: Expected outcome text
|
||||
[key: string]: any; // Additional custom properties
|
||||
}
|
||||
```
|
||||
|
||||
### CarouselConfig Interface
|
||||
|
||||
```tsx
|
||||
interface CarouselConfig {
|
||||
eyebrowText: string; // Branded tag text (e.g., "CONSULTING SERVICES")
|
||||
title: string; // Main section title
|
||||
description: string; // Section description
|
||||
ctaText: string; // CTA button text
|
||||
ctaAction: () => void; // CTA button click handler
|
||||
ctaAriaLabel: string; // Accessibility label for CTA
|
||||
}
|
||||
```
|
||||
|
||||
## Real-World Examples
|
||||
|
||||
### Creating a Training Programs Carousel
|
||||
|
||||
```tsx
|
||||
// 1. Define your data
|
||||
const trainingPrograms: CarouselItem[] = [
|
||||
{
|
||||
title: "Executive Leadership Program",
|
||||
description: "Comprehensive leadership development for senior executives",
|
||||
icon: Crown,
|
||||
features: [
|
||||
"Strategic thinking development",
|
||||
"Executive presence coaching",
|
||||
"Board governance training",
|
||||
"Crisis leadership skills"
|
||||
],
|
||||
outcome: "Enhanced executive effectiveness and organizational impact within 6 months"
|
||||
},
|
||||
// ... more programs
|
||||
];
|
||||
|
||||
// 2. Configure the carousel
|
||||
const trainingConfig: CarouselConfig = {
|
||||
eyebrowText: "TRAINING PROGRAMS",
|
||||
title: "Leadership Training Excellence",
|
||||
description: "Comprehensive training programs designed to develop leadership capabilities at every organizational level.",
|
||||
ctaText: "Explore Training Programs",
|
||||
ctaAction: () => navigateTo('/training'),
|
||||
ctaAriaLabel: "Explore our leadership training programs"
|
||||
};
|
||||
|
||||
// 3. Use in your component
|
||||
export function TrainingSection() {
|
||||
return (
|
||||
<section className="py-24" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<ReusableCarousel
|
||||
items={trainingPrograms}
|
||||
config={trainingConfig}
|
||||
featuresTitle="Program Highlights:"
|
||||
outcomesTitle="Program Results:"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Creating a Technology Solutions Carousel
|
||||
|
||||
```tsx
|
||||
const techSolutions: CarouselItem[] = [
|
||||
{
|
||||
title: "Learning Management System",
|
||||
description: "Advanced LMS platform for enterprise learning",
|
||||
icon: Monitor,
|
||||
features: [
|
||||
"Cloud-based infrastructure",
|
||||
"Mobile-responsive design",
|
||||
"Advanced analytics",
|
||||
"Integration capabilities"
|
||||
],
|
||||
outcome: "Improved learning engagement and completion rates",
|
||||
// Custom properties for this use case
|
||||
deployment: "Cloud or On-premise",
|
||||
integration: "API-first architecture"
|
||||
},
|
||||
// ... more solutions
|
||||
];
|
||||
|
||||
// Custom renderer for technology-specific cards
|
||||
const techRenderer = (item: CarouselItem, index: number) => (
|
||||
<Card className="border border-gray-200 h-full overflow-hidden">
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center gap-4 mb-4">
|
||||
<div className="w-14 h-14 rounded-xl bg-blue-100 flex items-center justify-center">
|
||||
{React.createElement(item.icon, { className: "w-7 h-7 text-blue-600" })}
|
||||
</div>
|
||||
<h3 className="text-h4 text-black">{item.title}</h3>
|
||||
</div>
|
||||
|
||||
<p className="text-body text-muted mb-6">{item.description}</p>
|
||||
|
||||
{/* Custom technology info */}
|
||||
<div className="bg-gray-50 rounded-lg p-4 mb-6">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<span className="text-small font-medium text-black">Deployment:</span>
|
||||
<span className="text-small text-muted ml-2">{item.deployment}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-small font-medium text-black">Integration:</span>
|
||||
<span className="text-small text-muted ml-2">{item.integration}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Standard features section */}
|
||||
{item.features && (
|
||||
<div className="mb-6">
|
||||
<h4 className="text-subhead text-black mb-4">Key Features:</h4>
|
||||
<div className="space-y-2">
|
||||
{item.features.map((feature: string, idx: number) => (
|
||||
<div key={idx} className="flex items-center gap-2">
|
||||
<CheckCircle className="w-4 h-4 text-blue-600" />
|
||||
<span className="text-small text-muted">{feature}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
|
||||
// Usage with custom renderer
|
||||
<ReusableCarousel
|
||||
items={techSolutions}
|
||||
config={techConfig}
|
||||
customCardRenderer={techRenderer}
|
||||
cardWidth="75%"
|
||||
cardSpacing="mr-6"
|
||||
/>
|
||||
```
|
||||
|
||||
## Design Guidelines
|
||||
|
||||
### When to Use
|
||||
|
||||
- **Multiple Related Items**: When you have 3+ related items to showcase
|
||||
- **Detailed Information**: When each item needs substantial description and features
|
||||
- **Call-to-Action Flow**: When the section should lead to a specific action
|
||||
- **Consistent Branding**: When you want to maintain the established design language
|
||||
|
||||
### When NOT to Use
|
||||
|
||||
- **Simple Lists**: For basic lists without detailed descriptions
|
||||
- **Static Content**: For content that doesn't benefit from carousel navigation
|
||||
- **Single Items**: For individual content pieces
|
||||
- **Grid Layouts**: When users need to see all items simultaneously
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. **Content Length**: Keep titles concise (2-6 words), descriptions to 1-2 sentences
|
||||
2. **Feature Lists**: Limit to 4-6 features per item for optimal readability
|
||||
3. **Icons**: Use consistent icon style from Lucide React library
|
||||
4. **Outcomes**: Make outcomes specific and measurable when possible
|
||||
5. **CTA Alignment**: Ensure CTA leads to relevant, valuable content
|
||||
|
||||
## Accessibility Features
|
||||
|
||||
- **Keyboard Navigation**: Full keyboard support for carousel controls
|
||||
- **ARIA Labels**: Proper accessibility labels for all interactive elements
|
||||
- **Focus Management**: Clear focus indicators and logical tab order
|
||||
- **Screen Readers**: Semantic HTML structure for assistive technologies
|
||||
|
||||
## Browser Support
|
||||
|
||||
- **Modern Browsers**: Full support in Chrome, Firefox, Safari, Edge
|
||||
- **Responsive Design**: Optimized for all device sizes
|
||||
- **Performance**: Optimized animations and minimal re-renders
|
||||
- **Fallbacks**: Graceful degradation for older browsers
|
||||
|
||||
## Contributing
|
||||
|
||||
When extending the reusable carousel:
|
||||
|
||||
1. **Maintain Consistency**: Keep the established design patterns
|
||||
2. **Test Accessibility**: Ensure all new features are accessible
|
||||
3. **Document Changes**: Update this documentation for new features
|
||||
4. **Performance**: Consider performance impact of customizations
|
||||
|
||||
This reusable carousel system allows you to quickly implement consistent, professional carousels across any section while maintaining design cohesion and user experience standards.
|
||||
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:"
|
||||
/>
|
||||
);
|
||||
}
|
||||
90
src/components/Router.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
// Enhanced router for reliable navigation with proper browser integration
|
||||
let currentPath = window.location.pathname + window.location.search || '/';
|
||||
const listeners: (() => void)[] = [];
|
||||
|
||||
export function navigateTo(path: string) {
|
||||
try {
|
||||
console.log(`Navigating to: ${path}`);
|
||||
|
||||
// Update current path
|
||||
currentPath = path;
|
||||
|
||||
// Update browser URL and history
|
||||
if (window.history && window.history.pushState) {
|
||||
window.history.pushState({ path }, '', path);
|
||||
} else {
|
||||
// Fallback for older browsers
|
||||
window.location.href = path;
|
||||
return;
|
||||
}
|
||||
|
||||
// Force a state change event
|
||||
window.dispatchEvent(new PopStateEvent('popstate', { state: { path } }));
|
||||
|
||||
// Notify all listeners of the route change
|
||||
listeners.forEach(listener => {
|
||||
try {
|
||||
listener();
|
||||
} catch (error) {
|
||||
console.error('Error in route listener:', error);
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Navigation error:', error);
|
||||
// Ultimate fallback
|
||||
window.location.href = path;
|
||||
}
|
||||
}
|
||||
|
||||
export function getCurrentPath() {
|
||||
try {
|
||||
// Always get the current path from the browser URL
|
||||
const path = window.location.pathname + window.location.search;
|
||||
currentPath = path;
|
||||
return path;
|
||||
} catch (error) {
|
||||
console.error('Error getting current path:', error);
|
||||
return currentPath;
|
||||
}
|
||||
}
|
||||
|
||||
export function addRouteListener(listener: () => void) {
|
||||
listeners.push(listener);
|
||||
|
||||
// Listen for browser back/forward button events
|
||||
const handlePopState = (event: PopStateEvent) => {
|
||||
try {
|
||||
currentPath = window.location.pathname + window.location.search;
|
||||
listener();
|
||||
} catch (error) {
|
||||
console.error('Error in popstate handler:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Listen for our custom navigation events
|
||||
const handleNavigation = () => {
|
||||
try {
|
||||
listener();
|
||||
} catch (error) {
|
||||
console.error('Error in navigation handler:', error);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('popstate', handlePopState);
|
||||
window.addEventListener('navigation', handleNavigation);
|
||||
|
||||
return () => {
|
||||
const index = listeners.indexOf(listener);
|
||||
if (index > -1) {
|
||||
listeners.splice(index, 1);
|
||||
}
|
||||
window.removeEventListener('popstate', handlePopState);
|
||||
window.removeEventListener('navigation', handleNavigation);
|
||||
};
|
||||
}
|
||||
|
||||
// Helper function to trigger navigation events
|
||||
export function triggerNavigationUpdate() {
|
||||
window.dispatchEvent(new CustomEvent('navigation'));
|
||||
}
|
||||
586
src/components/SelfLearnerSignIn.tsx
Normal file
@@ -0,0 +1,586 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Button } from './ui/button';
|
||||
import { Input } from './ui/input';
|
||||
import { Label } from './ui/label';
|
||||
import { Card, CardContent } from './ui/card';
|
||||
import { Checkbox } from './ui/checkbox';
|
||||
import {
|
||||
BookOpen,
|
||||
Users,
|
||||
Target,
|
||||
Award,
|
||||
ArrowRight,
|
||||
Eye,
|
||||
EyeOff,
|
||||
User
|
||||
} from 'lucide-react';
|
||||
import { navigateTo } from './Router';
|
||||
|
||||
export function SelfLearnerSignIn() {
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [rememberMe, setRememberMe] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const handleSignIn = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setIsLoading(true);
|
||||
|
||||
// Simulate authentication
|
||||
setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
// Navigate to dashboard or success page
|
||||
navigateTo('/dashboard');
|
||||
}, 1500);
|
||||
};
|
||||
|
||||
const transformationFeatures = [
|
||||
{
|
||||
icon: BookOpen,
|
||||
title: 'World-Class Content',
|
||||
description: 'Access premium leadership courses from industry experts'
|
||||
},
|
||||
{
|
||||
icon: Users,
|
||||
title: 'Global Community',
|
||||
description: 'Join 25,000+ leaders from top organizations worldwide'
|
||||
},
|
||||
{
|
||||
icon: Award,
|
||||
title: 'Recognized Credentials',
|
||||
description: 'Earn certificates valued by Fortune 500 companies'
|
||||
},
|
||||
{
|
||||
icon: Target,
|
||||
title: 'Personalized Path',
|
||||
description: 'Get customized learning recommendations based on your goals'
|
||||
}
|
||||
];
|
||||
|
||||
const learningFeatures = [
|
||||
{
|
||||
icon: BookOpen,
|
||||
title: 'Expert-Led Programs',
|
||||
description: 'Learn from industry leaders and world-class faculty'
|
||||
},
|
||||
{
|
||||
icon: Users,
|
||||
title: 'Peer Learning Network',
|
||||
description: 'Connect with like-minded professionals and expand your network'
|
||||
},
|
||||
{
|
||||
icon: Target,
|
||||
title: 'Personalized Growth',
|
||||
description: 'Tailored learning paths to accelerate your leadership journey'
|
||||
},
|
||||
{
|
||||
icon: Award,
|
||||
title: 'Recognized Certification',
|
||||
description: 'Industry-recognized credentials to advance your career'
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={{ backgroundColor: '#FFFFFF' }}>
|
||||
{/* Main Content */}
|
||||
<div className="section-margin-x py-16">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
|
||||
{/* Header Section */}
|
||||
<div className="text-center mb-12">
|
||||
<div className="mb-6">
|
||||
<span
|
||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-full text-sm font-medium"
|
||||
style={{
|
||||
backgroundColor: 'rgba(248, 195, 1, 0.15)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
<User className="w-4 h-4" />
|
||||
Individual Learning
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h1
|
||||
className="mb-6"
|
||||
style={{
|
||||
fontSize: 'var(--font-h2)',
|
||||
fontWeight: 'var(--font-weight-h2)',
|
||||
lineHeight: 'var(--line-height-h2)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Welcome Back to Your Leadership Journey
|
||||
</h1>
|
||||
|
||||
<p
|
||||
className="mb-12 max-w-2xl mx-auto"
|
||||
style={{
|
||||
fontSize: 'var(--font-body-lg)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-body-lg)'
|
||||
}}
|
||||
>
|
||||
Continue your transformation with KLC's world-class leadership development
|
||||
programs. Access your personalized learning dashboard and connect with a global
|
||||
community of leaders.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Two Column Layout */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-start">
|
||||
|
||||
{/* Left Column - Content */}
|
||||
<div>
|
||||
{/* Leadership Transformation Section */}
|
||||
<div className="mb-12">
|
||||
<div className="mb-6">
|
||||
<span
|
||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-full text-sm font-medium"
|
||||
style={{
|
||||
backgroundColor: 'rgba(248, 195, 1, 0.15)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
⭐ Join KLC Community
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h2
|
||||
className="mb-4"
|
||||
style={{
|
||||
fontSize: 'var(--font-h3)',
|
||||
fontWeight: 'var(--font-weight-h3)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Start Your Leadership Transformation
|
||||
</h2>
|
||||
|
||||
<p
|
||||
className="mb-8"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-body)'
|
||||
}}
|
||||
>
|
||||
Join thousands of professionals who have accelerated their careers through KLC's
|
||||
proven leadership development programs. Your journey to becoming an exceptional leader starts here.
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{transformationFeatures.map((feature, index) => (
|
||||
<div key={index} className="flex items-start gap-3">
|
||||
<div
|
||||
className="w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0"
|
||||
style={{ backgroundColor: 'var(--color-primary)' }}
|
||||
>
|
||||
<feature.icon className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3
|
||||
className="font-medium mb-1"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{feature.title}
|
||||
</h3>
|
||||
<p
|
||||
style={{
|
||||
fontSize: '0.75rem',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: '1.3'
|
||||
}}
|
||||
>
|
||||
{feature.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Your Learning Experience */}
|
||||
<div>
|
||||
<h2
|
||||
className="mb-8"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Your Learning Experience
|
||||
</h2>
|
||||
|
||||
<div className="space-y-6">
|
||||
{learningFeatures.map((feature, index) => (
|
||||
<div key={index} className="flex items-start gap-4">
|
||||
<div
|
||||
className="w-12 h-12 rounded-lg flex items-center justify-center flex-shrink-0"
|
||||
style={{ backgroundColor: 'var(--color-primary)' }}
|
||||
>
|
||||
<feature.icon className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3
|
||||
className="font-medium mb-2"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{feature.title}
|
||||
</h3>
|
||||
<p
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-small)'
|
||||
}}
|
||||
>
|
||||
{feature.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Existing Account Note */}
|
||||
<div
|
||||
className="mt-8 p-4 rounded-lg border"
|
||||
style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}
|
||||
>
|
||||
<p
|
||||
className="mb-2"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontWeight: '500',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Already have an account?
|
||||
</p>
|
||||
<div className="space-y-2">
|
||||
<button
|
||||
onClick={() => navigateTo('/self-learner-signin')}
|
||||
className="block text-blue-600 hover:text-blue-700 transition-colors"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Sign in to your account
|
||||
</button>
|
||||
<button
|
||||
onClick={() => navigateTo('/corporate-login')}
|
||||
className="block text-blue-600 hover:text-blue-700 transition-colors"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Corporate sign in
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Column - Sign In Form */}
|
||||
<div>
|
||||
<Card className="shadow-lg border-0">
|
||||
<CardContent className="p-8">
|
||||
|
||||
{/* Form Header */}
|
||||
<div className="text-center mb-8">
|
||||
<h2
|
||||
className="mb-2"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Welcome Back
|
||||
</h2>
|
||||
<p
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Sign in to your KLC account
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Sign In Form */}
|
||||
<form onSubmit={handleSignIn} className="space-y-6">
|
||||
|
||||
{/* Email Field */}
|
||||
<div>
|
||||
<Label
|
||||
htmlFor="email"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontWeight: '500',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Email Address *
|
||||
</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="you@example.com"
|
||||
required
|
||||
className="mt-2 h-12"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Password Field */}
|
||||
<div>
|
||||
<Label
|
||||
htmlFor="password"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontWeight: '500',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Password *
|
||||
</Label>
|
||||
<div className="relative mt-2">
|
||||
<Input
|
||||
id="password"
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder="Enter your password"
|
||||
required
|
||||
className="h-12 pr-12"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-500 hover:text-gray-700"
|
||||
>
|
||||
{showPassword ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Remember Me & Forgot Password */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="remember"
|
||||
checked={rememberMe}
|
||||
onCheckedChange={setRememberMe}
|
||||
/>
|
||||
<Label
|
||||
htmlFor="remember"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Remember me
|
||||
</Label>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => navigateTo('/forgot-password')}
|
||||
className="text-blue-600 hover:text-blue-700 transition-colors"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Forgot password?
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Sign In Button */}
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={isLoading}
|
||||
className="w-full h-14 text-white font-medium flex items-center justify-center gap-2"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-primary)',
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
borderRadius: '10px'
|
||||
}}
|
||||
>
|
||||
{isLoading ? (
|
||||
<div className="w-5 h-5 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
||||
) : (
|
||||
<>
|
||||
Sign In
|
||||
<ArrowRight className="w-5 h-5" />
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
{/* Demo Credentials */}
|
||||
<div
|
||||
className="mt-8 p-4 rounded-lg border"
|
||||
style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}
|
||||
>
|
||||
<h4
|
||||
className="mb-2 flex items-center gap-2"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontWeight: '500',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
<div className="w-4 h-4 rounded-full bg-gray-300 flex items-center justify-center">
|
||||
<span className="text-xs text-gray-600">i</span>
|
||||
</div>
|
||||
Demo credentials:
|
||||
</h4>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-small)'
|
||||
}}
|
||||
>
|
||||
Email: demo@klc.edu.in | Password: demo123
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sign Up Links */}
|
||||
<div className="text-center mt-8 space-y-3">
|
||||
<div>
|
||||
<span
|
||||
className="text-gray-600"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Don't have an account?{' '}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => navigateTo('/self-learner-signup')}
|
||||
className="text-blue-600 hover:text-blue-700 transition-colors font-medium"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Sign up here
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<span
|
||||
className="text-gray-600"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Looking for corporate access?{' '}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => navigateTo('/corporate-login')}
|
||||
className="text-blue-600 hover:text-blue-700 transition-colors font-medium"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Corporate Sign In
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Need Help Section - Separate Section Below */}
|
||||
<div
|
||||
className="section-margin-x py-12"
|
||||
style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}
|
||||
>
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<h3
|
||||
className="mb-4"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Need Help Getting Started?
|
||||
</h3>
|
||||
<p
|
||||
className="mb-6"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-body)'
|
||||
}}
|
||||
>
|
||||
Our learning advisors are here to help you choose the right programs and get the most out of your leadership development experience.
|
||||
</p>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="border-2 border-blue-600 text-blue-600 hover:bg-blue-600 hover:text-white"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontWeight: '500',
|
||||
padding: '0.75rem 2rem',
|
||||
borderRadius: '10px'
|
||||
}}
|
||||
>
|
||||
Speak with Learning Advisor
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
504
src/components/SelfLearnerSignUp.tsx
Normal file
@@ -0,0 +1,504 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Button } from './ui/button';
|
||||
import { Input } from './ui/input';
|
||||
import { Label } from './ui/label';
|
||||
import { Card, CardContent } from './ui/card';
|
||||
import { Checkbox } from './ui/checkbox';
|
||||
import {
|
||||
BookOpen,
|
||||
Users,
|
||||
Target,
|
||||
Award,
|
||||
ArrowRight,
|
||||
Eye,
|
||||
EyeOff,
|
||||
User,
|
||||
Mail,
|
||||
Lock,
|
||||
Phone
|
||||
} from 'lucide-react';
|
||||
import { navigateTo } from './Router';
|
||||
|
||||
export function SelfLearnerSignUp() {
|
||||
const [formData, setFormData] = useState({
|
||||
fullName: '',
|
||||
email: '',
|
||||
password: '',
|
||||
phoneNumber: ''
|
||||
});
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [agreeToTerms, setAgreeToTerms] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const handleInputChange = (field: string, value: string) => {
|
||||
setFormData(prev => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
const handleSignUp = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!agreeToTerms) {
|
||||
alert('Please agree to the Terms of Service and Privacy Policy');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
// Simulate registration
|
||||
setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
// Navigate to dashboard or success page
|
||||
navigateTo('/dashboard');
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
const transformationFeatures = [
|
||||
{
|
||||
icon: BookOpen,
|
||||
title: 'World-Class Content',
|
||||
description: 'Access premium leadership courses from industry experts'
|
||||
},
|
||||
{
|
||||
icon: Users,
|
||||
title: 'Global Community',
|
||||
description: 'Join 25,000+ leaders from top organizations worldwide'
|
||||
},
|
||||
{
|
||||
icon: Award,
|
||||
title: 'Recognized Credentials',
|
||||
description: 'Earn certificates valued by Fortune 500 companies'
|
||||
},
|
||||
{
|
||||
icon: Target,
|
||||
title: 'Personalized Path',
|
||||
description: 'Get customized learning recommendations based on your goals'
|
||||
}
|
||||
];
|
||||
|
||||
const learningFeatures = [
|
||||
{
|
||||
icon: BookOpen,
|
||||
title: 'World-Class Content',
|
||||
description: 'Access cutting-edge leadership programs from industry experts and renowned faculty'
|
||||
},
|
||||
{
|
||||
icon: Users,
|
||||
title: 'Global Network',
|
||||
description: 'Connect with ambitious leaders worldwide and build lasting professional relationships'
|
||||
},
|
||||
{
|
||||
icon: Target,
|
||||
title: 'Personalized Learning',
|
||||
description: 'Get customized learning paths based on your goals and leadership style'
|
||||
},
|
||||
{
|
||||
icon: Award,
|
||||
title: 'Recognized Credentials',
|
||||
description: 'Earn certificates and credentials that advance your career and open new opportunities'
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={{ backgroundColor: '#FFFFFF' }}>
|
||||
{/* Main Content */}
|
||||
<div className="section-margin-x py-16">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
|
||||
{/* Two Column Layout */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-start">
|
||||
|
||||
{/* Left Column - Content */}
|
||||
<div>
|
||||
{/* Leadership Transformation Section */}
|
||||
<div className="mb-12">
|
||||
<div className="mb-6">
|
||||
<span
|
||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-full text-sm font-medium"
|
||||
style={{
|
||||
backgroundColor: 'rgba(248, 195, 1, 0.15)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
⭐ Join KLC Community
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h2
|
||||
className="mb-4"
|
||||
style={{
|
||||
fontSize: 'var(--font-h3)',
|
||||
fontWeight: 'var(--font-weight-h3)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Start Your Leadership Transformation
|
||||
</h2>
|
||||
|
||||
<p
|
||||
className="mb-8"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-body)'
|
||||
}}
|
||||
>
|
||||
Join thousands of professionals who have accelerated their careers through KLC's
|
||||
proven leadership development programs. Your journey to becoming an exceptional leader starts here.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Your Learning Experience Awaits */}
|
||||
<div>
|
||||
<h2
|
||||
className="mb-8"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Your Learning Experience Awaits
|
||||
</h2>
|
||||
|
||||
<div className="space-y-6">
|
||||
{learningFeatures.map((feature, index) => (
|
||||
<div key={index} className="flex items-start gap-4">
|
||||
<div
|
||||
className="w-12 h-12 rounded-lg flex items-center justify-center flex-shrink-0"
|
||||
style={{ backgroundColor: 'var(--color-primary)' }}
|
||||
>
|
||||
<feature.icon className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3
|
||||
className="font-medium mb-2"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{feature.title}
|
||||
</h3>
|
||||
<p
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-small)'
|
||||
}}
|
||||
>
|
||||
{feature.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Column - Sign Up Form */}
|
||||
<div>
|
||||
<Card className="shadow-lg border-0">
|
||||
<CardContent className="p-8">
|
||||
|
||||
{/* Form Header */}
|
||||
<div className="text-center mb-8">
|
||||
<h2
|
||||
className="mb-2"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Create Your Account
|
||||
</h2>
|
||||
<p
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Join KLC and start your leadership journey
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Sign Up Form */}
|
||||
<form onSubmit={handleSignUp} className="space-y-6">
|
||||
|
||||
{/* Full Name */}
|
||||
<div>
|
||||
<Label
|
||||
htmlFor="fullName"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontWeight: '500',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Full Name *
|
||||
</Label>
|
||||
<div className="relative mt-2">
|
||||
<div className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500">
|
||||
<User className="w-5 h-5" />
|
||||
</div>
|
||||
<Input
|
||||
id="fullName"
|
||||
type="text"
|
||||
value={formData.fullName}
|
||||
onChange={(e) => handleInputChange('fullName', e.target.value)}
|
||||
placeholder="Your full name"
|
||||
required
|
||||
className="h-12 pl-12"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
backgroundColor: '#F3F3F5'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Email Address */}
|
||||
<div>
|
||||
<Label
|
||||
htmlFor="email"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontWeight: '500',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Email Address *
|
||||
</Label>
|
||||
<div className="relative mt-2">
|
||||
<div className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500">
|
||||
<Mail className="w-5 h-5" />
|
||||
</div>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
value={formData.email}
|
||||
onChange={(e) => handleInputChange('email', e.target.value)}
|
||||
placeholder="you@example.com"
|
||||
required
|
||||
className="h-12 pl-12"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
backgroundColor: '#F3F3F5'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Password */}
|
||||
<div>
|
||||
<Label
|
||||
htmlFor="password"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontWeight: '500',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Password *
|
||||
</Label>
|
||||
<div className="relative mt-2">
|
||||
<div className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500">
|
||||
<Lock className="w-5 h-5" />
|
||||
</div>
|
||||
<Input
|
||||
id="password"
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
value={formData.password}
|
||||
onChange={(e) => handleInputChange('password', e.target.value)}
|
||||
placeholder="Create a strong password"
|
||||
required
|
||||
className="h-12 pl-12 pr-12"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
backgroundColor: '#F3F3F5'
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-500 hover:text-gray-700"
|
||||
>
|
||||
{showPassword ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Phone Number (Optional) */}
|
||||
<div>
|
||||
<Label
|
||||
htmlFor="phoneNumber"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontWeight: '500',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Phone Number (Optional)
|
||||
</Label>
|
||||
<div className="relative mt-2">
|
||||
<div className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500">
|
||||
<Phone className="w-5 h-5" />
|
||||
</div>
|
||||
<Input
|
||||
id="phoneNumber"
|
||||
type="tel"
|
||||
value={formData.phoneNumber}
|
||||
onChange={(e) => handleInputChange('phoneNumber', e.target.value)}
|
||||
placeholder="+1 234 567 8900"
|
||||
className="h-12 pl-12"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
backgroundColor: '#F3F3F5'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Terms and Conditions */}
|
||||
<div className="flex items-start space-x-2">
|
||||
<Checkbox
|
||||
id="agreeToTerms"
|
||||
checked={agreeToTerms}
|
||||
onCheckedChange={setAgreeToTerms}
|
||||
className="mt-1"
|
||||
/>
|
||||
<Label
|
||||
htmlFor="agreeToTerms"
|
||||
className="text-sm leading-relaxed"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-small)'
|
||||
}}
|
||||
>
|
||||
I agree to the{' '}
|
||||
<span className="text-blue-600 hover:text-blue-700 cursor-pointer">
|
||||
Terms of Service
|
||||
</span>{' '}
|
||||
and{' '}
|
||||
<span className="text-blue-600 hover:text-blue-700 cursor-pointer">
|
||||
Privacy Policy
|
||||
</span>
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
{/* Create Account Button */}
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={isLoading}
|
||||
className="w-full h-14 text-white font-medium flex items-center justify-center gap-2"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-primary)',
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
borderRadius: '10px'
|
||||
}}
|
||||
>
|
||||
{isLoading ? (
|
||||
<div className="w-5 h-5 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
||||
) : (
|
||||
<>
|
||||
Create Account
|
||||
<ArrowRight className="w-5 h-5" />
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
{/* Sign In Link */}
|
||||
<div className="text-center mt-6">
|
||||
<span
|
||||
className="text-gray-600"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Already have an account?{' '}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => navigateTo('/self-learner-signin')}
|
||||
className="text-blue-600 hover:text-blue-700 transition-colors font-medium"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Sign in here
|
||||
</button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Need Help Section - Separate Section Below */}
|
||||
<div
|
||||
className="section-margin-x py-12"
|
||||
style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}
|
||||
>
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<h3
|
||||
className="mb-4"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Questions About Your Learning Journey?
|
||||
</h3>
|
||||
<p
|
||||
className="mb-6"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
lineHeight: 'var(--line-height-body)'
|
||||
}}
|
||||
>
|
||||
Our learning advisors are here to help you choose the right programs and get the most out of your leadership development experience.
|
||||
</p>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="border-2 border-blue-600 text-blue-600 hover:bg-blue-600 hover:text-white"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontWeight: '500',
|
||||
padding: '0.75rem 2rem',
|
||||
borderRadius: '10px'
|
||||
}}
|
||||
>
|
||||
Speak with Learning Advisor
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
47
src/components/ServicesCTASection.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import { motion } from "motion/react";
|
||||
import { ArrowRight } from "lucide-react";
|
||||
|
||||
export function ServicesCTASection() {
|
||||
return (
|
||||
<section className="py-16 px-[var(--section-margin-x)] max-lg:px-8 max-md:py-12">
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.7 }}
|
||||
viewport={{ once: true }}
|
||||
className="space-y-8"
|
||||
>
|
||||
{/* CTA Button */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.3 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<button className="services-cta-button">
|
||||
<div className="services-cta-icon">
|
||||
<ArrowRight size={20} />
|
||||
</div>
|
||||
<span className="services-cta-text">
|
||||
Get Started Today
|
||||
</span>
|
||||
</button>
|
||||
</motion.div>
|
||||
|
||||
{/* Supporting Text */}
|
||||
<motion.p
|
||||
className="text-sm max-md:text-xs"
|
||||
style={{ color: 'var(--color-brand-gray-muted)' }}
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
transition={{ duration: 0.6, delay: 0.5 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
Free consultation • No commitment required • Expert guidance
|
||||
</motion.p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
237
src/components/ServicesSection.tsx
Normal file
@@ -0,0 +1,237 @@
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { motion } from "motion/react";
|
||||
import {
|
||||
Users,
|
||||
Settings,
|
||||
User,
|
||||
Globe,
|
||||
MessageSquare,
|
||||
GraduationCap,
|
||||
ArrowRight
|
||||
} from "lucide-react";
|
||||
import { BrandedTag } from "./about/BrandedTag";
|
||||
import { PrimaryCTAButton } from "./PrimaryCTAButton";
|
||||
import { navigateTo } from "./Router";
|
||||
|
||||
// Services data
|
||||
const recognitionItems = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Leadership Development",
|
||||
description: "Comprehensive programs designed to cultivate strategic thinking and emotional intelligence. Develop capabilities that drive organizational success through authentic leadership practices.",
|
||||
icon: <Users size={28} />,
|
||||
badge: "CORE PROGRAM",
|
||||
badgeColor: "#F8C301"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Management Development",
|
||||
description: "Essential skills training for first-time and experienced managers seeking growth. Focus on communication, delegation, and performance management excellence.",
|
||||
icon: <Settings size={28} />,
|
||||
badge: "POPULAR",
|
||||
badgeColor: "#04045B"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Culture Competence",
|
||||
description: "Build cultural awareness and inclusive practices that enhance team collaboration. Navigate cultural differences with confidence and create inclusive environments.",
|
||||
icon: <Globe size={28} />,
|
||||
badge: "GLOBAL FOCUS",
|
||||
badgeColor: "#F8C301"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "Executive Coaching",
|
||||
description: "One-on-one personalized development for senior leaders and high-potential talent. Strategic guidance for complex leadership challenges and career advancement.",
|
||||
icon: <User size={28} />,
|
||||
badge: "PREMIUM",
|
||||
badgeColor: "#04045B"
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: "Communication Excellence",
|
||||
description: "Master the art of influential communication across all organizational levels. Develop presentation skills, difficult conversation navigation, and stakeholder engagement.",
|
||||
icon: <MessageSquare size={28} />,
|
||||
badge: "ESSENTIAL",
|
||||
badgeColor: "#F8C301"
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: "Change Leadership",
|
||||
description: "Guide organizations through transformation with confidence and clarity. Learn frameworks for managing resistance, building momentum, and sustaining change initiatives.",
|
||||
icon: <GraduationCap size={28} />,
|
||||
badge: "STRATEGIC",
|
||||
badgeColor: "#04045B"
|
||||
}
|
||||
];
|
||||
|
||||
export function ServicesSection() {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const cardRefs = useRef<(HTMLDivElement | null)[]>([]);
|
||||
const sectionRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Add card refs helper
|
||||
const addCardRef = (el: HTMLDivElement | null, index: number) => {
|
||||
cardRefs.current[index] = el;
|
||||
};
|
||||
|
||||
// Intersection observer for animations
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
setIsVisible(true);
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold: 0.2 }
|
||||
);
|
||||
|
||||
if (sectionRef.current) {
|
||||
observer.observe(sectionRef.current);
|
||||
}
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
// Keyboard navigation
|
||||
const handleKeyDown = (e: React.KeyboardEvent, index: number) => {
|
||||
if (e.key === 'ArrowDown' && index < cardRefs.current.length - 1) {
|
||||
cardRefs.current[index + 1]?.focus();
|
||||
e.preventDefault();
|
||||
} else if (e.key === 'ArrowUp' && index > 0) {
|
||||
cardRefs.current[index - 1]?.focus();
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<section
|
||||
ref={sectionRef}
|
||||
className="py-16 lg:py-20"
|
||||
style={{
|
||||
backgroundColor: '#F7F7FD',
|
||||
fontFamily: 'var(--font-family-brand)'
|
||||
}}
|
||||
aria-labelledby="recognition-section-heading"
|
||||
>
|
||||
<div className="section-margin-x">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<div className="grid grid-cols-12 gap-12 min-h-screen">
|
||||
{/* Left Side - Sticky Content */}
|
||||
<div className="col-span-5 sticky top-24 self-start">
|
||||
<div className="recognition-header pr-8">
|
||||
<BrandedTag
|
||||
text="Leadership Development Programs"
|
||||
/>
|
||||
<h2
|
||||
id="recognition-section-heading"
|
||||
className="text-h2 mb-6"
|
||||
>
|
||||
Services That Shape Stronger Leaders
|
||||
</h2>
|
||||
<p className="text-body-lg text-muted">
|
||||
Our comprehensive leadership development programs are designed to build future-ready leaders who thrive in complexity and drive measurable organizational impact.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Side - Scrolling Cards */}
|
||||
<div className="col-span-7">
|
||||
<div
|
||||
className="recognition-cards space-y-6"
|
||||
role="list"
|
||||
aria-label="Leadership development services"
|
||||
>
|
||||
{recognitionItems.map((item, index) => (
|
||||
<div
|
||||
key={item.id}
|
||||
ref={(el) => addCardRef(el, index)}
|
||||
className={`recognition-card group scroll-animate-stagger focus-ring ${isVisible ? 'animate-in' : ''}`}
|
||||
role="listitem"
|
||||
aria-labelledby={`recognition-title-${item.id}`}
|
||||
aria-describedby={`recognition-desc-${item.id}`}
|
||||
tabIndex={0}
|
||||
onKeyDown={(e) => handleKeyDown(e, index)}
|
||||
style={{
|
||||
transitionDelay: `${(index + 1) * 150}ms`,
|
||||
opacity: isVisible ? 1 : 0
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="p-8 transition-all duration-300 hover:shadow-xl hover:-translate-y-1 border bg-white"
|
||||
style={{
|
||||
borderColor: 'var(--color-border)',
|
||||
borderRadius: '12px',
|
||||
fontFamily: 'var(--font-family-brand)'
|
||||
}}
|
||||
>
|
||||
<div className="flex items-start justify-between mb-6">
|
||||
<div
|
||||
className="w-14 h-14 flex items-center justify-center transition-transform duration-300 group-hover:scale-110"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-brand-primary)',
|
||||
borderRadius: '12px',
|
||||
color: 'white'
|
||||
}}
|
||||
>
|
||||
{item.icon}
|
||||
</div>
|
||||
{item.badge && (
|
||||
<div
|
||||
className="px-3 py-1 text-xs font-bold uppercase tracking-wider"
|
||||
style={{
|
||||
backgroundColor: item.badgeColor,
|
||||
color: item.badgeColor === '#F8C301' ? 'var(--color-brand-black)' : 'white',
|
||||
borderRadius: '20px',
|
||||
fontFamily: 'var(--font-family-brand)'
|
||||
}}
|
||||
>
|
||||
{item.badge}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="recognition-card-content">
|
||||
<h3
|
||||
id={`recognition-title-${item.id}`}
|
||||
className="text-h4 mb-4"
|
||||
>
|
||||
{item.title}
|
||||
</h3>
|
||||
<p
|
||||
id={`recognition-desc-${item.id}`}
|
||||
className="text-small text-muted leading-relaxed"
|
||||
>
|
||||
{item.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CTA Button - Updated to redirect to leadership journey */}
|
||||
<div className="flex justify-center mt-16">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.6 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<PrimaryCTAButton
|
||||
text="Get Started Today"
|
||||
onClick={() => navigateTo('/leadership-journey')}
|
||||
ariaLabel="Get started with leadership development programs"
|
||||
className="get-started-cta-override"
|
||||
/>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
374
src/components/StackedOfferSection.tsx
Normal file
@@ -0,0 +1,374 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
|
||||
/**
|
||||
* StackedOfferSection Component
|
||||
*
|
||||
* @param {Object} props - Component props
|
||||
* @param {Array} props.cards - Array of card objects with the following structure:
|
||||
* - id: number
|
||||
* - title: string
|
||||
* - subtitle?: string
|
||||
* - description?: string
|
||||
* - badge?: string
|
||||
* - color: string (no longer used - all cards are white)
|
||||
* - features?: string[]
|
||||
* - stats?: Array<{label: string, value: string}>
|
||||
* - icon?: React.ComponentType
|
||||
*/
|
||||
|
||||
export type StackedOfferCard = {
|
||||
id: number;
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
description?: string;
|
||||
badge?: string;
|
||||
features?: string[];
|
||||
stats?: Array<{ label: string; value: string }>;
|
||||
icon?: React.ComponentType<any>;
|
||||
};
|
||||
|
||||
const demoCards: StackedOfferCard[] = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Leadership Foundations",
|
||||
subtitle: "Orientation • Strategy",
|
||||
description:
|
||||
"Build strong leadership behaviors with research-backed frameworks and deliberate practice.",
|
||||
badge: "Featured",
|
||||
color: "from-indigo-500 to-blue-600", // No longer used
|
||||
features: ["Decision Making", "Self-awareness", "Feedback Loops", "Coaching"],
|
||||
stats: [
|
||||
{ label: "Modules", value: "08" },
|
||||
{ label: "Hours", value: "12" },
|
||||
{ label: "Format", value: "Self-paced" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Executive Presence",
|
||||
subtitle: "Influence • Communication",
|
||||
description:
|
||||
"Elevate your impact with persuasive narratives, clarity, and confident delivery.",
|
||||
badge: "New",
|
||||
color: "from-emerald-500 to-teal-600", // No longer used
|
||||
features: ["Narratives", "Storylining", "Speaking", "Signals"],
|
||||
stats: [
|
||||
{ label: "Modules", value: "06" },
|
||||
{ label: "Hours", value: "10" },
|
||||
{ label: "Format", value: "Cohort" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Strategic Execution",
|
||||
subtitle: "Operating Rhythm",
|
||||
description:
|
||||
"Translate strategy into outcomes with ruthless prioritization and cadence.",
|
||||
color: "from-rose-500 to-orange-500", // No longer used
|
||||
features: ["OKRs", "Cadence", "Reviews", "Debriefs"],
|
||||
stats: [
|
||||
{ label: "Modules", value: "07" },
|
||||
{ label: "Hours", value: "11" },
|
||||
{ label: "Format", value: "Hybrid" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "Change Leadership",
|
||||
subtitle: "Adoption • Momentum",
|
||||
description:
|
||||
"Lead transformation with systems thinking and stakeholder alignment.",
|
||||
color: "from-purple-500 to-fuchsia-600", // No longer used
|
||||
features: ["Systems", "Stakeholders", "Momentum", "Sustain"],
|
||||
stats: [
|
||||
{ label: "Modules", value: "05" },
|
||||
{ label: "Hours", value: "09" },
|
||||
{ label: "Format", value: "Self-paced" }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
type StackedOfferSectionProps = {
|
||||
cards?: StackedOfferCard[];
|
||||
headingTitle?: string;
|
||||
headingEmphasis?: string;
|
||||
headingDescription?: string;
|
||||
};
|
||||
|
||||
export default function StackedOfferSection({
|
||||
cards = demoCards,
|
||||
headingTitle = "What We",
|
||||
headingEmphasis = "Offer",
|
||||
headingDescription = "Comprehensive executive leadership development solutions designed to transform\nsenior leadership capabilities and drive organizational excellence.",
|
||||
}: StackedOfferSectionProps) {
|
||||
const sectionRef = useRef<HTMLDivElement | null>(null);
|
||||
const [activeIndex, setActiveIndex] = useState(0);
|
||||
const [stickyStartPosition, setStickyStartPosition] = useState<number | null>(null);
|
||||
const [isSticky, setIsSticky] = useState(false);
|
||||
|
||||
// Constants - Increased card height from 400px to 480px
|
||||
const CARD_HEIGHT = 480;
|
||||
const SCROLL_PER_CARD = 200; // Pixels to scroll for each card
|
||||
|
||||
useEffect(() => {
|
||||
let ticking = false;
|
||||
|
||||
const handleScroll = () => {
|
||||
if (!ticking) {
|
||||
requestAnimationFrame(() => {
|
||||
if (!sectionRef.current) return;
|
||||
|
||||
const section = sectionRef.current;
|
||||
const rect = section.getBoundingClientRect();
|
||||
const currentScrollY = window.scrollY;
|
||||
|
||||
// Check if section has reached the top (becomes sticky)
|
||||
const sectionIsSticky = rect.top <= 0;
|
||||
|
||||
if (sectionIsSticky && !isSticky) {
|
||||
// Section just became sticky - record the scroll position
|
||||
setStickyStartPosition(currentScrollY);
|
||||
setIsSticky(true);
|
||||
setActiveIndex(0); // Start with first card
|
||||
return;
|
||||
} else if (!sectionIsSticky && isSticky) {
|
||||
// Section is no longer sticky - reset everything
|
||||
setIsSticky(false);
|
||||
setStickyStartPosition(null);
|
||||
setActiveIndex(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// If section is sticky and we have a starting position
|
||||
if (sectionIsSticky && stickyStartPosition !== null) {
|
||||
// Calculate how much we've scrolled since becoming sticky
|
||||
const scrolledSinceSticky = currentScrollY - stickyStartPosition;
|
||||
|
||||
// Determine which card should be active based on scroll distance
|
||||
let newCardIndex = 0;
|
||||
|
||||
if (scrolledSinceSticky >= SCROLL_PER_CARD * 3) {
|
||||
newCardIndex = 3; // Card 4 (Change Leadership)
|
||||
} else if (scrolledSinceSticky >= SCROLL_PER_CARD * 2) {
|
||||
newCardIndex = 2; // Card 3 (Strategic Execution)
|
||||
} else if (scrolledSinceSticky >= SCROLL_PER_CARD) {
|
||||
newCardIndex = 1; // Card 2 (Executive Presence)
|
||||
} else {
|
||||
newCardIndex = 0; // Card 1 (Leadership Foundations)
|
||||
}
|
||||
|
||||
// Only update if card index has changed
|
||||
if (newCardIndex !== activeIndex) {
|
||||
setActiveIndex(newCardIndex);
|
||||
}
|
||||
}
|
||||
|
||||
ticking = false;
|
||||
});
|
||||
ticking = true;
|
||||
}
|
||||
};
|
||||
|
||||
// Add scroll listener
|
||||
window.addEventListener('scroll', handleScroll, { passive: true });
|
||||
|
||||
// Initial call
|
||||
handleScroll();
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScroll);
|
||||
};
|
||||
}, [activeIndex, stickyStartPosition, isSticky]);
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
{/* Sticky Section - What We Offer */}
|
||||
<section
|
||||
ref={sectionRef}
|
||||
className="sticky top-0 py-20 min-h-screen flex items-center justify-center z-10"
|
||||
style={{ backgroundColor: '#FFFFFF' }}
|
||||
>
|
||||
<div className="container mx-auto section-margin-x">
|
||||
{/* Heading */}
|
||||
<div className="text-center mb-16 max-w-4xl mx-auto">
|
||||
<h2 className="text-h2 mb-6">
|
||||
{headingTitle} <span className="text-primary">{headingEmphasis}</span>
|
||||
</h2>
|
||||
<p className="text-body-lg text-muted" style={{ whiteSpace: 'pre-line' }}>
|
||||
{headingDescription}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Desktop: Stacking Cards */}
|
||||
<div className="hidden lg:block relative">
|
||||
{/* Stack Container - Increased width from max-w-2xl to max-w-3xl and height to match new card height */}
|
||||
<div className="relative mx-auto max-w-3xl h-[480px]">
|
||||
{/* Stack Area */}
|
||||
<div className="relative w-full h-full flex items-center justify-center">
|
||||
{cards.map((card, index) => {
|
||||
const shouldShow = index <= activeIndex;
|
||||
const isActive = activeIndex === index;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={card.id}
|
||||
className={`
|
||||
absolute inset-0 transition-all duration-700 ease-out
|
||||
${shouldShow
|
||||
? 'opacity-100 translate-y-0 scale-100'
|
||||
: 'opacity-0 translate-y-full scale-95'
|
||||
}
|
||||
`}
|
||||
style={{
|
||||
zIndex: 10 + index,
|
||||
transitionDelay: shouldShow ? `${index * 100}ms` : '0ms'
|
||||
}}
|
||||
>
|
||||
{/* Card Content - White Background with Brand Colors */}
|
||||
<div
|
||||
className="w-full h-full rounded-3xl p-8 bg-white border border-gray-200 card-shadow-dramatic"
|
||||
style={{ height: `${CARD_HEIGHT}px` }}
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="flex items-start justify-between mb-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-16 h-16 rounded-2xl bg-blue-50 border border-blue-100 flex items-center justify-center">
|
||||
{card.icon && <card.icon className="w-8 h-8 text-primary" />}
|
||||
</div>
|
||||
<div>
|
||||
{card.badge && (
|
||||
<span className="inline-flex items-center px-2.5 py-0.5 rounded-md text-primary border border-primary bg-blue-50 mb-2 text-xs font-medium">
|
||||
{card.badge}
|
||||
</span>
|
||||
)}
|
||||
{card.subtitle && (
|
||||
<div className="text-sm text-muted">{card.subtitle}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<div className="text-4xl font-bold mb-1 text-primary">
|
||||
{String(card.id).padStart(2, "0")}
|
||||
</div>
|
||||
<div className="w-8 h-0.5 bg-primary ml-auto" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="mb-6">
|
||||
<h3 className="text-h3 mb-3 text-black">{card.title}</h3>
|
||||
{card.description && (
|
||||
<p className="text-body text-muted leading-relaxed mb-4">
|
||||
{card.description}
|
||||
</p>
|
||||
)}
|
||||
{card.features?.length && (
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
{card.features.map((feature, idx) => (
|
||||
<div key={idx} className="flex items-center gap-2">
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-primary" />
|
||||
<span className="text-sm text-black">{feature}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Footer stats */}
|
||||
{card.stats?.length && (
|
||||
<div className="flex justify-between items-center pt-6 border-t border-gray-200">
|
||||
{card.stats.map((stat, idx) => (
|
||||
<div key={idx} className="text-center">
|
||||
<div className="text-2xl font-bold text-primary">{stat.value}</div>
|
||||
<div className="text-xs text-muted">{stat.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Progress Indicators */}
|
||||
<div className="hidden lg:block fixed right-8 top-1/2 transform -translate-y-1/2 z-50">
|
||||
|
||||
</div>
|
||||
|
||||
{/* Mobile/Tablet: Simple Card List - Updated with White Background */}
|
||||
<div className="lg:hidden">
|
||||
<div className="space-y-8">
|
||||
{cards.map((card, index) => (
|
||||
<div
|
||||
key={`mobile-${card.id}`}
|
||||
className="rounded-2xl p-6 bg-white border border-gray-200 card-shadow-elevated card-hover-lift"
|
||||
>
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-12 h-12 rounded-xl bg-blue-50 border border-blue-100 flex items-center justify-center">
|
||||
{card.icon && <card.icon className="w-6 h-6 text-primary" />}
|
||||
</div>
|
||||
<div>
|
||||
{card.badge && (
|
||||
<span className="inline-block px-2 py-0.5 rounded text-xs bg-blue-50 border border-primary text-primary mb-1 font-medium">
|
||||
{card.badge}
|
||||
</span>
|
||||
)}
|
||||
{card.subtitle && (
|
||||
<div className="text-xs text-muted">{card.subtitle}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 className="text-h4 mb-2 text-black">{card.title}</h3>
|
||||
{card.description && (
|
||||
<p className="text-body text-muted mb-4">{card.description}</p>
|
||||
)}
|
||||
|
||||
{card.features?.length && (
|
||||
<div className="grid grid-cols-2 gap-2 mb-4">
|
||||
{card.features.map((feature, idx) => (
|
||||
<div key={idx} className="flex items-center gap-2">
|
||||
<div className="w-1 h-1 rounded-full bg-primary" />
|
||||
<span className="text-xs text-black">{feature}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{card.stats?.length && (
|
||||
<div className="flex justify-between pt-4 border-t border-gray-200">
|
||||
{card.stats.map((stat, idx) => (
|
||||
<div key={idx} className="text-center">
|
||||
<div className="text-lg font-bold text-primary">{stat.value}</div>
|
||||
<div className="text-xs text-muted">{stat.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Optimized Scroll Space - Matches actual scroll distance needed */}
|
||||
<div style={{ height: `${SCROLL_PER_CARD * (cards.length - 1) + 100}px` }} className="relative">
|
||||
{/*
|
||||
This div creates the exact scroll space needed for card reveals:
|
||||
- Card 1: 0px (immediately when sticky)
|
||||
- Card 2: 200px
|
||||
- Card 3: 400px
|
||||
- Card 4: 600px
|
||||
Total: 600px + 100px buffer = 700px
|
||||
*/}
|
||||
<div className="w-full h-full" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
100
src/components/StandardCTAButton.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import React, { useState } from 'react';
|
||||
import { ArrowRight } from 'lucide-react';
|
||||
|
||||
interface StandardCTAButtonProps {
|
||||
text: string;
|
||||
onClick: () => void;
|
||||
ariaLabel?: string;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export function StandardCTAButton({
|
||||
text,
|
||||
onClick,
|
||||
ariaLabel,
|
||||
className = '',
|
||||
disabled = false
|
||||
}: StandardCTAButtonProps) {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
aria-label={ariaLabel || text}
|
||||
className={`standard-cta-button ${className}`}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: '8px',
|
||||
padding: '16px 28px',
|
||||
borderRadius: '12px',
|
||||
border: 'none',
|
||||
cursor: disabled ? 'not-allowed' : 'pointer',
|
||||
fontSize: 'var(--font-body)',
|
||||
fontWeight: 'var(--font-weight-h3)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
backgroundColor: disabled ? '#9CA3AF' : 'var(--color-primary)',
|
||||
color: '#FFFFFF',
|
||||
boxShadow: disabled
|
||||
? 'none'
|
||||
: '0 2px 4px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06)',
|
||||
transition: 'all 200ms ease-in-out',
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
opacity: disabled ? 0.6 : 1,
|
||||
transform: isHovered && !disabled ? 'translateY(-1px)' : 'translateY(0)',
|
||||
...(isHovered && !disabled && {
|
||||
backgroundColor: '#030359', // Darker shade of primary brand color
|
||||
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.15), 0 2px 4px rgba(0, 0, 0, 0.1)'
|
||||
})
|
||||
}}
|
||||
>
|
||||
{/* Text with underline animation */}
|
||||
<span
|
||||
style={{
|
||||
position: 'relative',
|
||||
display: 'inline-block',
|
||||
transition: 'all 200ms ease-in-out'
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
{/* Animated underline */}
|
||||
<span
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: '-2px',
|
||||
left: '0',
|
||||
height: '1px',
|
||||
backgroundColor: '#FFFFFF',
|
||||
transition: 'width 200ms ease-in-out',
|
||||
width: isHovered && !disabled ? '100%' : '0%'
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
|
||||
{/* Arrow icon with slide-in animation */}
|
||||
<span
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
transition: 'transform 200ms ease-in-out',
|
||||
transform: isHovered && !disabled ? 'translateX(0)' : 'translateX(4px)',
|
||||
opacity: isHovered && !disabled ? 1 : 0.8
|
||||
}}
|
||||
>
|
||||
<ArrowRight
|
||||
size={16}
|
||||
style={{
|
||||
color: '#FFFFFF',
|
||||
transition: 'all 200ms ease-in-out'
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
143
src/components/StatsSection.tsx
Normal file
@@ -0,0 +1,143 @@
|
||||
import { Button } from "./ui/button";
|
||||
import { useAnimatedCounter } from "./hooks/useAnimatedCounter";
|
||||
import { ArrowUpRight } from "lucide-react";
|
||||
import { useState, useEffect } from "react";
|
||||
import { BrandedTag } from "./about/BrandedTag";
|
||||
import { PrimaryCTAButton } from "./PrimaryCTAButton";
|
||||
|
||||
interface StatItemProps {
|
||||
end: number;
|
||||
suffix: string;
|
||||
label: string;
|
||||
duration?: number;
|
||||
}
|
||||
|
||||
function StatItem({ end, suffix, label, duration = 2000 }: StatItemProps) {
|
||||
const { count, ref } = useAnimatedCounter({ end, suffix, duration });
|
||||
|
||||
return (
|
||||
<div className="mb-0">
|
||||
{/* Top line */}
|
||||
<div
|
||||
className="w-full h-[1px] mb-4"
|
||||
style={{ backgroundColor: 'var(--color-brand-gray-muted)' }}
|
||||
/>
|
||||
|
||||
{/* Large number */}
|
||||
<div
|
||||
ref={ref}
|
||||
className="stats-number mb-3 leading-none"
|
||||
style={{
|
||||
color: 'var(--color-brand-primary)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{count}
|
||||
</div>
|
||||
|
||||
{/* Yellow square and label */}
|
||||
<div className="flex items-center mb-4">
|
||||
<div
|
||||
className="w-2 h-2 mr-3 flex-shrink-0"
|
||||
style={{ backgroundColor: 'var(--color-brand-accent)' }}
|
||||
/>
|
||||
<span
|
||||
className="text-eyebrow"
|
||||
style={{ color: 'var(--color-brand-black)' }}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Bottom line */}
|
||||
<div
|
||||
className="w-full h-[1px]"
|
||||
style={{ backgroundColor: 'var(--color-brand-gray-muted)' }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function StatsSection() {
|
||||
return (
|
||||
<section
|
||||
className="py-20"
|
||||
style={{ backgroundColor: 'var(--color-brand-bg-light)' }}
|
||||
>
|
||||
<div className="section-margin-x">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<div className="mb-12 lg:mb-16 md:mb-12 sm:mb-8">
|
||||
{/* Branded Tag */}
|
||||
<BrandedTag
|
||||
text="Serving Leaders Across Industries"
|
||||
/>
|
||||
|
||||
{/* Main Heading */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-8 lg:gap-12 items-start">
|
||||
<div className="lg:col-span-6 md:col-span-8 sm:col-span-12">
|
||||
<h2 className="text-h2 mb-8">
|
||||
Driving impact by building future-ready leaders who thrive in
|
||||
complexity and lead with confidence.
|
||||
</h2>
|
||||
{/* CTA Button */}
|
||||
<PrimaryCTAButton
|
||||
text="About Us"
|
||||
onClick={() => console.log('About us clicked')}
|
||||
ariaLabel="Learn more about KLC"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Desktop Statistics */}
|
||||
<div className="hidden lg:block lg:col-start-9 lg:col-end-13">
|
||||
<div className="space-y-6">
|
||||
<StatItem
|
||||
end={27000}
|
||||
suffix="+"
|
||||
label="LEADERS DEVELOPED"
|
||||
duration={2500}
|
||||
/>
|
||||
<StatItem
|
||||
end={150}
|
||||
suffix="+"
|
||||
label="CORPORATE CLIENTS"
|
||||
duration={2000}
|
||||
/>
|
||||
<StatItem
|
||||
end={20}
|
||||
suffix="+"
|
||||
label="COUNTRIES SERVED"
|
||||
duration={1800}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Statistics - Show below content on mobile/tablet */}
|
||||
<div className="block lg:hidden mt-12">
|
||||
<div className="grid grid-cols-2 gap-6 sm:gap-8">
|
||||
<StatItem
|
||||
end={27000}
|
||||
suffix="+"
|
||||
label="LEADERS DEVELOPED"
|
||||
duration={2500}
|
||||
/>
|
||||
<StatItem
|
||||
end={150}
|
||||
suffix="+"
|
||||
label="CORPORATE CLIENTS"
|
||||
duration={2000}
|
||||
/>
|
||||
<StatItem
|
||||
end={20}
|
||||
suffix="+"
|
||||
label="COUNTRIES SERVED"
|
||||
duration={1800}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
853
src/components/Terms.tsx
Normal file
@@ -0,0 +1,853 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Navigation } from "./Navigation";
|
||||
import { Footer } from "./Footer";
|
||||
import {
|
||||
ChevronRight,
|
||||
Download,
|
||||
ExternalLink,
|
||||
} from "lucide-react";
|
||||
|
||||
interface TocItem {
|
||||
id: string;
|
||||
title: string;
|
||||
level: number;
|
||||
}
|
||||
|
||||
export function Terms() {
|
||||
const [activeSection, setActiveSection] =
|
||||
useState<string>("acceptance");
|
||||
const [tocOpen, setTocOpen] = useState(false);
|
||||
|
||||
// Table of Contents items
|
||||
const tocItems: TocItem[] = [
|
||||
{
|
||||
id: "acceptance",
|
||||
title: "Acceptance of Terms",
|
||||
level: 1,
|
||||
},
|
||||
{
|
||||
id: "eligibility",
|
||||
title: "Eligibility & Jurisdiction",
|
||||
level: 1,
|
||||
},
|
||||
{ id: "use-of-site", title: "Use of Site", level: 1 },
|
||||
{
|
||||
id: "purchases-refunds",
|
||||
title: "Purchases & Refunds",
|
||||
level: 1,
|
||||
},
|
||||
{
|
||||
id: "intellectual-property",
|
||||
title: "Intellectual Property",
|
||||
level: 1,
|
||||
},
|
||||
{
|
||||
id: "disclaimers",
|
||||
title: "Disclaimers & Limitations",
|
||||
level: 1,
|
||||
},
|
||||
{ id: "governing-law", title: "Governing Law", level: 1 },
|
||||
{
|
||||
id: "modifications",
|
||||
title: "Terms Modifications",
|
||||
level: 1,
|
||||
},
|
||||
{
|
||||
id: "contact-info",
|
||||
title: "Contact Information",
|
||||
level: 1,
|
||||
},
|
||||
{ id: "download-pdf", title: "Download PDF", level: 1 },
|
||||
];
|
||||
|
||||
// Scroll to section and update active state
|
||||
const scrollToSection = (sectionId: string) => {
|
||||
const element = document.getElementById(sectionId);
|
||||
if (element) {
|
||||
const offsetTop = element.offsetTop - 100; // Account for sticky nav
|
||||
window.scrollTo({
|
||||
top: offsetTop,
|
||||
behavior: "smooth",
|
||||
});
|
||||
setActiveSection(sectionId);
|
||||
}
|
||||
};
|
||||
|
||||
// Update active section on scroll
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
const sections = tocItems
|
||||
.map((item) => document.getElementById(item.id))
|
||||
.filter(Boolean);
|
||||
const scrollPosition = window.scrollY + 150;
|
||||
|
||||
for (let i = sections.length - 1; i >= 0; i--) {
|
||||
const section = sections[i];
|
||||
if (section && section.offsetTop <= scrollPosition) {
|
||||
setActiveSection(section.id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
return () =>
|
||||
window.removeEventListener("scroll", handleScroll);
|
||||
}, []);
|
||||
|
||||
// Handle URL fragments
|
||||
useEffect(() => {
|
||||
const hash = window.location.hash.replace("#", "");
|
||||
if (hash && tocItems.find((item) => item.id === hash)) {
|
||||
setTimeout(() => scrollToSection(hash), 100);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const lastUpdated = "2025-01-01";
|
||||
const versionHash = "v25.01";
|
||||
|
||||
return (
|
||||
<div style={{ backgroundColor: "#FFFFFF" }}>
|
||||
<Navigation />
|
||||
|
||||
{/* Hero Banner */}
|
||||
<section
|
||||
className="py-16 section-margin-x"
|
||||
style={{ backgroundColor: "#FFFFFF" }}
|
||||
>
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<div className="inline-flex items-center gap-2 px-4 py-2 bg-blue-100 text-blue-800 rounded-full text-small mb-6">
|
||||
<span className="w-2 h-2 bg-blue-600 rounded-full"></span>
|
||||
Legal Document
|
||||
<span className="ml-2 px-2 py-1 bg-blue-200 text-blue-900 rounded text-xs font-medium">
|
||||
{versionHash}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h1 className="text-h1 mb-6">Terms & Conditions</h1>
|
||||
|
||||
<p className="text-body-lg text-muted max-w-2xl mx-auto mb-4">
|
||||
These terms govern your use of the Kautilya
|
||||
Leadership Centre website and services. Please read
|
||||
carefully before using our platform.
|
||||
</p>
|
||||
|
||||
<p className="text-small text-muted">
|
||||
Last updated:{" "}
|
||||
{new Date(lastUpdated).toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div
|
||||
className="section-margin-x"
|
||||
style={{ backgroundColor: "#FFFFFF" }}
|
||||
>
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<div className="grid grid-cols-12 gap-8">
|
||||
{/* Sticky Table of Contents */}
|
||||
<div className="col-span-12 lg:col-span-3">
|
||||
<div className="sticky top-24">
|
||||
<nav
|
||||
aria-label="On-page navigation"
|
||||
className="bg-white border border-gray-200 rounded-lg p-6 shadow-sm"
|
||||
>
|
||||
<div className="flex items-center justify-between mb-4 lg:hidden">
|
||||
<h3 className="text-h4">Contents</h3>
|
||||
<button
|
||||
onClick={() => setTocOpen(!tocOpen)}
|
||||
className="p-2 hover:bg-gray-100 rounded-md transition-colors"
|
||||
aria-expanded={tocOpen}
|
||||
>
|
||||
<ChevronRight
|
||||
className={`w-4 h-4 transition-transform ${tocOpen ? "rotate-90" : ""}`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`space-y-2 ${tocOpen ? "block" : "hidden lg:block"}`}
|
||||
>
|
||||
{tocItems.map((item) => (
|
||||
<button
|
||||
key={item.id}
|
||||
onClick={() => scrollToSection(item.id)}
|
||||
className={`block w-full text-left p-2 rounded-md text-small transition-all duration-200 hover:bg-gray-100 ${
|
||||
activeSection === item.id
|
||||
? "bg-blue-50 text-primary border-l-2 border-primary pl-3"
|
||||
: "text-muted hover:text-black"
|
||||
}`}
|
||||
>
|
||||
{item.title}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="col-span-12 lg:col-span-9">
|
||||
<div
|
||||
className="max-w-3xl prose prose-lg"
|
||||
style={{ maxWidth: "720px" }}
|
||||
>
|
||||
{/* Acceptance of Terms */}
|
||||
<section
|
||||
id="acceptance"
|
||||
className="mb-12 scroll-mt-24"
|
||||
>
|
||||
<h2
|
||||
className="text-h2 mb-6 group cursor-pointer"
|
||||
onClick={() =>
|
||||
scrollToSection("acceptance")
|
||||
}
|
||||
>
|
||||
<span className="inline-flex items-center gap-2">
|
||||
Acceptance of Terms
|
||||
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
</span>
|
||||
</h2>
|
||||
<div className="space-y-4">
|
||||
<p className="text-body-lg">
|
||||
By accessing and using the Kautilya
|
||||
Leadership Centre (KLC) website and
|
||||
services, you accept and agree to be bound
|
||||
by the terms and provision of this
|
||||
agreement.
|
||||
</p>
|
||||
<p className="text-body">
|
||||
If you do not agree to abide by the above,
|
||||
please do not use this service. Your
|
||||
continued use of our platform constitutes
|
||||
acceptance of these terms as they may be
|
||||
modified from time to time.
|
||||
</p>
|
||||
<div className="bg-yellow-50 border-l-4 border-yellow-400 p-4 rounded-r-lg">
|
||||
<p className="text-body text-yellow-800">
|
||||
<strong>Important:</strong> These terms
|
||||
create a legally binding agreement
|
||||
between you and KLC. Please ensure you
|
||||
understand your rights and obligations.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Eligibility & Jurisdiction */}
|
||||
<section
|
||||
id="eligibility"
|
||||
className="mb-12 scroll-mt-24"
|
||||
>
|
||||
<h2
|
||||
className="text-h2 mb-6 group cursor-pointer"
|
||||
onClick={() =>
|
||||
scrollToSection("eligibility")
|
||||
}
|
||||
>
|
||||
<span className="inline-flex items-center gap-2">
|
||||
Eligibility & Jurisdiction
|
||||
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
</span>
|
||||
</h2>
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-h3 mb-4">
|
||||
Age Requirements
|
||||
</h3>
|
||||
<p className="text-body">
|
||||
You must be at least 18 years of age to
|
||||
use our services. If you are between 13
|
||||
and 18 years old, you may only use our
|
||||
services with the involvement and consent
|
||||
of a parent or guardian.
|
||||
</p>
|
||||
|
||||
<h3 className="text-h3 mb-4 mt-8">
|
||||
Geographic Scope
|
||||
</h3>
|
||||
<p className="text-body">
|
||||
Our services are primarily intended for
|
||||
users in India and regions where our
|
||||
educational content and leadership
|
||||
development programs are legally
|
||||
permissible and culturally appropriate.
|
||||
</p>
|
||||
|
||||
<h3 className="text-h3 mb-4 mt-8">
|
||||
Corporate Access
|
||||
</h3>
|
||||
<p className="text-body">
|
||||
Organizations and corporate entities
|
||||
accessing our services must ensure they
|
||||
have proper authorization to enter into
|
||||
these terms on behalf of their
|
||||
organization.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Use of Site */}
|
||||
<section
|
||||
id="use-of-site"
|
||||
className="mb-12 scroll-mt-24"
|
||||
>
|
||||
<h2
|
||||
className="text-h2 mb-6 group cursor-pointer"
|
||||
onClick={() =>
|
||||
scrollToSection("use-of-site")
|
||||
}
|
||||
>
|
||||
<span className="inline-flex items-center gap-2">
|
||||
Use of Site
|
||||
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
</span>
|
||||
</h2>
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-h3 mb-4">
|
||||
Permitted Uses
|
||||
</h3>
|
||||
<ul className="list-disc pl-6 space-y-2 text-body">
|
||||
<li>
|
||||
Accessing educational content and
|
||||
leadership development materials
|
||||
</li>
|
||||
<li>
|
||||
Participating in webinars, courses, and
|
||||
training programs
|
||||
</li>
|
||||
<li>
|
||||
Using our virtual learning facility for
|
||||
authorized purposes
|
||||
</li>
|
||||
<li>
|
||||
Downloading materials explicitly marked
|
||||
as downloadable
|
||||
</li>
|
||||
<li>
|
||||
Sharing content through our designated
|
||||
social sharing features
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 className="text-h3 mb-4 mt-8">
|
||||
Prohibited Activities
|
||||
</h3>
|
||||
<ul className="list-disc pl-6 space-y-2 text-body">
|
||||
<li>
|
||||
Unauthorized copying, distribution, or
|
||||
modification of our content
|
||||
</li>
|
||||
<li>
|
||||
Using automated systems to access or
|
||||
scrape our website
|
||||
</li>
|
||||
<li>
|
||||
Attempting to gain unauthorized access
|
||||
to our systems
|
||||
</li>
|
||||
<li>
|
||||
Sharing login credentials or accessing
|
||||
accounts not belonging to you
|
||||
</li>
|
||||
<li>
|
||||
Using our services for any illegal or
|
||||
harmful purposes
|
||||
</li>
|
||||
<li>
|
||||
Disrupting the normal operation of our
|
||||
platform
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div className="bg-red-50 border-l-4 border-red-400 p-4 rounded-r-lg mt-6">
|
||||
<p className="text-body text-red-800">
|
||||
<strong>Warning:</strong> Violation of
|
||||
these terms may result in immediate
|
||||
termination of your access to our
|
||||
services and potential legal action.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Purchases & Refunds */}
|
||||
<section
|
||||
id="purchases-refunds"
|
||||
className="mb-12 scroll-mt-24"
|
||||
>
|
||||
<h2
|
||||
className="text-h2 mb-6 group cursor-pointer"
|
||||
onClick={() =>
|
||||
scrollToSection("purchases-refunds")
|
||||
}
|
||||
>
|
||||
<span className="inline-flex items-center gap-2">
|
||||
Purchases & Refunds
|
||||
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
</span>
|
||||
</h2>
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-h3 mb-4">
|
||||
Payment Terms
|
||||
</h3>
|
||||
<p className="text-body">
|
||||
All payments must be made in full before
|
||||
accessing paid content or services. We
|
||||
accept major credit cards, debit cards,
|
||||
and approved corporate payment methods.
|
||||
</p>
|
||||
|
||||
<h3 className="text-h3 mb-4 mt-8">
|
||||
GST & Taxes
|
||||
</h3>
|
||||
<p className="text-body">
|
||||
All prices are inclusive of applicable
|
||||
Goods and Services Tax (GST) as required
|
||||
by Indian law. International customers may
|
||||
be subject to additional taxes in their
|
||||
jurisdiction.
|
||||
</p>
|
||||
|
||||
<h3 className="text-h3 mb-4 mt-8">
|
||||
Refund Policy
|
||||
</h3>
|
||||
<div className="bg-gray-50 border border-gray-200 rounded-lg p-6">
|
||||
<h4 className="text-body font-semibold mb-3">
|
||||
Standard Refund Terms:
|
||||
</h4>
|
||||
<ul className="list-disc pl-6 space-y-2 text-body">
|
||||
<li>
|
||||
<strong>Online Courses:</strong> 7-day
|
||||
refund period from date of purchase
|
||||
(before 25% completion)
|
||||
</li>
|
||||
<li>
|
||||
<strong>Live Webinars:</strong> Full
|
||||
refund if cancelled 24 hours before
|
||||
start time
|
||||
</li>
|
||||
<li>
|
||||
<strong>Corporate Programs:</strong>{" "}
|
||||
Refund terms specified in individual
|
||||
agreements
|
||||
</li>
|
||||
<li>
|
||||
<strong>Digital Materials:</strong> No
|
||||
refunds after download or access
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4 className="text-body font-semibold mb-3 mt-6">
|
||||
Refund Process:
|
||||
</h4>
|
||||
<p className="text-body">
|
||||
To request a refund, contact our support
|
||||
team with your order details. Approved
|
||||
refunds will be processed within 7-10
|
||||
business days to the original payment
|
||||
method.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Intellectual Property */}
|
||||
<section
|
||||
id="intellectual-property"
|
||||
className="mb-12 scroll-mt-24"
|
||||
>
|
||||
<h2
|
||||
className="text-h2 mb-6 group cursor-pointer"
|
||||
onClick={() =>
|
||||
scrollToSection("intellectual-property")
|
||||
}
|
||||
>
|
||||
<span className="inline-flex items-center gap-2">
|
||||
Intellectual Property
|
||||
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
</span>
|
||||
</h2>
|
||||
<div className="space-y-4">
|
||||
<p className="text-body-lg">
|
||||
All content on this website, including but
|
||||
not limited to text, graphics, logos,
|
||||
images, audio clips, video content, and
|
||||
software, is the property of KLC or its
|
||||
content suppliers.
|
||||
</p>
|
||||
|
||||
<h3 className="text-h3 mb-4 mt-8">
|
||||
Copyright Notice
|
||||
</h3>
|
||||
<div className="bg-blue-50 border border-blue-200 rounded-lg p-6">
|
||||
<p className="text-body text-blue-800">
|
||||
© 2025 Kautilya Leadership Centre. All
|
||||
rights reserved. This website and its
|
||||
contents are protected by copyright,
|
||||
trademark, and other intellectual
|
||||
property laws.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h3 className="text-h3 mb-4 mt-8">
|
||||
Limited License
|
||||
</h3>
|
||||
<p className="text-body">
|
||||
We grant you a limited, non-exclusive,
|
||||
non-transferable license to access and use
|
||||
our content for personal or internal
|
||||
business purposes in accordance with these
|
||||
terms.
|
||||
</p>
|
||||
|
||||
<h3 className="text-h3 mb-4 mt-8">
|
||||
Trademark Information
|
||||
</h3>
|
||||
<p className="text-body">
|
||||
"Kautilya Leadership Centre" and all
|
||||
related logos are trademarks of KLC. You
|
||||
may not use our trademarks without prior
|
||||
written permission.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Disclaimers & Limitations */}
|
||||
<section
|
||||
id="disclaimers"
|
||||
className="mb-12 scroll-mt-24"
|
||||
>
|
||||
<h2
|
||||
className="text-h2 mb-6 group cursor-pointer"
|
||||
onClick={() =>
|
||||
scrollToSection("disclaimers")
|
||||
}
|
||||
>
|
||||
<span className="inline-flex items-center gap-2">
|
||||
Disclaimers & Limitations
|
||||
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
</span>
|
||||
</h2>
|
||||
<div className="space-y-4">
|
||||
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-6">
|
||||
<h3 className="text-h3 mb-4 text-yellow-800">
|
||||
Important Legal Notice
|
||||
</h3>
|
||||
<p className="text-body text-yellow-800">
|
||||
The information provided on this website
|
||||
is for educational purposes only and
|
||||
does not constitute professional advice.
|
||||
KLC makes no warranties about the
|
||||
completeness, reliability, or accuracy
|
||||
of this information.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h3 className="text-h3 mb-4 mt-8">
|
||||
Limitation of Liability
|
||||
</h3>
|
||||
<p className="text-body">
|
||||
In no event shall KLC be liable for any
|
||||
direct, indirect, incidental, special, or
|
||||
consequential damages resulting from your
|
||||
use of our website or services, even if we
|
||||
have been advised of the possibility of
|
||||
such damages.
|
||||
</p>
|
||||
|
||||
<h3 className="text-h3 mb-4 mt-8">
|
||||
Service Availability
|
||||
</h3>
|
||||
<p className="text-body">
|
||||
We strive to maintain continuous service
|
||||
availability but do not guarantee
|
||||
uninterrupted access. Scheduled
|
||||
maintenance and unforeseen technical
|
||||
issues may temporarily affect service
|
||||
availability.
|
||||
</p>
|
||||
|
||||
<h3 className="text-h3 mb-4 mt-8">
|
||||
Third-Party Links
|
||||
</h3>
|
||||
<p className="text-body">
|
||||
Our website may contain links to
|
||||
third-party websites. We are not
|
||||
responsible for the content, privacy
|
||||
policies, or practices of these external
|
||||
sites.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Governing Law */}
|
||||
<section
|
||||
id="governing-law"
|
||||
className="mb-12 scroll-mt-24"
|
||||
>
|
||||
<h2
|
||||
className="text-h2 mb-6 group cursor-pointer"
|
||||
onClick={() =>
|
||||
scrollToSection("governing-law")
|
||||
}
|
||||
>
|
||||
<span className="inline-flex items-center gap-2">
|
||||
Governing Law
|
||||
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
</span>
|
||||
</h2>
|
||||
<div className="space-y-4">
|
||||
<p className="text-body-lg">
|
||||
These terms and conditions are governed by
|
||||
and construed in accordance with the laws
|
||||
of India.
|
||||
</p>
|
||||
|
||||
<h3 className="text-h3 mb-4 mt-8">
|
||||
Jurisdiction
|
||||
</h3>
|
||||
<div className="bg-gray-50 border border-gray-200 rounded-lg p-6">
|
||||
<p className="text-body">
|
||||
<strong>Exclusive Jurisdiction:</strong>{" "}
|
||||
Chennai, Tamil Nadu, India
|
||||
</p>
|
||||
<p className="text-body mt-2">
|
||||
Any disputes arising from these terms or
|
||||
your use of our services shall be
|
||||
subject to the exclusive jurisdiction of
|
||||
the courts in Chennai, Tamil Nadu,
|
||||
India.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h3 className="text-h3 mb-4 mt-8">
|
||||
Dispute Resolution
|
||||
</h3>
|
||||
<p className="text-body">
|
||||
We encourage resolving disputes through
|
||||
direct communication. If formal legal
|
||||
action becomes necessary, both parties
|
||||
agree to first attempt mediation before
|
||||
pursuing litigation.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Terms Modifications */}
|
||||
<section
|
||||
id="modifications"
|
||||
className="mb-12 scroll-mt-24"
|
||||
>
|
||||
<h2
|
||||
className="text-h2 mb-6 group cursor-pointer"
|
||||
onClick={() =>
|
||||
scrollToSection("modifications")
|
||||
}
|
||||
>
|
||||
<span className="inline-flex items-center gap-2">
|
||||
Terms Modifications
|
||||
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
</span>
|
||||
</h2>
|
||||
<div className="space-y-4">
|
||||
<p className="text-body-lg">
|
||||
KLC reserves the right to modify these
|
||||
terms at any time. Changes will be
|
||||
effective immediately upon posting on this
|
||||
website.
|
||||
</p>
|
||||
|
||||
<h3 className="text-h3 mb-4 mt-8">
|
||||
Notification Process
|
||||
</h3>
|
||||
<ul className="list-disc pl-6 space-y-2 text-body">
|
||||
<li>
|
||||
Material changes will be highlighted at
|
||||
the top of this page
|
||||
</li>
|
||||
<li>
|
||||
Registered users will receive email
|
||||
notifications of significant updates
|
||||
</li>
|
||||
<li>
|
||||
The "Last Updated" date will reflect the
|
||||
most recent modifications
|
||||
</li>
|
||||
<li>
|
||||
Version history is available upon
|
||||
request
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div className="bg-blue-50 border-l-4 border-blue-400 p-4 rounded-r-lg mt-6">
|
||||
<p className="text-body text-blue-800">
|
||||
<strong>Your Responsibility:</strong> It
|
||||
is your responsibility to periodically
|
||||
review these terms. Continued use of our
|
||||
services after changes constitutes
|
||||
acceptance of the modified terms.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Contact Information */}
|
||||
<section
|
||||
id="contact-info"
|
||||
className="mb-12 scroll-mt-24"
|
||||
>
|
||||
<h2
|
||||
className="text-h2 mb-6 group cursor-pointer"
|
||||
onClick={() =>
|
||||
scrollToSection("contact-info")
|
||||
}
|
||||
>
|
||||
<span className="inline-flex items-center gap-2">
|
||||
Contact Information
|
||||
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
</span>
|
||||
</h2>
|
||||
<div className="space-y-4">
|
||||
<p className="text-body-lg">
|
||||
For questions about these terms or to
|
||||
report violations, please contact our
|
||||
legal team:
|
||||
</p>
|
||||
|
||||
<div className="bg-white border border-gray-200 rounded-lg p-6 space-y-4">
|
||||
<div>
|
||||
<h3 className="text-h4 mb-2">
|
||||
Legal Inquiries
|
||||
</h3>
|
||||
<p className="text-body">
|
||||
<strong>Email:</strong>{" "}
|
||||
legal@klc.edu.in
|
||||
<br />
|
||||
<strong>Response Time:</strong> 3-5
|
||||
business days
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-h4 mb-2">
|
||||
Registered Office
|
||||
</h3>
|
||||
<p className="text-body">
|
||||
Kautilya Leadership Centre
|
||||
<br />
|
||||
123 Leadership Avenue
|
||||
<br />
|
||||
Chennai, Tamil Nadu 600001
|
||||
<br />
|
||||
India
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-h4 mb-2">
|
||||
General Inquiries
|
||||
</h3>
|
||||
<p className="text-body">
|
||||
<strong>Phone:</strong>{" "}
|
||||
+91-44-1234-5678
|
||||
<br />
|
||||
<strong>Email:</strong>{" "}
|
||||
info@klc.edu.in
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Download PDF */}
|
||||
<section
|
||||
id="download-pdf"
|
||||
className="mb-12 scroll-mt-24"
|
||||
>
|
||||
<h2
|
||||
className="text-h2 mb-6 group cursor-pointer"
|
||||
onClick={() =>
|
||||
scrollToSection("download-pdf")
|
||||
}
|
||||
>
|
||||
<span className="inline-flex items-center gap-2">
|
||||
Download PDF
|
||||
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
</span>
|
||||
</h2>
|
||||
<div className="space-y-4">
|
||||
<p className="text-body">
|
||||
Download a timestamped PDF version of
|
||||
these terms for your records.
|
||||
</p>
|
||||
|
||||
<div className="bg-gray-50 border border-gray-200 rounded-lg p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 className="text-h4 mb-2">
|
||||
Terms & Conditions PDF
|
||||
</h3>
|
||||
<p className="text-small text-muted">
|
||||
Version {versionHash} • Last updated{" "}
|
||||
{new Date(
|
||||
lastUpdated,
|
||||
).toLocaleDateString()}{" "}
|
||||
• 156 KB
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => {
|
||||
// In a real implementation, this would download the actual PDF
|
||||
alert(
|
||||
"PDF download would start here",
|
||||
);
|
||||
}}
|
||||
className="flex items-center gap-2 px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors"
|
||||
aria-label="Download Terms & Conditions PDF (156 KB)"
|
||||
>
|
||||
<Download className="w-4 h-4" />
|
||||
Download PDF
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-small text-muted">
|
||||
The PDF version includes a digital
|
||||
signature and timestamp for authenticity
|
||||
verification.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Footer />
|
||||
|
||||
{/* Print Styles */}
|
||||
<style>{`
|
||||
@media print {
|
||||
.sticky { position: static !important; }
|
||||
nav[aria-label="On-page navigation"] { display: none !important; }
|
||||
.bg-gradient-to-r { background: white !important; }
|
||||
.text-white { color: black !important; }
|
||||
.bg-blue-50, .bg-yellow-50, .bg-red-50, .bg-gray-50 {
|
||||
background: white !important;
|
||||
border: 1px solid #ccc !important;
|
||||
}
|
||||
.shadow-sm, .shadow-lg { box-shadow: none !important; }
|
||||
h1, h2, h3, h4 { break-after: avoid; }
|
||||
.space-y-4 > * { break-inside: avoid; }
|
||||
@page { margin: 2cm; }
|
||||
.page-break { page-break-before: always; }
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
286
src/components/TestimonialsHeroSection.tsx
Normal file
@@ -0,0 +1,286 @@
|
||||
import { motion } from "motion/react";
|
||||
import { ImageWithFallback } from "./figma/ImageWithFallback";
|
||||
import { Play, X } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
interface Testimonial {
|
||||
id: number;
|
||||
quote: string;
|
||||
name: string;
|
||||
title: string;
|
||||
company: string;
|
||||
image: string;
|
||||
hasVideo: boolean;
|
||||
}
|
||||
|
||||
const testimonials: Testimonial[] = [
|
||||
{
|
||||
id: 1,
|
||||
quote: "KLC's leadership programs fundamentally changed how I approach team management and strategic decision-making in our organization.",
|
||||
name: "Rachel Thompson",
|
||||
title: "Senior Vice President",
|
||||
company: "Global Enterprises",
|
||||
image: "https://images.unsplash.com/photo-1580489944761-15a19d654956?w=600&h=800&fit=crop&crop=face",
|
||||
hasVideo: true
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
quote: "KLC's leadership development programs have completely changed how we approach team building and strategic planning. The results speak for themselves.",
|
||||
name: "Sarah Chen",
|
||||
title: "Chief Executive Officer",
|
||||
company: "TechForward Solutions",
|
||||
image: "https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?w=600&h=800&fit=crop&crop=face",
|
||||
hasVideo: true
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
quote: "Working with KLC's consulting team was transformational. Their evidence-based approach helped us build stronger, more resilient leadership across all departments.",
|
||||
name: "Michael Rodriguez",
|
||||
title: "Head of Operations",
|
||||
company: "Innovation Dynamics Inc",
|
||||
image: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=600&h=800&fit=crop&crop=face",
|
||||
hasVideo: true
|
||||
}
|
||||
];
|
||||
|
||||
export function TestimonialsHeroSection() {
|
||||
const [currentTestimonial, setCurrentTestimonial] = useState(0);
|
||||
const [isVideoPlaying, setIsVideoPlaying] = useState(false);
|
||||
const [showLightbox, setShowLightbox] = useState(false);
|
||||
|
||||
const testimonial = testimonials[currentTestimonial];
|
||||
|
||||
const handlePlayVideo = () => {
|
||||
setShowLightbox(true);
|
||||
setIsVideoPlaying(true);
|
||||
};
|
||||
|
||||
const handleCloseLightbox = () => {
|
||||
setShowLightbox(false);
|
||||
setIsVideoPlaying(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<section
|
||||
className="relative py-20 flex items-center"
|
||||
style={{
|
||||
backgroundColor: '#F7F7FD'
|
||||
}}
|
||||
>
|
||||
<div className="w-full section-margin-x">
|
||||
{/* Branded Tag */}
|
||||
<motion.div
|
||||
className="flex justify-center mb-12"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
<div className="inline-flex items-center gap-3 bg-[rgba(248,195,1,0.1)] px-4 py-2 rounded-lg">
|
||||
<div className="w-[7px] h-[7px] bg-[#f8c301] rounded-full" />
|
||||
<span className="text-[#26231a] text-lg lg:text-lg md:text-base sm:text-sm uppercase tracking-wider font-light">
|
||||
Client Success Stories
|
||||
</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Main heading */}
|
||||
<motion.h2
|
||||
className="text-h2 leading-tight mb-12 text-center"
|
||||
style={{ color: 'var(--color-brand-primary)' }}
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.7, delay: 0.2 }}
|
||||
>
|
||||
What Our Clients Say
|
||||
</motion.h2>
|
||||
|
||||
{/* Split Layout Container with Max Width - Compact Version */}
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="flex h-[400px] rounded-2xl overflow-hidden shadow-2xl">
|
||||
{/* Left Side - Video Thumbnail */}
|
||||
<motion.div
|
||||
className="flex-1 relative group cursor-pointer"
|
||||
initial={{ opacity: 0, x: -50 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.4 }}
|
||||
onClick={handlePlayVideo}
|
||||
>
|
||||
<div className="relative w-full h-full overflow-hidden">
|
||||
{/* Video Thumbnail */}
|
||||
<ImageWithFallback
|
||||
src={testimonial.image}
|
||||
alt={testimonial.name}
|
||||
className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
|
||||
/>
|
||||
|
||||
{/* Dark overlay on hover */}
|
||||
<div className="absolute inset-0 bg-black/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||
|
||||
{/* Centered Play Button Overlay */}
|
||||
{testimonial.hasVideo && (
|
||||
<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 group-hover:scale-110 transition-all duration-300"
|
||||
whileHover={{ scale: 1.15 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
>
|
||||
<Play
|
||||
className="w-7 h-7 ml-1 text-blue-600"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</motion.div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Watch Video Text */}
|
||||
<div className="absolute bottom-4 left-4 text-white">
|
||||
<div className="bg-black/30 backdrop-blur-sm rounded-lg px-3 py-1">
|
||||
<span className="text-xs font-medium">▶ Video</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Right Side - Quote Content with White Background - Compact */}
|
||||
<motion.div
|
||||
className="flex-1 flex items-center justify-center p-8 relative"
|
||||
style={{
|
||||
backgroundColor: 'white'
|
||||
}}
|
||||
initial={{ opacity: 0, x: 50 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.6 }}
|
||||
>
|
||||
{/* Large Opening Quotation Mark */}
|
||||
<div
|
||||
className="absolute top-4 left-4 text-3xl opacity-30"
|
||||
style={{ color: 'var(--color-brand-primary)' }}
|
||||
>
|
||||
"
|
||||
</div>
|
||||
|
||||
<div className="space-y-4 max-w-lg relative z-10">
|
||||
{/* Quote with Compact Typography */}
|
||||
<blockquote
|
||||
className="text-lg leading-relaxed font-medium"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-brand)',
|
||||
color: 'var(--color-brand-black)'
|
||||
}}
|
||||
>
|
||||
{testimonial.quote}
|
||||
</blockquote>
|
||||
|
||||
{/* Attribution */}
|
||||
<div className="space-y-1 pt-3">
|
||||
<div
|
||||
className="text-base font-semibold"
|
||||
style={{ color: 'var(--color-brand-primary)' }}
|
||||
>
|
||||
{testimonial.name}
|
||||
</div>
|
||||
{testimonial.title && (
|
||||
<div
|
||||
className="text-xs"
|
||||
style={{ color: 'var(--color-brand-gray-muted)' }}
|
||||
>
|
||||
{testimonial.title} {testimonial.company && `at ${testimonial.company}`}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Testimonial Navigation Dots */}
|
||||
<div className="flex items-center space-x-2 pt-4">
|
||||
{testimonials.map((_, index) => (
|
||||
<button
|
||||
key={index}
|
||||
className={`w-2 h-2 rounded-full transition-all duration-300 ${
|
||||
index === currentTestimonial
|
||||
? 'opacity-100'
|
||||
: 'opacity-30 hover:opacity-60'
|
||||
}`}
|
||||
style={{
|
||||
backgroundColor: index === currentTestimonial
|
||||
? 'var(--color-brand-accent)'
|
||||
: 'var(--color-brand-gray-muted)'
|
||||
}}
|
||||
onClick={() => setCurrentTestimonial(index)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Large Closing Quotation Mark */}
|
||||
<div
|
||||
className="absolute bottom-4 right-4 text-3xl opacity-30"
|
||||
style={{ color: 'var(--color-brand-primary)' }}
|
||||
>
|
||||
"
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Video Lightbox Modal */}
|
||||
{showLightbox && (
|
||||
<motion.div
|
||||
className="fixed inset-0 bg-black/90 flex items-center justify-center z-50"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
>
|
||||
{/* Close Button */}
|
||||
<button
|
||||
className="absolute top-6 right-6 text-white hover:text-gray-300 transition-colors z-60"
|
||||
onClick={handleCloseLightbox}
|
||||
>
|
||||
<X className="w-8 h-8" />
|
||||
</button>
|
||||
|
||||
{/* Video Container */}
|
||||
<motion.div
|
||||
className="relative w-full max-w-4xl mx-4"
|
||||
initial={{ scale: 0.8, opacity: 0 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
>
|
||||
<div className="relative aspect-video bg-black rounded-lg overflow-hidden">
|
||||
{/* Placeholder for actual video */}
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-gray-900">
|
||||
<div className="text-center text-white">
|
||||
<Play className="w-16 h-16 mx-auto mb-4 opacity-60" fill="currentColor" />
|
||||
<p className="text-lg">Video would play here</p>
|
||||
<p className="text-sm opacity-70 mt-2">Replace with actual video element</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Video Controls */}
|
||||
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/60 to-transparent p-4">
|
||||
<div className="flex items-center space-x-3">
|
||||
<button
|
||||
className="text-white hover:text-gray-300 transition-colors"
|
||||
onClick={() => setIsVideoPlaying(!isVideoPlaying)}
|
||||
>
|
||||
<Play className="w-6 h-6" fill="currentColor" />
|
||||
</button>
|
||||
<div className="flex-1 bg-white/30 rounded-full h-1">
|
||||
<motion.div
|
||||
className="h-full rounded-full"
|
||||
style={{ backgroundColor: 'var(--color-brand-accent)' }}
|
||||
initial={{ width: '0%' }}
|
||||
animate={{ width: isVideoPlaying ? '100%' : '0%' }}
|
||||
transition={{ duration: 30, ease: "linear" }}
|
||||
/>
|
||||
</div>
|
||||
<span className="text-white text-sm">2:34</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
553
src/components/TestimonialsSection.tsx
Normal file
@@ -0,0 +1,553 @@
|
||||
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 {
|
||||
id?: number;
|
||||
name: string;
|
||||
role: string;
|
||||
company?: string;
|
||||
avatar?: string;
|
||||
image?: string;
|
||||
quote: string;
|
||||
rating: number;
|
||||
isVideo?: boolean;
|
||||
videoThumbnail?: string;
|
||||
videoUrl?: string;
|
||||
}
|
||||
|
||||
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"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "David Thompson",
|
||||
role: "Senior Manager",
|
||||
company: "Enterprise Solutions",
|
||||
avatar: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=400&h=400&fit=crop&crop=face",
|
||||
quote: "The personalized coaching and development programs have been game-changing for our organization's leadership pipeline and succession planning initiatives.",
|
||||
rating: 5,
|
||||
isVideo: false
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "Lisa Wang",
|
||||
role: "Product Manager",
|
||||
company: "Digital Ventures",
|
||||
avatar: "https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?w=400&h=400&fit=crop&crop=face",
|
||||
quote: "KLC has transformed how we think about leadership in the digital age. The insights and strategies have been invaluable for our team's growth and innovation culture.",
|
||||
rating: 5,
|
||||
isVideo: true,
|
||||
videoThumbnail: "https://images.unsplash.com/photo-1559136555-9303baea8ebd?w=600&h=300&fit=crop",
|
||||
videoUrl: "https://example.com/testimonial-video-3.mp4"
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "Robert Kim",
|
||||
role: "Regional Director",
|
||||
company: "Global Corp",
|
||||
avatar: "https://images.unsplash.com/photo-1519244703995-f4e0f30006d5?w=400&h=400&fit=crop&crop=face",
|
||||
quote: "The leadership development framework provided by KLC has been instrumental in building a more cohesive and effective leadership team across our regions.",
|
||||
rating: 4,
|
||||
isVideo: false
|
||||
}
|
||||
];
|
||||
|
||||
// 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>
|
||||
);
|
||||
}
|
||||
|
||||
// Individual Testimonial Card - Updated with Landing Page Design Standards
|
||||
function TestimonialCard({ testimonial, onPlayVideo }: {
|
||||
testimonial: Testimonial;
|
||||
onPlayVideo: (videoUrl: string) => void;
|
||||
}) {
|
||||
const avatarSrc = testimonial.avatar || testimonial.image;
|
||||
|
||||
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 */}
|
||||
{testimonial.isVideo ? (
|
||||
<div
|
||||
className="relative h-full cursor-pointer overflow-hidden group rounded-xl"
|
||||
onClick={() => onPlayVideo(testimonial.videoUrl || "")}
|
||||
>
|
||||
<ImageWithFallback
|
||||
src={testimonial.videoThumbnail || avatarSrc || ""}
|
||||
alt={`${testimonial.name} video testimonial`}
|
||||
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 || ""}
|
||||
alt={testimonial.name}
|
||||
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">
|
||||
{testimonial.name}
|
||||
</h4>
|
||||
<p className="text-xs text-white/80 truncate">
|
||||
{testimonial.role}
|
||||
{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}
|
||||
className={star <= testimonial.rating ? 'fill-current text-yellow-400' : 'text-white/40'}
|
||||
/>
|
||||
))}
|
||||
</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 || ""}
|
||||
alt={testimonial.name}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<h4 className="font-semibold text-black mb-1 text-sm">
|
||||
{testimonial.name}
|
||||
</h4>
|
||||
<p className="text-xs text-gray-600">
|
||||
{testimonial.role}
|
||||
</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}
|
||||
className={star <= testimonial.rating ? 'fill-current text-yellow-400' : 'text-gray-300'}
|
||||
/>
|
||||
))}
|
||||
</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">
|
||||
{testimonial.quote}
|
||||
</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.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.testimonial-card:hover {
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
`}</style>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
66
src/components/TrustStatsSection.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
interface StatItemProps {
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
function StatItem({ value, label }: StatItemProps) {
|
||||
return (
|
||||
<div className="text-center">
|
||||
{/* Large Numeric Value */}
|
||||
<div className="mb-4">
|
||||
<span
|
||||
className="text-6xl font-bold tracking-tight max-lg:text-5xl max-md:text-4xl"
|
||||
style={{ color: 'var(--color-brand-primary)' }}
|
||||
>
|
||||
{value}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Short Bold Title */}
|
||||
<h3
|
||||
className="text-lg font-semibold max-lg:text-base max-md:text-sm"
|
||||
style={{ color: 'var(--color-brand-black)' }}
|
||||
>
|
||||
{label}
|
||||
</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function TrustStatsSection() {
|
||||
const stats = [
|
||||
{
|
||||
value: "27,000+",
|
||||
label: "Leaders Developed"
|
||||
},
|
||||
{
|
||||
value: "150+",
|
||||
label: "Corporate Clients"
|
||||
},
|
||||
{
|
||||
value: "20+",
|
||||
label: "Countries Served"
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<section
|
||||
className="py-20 px-[200px] max-lg:px-8 max-md:py-16"
|
||||
style={{ backgroundColor: 'var(--color-brand-bg-white)' }}
|
||||
>
|
||||
{/* Centered content block with max-width: 1200px */}
|
||||
<div className="max-w-[1200px] mx-auto">
|
||||
{/* 3 Equal Columns Grid - Desktop / Stack Vertically - Mobile */}
|
||||
<div className="grid grid-cols-3 gap-12 max-lg:gap-8 max-md:grid-cols-1 max-md:gap-16">
|
||||
{stats.map((stat, index) => (
|
||||
<StatItem
|
||||
key={index}
|
||||
value={stat.value}
|
||||
label={stat.label}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
397
src/components/UpcomingWebinarsSection.tsx
Normal file
@@ -0,0 +1,397 @@
|
||||
import { ArrowRight, ChevronLeft, ChevronRight, ArrowUpRight } from "lucide-react";
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import svgPaths from "../imports/svg-ec87ex3oms";
|
||||
import { PrimaryCTAButton } from "./PrimaryCTAButton";
|
||||
import { navigateTo } from "./Router";
|
||||
import { sharedWebinarsData, type WebinarData } from "../data/webinarsData";
|
||||
|
||||
// WebinarCard Component with unified data structure and navigation
|
||||
interface WebinarCardProps {
|
||||
webinar: WebinarData;
|
||||
}
|
||||
|
||||
function WebinarCard({ webinar }: WebinarCardProps) {
|
||||
const handleCardClick = () => {
|
||||
// All webinar cards now navigate to the same route for consistency
|
||||
navigateTo(`/webinar/${webinar.slug}`);
|
||||
};
|
||||
|
||||
const handleKeyDown = (event: React.KeyboardEvent) => {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
event.preventDefault();
|
||||
handleCardClick();
|
||||
}
|
||||
};
|
||||
|
||||
// Format date for display
|
||||
const formatDate = (dateString: string) => {
|
||||
return new Date(dateString).toLocaleDateString('en-US', {
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
year: 'numeric'
|
||||
});
|
||||
};
|
||||
|
||||
// Get status badge styling
|
||||
const getStatusBadge = () => {
|
||||
switch (webinar.status) {
|
||||
case 'live':
|
||||
return (
|
||||
<span className="px-3 py-1 text-xs font-semibold text-white bg-red-600 rounded-full animate-pulse">
|
||||
LIVE
|
||||
</span>
|
||||
);
|
||||
case 'upcoming':
|
||||
return (
|
||||
<span className="px-3 py-1 text-xs font-semibold text-white bg-blue-600 rounded-full">
|
||||
UPCOMING
|
||||
</span>
|
||||
);
|
||||
case 'recorded':
|
||||
return (
|
||||
<span className="px-3 py-1 text-xs font-semibold text-white bg-green-600 rounded-full">
|
||||
RECORDED
|
||||
</span>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// Get action text based on status
|
||||
const getActionText = () => {
|
||||
switch (webinar.status) {
|
||||
case 'live':
|
||||
return 'Join Now';
|
||||
case 'upcoming':
|
||||
return 'Register';
|
||||
case 'recorded':
|
||||
return 'Watch Recording';
|
||||
default:
|
||||
return 'Learn More';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex-shrink-0 w-80 bg-white rounded-lg shadow-lg overflow-hidden cursor-pointer transition-all duration-300 hover:shadow-xl hover:transform hover:-translate-y-2 group"
|
||||
onClick={handleCardClick}
|
||||
onKeyDown={handleKeyDown}
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
aria-label={`View webinar: ${webinar.title}`}
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{/* Image Container */}
|
||||
<div className="relative h-48 overflow-hidden">
|
||||
<img
|
||||
src={webinar.thumbnail}
|
||||
alt={webinar.title}
|
||||
className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
|
||||
/>
|
||||
|
||||
{/* Status Badge */}
|
||||
<div className="absolute top-4 left-4">
|
||||
{getStatusBadge()}
|
||||
</div>
|
||||
|
||||
{/* Featured Badge */}
|
||||
{webinar.featured && (
|
||||
<div className="absolute top-4 right-4">
|
||||
<span className="px-3 py-1 text-xs font-semibold text-yellow-800 bg-yellow-100 rounded-full border border-yellow-200">
|
||||
Featured
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Hover Overlay */}
|
||||
<div className="absolute inset-0 bg-black bg-opacity-40 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex items-center justify-center">
|
||||
<div className="text-white text-center">
|
||||
<ArrowUpRight className="w-8 h-8 mx-auto mb-2" />
|
||||
<span className="text-sm font-medium">View Details</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="p-6">
|
||||
<h3
|
||||
className="text-lg font-semibold mb-3 text-gray-900 group-hover:text-blue-600 transition-colors duration-200 line-clamp-2"
|
||||
style={{
|
||||
color: 'var(--color-brand-black)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
lineHeight: 'var(--line-height-h4)'
|
||||
}}
|
||||
>
|
||||
{webinar.title}
|
||||
</h3>
|
||||
|
||||
<div className="space-y-2 mb-4">
|
||||
<p
|
||||
className="text-sm text-muted flex items-center"
|
||||
style={{
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontSize: 'var(--font-small)'
|
||||
}}
|
||||
>
|
||||
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||
</svg>
|
||||
{webinar.presenter}
|
||||
</p>
|
||||
|
||||
<p
|
||||
className="text-sm text-muted flex items-center"
|
||||
style={{
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontSize: 'var(--font-small)'
|
||||
}}
|
||||
>
|
||||
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||
</svg>
|
||||
{formatDate(webinar.date)} at {webinar.time} {webinar.timezone}
|
||||
</p>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<p
|
||||
className="text-sm text-muted flex items-center"
|
||||
style={{
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontSize: 'var(--font-small)'
|
||||
}}
|
||||
>
|
||||
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
{webinar.duration}
|
||||
</p>
|
||||
|
||||
<p
|
||||
className="text-sm text-muted flex items-center"
|
||||
style={{
|
||||
color: 'var(--color-gray-muted)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontSize: 'var(--font-small)'
|
||||
}}
|
||||
>
|
||||
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" />
|
||||
</svg>
|
||||
{webinar.attendees}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Action Indicator - Consistent with status */}
|
||||
<div className="flex items-center justify-between">
|
||||
<span
|
||||
className="text-sm font-medium group-hover:text-blue-600 transition-colors duration-200"
|
||||
style={{
|
||||
color: 'var(--color-primary)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontSize: 'var(--font-small)',
|
||||
fontWeight: 'var(--font-weight-subhead)'
|
||||
}}
|
||||
>
|
||||
{getActionText()}
|
||||
</span>
|
||||
<ArrowRight className="w-4 h-4 text-blue-600 group-hover:translate-x-1 transition-transform duration-200" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function UpcomingWebinarsSection() {
|
||||
const [canScrollLeft, setCanScrollLeft] = useState(false);
|
||||
const [canScrollRight, setCanScrollRight] = useState(true);
|
||||
const carouselRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const checkScrollButtons = () => {
|
||||
if (carouselRef.current) {
|
||||
const { scrollLeft, scrollWidth, clientWidth } = carouselRef.current;
|
||||
setCanScrollLeft(scrollLeft > 0);
|
||||
setCanScrollRight(scrollLeft < scrollWidth - clientWidth - 1);
|
||||
}
|
||||
};
|
||||
|
||||
const scrollLeft = () => {
|
||||
if (carouselRef.current) {
|
||||
carouselRef.current.scrollBy({ left: -340, behavior: 'smooth' });
|
||||
}
|
||||
};
|
||||
|
||||
const scrollRight = () => {
|
||||
if (carouselRef.current) {
|
||||
carouselRef.current.scrollBy({ left: 340, behavior: 'smooth' });
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const carousel = carouselRef.current;
|
||||
if (carousel) {
|
||||
carousel.addEventListener('scroll', checkScrollButtons);
|
||||
checkScrollButtons(); // Initial check
|
||||
return () => carousel.removeEventListener('scroll', checkScrollButtons);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="py-20"
|
||||
style={{ backgroundColor: '#FFFFFF' }}
|
||||
>
|
||||
<div className="section-margin-x">
|
||||
<div className="grid grid-cols-12 gap-16 items-start">
|
||||
|
||||
{/* Left Column - Content */}
|
||||
<div className="col-span-12 lg:col-span-5">
|
||||
{/* Heading */}
|
||||
<h2
|
||||
className="text-4xl leading-tight mb-6"
|
||||
style={{
|
||||
color: 'var(--color-brand-black)',
|
||||
fontFamily: 'var(--font-family-brand)',
|
||||
fontWeight: '700'
|
||||
}}
|
||||
>
|
||||
Upcoming Corporate Webinars
|
||||
</h2>
|
||||
|
||||
{/* Description */}
|
||||
<p
|
||||
className="text-lg leading-relaxed mb-8"
|
||||
style={{
|
||||
color: 'var(--color-brand-black)',
|
||||
fontFamily: 'var(--font-family-brand)',
|
||||
fontWeight: '400'
|
||||
}}
|
||||
>
|
||||
Join live sessions led by leadership experts designed for professionals looking to elevate strategic thinking, decision-making, and people leadership.
|
||||
</p>
|
||||
|
||||
{/* Navigation Controls */}
|
||||
<div className="flex gap-3 mb-8">
|
||||
<button
|
||||
className={`w-12 h-12 flex items-center justify-center rounded-lg border-2 transition-all duration-300 ${
|
||||
!canScrollLeft
|
||||
? 'opacity-40 cursor-not-allowed bg-gray-100 border-gray-300'
|
||||
: 'bg-white hover:scale-105 hover:shadow-lg active:scale-95'
|
||||
}`}
|
||||
onClick={scrollLeft}
|
||||
disabled={!canScrollLeft}
|
||||
aria-label="Previous webinar"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-brand)',
|
||||
borderColor: !canScrollLeft ? '#D1D5DB' : '#04045B',
|
||||
backgroundColor: !canScrollLeft ? '#F3F4F6' : 'white'
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
if (!canScrollLeft) return;
|
||||
e.currentTarget.style.backgroundColor = '#04045B';
|
||||
const icon = e.currentTarget.querySelector('svg');
|
||||
if (icon) icon.style.color = 'white';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (!canScrollLeft) return;
|
||||
e.currentTarget.style.backgroundColor = 'white';
|
||||
const icon = e.currentTarget.querySelector('svg');
|
||||
if (icon) icon.style.color = '#04045B';
|
||||
}}
|
||||
>
|
||||
<ChevronLeft
|
||||
size={20}
|
||||
style={{
|
||||
color: !canScrollLeft ? '#9CA3AF' : '#04045B',
|
||||
transition: 'color 0.3s ease'
|
||||
}}
|
||||
strokeWidth={2.5}
|
||||
/>
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`w-12 h-12 flex items-center justify-center rounded-lg border-2 transition-all duration-300 ${
|
||||
!canScrollRight
|
||||
? 'opacity-40 cursor-not-allowed bg-gray-100 border-gray-300'
|
||||
: 'bg-white hover:scale-105 hover:shadow-lg active:scale-95'
|
||||
}`}
|
||||
onClick={scrollRight}
|
||||
disabled={!canScrollRight}
|
||||
aria-label="Next webinar"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-brand)',
|
||||
borderColor: !canScrollRight ? '#D1D5DB' : '#04045B',
|
||||
backgroundColor: !canScrollRight ? '#F3F4F6' : 'white'
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
if (!canScrollRight) return;
|
||||
e.currentTarget.style.backgroundColor = '#04045B';
|
||||
const icon = e.currentTarget.querySelector('svg');
|
||||
if (icon) icon.style.color = 'white';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (!canScrollRight) return;
|
||||
e.currentTarget.style.backgroundColor = 'white';
|
||||
const icon = e.currentTarget.querySelector('svg');
|
||||
if (icon) icon.style.color = '#04045B';
|
||||
}}
|
||||
>
|
||||
<ChevronRight
|
||||
size={20}
|
||||
style={{
|
||||
color: !canScrollRight ? '#9CA3AF' : '#04045B',
|
||||
transition: 'color 0.3s ease'
|
||||
}}
|
||||
strokeWidth={2.5}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* CTA Button - Navigate to main webinars page */}
|
||||
<PrimaryCTAButton
|
||||
text="Explore All"
|
||||
onClick={() => navigateTo('/webinars')}
|
||||
ariaLabel="Explore all upcoming webinars"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Right Column - Carousel */}
|
||||
<div className="col-span-12 lg:col-span-7">
|
||||
<div className="relative">
|
||||
{/* Carousel Container */}
|
||||
<div
|
||||
ref={carouselRef}
|
||||
className="flex gap-6 overflow-x-hidden scroll-smooth"
|
||||
style={{
|
||||
scrollbarWidth: 'none',
|
||||
msOverflowStyle: 'none'
|
||||
}}
|
||||
>
|
||||
{/* Use shared webinar data for consistency */}
|
||||
{sharedWebinarsData.map((webinar) => (
|
||||
<WebinarCard key={webinar.id} webinar={webinar} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Fade Gradient Overlay */}
|
||||
<div
|
||||
className="absolute top-0 right-0 bottom-0 w-16 pointer-events-none"
|
||||
style={{
|
||||
background: 'linear-gradient(to right, transparent, #FFFFFF)'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
829
src/components/VirtualSpaceSection.tsx
Normal file
@@ -0,0 +1,829 @@
|
||||
import React, { useState } from "react";
|
||||
import { motion } from "motion/react";
|
||||
import {
|
||||
Building,
|
||||
Users,
|
||||
Presentation,
|
||||
Coffee,
|
||||
Play,
|
||||
Calendar,
|
||||
ArrowRight,
|
||||
Eye,
|
||||
X,
|
||||
ChevronLeft,
|
||||
ChevronRight
|
||||
} from "lucide-react";
|
||||
import { ImageWithFallback } from "./figma/ImageWithFallback";
|
||||
import { BrandedTag } from "./about/BrandedTag";
|
||||
import { PrimaryCTAButton } from "./PrimaryCTAButton";
|
||||
import { Button } from "./ui/button";
|
||||
import { Input } from "./ui/input";
|
||||
import { Label } from "./ui/label";
|
||||
import { Textarea } from "./ui/textarea";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select";
|
||||
import { navigateTo } from "./Router";
|
||||
|
||||
// Calendar helper functions
|
||||
const getDaysInMonth = (date: Date) => {
|
||||
return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
|
||||
};
|
||||
|
||||
const getFirstDayOfMonth = (date: Date) => {
|
||||
return new Date(date.getFullYear(), date.getMonth(), 1).getDay();
|
||||
};
|
||||
|
||||
const formatDate = (date: Date) => {
|
||||
return date.toISOString().split('T')[0];
|
||||
};
|
||||
|
||||
const isDateAvailable = (date: Date) => {
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
const selectedDate = new Date(date);
|
||||
selectedDate.setHours(0, 0, 0, 0);
|
||||
|
||||
// Available if it's today or in the future, and not a Sunday
|
||||
return selectedDate >= today && selectedDate.getDay() !== 0;
|
||||
};
|
||||
|
||||
const facilities = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Campus",
|
||||
description: "Our flagship campus offers comprehensive leadership development with state-of-the-art facilities.",
|
||||
icon: Building,
|
||||
image: "https://images.unsplash.com/photo-1497366216548-37526070297c?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1200&q=80",
|
||||
videoUrl: "https://www.youtube.com/embed/dQw4w9WgXcQ", // Virtual tour video
|
||||
capacity: "50-80",
|
||||
features: ["State-of-the-art facilities", "Comprehensive programs", "Modern infrastructure"]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Classroom",
|
||||
description: "Interactive learning environment with flexible seating and cutting-edge educational technology.",
|
||||
icon: Users,
|
||||
image: "https://images.unsplash.com/photo-1562774053-701939374585?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1200&q=80",
|
||||
videoUrl: "https://www.youtube.com/embed/dQw4w9WgXcQ", // Virtual tour video
|
||||
capacity: "20-30",
|
||||
features: ["Interactive learning", "Flexible seating", "Educational technology"]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Auditorium",
|
||||
description: "Premier presentation venue with theater-style seating and professional AV systems for impactful presentations.",
|
||||
icon: Presentation,
|
||||
image: "https://images.unsplash.com/photo-1540575467063-178a50c2df87?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1200&q=80",
|
||||
videoUrl: "https://www.youtube.com/embed/dQw4w9WgXcQ", // Virtual tour video
|
||||
capacity: "100-150",
|
||||
features: ["Theater-style seating", "Professional AV systems", "Impactful presentations"]
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Boardroom",
|
||||
description: "Executive meeting space designed for strategic discussions and high-level decision making with premium amenities.",
|
||||
icon: Coffee,
|
||||
image: "https://images.unsplash.com/photo-1497366754035-f200968a6e72?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1200&q=80",
|
||||
videoUrl: "https://www.youtube.com/embed/dQw4w9WgXcQ", // Virtual tour video
|
||||
capacity: "8-15",
|
||||
features: ["Executive meeting space", "Strategic discussions", "Premium amenities"]
|
||||
}
|
||||
];
|
||||
|
||||
interface BookingFormData {
|
||||
companyName: string;
|
||||
contactName: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
role: string;
|
||||
teamSize: string;
|
||||
facilityType: string;
|
||||
preferredDate: string;
|
||||
additionalRequirements: string;
|
||||
}
|
||||
|
||||
interface FacilityCardProps {
|
||||
facility: typeof facilities[0];
|
||||
index: number;
|
||||
onBookNow: (facility: typeof facilities[0]) => void;
|
||||
}
|
||||
|
||||
function FacilityCard({ facility, index, onBookNow }: FacilityCardProps) {
|
||||
const IconComponent = facility.icon;
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
className="relative group h-full overflow-hidden"
|
||||
initial={{ opacity: 0, y: 60 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.7, delay: index * 0.15 }}
|
||||
viewport={{ once: true, margin: "-50px" }}
|
||||
>
|
||||
{/* Background Image - Full Height */}
|
||||
<div className="absolute inset-0">
|
||||
<ImageWithFallback
|
||||
src={facility.image}
|
||||
alt={facility.name}
|
||||
className="w-full h-full object-cover transition-transform duration-700 ease-out group-hover:scale-110"
|
||||
/>
|
||||
{/* Dark overlay with hover effect */}
|
||||
<div className="absolute inset-0 bg-black/60 transition-all duration-500 ease-out group-hover:bg-black/40" />
|
||||
</div>
|
||||
|
||||
{/* Content - Positioned at bottom of card */}
|
||||
<div className="relative z-10 h-full flex flex-col justify-end p-8 max-lg:p-6">
|
||||
{/* Icon */}
|
||||
<div className="flex justify-center mb-4">
|
||||
<div
|
||||
className="w-16 h-16 rounded-2xl flex items-center justify-center bg-white/20 backdrop-blur-sm border border-white/30"
|
||||
>
|
||||
<IconComponent
|
||||
className="w-8 h-8 text-white"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<h3 className="text-3xl font-bold mb-3 text-white max-lg:text-2xl text-center">
|
||||
{facility.name}
|
||||
</h3>
|
||||
|
||||
{/* Description */}
|
||||
<p className="text-white/90 leading-relaxed mb-6 text-base max-lg:text-sm text-center max-lg:mb-5">
|
||||
{facility.description}
|
||||
</p>
|
||||
|
||||
{/* Book Now Button */}
|
||||
<div className="flex justify-center">
|
||||
<div className="hero-slide-button">
|
||||
<PrimaryCTAButton
|
||||
text="Book Now"
|
||||
onClick={() => onBookNow(facility)}
|
||||
ariaLabel={`Book ${facility.name} now`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
// Modal Component for Virtual Tour and Booking
|
||||
function BookingModal({
|
||||
facility,
|
||||
isOpen,
|
||||
onClose
|
||||
}: {
|
||||
facility: typeof facilities[0] | null;
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
}) {
|
||||
const [bookingForm, setBookingForm] = useState<BookingFormData>({
|
||||
companyName: '',
|
||||
contactName: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
role: '',
|
||||
teamSize: '',
|
||||
facilityType: facility?.name || '',
|
||||
preferredDate: '',
|
||||
additionalRequirements: ''
|
||||
});
|
||||
|
||||
// Calendar state
|
||||
const [currentMonth, setCurrentMonth] = useState(new Date());
|
||||
const [selectedDate, setSelectedDate] = useState<Date | null>(null);
|
||||
|
||||
// Lock body scroll when modal is open
|
||||
React.useEffect(() => {
|
||||
if (isOpen) {
|
||||
document.body.style.overflow = 'hidden';
|
||||
document.body.style.paddingRight = '15px'; // Prevent layout shift from scrollbar
|
||||
} else {
|
||||
document.body.style.overflow = '';
|
||||
document.body.style.paddingRight = '';
|
||||
}
|
||||
|
||||
// Cleanup on unmount or when modal closes
|
||||
return () => {
|
||||
document.body.style.overflow = '';
|
||||
document.body.style.paddingRight = '';
|
||||
};
|
||||
}, [isOpen]);
|
||||
|
||||
const handleFormSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
console.log('Booking form submitted:', bookingForm);
|
||||
// Here you would typically send the form data to your backend
|
||||
alert('Booking request submitted successfully! We will contact you soon.');
|
||||
onClose();
|
||||
};
|
||||
|
||||
const updateFormField = (field: keyof BookingFormData, value: string) => {
|
||||
setBookingForm(prev => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
// Calendar functions
|
||||
const handleDateSelect = (date: Date) => {
|
||||
if (isDateAvailable(date)) {
|
||||
setSelectedDate(date);
|
||||
updateFormField('preferredDate', formatDate(date));
|
||||
}
|
||||
};
|
||||
|
||||
const navigateMonth = (direction: 'prev' | 'next') => {
|
||||
setCurrentMonth(prev => {
|
||||
const newMonth = new Date(prev);
|
||||
if (direction === 'prev') {
|
||||
newMonth.setMonth(prev.getMonth() - 1);
|
||||
} else {
|
||||
newMonth.setMonth(prev.getMonth() + 1);
|
||||
}
|
||||
return newMonth;
|
||||
});
|
||||
};
|
||||
|
||||
const renderCalendar = () => {
|
||||
const daysInMonth = getDaysInMonth(currentMonth);
|
||||
const firstDay = getFirstDayOfMonth(currentMonth);
|
||||
const days = [];
|
||||
const monthNames = [
|
||||
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
||||
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
|
||||
];
|
||||
const dayNames = ['S', 'M', 'T', 'W', 'T', 'F', 'S'];
|
||||
|
||||
// Empty cells for days before the first day of the month
|
||||
for (let i = 0; i < firstDay; i++) {
|
||||
days.push(<div key={`empty-${i}`} className="h-6" />);
|
||||
}
|
||||
|
||||
// Days of the month
|
||||
for (let day = 1; day <= daysInMonth; day++) {
|
||||
const date = new Date(currentMonth.getFullYear(), currentMonth.getMonth(), day);
|
||||
const isAvailable = isDateAvailable(date);
|
||||
const isSelected = selectedDate &&
|
||||
date.getFullYear() === selectedDate.getFullYear() &&
|
||||
date.getMonth() === selectedDate.getMonth() &&
|
||||
date.getDate() === selectedDate.getDate();
|
||||
|
||||
days.push(
|
||||
<button
|
||||
key={day}
|
||||
type="button"
|
||||
onClick={() => handleDateSelect(date)}
|
||||
disabled={!isAvailable}
|
||||
className={`
|
||||
h-6 w-6 rounded text-xs font-medium transition-all duration-200 flex items-center justify-center
|
||||
${isSelected
|
||||
? 'bg-primary text-white shadow-sm'
|
||||
: isAvailable
|
||||
? 'hover:bg-blue-50 hover:text-primary text-gray-700'
|
||||
: 'text-gray-300 cursor-not-allowed'
|
||||
}
|
||||
`}
|
||||
style={{
|
||||
backgroundColor: isSelected ? 'var(--color-primary)' : undefined,
|
||||
color: isSelected ? 'white' : undefined
|
||||
}}
|
||||
>
|
||||
{day}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-lg border border-gray-200 p-3">
|
||||
{/* Calendar Header */}
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => navigateMonth('prev')}
|
||||
className="p-1 rounded hover:bg-gray-100 transition-colors"
|
||||
>
|
||||
<ChevronLeft className="w-3 h-3 text-gray-600" />
|
||||
</button>
|
||||
<h4 className="text-sm font-semibold text-gray-900">
|
||||
{monthNames[currentMonth.getMonth()]} {currentMonth.getFullYear()}
|
||||
</h4>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => navigateMonth('next')}
|
||||
className="p-1 rounded hover:bg-gray-100 transition-colors"
|
||||
>
|
||||
<ChevronRight className="w-3 h-3 text-gray-600" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Day Names */}
|
||||
<div className="grid grid-cols-7 gap-1 mb-2">
|
||||
{dayNames.map((day, index) => (
|
||||
<div key={`day-${index}`} className="h-6 flex items-center justify-center">
|
||||
<span className="text-xs font-medium text-gray-500">{day}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Calendar Grid */}
|
||||
<div className="grid grid-cols-7 gap-1 mb-3">
|
||||
{days}
|
||||
</div>
|
||||
|
||||
{/* Calendar Footer - Compact */}
|
||||
<div className="pt-2 border-t border-gray-200">
|
||||
<div className="flex items-center justify-center gap-3 text-xs text-gray-500">
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="w-2 h-2 rounded-full bg-primary"></div>
|
||||
<span>Selected</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="w-2 h-2 rounded-full border border-gray-300"></div>
|
||||
<span>Available</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
if (!isOpen || !facility) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0 bg-black/60 flex items-center justify-center p-2 lg:p-4 z-popup-modal virtual-space-modal-overlay"
|
||||
onClick={onClose}
|
||||
>
|
||||
<div
|
||||
className="bg-white rounded-xl lg:rounded-2xl max-w-5xl w-full h-[85vh] overflow-hidden virtual-space-modal-container flex flex-col"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{/* Modal Header - Compact */}
|
||||
<div
|
||||
className="flex items-center justify-between p-4 lg:p-6 border-b flex-shrink-0"
|
||||
style={{ backgroundColor: 'rgba(4, 4, 91, 0.02)' }}
|
||||
>
|
||||
<div className="flex items-center gap-3 lg:gap-4">
|
||||
<div
|
||||
className="w-10 h-10 lg:w-12 lg:h-12 rounded-lg flex items-center justify-center"
|
||||
style={{ backgroundColor: 'var(--color-primary)' }}
|
||||
>
|
||||
<facility.icon className="w-5 h-5 lg:w-6 lg:h-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-body lg:text-subhead mb-1">
|
||||
{facility.name} Virtual Tour & Booking
|
||||
</h2>
|
||||
<p className="text-small text-muted">
|
||||
Capacity: {facility.capacity} people
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={onClose}
|
||||
className="rounded-full w-8 h-8 lg:w-10 lg:h-10 p-0 hover:bg-gray-100"
|
||||
>
|
||||
<X className="w-4 h-4 lg:w-5 lg:h-5" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Modal Content - Side by Side Layout No Scroll */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 flex-1 min-h-0">
|
||||
{/* Left Side - Virtual Tour */}
|
||||
<div className="p-3 lg:p-6 border-r border-gray-100 flex flex-col min-h-0">
|
||||
<div className="flex flex-col h-full space-y-2 lg:space-y-3">
|
||||
{/* Video Section - Compact */}
|
||||
<div className="flex-shrink-0">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Play className="w-4 h-4 text-primary" />
|
||||
<h3 className="text-small lg:text-body font-semibold">Virtual Tour</h3>
|
||||
</div>
|
||||
|
||||
{/* Virtual Tour Container - Developer-Ready for 360 Viewer or Video */}
|
||||
<div className="aspect-video rounded-lg overflow-hidden bg-gray-100 shadow-md relative">
|
||||
{/*
|
||||
DEVELOPER NOTE: Replace this container with your preferred 360 viewer
|
||||
Popular options:
|
||||
- A-Frame (https://aframe.io/) for WebXR 360 content
|
||||
- Three.js with 360 photo/video support
|
||||
- Pannellum (https://pannellum.org/) for 360 photos
|
||||
- Krpano (https://krpano.com/) for advanced 360 tours
|
||||
- React 360 (https://facebook.github.io/react-360/)
|
||||
|
||||
Current implementation: Fallback video iframe
|
||||
Replace the entire div below with your 360 viewer component
|
||||
*/}
|
||||
<div
|
||||
id={`virtual-tour-container-${facility.id}`}
|
||||
className="w-full h-full relative"
|
||||
data-facility-id={facility.id}
|
||||
data-facility-name={facility.name}
|
||||
data-tour-type="360-viewer" // Change to "video" for video fallback
|
||||
>
|
||||
{/* Fallback: Video iframe - Replace this entire section with 360 viewer */}
|
||||
<iframe
|
||||
src={facility.videoUrl}
|
||||
title={`${facility.name} Virtual Tour`}
|
||||
className="w-full h-full"
|
||||
allowFullScreen
|
||||
frameBorder="0"
|
||||
/>
|
||||
|
||||
{/* 360 Viewer Placeholder - Remove iframe above and uncomment/implement below */}
|
||||
{/*
|
||||
Example A-Frame 360 implementation:
|
||||
<a-scene
|
||||
embedded
|
||||
style={{width: '100%', height: '100%'}}
|
||||
vr-mode-ui="enabled: false"
|
||||
>
|
||||
<a-sky src={facility.panoramaUrl || facility.image} />
|
||||
<a-camera wasd-controls-enabled="false" look-controls="enabled: true" />
|
||||
</a-scene>
|
||||
*/}
|
||||
|
||||
{/* Alternative: Custom 360 Photo Viewer Container */}
|
||||
{/*
|
||||
<div className="w-full h-full" id={`pannellum-${facility.id}`}>
|
||||
// Pannellum or other 360 viewer initialization
|
||||
</div>
|
||||
*/}
|
||||
</div>
|
||||
|
||||
{/* Interactive Controls Overlay (optional) */}
|
||||
<div className="absolute bottom-2 left-2 right-2 flex justify-between items-center pointer-events-none">
|
||||
<div className="bg-black/20 backdrop-blur-sm rounded px-2 py-1">
|
||||
<span className="text-white text-xs">360° Virtual Tour</span>
|
||||
</div>
|
||||
<div className="bg-black/20 backdrop-blur-sm rounded px-2 py-1">
|
||||
<span className="text-white text-xs">Click & Drag to Explore</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Compact Info Section */}
|
||||
<div className="flex-1 min-h-0 space-y-2 lg:space-y-3">
|
||||
{/* About - Compact */}
|
||||
<div className="bg-gray-50 rounded-lg p-2 lg:p-3">
|
||||
<h4 className="text-small font-semibold mb-1">About This Space</h4>
|
||||
<p className="text-xs lg:text-small text-muted leading-relaxed line-clamp-2">
|
||||
{facility.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Features - Compact */}
|
||||
<div className="bg-gray-50 rounded-lg p-2 lg:p-3">
|
||||
<h4 className="text-small font-semibold mb-2">Key Features</h4>
|
||||
<div className="space-y-1">
|
||||
{facility.features.slice(0, 3).map((feature, index) => (
|
||||
<div key={index} className="flex items-center gap-2">
|
||||
<div
|
||||
className="w-1.5 h-1.5 rounded-full flex-shrink-0"
|
||||
style={{ backgroundColor: 'var(--color-primary)' }}
|
||||
/>
|
||||
<span className="text-xs lg:text-small">{feature}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quick Stats - Compact */}
|
||||
<div className="grid grid-cols-2 gap-2 p-2 lg:p-3 bg-blue-50 rounded-lg">
|
||||
<div className="text-center">
|
||||
<div className="text-xs lg:text-small font-semibold text-primary">Capacity</div>
|
||||
<div className="text-xs lg:text-small text-muted">{facility.capacity}</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-xs lg:text-small font-semibold text-primary">Zone</div>
|
||||
<div className="text-xs lg:text-small text-muted">Premium</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Action Button - Compact */}
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => navigateTo('/services/learning-facility')}
|
||||
className="w-full h-8 lg:h-10 text-xs lg:text-small"
|
||||
>
|
||||
<Building className="w-3 h-3 lg:w-4 lg:h-4 mr-1" />
|
||||
View All Facilities
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Side - Booking Form */}
|
||||
<div className="p-4 lg:p-6 flex flex-col min-h-0 overflow-y-auto">
|
||||
<form onSubmit={handleFormSubmit} className="flex flex-col h-full">
|
||||
{/* Form Header */}
|
||||
<div className="flex items-center gap-3 mb-4 flex-shrink-0">
|
||||
<Calendar className="w-5 h-5 text-primary" />
|
||||
<h3 className="text-subhead">Book This Space</h3>
|
||||
</div>
|
||||
|
||||
{/* Form Content with Compact Spacing */}
|
||||
<div className="flex-1 min-h-0 space-y-4 overflow-y-auto pr-2 virtual-space-modal-scroll">
|
||||
{/* Company Information Section */}
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-1">
|
||||
<h4 className="text-small font-medium text-primary">Company Information</h4>
|
||||
<div className="w-10 h-0.5 bg-primary"></div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-2">
|
||||
<div>
|
||||
<Label htmlFor="companyName" className="text-xs font-normal text-black mb-1 block">
|
||||
Company Name *
|
||||
</Label>
|
||||
<Input
|
||||
id="companyName"
|
||||
required
|
||||
value={bookingForm.companyName}
|
||||
onChange={(e) => updateFormField('companyName', e.target.value)}
|
||||
placeholder="Company"
|
||||
className="h-8 text-sm border border-gray-300 rounded focus:border-primary focus:ring-1 focus:ring-primary/30 transition-all duration-200 placeholder:text-gray-400 placeholder:opacity-50"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="contactName" className="text-xs font-normal text-black mb-1 block">
|
||||
Contact Person *
|
||||
</Label>
|
||||
<Input
|
||||
id="contactName"
|
||||
required
|
||||
value={bookingForm.contactName}
|
||||
onChange={(e) => updateFormField('contactName', e.target.value)}
|
||||
placeholder="Name"
|
||||
className="h-8 text-sm border border-gray-300 rounded focus:border-primary focus:ring-1 focus:ring-primary/30 transition-all duration-200 placeholder:text-gray-400 placeholder:opacity-50"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-2">
|
||||
<div>
|
||||
<Label htmlFor="email" className="text-xs font-normal text-black mb-1 block">
|
||||
Email Address *
|
||||
</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
required
|
||||
value={bookingForm.email}
|
||||
onChange={(e) => updateFormField('email', e.target.value)}
|
||||
placeholder="email@company.com"
|
||||
className="h-8 text-sm border border-gray-300 rounded focus:border-primary focus:ring-1 focus:ring-primary/30 transition-all duration-200 placeholder:text-gray-400 placeholder:opacity-50"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="phone" className="text-xs font-normal text-black mb-1 block">
|
||||
Phone Number *
|
||||
</Label>
|
||||
<Input
|
||||
id="phone"
|
||||
required
|
||||
value={bookingForm.phone}
|
||||
onChange={(e) => updateFormField('phone', e.target.value)}
|
||||
placeholder="+91 98765 43210"
|
||||
className="h-8 text-sm border border-gray-300 rounded focus:border-primary focus:ring-1 focus:ring-primary/30 transition-all duration-200 placeholder:text-gray-400 placeholder:opacity-50"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-2">
|
||||
<div>
|
||||
<Label htmlFor="role" className="text-xs font-normal text-black mb-1 block">
|
||||
Your Role *
|
||||
</Label>
|
||||
<Select value={bookingForm.role} onValueChange={(value) => updateFormField('role', value)}>
|
||||
<SelectTrigger className="h-8 text-sm border border-gray-300 rounded focus:border-primary focus:ring-1 focus:ring-primary/30 transition-all duration-200">
|
||||
<SelectValue placeholder="Role" className="text-gray-400 opacity-50" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="ceo">CEO/Founder</SelectItem>
|
||||
<SelectItem value="hr-director">HR Director</SelectItem>
|
||||
<SelectItem value="training-manager">Training Manager</SelectItem>
|
||||
<SelectItem value="operations-manager">Operations Manager</SelectItem>
|
||||
<SelectItem value="executive-assistant">Executive Assistant</SelectItem>
|
||||
<SelectItem value="other">Other</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="teamSize" className="text-xs font-normal text-black mb-1 block">
|
||||
Expected Team Size *
|
||||
</Label>
|
||||
<Select value={bookingForm.teamSize} onValueChange={(value) => updateFormField('teamSize', value)}>
|
||||
<SelectTrigger className="h-8 text-sm border border-gray-300 rounded focus:border-primary focus:ring-1 focus:ring-primary/30 transition-all duration-200">
|
||||
<SelectValue placeholder="Size" className="text-gray-400 opacity-50" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="1-5">1-5 people</SelectItem>
|
||||
<SelectItem value="6-15">6-15 people</SelectItem>
|
||||
<SelectItem value="16-30">16-30 people</SelectItem>
|
||||
<SelectItem value="31-50">31-50 people</SelectItem>
|
||||
<SelectItem value="50+">50+ people</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Booking Details Section */}
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-1">
|
||||
<h4 className="text-small font-medium text-primary">Select Your Date</h4>
|
||||
<div className="w-10 h-0.5 bg-primary"></div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
{/* Selected Facility Info */}
|
||||
<div className="bg-blue-50 rounded p-2 border border-blue-100">
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="w-6 h-6 rounded flex items-center justify-center"
|
||||
style={{ backgroundColor: 'var(--color-primary)' }}
|
||||
>
|
||||
<facility.icon className="w-3 h-3 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-900">{facility.name}</p>
|
||||
<p className="text-xs text-gray-600">Capacity: {facility.capacity} people</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Calendar Date Selection */}
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-normal text-black">
|
||||
Choose Your Preferred Date *
|
||||
</Label>
|
||||
{renderCalendar()}
|
||||
{selectedDate && (
|
||||
<div className="mt-2 p-2 bg-green-50 rounded border border-green-200">
|
||||
<div className="flex items-center gap-2">
|
||||
<Calendar className="w-3 h-3 text-green-600" />
|
||||
<span className="text-xs font-medium text-green-800">
|
||||
Selected: {selectedDate.toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric'
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Additional Requirements */}
|
||||
<div>
|
||||
<Label htmlFor="additionalRequirements" className="text-xs font-normal text-black mb-1 block">
|
||||
Additional Requirements
|
||||
</Label>
|
||||
<Textarea
|
||||
id="additionalRequirements"
|
||||
value={bookingForm.additionalRequirements}
|
||||
onChange={(e) => updateFormField('additionalRequirements', e.target.value)}
|
||||
placeholder="Special setup, AV equipment, catering..."
|
||||
rows={2}
|
||||
className="text-sm border border-gray-300 rounded focus:border-primary focus:ring-1 focus:ring-primary/30 transition-all duration-200 placeholder:text-gray-400 placeholder:opacity-50 resize-none"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Form Actions */}
|
||||
<div className="flex flex-col sm:flex-row gap-2 pt-3 border-t border-gray-200 mt-3 flex-shrink-0">
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={!selectedDate}
|
||||
className="flex-1 h-9 text-sm font-medium bg-primary hover:bg-primary/90 disabled:bg-gray-300 disabled:cursor-not-allowed text-white border-0 rounded shadow-sm hover:shadow-md transition-all duration-200"
|
||||
style={{
|
||||
backgroundColor: selectedDate ? 'var(--color-primary)' : '#d1d5db',
|
||||
color: 'white'
|
||||
}}
|
||||
>
|
||||
<Calendar className="w-3 h-3 mr-2" />
|
||||
{selectedDate ? 'Submit Request' : 'Select Date First'}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={onClose}
|
||||
className="px-4 h-9 text-sm font-normal border border-gray-300 hover:border-gray-400 hover:bg-gray-50 rounded transition-all duration-200"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function VirtualSpaceSection() {
|
||||
const [selectedFacility, setSelectedFacility] = useState<typeof facilities[0] | null>(null);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
|
||||
const handleBookNow = (facility: typeof facilities[0]) => {
|
||||
setSelectedFacility(facility);
|
||||
setIsModalOpen(true);
|
||||
};
|
||||
|
||||
const handleCloseModal = () => {
|
||||
setIsModalOpen(false);
|
||||
setSelectedFacility(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="relative overflow-hidden">
|
||||
{/* Full-Width Image Section with Overlaid Content */}
|
||||
<div className="relative h-screen min-h-[600px] max-h-[800px]">
|
||||
{/* Facility Cards Grid - Single Row, Side by Side */}
|
||||
<div className="h-full grid grid-cols-4 max-lg:grid-cols-2 max-md:grid-cols-1">
|
||||
{facilities.map((facility, index) => (
|
||||
<FacilityCard
|
||||
key={facility.id}
|
||||
facility={facility}
|
||||
index={index}
|
||||
onBookNow={handleBookNow}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Overlaid Content - Centered at top of image section */}
|
||||
<div className="absolute top-0 left-0 right-0 z-20 py-16 max-md:py-12 section-margin-x">
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
{/* Branded Tag */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<BrandedTag
|
||||
text="Virtual Learning Environment"
|
||||
className="justify-center"
|
||||
variant="white"
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
{/* Main Heading */}
|
||||
<motion.h2
|
||||
className="text-5xl font-bold leading-tight mb-4 max-lg:text-4xl max-md:text-3xl text-white"
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.2 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
Experience Our Space Virtually
|
||||
</motion.h2>
|
||||
|
||||
{/* Subheading */}
|
||||
<motion.p
|
||||
className="text-lg leading-relaxed max-w-2xl mx-auto max-lg:text-base mb-6 text-white/90"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.4 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
Take a virtual walk through our state-of-the-art facility designed to inspire leadership excellence and foster collaborative learning.
|
||||
</motion.p>
|
||||
|
||||
{/* Main CTA Button - Explore Our Space */}
|
||||
<motion.div
|
||||
className="flex justify-center"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.6 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<div className="hero-slide-button">
|
||||
<PrimaryCTAButton
|
||||
text="Explore Our Space"
|
||||
onClick={() => navigateTo('/services/learning-facility')}
|
||||
ariaLabel="Explore our virtual learning space and facilities"
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Additional overlay for better text readability over images */}
|
||||
<div className="absolute top-0 left-0 right-0 h-80 bg-gradient-to-b from-black/70 via-black/40 to-transparent z-10" />
|
||||
</div>
|
||||
|
||||
{/* Booking Modal */}
|
||||
<BookingModal
|
||||
facility={selectedFacility}
|
||||
isOpen={isModalOpen}
|
||||
onClose={handleCloseModal}
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
1349
src/components/VirtualTour.tsx
Normal file
69
src/components/WebcastCTABanner.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import { ArrowRight } from "lucide-react";
|
||||
import { ImageWithFallback } from "./figma/ImageWithFallback";
|
||||
import { BrandedTag } from "./about/BrandedTag";
|
||||
import { PrimaryCTAButton } from "./PrimaryCTAButton";
|
||||
import { navigateTo } from "./Router";
|
||||
|
||||
export function WebcastCTABanner() {
|
||||
return (
|
||||
<section className="relative h-[700px] overflow-hidden">
|
||||
{/* Background Image */}
|
||||
<div className="absolute inset-0">
|
||||
<ImageWithFallback
|
||||
src="https://images.unsplash.com/photo-1712903276180-eda90d32c182?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHx2aXJ0dWFsJTIwd2ViaW5hciUyMG9ubGluZSUyMG1lZXRpbmclMjBkaWdpdGFsJTIwcHJlc2VudGF0aW9ufGVufDF8fHx8MTc1NjEzMDY1MHww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral"
|
||||
alt="Virtual webinar and online meeting digital presentation"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
|
||||
{/* Subtle dark overlay for overall image */}
|
||||
<div className="absolute inset-0 bg-black/30" />
|
||||
|
||||
{/* Gradient overlay for better text readability */}
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-black/20 via-transparent to-black/60" />
|
||||
</div>
|
||||
|
||||
{/* Content Container */}
|
||||
<div className="relative h-full flex items-center justify-end section-margin-x">
|
||||
{/* CTA Content Block */}
|
||||
<div
|
||||
className="bg-opacity-95 backdrop-blur-sm rounded-lg p-16 max-w-2xl"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-brand-primary)'
|
||||
}}
|
||||
>
|
||||
{/* Branded Tag */}
|
||||
<BrandedTag text="Join Our Webcasts" variant="white" />
|
||||
|
||||
{/* Main Headline */}
|
||||
<h2
|
||||
className="text-h2-white mb-8"
|
||||
>
|
||||
Want to host your own webcast?
|
||||
<span
|
||||
className="italic"
|
||||
style={{ color: 'var(--color-brand-accent)' }}
|
||||
>
|
||||
{" "}Partner with us{" "}
|
||||
</span>
|
||||
to share your expertise with our community.
|
||||
</h2>
|
||||
|
||||
{/* CTA Button - Updated to redirect to contact page */}
|
||||
<PrimaryCTAButton
|
||||
text="Propose a Webcast"
|
||||
onClick={() => navigateTo('/contact?topic=webcast')}
|
||||
ariaLabel="Propose a webcast partnership with KLC"
|
||||
className="cta-banner-yellow"
|
||||
/>
|
||||
|
||||
{/* Supporting Text */}
|
||||
<p
|
||||
className="text-body-white mt-6 opacity-90"
|
||||
>
|
||||
Share your leadership insights with our engaged community through expert-led webcasts and interactive sessions.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
610
src/components/WebcastDetail.tsx
Normal file
@@ -0,0 +1,610 @@
|
||||
import React from 'react';
|
||||
import { Button } from './ui/button';
|
||||
import { Badge } from './ui/badge';
|
||||
import { Calendar, Clock, Users, Play, Download, ArrowLeft, Share2, BookOpen } from 'lucide-react';
|
||||
import { navigateTo } from './Router';
|
||||
import { ImageWithFallback } from './figma/ImageWithFallback';
|
||||
|
||||
interface WebcastDetailProps {
|
||||
params: {
|
||||
slug: string;
|
||||
};
|
||||
}
|
||||
|
||||
// Mock webcast data - in a real app, this would come from an API
|
||||
const webcastData = {
|
||||
'leadership-in-digital-age': {
|
||||
id: '1',
|
||||
title: 'Leadership in the Digital Age: Navigating Change and Innovation',
|
||||
description: 'Discover how modern leaders can adapt and thrive in an increasingly digital world. This comprehensive webcast covers digital transformation strategies, leading remote teams, and fostering innovation.',
|
||||
presenter: 'Dr. Sarah Mitchell',
|
||||
presenterTitle: 'Chief Digital Transformation Officer',
|
||||
presenterBio: 'Dr. Sarah Mitchell is a renowned expert in digital transformation with over 15 years of experience helping organizations navigate digital change.',
|
||||
date: '2024-02-15',
|
||||
time: '2:00 PM EST',
|
||||
duration: '90 minutes',
|
||||
attendees: '2,400+',
|
||||
category: 'Digital Transformation',
|
||||
tags: ['Leadership', 'Digital Strategy', 'Innovation', 'Change Management'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1560472355-536de3962603?w=800&h=400&fit=crop',
|
||||
status: 'Available',
|
||||
recordingUrl: '#',
|
||||
resourcesUrl: '#',
|
||||
slidesUrl: '#',
|
||||
keyTakeaways: [
|
||||
'Understanding the digital leadership mindset',
|
||||
'Strategies for leading remote and hybrid teams',
|
||||
'Building a culture of innovation',
|
||||
'Managing digital transformation initiatives',
|
||||
'Leveraging technology for competitive advantage'
|
||||
],
|
||||
agenda: [
|
||||
{ time: '2:00 PM', topic: 'Introduction to Digital Leadership' },
|
||||
{ time: '2:15 PM', topic: 'The Digital Transformation Landscape' },
|
||||
{ time: '2:45 PM', topic: 'Leading Remote Teams Effectively' },
|
||||
{ time: '3:15 PM', topic: 'Q&A Session' },
|
||||
{ time: '3:30 PM', topic: 'Closing Remarks and Next Steps' }
|
||||
]
|
||||
},
|
||||
'building-resilient-teams': {
|
||||
id: '2',
|
||||
title: 'Building Resilient Teams: Strategies for Sustainable Performance',
|
||||
description: 'Learn proven methodologies for creating teams that can withstand challenges and maintain high performance under pressure.',
|
||||
presenter: 'Marcus Rodriguez',
|
||||
presenterTitle: 'Director of Organizational Development',
|
||||
presenterBio: 'Marcus Rodriguez specializes in team dynamics and organizational resilience with experience across Fortune 500 companies.',
|
||||
date: '2024-02-08',
|
||||
time: '1:00 PM EST',
|
||||
duration: '75 minutes',
|
||||
attendees: '1,850+',
|
||||
category: 'Team Development',
|
||||
tags: ['Team Building', 'Resilience', 'Performance', 'Leadership'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=800&h=400&fit=crop',
|
||||
status: 'Available',
|
||||
recordingUrl: '#',
|
||||
resourcesUrl: '#',
|
||||
slidesUrl: '#',
|
||||
keyTakeaways: [
|
||||
'Principles of team resilience',
|
||||
'Building psychological safety',
|
||||
'Managing team stress and pressure',
|
||||
'Creating sustainable performance systems',
|
||||
'Tools for team assessment and development'
|
||||
],
|
||||
agenda: [
|
||||
{ time: '1:00 PM', topic: 'Understanding Team Resilience' },
|
||||
{ time: '1:20 PM', topic: 'Building Psychological Safety' },
|
||||
{ time: '1:45 PM', topic: 'Sustainable Performance Strategies' },
|
||||
{ time: '2:10 PM', topic: 'Interactive Q&A' },
|
||||
{ time: '2:15 PM', topic: 'Resources and Action Planning' }
|
||||
]
|
||||
},
|
||||
'strategic-decision-making': {
|
||||
id: '3',
|
||||
title: 'Strategic Decision Making: Tools and Frameworks for Leaders',
|
||||
description: 'Master the art of strategic thinking and decision-making with proven frameworks and methodologies used by top executives.',
|
||||
presenter: 'Dr. Emily Chen',
|
||||
presenterTitle: 'Strategic Leadership Consultant',
|
||||
presenterBio: 'Dr. Emily Chen is a strategic leadership consultant who has advised C-suite executives across multiple industries.',
|
||||
date: '2024-01-25',
|
||||
time: '3:00 PM EST',
|
||||
duration: '60 minutes',
|
||||
attendees: '3,200+',
|
||||
category: 'Strategy',
|
||||
tags: ['Strategic Thinking', 'Decision Making', 'Leadership', 'Planning'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=800&h=400&fit=crop',
|
||||
status: 'Available',
|
||||
recordingUrl: '#',
|
||||
resourcesUrl: '#',
|
||||
slidesUrl: '#',
|
||||
keyTakeaways: [
|
||||
'Strategic thinking frameworks',
|
||||
'Decision-making methodologies',
|
||||
'Risk assessment and management',
|
||||
'Stakeholder analysis techniques',
|
||||
'Implementation and monitoring strategies'
|
||||
],
|
||||
agenda: [
|
||||
{ time: '3:00 PM', topic: 'Introduction to Strategic Decision Making' },
|
||||
{ time: '3:15 PM', topic: 'Framework Deep Dive' },
|
||||
{ time: '3:35 PM', topic: 'Case Study Analysis' },
|
||||
{ time: '3:50 PM', topic: 'Q&A and Discussion' },
|
||||
{ time: '4:00 PM', topic: 'Action Planning' }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
export function WebcastDetail({ params }: WebcastDetailProps) {
|
||||
const webcast = webcastData[params.slug as keyof typeof webcastData];
|
||||
|
||||
if (!webcast) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center" style={{ backgroundColor: '#F7F7FD' }}>
|
||||
<div className="text-center">
|
||||
<h1 className="text-h2 mb-4">Webcast Not Found</h1>
|
||||
<p className="text-body-lg text-muted mb-8">
|
||||
The webcast you're looking for doesn't exist.
|
||||
</p>
|
||||
<Button
|
||||
onClick={() => navigateTo('/learning/webcast')}
|
||||
className="brand-button-system"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
||||
Back to Webcasts
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const formattedDate = new Date(webcast.date).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
});
|
||||
|
||||
return (
|
||||
<div style={{ backgroundColor: '#F7F7FD' }}>
|
||||
{/* Header Section */}
|
||||
<div className="hero-margin-x py-12">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
{/* Back Button */}
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => navigateTo('/learning/webcast')}
|
||||
className="mb-6 hover:bg-white/50"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-black)'
|
||||
}}
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
||||
Back to Webcasts
|
||||
</Button>
|
||||
|
||||
{/* Webcast Header */}
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden">
|
||||
{/* Webcast Image */}
|
||||
<div className="aspect-video w-full bg-gray-100 relative">
|
||||
<ImageWithFallback
|
||||
src={webcast.thumbnail}
|
||||
alt={webcast.title}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-black bg-opacity-40 flex items-center justify-center">
|
||||
<Button
|
||||
variant="default"
|
||||
size="lg"
|
||||
className="bg-white text-black hover:bg-gray-100"
|
||||
onClick={() => window.open(webcast.recordingUrl, '_blank')}
|
||||
>
|
||||
<Play className="w-6 h-6 mr-2" />
|
||||
Watch Recording
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Webcast Content */}
|
||||
<div className="p-8">
|
||||
{/* Category Badge */}
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="mb-4"
|
||||
style={{
|
||||
backgroundColor: 'rgba(248, 195, 1, 0.1)',
|
||||
color: 'var(--color-black)',
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{webcast.category}
|
||||
</Badge>
|
||||
|
||||
{/* Title */}
|
||||
<h1
|
||||
className="mb-4"
|
||||
style={{
|
||||
fontSize: 'var(--font-h2)',
|
||||
fontWeight: 'var(--font-weight-h2)',
|
||||
lineHeight: 'var(--line-height-h2)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{webcast.title}
|
||||
</h1>
|
||||
|
||||
{/* Meta Information */}
|
||||
<div className="flex flex-wrap gap-6 mb-6 text-muted">
|
||||
<div className="flex items-center gap-2">
|
||||
<Calendar className="w-4 h-4" />
|
||||
<span style={{ fontSize: 'var(--font-body)', fontFamily: 'var(--font-family-base)' }}>
|
||||
{formattedDate}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock className="w-4 h-4" />
|
||||
<span style={{ fontSize: 'var(--font-body)', fontFamily: 'var(--font-family-base)' }}>
|
||||
{webcast.duration}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Users className="w-4 h-4" />
|
||||
<span style={{ fontSize: 'var(--font-body)', fontFamily: 'var(--font-family-base)' }}>
|
||||
{webcast.attendees} attendees
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<p
|
||||
className="mb-8"
|
||||
style={{
|
||||
fontSize: 'var(--font-body-lg)',
|
||||
lineHeight: 'var(--line-height-body-lg)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{webcast.description}
|
||||
</p>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<Button
|
||||
onClick={() => window.open(webcast.recordingUrl, '_blank')}
|
||||
className="brand-button-system"
|
||||
>
|
||||
<Play className="w-4 h-4 mr-2" />
|
||||
Watch Recording
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => window.open(webcast.slidesUrl, '_blank')}
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
borderColor: 'var(--color-primary)'
|
||||
}}
|
||||
>
|
||||
<Download className="w-4 h-4 mr-2" />
|
||||
Download Slides
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => window.open(webcast.resourcesUrl, '_blank')}
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
borderColor: 'var(--color-primary)'
|
||||
}}
|
||||
>
|
||||
<BookOpen className="w-4 h-4 mr-2" />
|
||||
Resources
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
borderColor: 'var(--color-primary)'
|
||||
}}
|
||||
>
|
||||
<Share2 className="w-4 h-4 mr-2" />
|
||||
Share
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content Sections */}
|
||||
<div className="section-margin-x pb-12">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
{/* Main Content */}
|
||||
<div className="lg:col-span-2 space-y-8">
|
||||
{/* Presenter Information */}
|
||||
<div className="bg-white rounded-lg p-6 shadow-sm border border-gray-200">
|
||||
<h2
|
||||
className="mb-4"
|
||||
style={{
|
||||
fontSize: 'var(--font-h3)',
|
||||
fontWeight: 'var(--font-weight-h3)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
About the Presenter
|
||||
</h2>
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-16 h-16 bg-gray-200 rounded-full flex items-center justify-center">
|
||||
<span
|
||||
className="text-xl font-semibold"
|
||||
style={{
|
||||
color: 'var(--color-primary)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{webcast.presenter.split(' ').map(n => n[0]).join('')}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<h3
|
||||
className="mb-1"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{webcast.presenter}
|
||||
</h3>
|
||||
<p
|
||||
className="text-muted mb-2"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{webcast.presenterTitle}
|
||||
</p>
|
||||
<p
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
lineHeight: 'var(--line-height-body)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{webcast.presenterBio}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Key Takeaways */}
|
||||
<div className="bg-white rounded-lg p-6 shadow-sm border border-gray-200">
|
||||
<h2
|
||||
className="mb-4"
|
||||
style={{
|
||||
fontSize: 'var(--font-h3)',
|
||||
fontWeight: 'var(--font-weight-h3)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Key Takeaways
|
||||
</h2>
|
||||
<ul className="space-y-2">
|
||||
{webcast.keyTakeaways.map((takeaway, index) => (
|
||||
<li key={index} className="flex items-start gap-3">
|
||||
<div
|
||||
className="w-2 h-2 rounded-full mt-2 flex-shrink-0"
|
||||
style={{ backgroundColor: 'var(--color-accent)' }}
|
||||
/>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
lineHeight: 'var(--line-height-body)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{takeaway}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Agenda */}
|
||||
<div className="bg-white rounded-lg p-6 shadow-sm border border-gray-200">
|
||||
<h2
|
||||
className="mb-4"
|
||||
style={{
|
||||
fontSize: 'var(--font-h3)',
|
||||
fontWeight: 'var(--font-weight-h3)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Agenda
|
||||
</h2>
|
||||
<div className="space-y-3">
|
||||
{webcast.agenda.map((item, index) => (
|
||||
<div key={index} className="flex items-start gap-4 py-2">
|
||||
<span
|
||||
className="text-sm font-medium text-muted min-w-[80px]"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{item.time}
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
lineHeight: 'var(--line-height-body)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{item.topic}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sidebar */}
|
||||
<div className="space-y-6">
|
||||
{/* Tags */}
|
||||
<div className="bg-white rounded-lg p-6 shadow-sm border border-gray-200">
|
||||
<h3
|
||||
className="mb-4"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Topics Covered
|
||||
</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{webcast.tags.map((tag, index) => (
|
||||
<Badge
|
||||
key={index}
|
||||
variant="secondary"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-bg-light)',
|
||||
color: 'var(--color-black)',
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{tag}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quick Actions */}
|
||||
<div className="bg-white rounded-lg p-6 shadow-sm border border-gray-200">
|
||||
<h3
|
||||
className="mb-4"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Quick Actions
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full justify-start"
|
||||
onClick={() => window.open(webcast.recordingUrl, '_blank')}
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
borderColor: 'var(--color-primary)'
|
||||
}}
|
||||
>
|
||||
<Play className="w-4 h-4 mr-2" />
|
||||
Watch Recording
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full justify-start"
|
||||
onClick={() => window.open(webcast.slidesUrl, '_blank')}
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
borderColor: 'var(--color-primary)'
|
||||
}}
|
||||
>
|
||||
<Download className="w-4 h-4 mr-2" />
|
||||
Download Slides
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full justify-start"
|
||||
onClick={() => window.open(webcast.resourcesUrl, '_blank')}
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
borderColor: 'var(--color-primary)'
|
||||
}}
|
||||
>
|
||||
<BookOpen className="w-4 h-4 mr-2" />
|
||||
Resources
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Related Webcasts */}
|
||||
<div className="bg-white rounded-lg p-6 shadow-sm border border-gray-200">
|
||||
<h3
|
||||
className="mb-4"
|
||||
style={{
|
||||
fontSize: 'var(--font-h4)',
|
||||
fontWeight: 'var(--font-weight-h4)',
|
||||
color: 'var(--color-black)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Related Webcasts
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full justify-start h-auto p-0"
|
||||
onClick={() => navigateTo('/learning/webcast/building-resilient-teams')}
|
||||
>
|
||||
<div className="text-left">
|
||||
<div
|
||||
className="font-medium mb-1"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-black)'
|
||||
}}
|
||||
>
|
||||
Building Resilient Teams
|
||||
</div>
|
||||
<div
|
||||
className="text-sm text-muted"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Team Development
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full justify-start h-auto p-0"
|
||||
onClick={() => navigateTo('/learning/webcast/strategic-decision-making')}
|
||||
>
|
||||
<div className="text-left">
|
||||
<div
|
||||
className="font-medium mb-1"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-black)'
|
||||
}}
|
||||
>
|
||||
Strategic Decision Making
|
||||
</div>
|
||||
<div
|
||||
className="text-sm text-muted"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
Strategy
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
1075
src/components/WebinarDetail.tsx
Normal file
764
src/components/Webinars.tsx
Normal file
@@ -0,0 +1,764 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { Button } from './ui/button';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './ui/card';
|
||||
import { Badge } from './ui/badge';
|
||||
import { Input } from './ui/input';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
|
||||
import { Slider } from './ui/slider';
|
||||
import { ImageWithFallback } from './figma/ImageWithFallback';
|
||||
import { PrimaryCTAButton } from './PrimaryCTAButton';
|
||||
import { navigateTo } from './Router';
|
||||
import { sharedWebinarsData, type WebinarData } from '../data/webinarsData';
|
||||
import { WebcastCTABanner } from './WebcastCTABanner';
|
||||
import {
|
||||
Search,
|
||||
Calendar,
|
||||
Clock,
|
||||
Users,
|
||||
Play,
|
||||
ArrowRight,
|
||||
Filter,
|
||||
Grid,
|
||||
List,
|
||||
SortAsc,
|
||||
Eye,
|
||||
Star,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
X
|
||||
} from 'lucide-react';
|
||||
|
||||
export function Webinars() {
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [selectedCategory, setSelectedCategory] = useState('All Categories');
|
||||
const [selectedFormat, setSelectedFormat] = useState('All Formats');
|
||||
const [selectedLevel, setSelectedLevel] = useState('All Levels');
|
||||
|
||||
// Updated state for multi-select status pills
|
||||
const [selectedStatuses, setSelectedStatuses] = useState<string[]>([]);
|
||||
|
||||
// Updated state for duration slider (min, max in minutes)
|
||||
const [durationRange, setDurationRange] = useState([0, 120]);
|
||||
|
||||
// Attendee range slider state
|
||||
const [attendeeRange, setAttendeeRange] = useState([0, 5000]);
|
||||
|
||||
const [sortBy, setSortBy] = useState('Most Popular');
|
||||
const [viewType, setViewType] = useState<'grid' | 'list'>('grid');
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const webinarsPerPage = 6;
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Use shared webinars data instead of local mock data
|
||||
const webinars = sharedWebinarsData;
|
||||
|
||||
// Get unique values for filters from shared data
|
||||
const categories = ['All Categories', ...Array.from(new Set(webinars.map(webinar => webinar.category)))];
|
||||
const formats = ['All Formats', ...Array.from(new Set(webinars.map(webinar => webinar.format)))];
|
||||
const levels = ['All Levels', ...Array.from(new Set(webinars.map(webinar => webinar.level)))];
|
||||
|
||||
// Status options for pills - updated to match shared data structure
|
||||
const statusOptions = [
|
||||
{ value: 'upcoming', label: '📅 Upcoming', color: 'bg-blue-100 text-blue-800 border-blue-200' },
|
||||
{ value: 'live', label: '🔴 Live', color: 'bg-red-100 text-red-800 border-red-200' },
|
||||
{ value: 'recorded', label: '▶️ Recorded', color: 'bg-green-100 text-green-800 border-green-200' },
|
||||
{ value: 'featured', label: '⭐ Featured', color: 'bg-yellow-100 text-yellow-800 border-yellow-200' }
|
||||
];
|
||||
|
||||
const sortOptions = [
|
||||
{ value: 'Most Popular', label: 'Most Popular' },
|
||||
{ value: 'newest', label: 'Newest First' },
|
||||
{ value: 'oldest', label: 'Oldest First' },
|
||||
{ value: 'title', label: 'Title A-Z' },
|
||||
{ value: 'duration', label: 'Duration' }
|
||||
];
|
||||
|
||||
// Helper function to convert attendees string to number
|
||||
const parseAttendees = (attendeesStr: string): number => {
|
||||
const numStr = attendeesStr.replace(/[^\d]/g, '');
|
||||
return parseInt(numStr) || 0;
|
||||
};
|
||||
|
||||
// Helper function to convert duration string to minutes
|
||||
const parseDuration = (durationStr: string): number => {
|
||||
const numStr = durationStr.replace(/[^\d]/g, '');
|
||||
return parseInt(numStr) || 0;
|
||||
};
|
||||
|
||||
// Filter and sort webinars
|
||||
const filteredWebinars = webinars.filter(webinar => {
|
||||
const matchesSearch = webinar.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
webinar.description.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
webinar.presenter.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
webinar.tags.some(tag => tag.toLowerCase().includes(searchTerm.toLowerCase()));
|
||||
const matchesCategory = selectedCategory === 'All Categories' || webinar.category === selectedCategory;
|
||||
const matchesFormat = selectedFormat === 'All Formats' || webinar.format === selectedFormat;
|
||||
const matchesLevel = selectedLevel === 'All Levels' || webinar.level === selectedLevel;
|
||||
|
||||
// Updated status filter for multi-select with shared data structure
|
||||
const matchesStatus = selectedStatuses.length === 0 ||
|
||||
selectedStatuses.some(status => {
|
||||
if (status === 'featured') return webinar.featured;
|
||||
return webinar.status === status;
|
||||
});
|
||||
|
||||
// Duration filter using range
|
||||
const durationMinutes = parseDuration(webinar.duration);
|
||||
const matchesDuration = durationMinutes >= durationRange[0] && durationMinutes <= durationRange[1];
|
||||
|
||||
// Attendee filter using range
|
||||
const attendeeCount = parseAttendees(webinar.attendees);
|
||||
const matchesAttendees = attendeeCount >= attendeeRange[0] && attendeeCount <= attendeeRange[1];
|
||||
|
||||
return matchesSearch && matchesCategory && matchesFormat && matchesLevel && matchesStatus && matchesDuration && matchesAttendees;
|
||||
}).sort((a, b) => {
|
||||
switch (sortBy) {
|
||||
case 'newest':
|
||||
return new Date(b.date).getTime() - new Date(a.date).getTime();
|
||||
case 'oldest':
|
||||
return new Date(a.date).getTime() - new Date(b.date).getTime();
|
||||
case 'title':
|
||||
return a.title.localeCompare(b.title);
|
||||
case 'duration':
|
||||
return parseDuration(b.duration) - parseDuration(a.duration);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
// Statistics
|
||||
const stats = {
|
||||
total: webinars.length,
|
||||
upcoming: webinars.filter(w => w.status === 'upcoming').length,
|
||||
live: webinars.filter(w => w.status === 'live').length,
|
||||
recorded: webinars.filter(w => w.status === 'recorded').length,
|
||||
featured: webinars.filter(w => w.featured).length,
|
||||
categories: new Set(webinars.map(w => w.category)).size
|
||||
};
|
||||
|
||||
// Paginate results
|
||||
const totalPages = Math.ceil(filteredWebinars.length / webinarsPerPage);
|
||||
const currentWebinars = filteredWebinars.slice((currentPage - 1) * webinarsPerPage, currentPage * webinarsPerPage);
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
return new Date(dateString).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
});
|
||||
};
|
||||
|
||||
const clearAllFilters = () => {
|
||||
setSearchTerm('');
|
||||
setSelectedCategory('All Categories');
|
||||
setSelectedFormat('All Formats');
|
||||
setSelectedLevel('All Levels');
|
||||
setSelectedStatuses([]);
|
||||
setDurationRange([0, 120]);
|
||||
setAttendeeRange([0, 5000]);
|
||||
setSortBy('Most Popular');
|
||||
};
|
||||
|
||||
const hasActiveFilters = searchTerm ||
|
||||
selectedCategory !== 'All Categories' ||
|
||||
selectedFormat !== 'All Formats' ||
|
||||
selectedLevel !== 'All Levels' ||
|
||||
selectedStatuses.length > 0 ||
|
||||
durationRange[0] !== 0 || durationRange[1] !== 120 ||
|
||||
attendeeRange[0] !== 0 || attendeeRange[1] !== 5000;
|
||||
|
||||
// Status pill toggle function
|
||||
const toggleStatus = (status: string) => {
|
||||
setSelectedStatuses(prev =>
|
||||
prev.includes(status)
|
||||
? prev.filter(s => s !== status)
|
||||
: [...prev, status]
|
||||
);
|
||||
};
|
||||
|
||||
// Updated WebinarCard component that navigates to consistent route
|
||||
const WebinarCard = ({ webinar }: { webinar: WebinarData }) => {
|
||||
const handleCardClick = () => {
|
||||
// Navigate to consistent webinar detail route
|
||||
navigateTo(`/webinar/${webinar.slug}`);
|
||||
};
|
||||
|
||||
const getStatusBadge = () => {
|
||||
switch (webinar.status) {
|
||||
case 'live':
|
||||
return <Badge className="bg-red-600 text-white animate-pulse">LIVE</Badge>;
|
||||
case 'upcoming':
|
||||
return <Badge className="bg-blue-600 text-white">UPCOMING</Badge>;
|
||||
case 'recorded':
|
||||
return <Badge className="bg-green-600 text-white">RECORDED</Badge>;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const getActionText = () => {
|
||||
switch (webinar.status) {
|
||||
case 'live':
|
||||
return 'Join Now';
|
||||
case 'upcoming':
|
||||
return 'Register';
|
||||
case 'recorded':
|
||||
return 'Watch Recording';
|
||||
default:
|
||||
return 'Learn More';
|
||||
}
|
||||
};
|
||||
|
||||
if (viewType === 'list') {
|
||||
return (
|
||||
<Card
|
||||
className="mb-4 cursor-pointer transition-all duration-300 hover:shadow-lg hover:transform hover:-translate-y-1"
|
||||
onClick={handleCardClick}
|
||||
>
|
||||
<CardContent className="p-6">
|
||||
<div className="flex gap-6">
|
||||
{/* Thumbnail */}
|
||||
<div className="flex-shrink-0 w-32 h-24 rounded-lg overflow-hidden">
|
||||
<ImageWithFallback
|
||||
src={webinar.thumbnail}
|
||||
alt={webinar.title}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="flex-1">
|
||||
<div className="flex justify-between items-start mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
{getStatusBadge()}
|
||||
{webinar.featured && (
|
||||
<Badge className="bg-yellow-100 text-yellow-800">Featured</Badge>
|
||||
)}
|
||||
</div>
|
||||
<span className="text-small text-gray-500">{formatDate(webinar.date)}</span>
|
||||
</div>
|
||||
|
||||
<h3 className="text-h4 mb-2 line-clamp-2">{webinar.title}</h3>
|
||||
<p className="text-body text-gray-600 mb-3 line-clamp-2">{webinar.description}</p>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4 text-small text-gray-500">
|
||||
<span className="flex items-center gap-1">
|
||||
<Users className="w-4 h-4" />
|
||||
{webinar.presenter}
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Clock className="w-4 h-4" />
|
||||
{webinar.duration}
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Eye className="w-4 h-4" />
|
||||
{webinar.attendees}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-primary font-medium">
|
||||
<span className="text-small">{getActionText()}</span>
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
className="cursor-pointer transition-all duration-300 hover:shadow-lg hover:transform hover:-translate-y-2 group overflow-hidden"
|
||||
onClick={handleCardClick}
|
||||
>
|
||||
{/* Image */}
|
||||
<div className="aspect-video relative overflow-hidden">
|
||||
<ImageWithFallback
|
||||
src={webinar.thumbnail}
|
||||
alt={webinar.title}
|
||||
className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
|
||||
/>
|
||||
|
||||
{/* Status Badge */}
|
||||
<div className="absolute top-4 left-4">
|
||||
{getStatusBadge()}
|
||||
</div>
|
||||
|
||||
{/* Featured Badge */}
|
||||
{webinar.featured && (
|
||||
<div className="absolute top-4 right-4">
|
||||
<Badge className="bg-yellow-100 text-yellow-800 border border-yellow-200">
|
||||
<Star className="w-3 h-3 mr-1" />
|
||||
Featured
|
||||
</Badge>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Play Icon Overlay */}
|
||||
<div className="absolute inset-0 bg-black bg-opacity-40 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex items-center justify-center">
|
||||
<div className="bg-white bg-opacity-90 rounded-full p-3">
|
||||
<Play className="w-6 h-6 text-gray-800" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{webinar.category}
|
||||
</Badge>
|
||||
<span className="text-small text-gray-500">{formatDate(webinar.date)}</span>
|
||||
</div>
|
||||
|
||||
<h3 className="text-h4 mb-3 line-clamp-2 group-hover:text-primary transition-colors">
|
||||
{webinar.title}
|
||||
</h3>
|
||||
|
||||
<p className="text-body text-gray-600 mb-4 line-clamp-2">
|
||||
{webinar.description}
|
||||
</p>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2 text-small text-gray-500">
|
||||
<Users className="w-4 h-4" />
|
||||
<span>{webinar.presenter}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between text-small text-gray-500">
|
||||
<div className="flex items-center gap-1">
|
||||
<Clock className="w-4 h-4" />
|
||||
<span>{webinar.duration}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Eye className="w-4 h-4" />
|
||||
<span>{webinar.attendees}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between mt-4 pt-4 border-t">
|
||||
<div className="flex items-center gap-1">
|
||||
{webinar.tags.slice(0, 2).map((tag, index) => (
|
||||
<Badge key={index} variant="outline" className="text-xs">
|
||||
{tag}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-primary font-medium group-hover:translate-x-1 transition-transform">
|
||||
<span className="text-small">{getActionText()}</span>
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ backgroundColor: '#FFFFFF' }}>
|
||||
{/* Hero Section with Background Image */}
|
||||
<section className="relative h-[400px] overflow-hidden">
|
||||
{/* Background Image */}
|
||||
<div className="absolute inset-0">
|
||||
<ImageWithFallback
|
||||
src="https://images.unsplash.com/photo-1652265540589-46f91535337b?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxidXNpbmVzcyUyMHByZXNlbnRhdGlvbiUyMHdlYmluYXIlMjBjb25mZXJlbmNlfGVufDF8fHx8MTc1NTg1NDI3MHww&ixlib=rb-4.1.0&q=80&w=1080"
|
||||
alt="Professional webinar and conference presentation"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-black/60" />
|
||||
</div>
|
||||
|
||||
{/* Hero Content */}
|
||||
<div className="relative h-full flex flex-col justify-center section-margin-x">
|
||||
<div className="text-center">
|
||||
<h1 className="text-h1-white mb-6">
|
||||
Leadership Webcasts &<br />
|
||||
Expert Insights
|
||||
</h1>
|
||||
|
||||
<p className="text-body-lg-white max-w-3xl mx-auto">
|
||||
Explore our comprehensive collection of expert insights, research, and practical guidance
|
||||
to elevate your leadership journey and drive organizational excellence.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Statistics Strip at Bottom */}
|
||||
<div className="absolute bottom-0 left-0 right-0">
|
||||
<div className="bg-black/80 backdrop-blur-sm px-8 py-6">
|
||||
<div className="section-margin-x">
|
||||
<div className="grid grid-cols-3 gap-8 text-center">
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">{stats.total}+</div>
|
||||
<div className="text-small-white">Expert Webcasts</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">{stats.categories}</div>
|
||||
<div className="text-small-white">Categories</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">15,200</div>
|
||||
<div className="text-small-white">Total Views</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Search and Controls Section */}
|
||||
<section className="py-8" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="section-margin-x">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 mb-6">
|
||||
{/* Search Bar */}
|
||||
<div className="relative max-w-md flex-1">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Search webcasts..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="pl-10 pr-4 py-3 text-body rounded-lg border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 transition-all duration-200 w-full bg-gray-50"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
height: '48px'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* View Toggle and Sort */}
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center border border-gray-300 rounded-lg overflow-hidden">
|
||||
<button
|
||||
onClick={() => setViewType('grid')}
|
||||
className={`p-2 transition-colors ${
|
||||
viewType === 'grid'
|
||||
? 'text-white'
|
||||
: 'bg-white text-gray-600 hover:bg-gray-50'
|
||||
}`}
|
||||
style={{
|
||||
backgroundColor: viewType === 'grid' ? 'var(--color-primary)' : undefined
|
||||
}}
|
||||
aria-label="Grid view"
|
||||
>
|
||||
<Grid className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setViewType('list')}
|
||||
className={`p-2 transition-colors ${
|
||||
viewType === 'list'
|
||||
? 'text-white'
|
||||
: 'bg-white text-gray-600 hover:bg-gray-50'
|
||||
}`}
|
||||
style={{
|
||||
backgroundColor: viewType === 'list' ? 'var(--color-primary)' : undefined
|
||||
}}
|
||||
aria-label="List view"
|
||||
>
|
||||
<List className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Select value={sortBy} onValueChange={setSortBy}>
|
||||
<SelectTrigger className="w-40 text-body">
|
||||
<SelectValue placeholder="Sort by" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{sortOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Main Content Section with Sidebar */}
|
||||
<section className="pb-16" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="section-margin-x">
|
||||
<div className="grid grid-cols-12 gap-8">
|
||||
{/* Left Sidebar - Sticky Filters */}
|
||||
<div className="col-span-12 lg:col-span-3">
|
||||
<div className="sticky top-4">
|
||||
<Card className="bg-white border border-gray-200 rounded-lg shadow-md overflow-hidden">
|
||||
{/* Filter Header */}
|
||||
<div className="bg-gray-50 px-4 py-3 border-b border-gray-200">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="p-1.5 rounded-md" style={{ backgroundColor: 'rgba(4, 4, 91, 0.1)' }}>
|
||||
<Filter className="w-3.5 h-3.5" style={{ color: 'var(--color-primary)' }} />
|
||||
</div>
|
||||
<h3 className="text-body font-semibold text-gray-800">
|
||||
Filters
|
||||
</h3>
|
||||
</div>
|
||||
{hasActiveFilters && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={clearAllFilters}
|
||||
className="text-xs px-2 py-1 rounded-md transition-colors filter-clear-btn"
|
||||
>
|
||||
<X className="w-3 h-3 mr-1" />
|
||||
Clear
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Filter Content */}
|
||||
<div className="p-4">
|
||||
<div className="space-y-6">
|
||||
{/* Category Filter */}
|
||||
<div className="filter-section">
|
||||
<label className="block text-small mb-2 font-medium text-gray-700">
|
||||
Category
|
||||
</label>
|
||||
<Select value={selectedCategory} onValueChange={setSelectedCategory}>
|
||||
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors">
|
||||
<SelectValue placeholder="All Categories" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{categories.map((category) => (
|
||||
<SelectItem key={category} value={category} className="text-small">
|
||||
{category}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Format Filter */}
|
||||
<div className="filter-section">
|
||||
<label className="block text-small mb-2 font-medium text-gray-700">
|
||||
Format
|
||||
</label>
|
||||
<Select value={selectedFormat} onValueChange={setSelectedFormat}>
|
||||
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors">
|
||||
<SelectValue placeholder="All Formats" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{formats.map((format) => (
|
||||
<SelectItem key={format} value={format} className="text-small">
|
||||
{format}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Level Filter */}
|
||||
<div className="filter-section">
|
||||
<label className="block text-small mb-2 font-medium text-gray-700">
|
||||
Level
|
||||
</label>
|
||||
<Select value={selectedLevel} onValueChange={setSelectedLevel}>
|
||||
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors">
|
||||
<SelectValue placeholder="All Levels" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{levels.map((level) => (
|
||||
<SelectItem key={level} value={level} className="text-small">
|
||||
{level}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Status Filter - Multi-select Pills */}
|
||||
<div className="filter-section">
|
||||
<label className="block text-small mb-3 font-medium text-gray-700">
|
||||
Status
|
||||
</label>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{statusOptions.map((status) => (
|
||||
<button
|
||||
key={status.value}
|
||||
onClick={() => toggleStatus(status.value)}
|
||||
className={`
|
||||
px-3 py-1.5 rounded-full text-xs font-medium border transition-all duration-200
|
||||
${selectedStatuses.includes(status.value)
|
||||
? `${status.color} ring-2 ring-blue-200 shadow-sm`
|
||||
: 'bg-gray-50 text-gray-600 border-gray-200 hover:bg-gray-100 hover:border-gray-300'
|
||||
}
|
||||
`}
|
||||
>
|
||||
{status.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
{selectedStatuses.length > 0 && (
|
||||
<div className="mt-2 text-xs text-gray-500">
|
||||
{selectedStatuses.length} selected
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Duration Filter - Slider */}
|
||||
<div className="filter-section">
|
||||
<label className="block text-small mb-3 font-medium text-gray-700">
|
||||
Duration
|
||||
</label>
|
||||
<div className="px-2">
|
||||
<Slider
|
||||
value={durationRange}
|
||||
onValueChange={setDurationRange}
|
||||
max={120}
|
||||
min={0}
|
||||
step={5}
|
||||
className="w-full"
|
||||
/>
|
||||
<div className="flex justify-between mt-2 text-xs text-gray-500">
|
||||
<span>{durationRange[0]} min</span>
|
||||
<span>{durationRange[1]} min</span>
|
||||
</div>
|
||||
<div className="mt-1 text-center text-xs text-gray-400">
|
||||
{durationRange[0] === 0 && durationRange[1] === 120
|
||||
? 'All durations'
|
||||
: `${durationRange[0]}-${durationRange[1]} minutes`
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Attendee Count Filter - Slider */}
|
||||
<div className="filter-section">
|
||||
<label className="block text-small mb-3 font-medium text-gray-700">
|
||||
Attendees
|
||||
</label>
|
||||
<div className="px-2">
|
||||
<Slider
|
||||
value={attendeeRange}
|
||||
onValueChange={setAttendeeRange}
|
||||
max={5000}
|
||||
min={0}
|
||||
step={100}
|
||||
className="w-full"
|
||||
/>
|
||||
<div className="flex justify-between mt-2 text-xs text-gray-500">
|
||||
<span>{attendeeRange[0].toLocaleString()}</span>
|
||||
<span>{attendeeRange[1].toLocaleString()}+</span>
|
||||
</div>
|
||||
<div className="mt-1 text-center text-xs text-gray-400">
|
||||
{attendeeRange[0] === 0 && attendeeRange[1] === 5000
|
||||
? 'Any size'
|
||||
: `${attendeeRange[0].toLocaleString()}-${attendeeRange[1].toLocaleString()}+`
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Main Content */}
|
||||
<div className="col-span-12 lg:col-span-9">
|
||||
{/* Results Header */}
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div className="text-body text-gray-600">
|
||||
Showing {currentWebinars.length} of {filteredWebinars.length} webcasts
|
||||
</div>
|
||||
<div className="text-small text-gray-500">
|
||||
Page {currentPage} of {totalPages}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content Area */}
|
||||
<div ref={containerRef}>
|
||||
{currentWebinars.length === 0 ? (
|
||||
<div className="text-center py-12">
|
||||
<div className="text-gray-400 mb-4">
|
||||
<Search className="w-12 h-12 mx-auto mb-4" />
|
||||
</div>
|
||||
<h3 className="text-h4 mb-2">No webcasts found</h3>
|
||||
<p className="text-body text-gray-600 mb-4">
|
||||
Try adjusting your filters or search terms
|
||||
</p>
|
||||
{hasActiveFilters && (
|
||||
<Button onClick={clearAllFilters}>
|
||||
Clear all filters
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{/* Grid View */}
|
||||
{viewType === 'grid' ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6 mb-8">
|
||||
{currentWebinars.map((webinar) => (
|
||||
<WebinarCard key={webinar.id} webinar={webinar} />
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
/* List View */
|
||||
<div className="space-y-4 mb-8">
|
||||
{currentWebinars.map((webinar) => (
|
||||
<WebinarCard key={webinar.id} webinar={webinar} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Pagination */}
|
||||
{totalPages > 1 && (
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setCurrentPage(prev => Math.max(1, prev - 1))}
|
||||
disabled={currentPage === 1}
|
||||
>
|
||||
<ChevronLeft className="w-4 h-4 mr-1" />
|
||||
Previous
|
||||
</Button>
|
||||
|
||||
<div className="flex items-center gap-1">
|
||||
{Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
|
||||
const page = Math.max(1, Math.min(totalPages - 4, currentPage - 2)) + i;
|
||||
return (
|
||||
<Button
|
||||
key={page}
|
||||
variant={currentPage === page ? "default" : "outline"}
|
||||
size="sm"
|
||||
onClick={() => setCurrentPage(page)}
|
||||
className="w-10"
|
||||
>
|
||||
{page}
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setCurrentPage(prev => Math.min(totalPages, prev + 1))}
|
||||
disabled={currentPage === totalPages}
|
||||
>
|
||||
Next
|
||||
<ChevronRight className="w-4 h-4 ml-1" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Webcast CTA Banner */}
|
||||
<WebcastCTABanner />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
738
src/components/WebinarsListing.tsx
Normal file
@@ -0,0 +1,738 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { Button } from './ui/button';
|
||||
import { Input } from './ui/input';
|
||||
import { Badge } from './ui/badge';
|
||||
import { Card, CardContent } from './ui/card';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from './ui/avatar';
|
||||
import {
|
||||
Search,
|
||||
Filter,
|
||||
ChevronDown,
|
||||
Play,
|
||||
Calendar,
|
||||
Clock,
|
||||
Users,
|
||||
X,
|
||||
Mail,
|
||||
ArrowRight,
|
||||
ExternalLink,
|
||||
Loader2
|
||||
} from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'motion/react';
|
||||
import { navigateTo } from './Router';
|
||||
import { ImageWithFallback } from './figma/ImageWithFallback';
|
||||
import { BrandedTag } from './about/BrandedTag';
|
||||
import { PrimaryCTAButton } from './PrimaryCTAButton';
|
||||
|
||||
|
||||
// Mock data for webinars
|
||||
const mockWebinars = {
|
||||
upcoming: [
|
||||
{
|
||||
id: 1,
|
||||
title: "Future-Ready Leadership: Navigating Digital Transformation",
|
||||
abstract: "Explore how modern leaders are adapting to digital disruption and driving organizational change through innovative leadership approaches.",
|
||||
speaker: {
|
||||
name: "Sarah Chen",
|
||||
title: "Chief Digital Officer",
|
||||
avatar: "https://images.unsplash.com/photo-1494790108755-2616b612b0e0?w=80&h=80&fit=crop&crop=face"
|
||||
},
|
||||
date: "2024-12-15",
|
||||
time: "14:00",
|
||||
duration: "60 min",
|
||||
status: "upcoming",
|
||||
theme: "Digital Leadership",
|
||||
registrationOpen: true,
|
||||
coverImage: "https://images.unsplash.com/photo-1542744094-24638eff58bb?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxidXNpbmVzcyUyMHN0cmF0ZWd5JTIwZGlnaXRhbCUyMHRyYW5zZm9ybWF0aW9ufGVufDF8fHx8MTc1NjExNDc5OXww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral",
|
||||
attendees: 245
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Building Resilient Teams in Uncertain Times",
|
||||
abstract: "Learn proven strategies for developing team resilience and maintaining high performance during challenging periods.",
|
||||
speaker: {
|
||||
name: "Marcus Thompson",
|
||||
title: "Leadership Coach",
|
||||
avatar: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=80&h=80&fit=crop&crop=face"
|
||||
},
|
||||
date: "2024-12-18",
|
||||
time: "15:30",
|
||||
duration: "45 min",
|
||||
status: "upcoming",
|
||||
theme: "Team Leadership",
|
||||
registrationOpen: true,
|
||||
coverImage: "https://images.unsplash.com/photo-1598015132635-131afe3ba07f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxsZWFkZXJzaGlwJTIwdGVhbSUyMGNvbGxhYm9yYXRpb258ZW58MXx8fHwxNzU2MTE0Nzk0fDA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral",
|
||||
attendees: 189
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "The Art of Strategic Decision Making",
|
||||
abstract: "Master the frameworks and methodologies that top executives use to make complex strategic decisions.",
|
||||
speaker: {
|
||||
name: "Dr. Rachel Park",
|
||||
title: "Strategy Consultant",
|
||||
avatar: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=80&h=80&fit=crop&crop=face"
|
||||
},
|
||||
date: "2024-12-20",
|
||||
time: "13:00",
|
||||
duration: "75 min",
|
||||
status: "upcoming",
|
||||
theme: "Strategic Leadership",
|
||||
registrationOpen: false,
|
||||
coverImage: "https://images.unsplash.com/photo-1742996111692-2d924f12a058?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxwcm9mZXNzaW9uYWwlMjBidXNpbmVzcyUyMG1lZXRpbmclMjBjb25mZXJlbmNlfGVufDF8fHx8MTc1NjExNDc4NXww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral",
|
||||
attendees: 312
|
||||
}
|
||||
],
|
||||
past: [
|
||||
{
|
||||
id: 4,
|
||||
title: "Emotional Intelligence in Leadership",
|
||||
abstract: "Discover how emotional intelligence drives effective leadership and creates positive organizational culture.",
|
||||
speaker: {
|
||||
name: "James Wilson",
|
||||
title: "Executive Coach",
|
||||
avatar: "/api/placeholder/40/40"
|
||||
},
|
||||
date: "2024-11-25",
|
||||
time: "14:00",
|
||||
duration: "60 min",
|
||||
status: "replay",
|
||||
theme: "Personal Leadership",
|
||||
coverImage: "https://images.unsplash.com/photo-1561993629-67302018480e?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxlbW90aW9uYWwlMjBpbnRlbGxpZ2VuY2UlMjBwcm9mZXNzaW9uYWwlMjBkZXZlbG9wbWVudHxlbnwxfHx8fDE3NTYxMTQ4NTJ8MA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral",
|
||||
views: 1245
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: "Innovation Leadership: Fostering Creative Culture",
|
||||
abstract: "Learn how to create an environment that encourages innovation and creative problem-solving.",
|
||||
speaker: {
|
||||
name: "Lisa Rodriguez",
|
||||
title: "Innovation Director",
|
||||
avatar: "/api/placeholder/40/40"
|
||||
},
|
||||
date: "2024-11-20",
|
||||
time: "16:00",
|
||||
duration: "50 min",
|
||||
status: "replay",
|
||||
theme: "Innovation Leadership",
|
||||
coverImage: "https://images.unsplash.com/photo-1744339699989-550c61f3ecb8?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxpbm5vdmF0aW9uJTIwY3JlYXRpdmUlMjB3b3JrcGxhY2V8ZW58MXx8fHwxNzU2MTE0ODU2fDA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral",
|
||||
views: 892
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: "Global Leadership: Managing Across Cultures",
|
||||
abstract: "Navigate the complexities of leading diverse, multicultural teams in a global business environment.",
|
||||
speaker: {
|
||||
name: "Ahmed Hassan",
|
||||
title: "Global Operations Manager",
|
||||
avatar: "/api/placeholder/40/40"
|
||||
},
|
||||
date: "2024-11-15",
|
||||
time: "10:00",
|
||||
duration: "65 min",
|
||||
status: "replay",
|
||||
theme: "Global Leadership",
|
||||
coverImage: "https://images.unsplash.com/photo-1650784854945-264d5b0b6b07?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxnbG9iYWwlMjBtdWx0aWN1bHR1cmFsJTIwYnVzaW5lc3MlMjB0ZWFtfGVufDF8fHx8MTc1NjExNDg2Mnww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral",
|
||||
views: 1567
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const themes = [
|
||||
"All Themes",
|
||||
"Digital Leadership",
|
||||
"Team Leadership",
|
||||
"Strategic Leadership",
|
||||
"Personal Leadership",
|
||||
"Innovation Leadership",
|
||||
"Global Leadership"
|
||||
];
|
||||
|
||||
const speakers = [
|
||||
"All Speakers",
|
||||
"Sarah Chen",
|
||||
"Marcus Thompson",
|
||||
"Dr. Rachel Park",
|
||||
"James Wilson",
|
||||
"Lisa Rodriguez",
|
||||
"Ahmed Hassan"
|
||||
];
|
||||
|
||||
export function WebinarsListing() {
|
||||
const [activeTab, setActiveTab] = useState('upcoming');
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [selectedTheme, setSelectedTheme] = useState('All Themes');
|
||||
const [selectedSpeaker, setSelectedSpeaker] = useState('All Speakers');
|
||||
const [filtersOpen, setFiltersOpen] = useState(false);
|
||||
const [emailInput, setEmailInput] = useState('');
|
||||
const [isSubscribing, setIsSubscribing] = useState(false);
|
||||
const [loadingMore, setLoadingMore] = useState(false);
|
||||
const [page, setPage] = useState(1);
|
||||
const searchTimeout = useRef(null);
|
||||
|
||||
// Handle URL hash changes for deep linking
|
||||
useEffect(() => {
|
||||
const hash = window.location.hash.replace('#', '');
|
||||
if (hash === 'upcoming' || hash === 'past') {
|
||||
setActiveTab(hash);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Update URL hash when tab changes
|
||||
const handleTabChange = (value) => {
|
||||
setActiveTab(value);
|
||||
window.history.pushState(null, null, `/webinars#${value}`);
|
||||
|
||||
// Announce to screen readers
|
||||
const announcement = `Switched to ${value} webinars. Showing ${getFilteredWebinars(value).length} results.`;
|
||||
const liveRegion = document.createElement('div');
|
||||
liveRegion.setAttribute('aria-live', 'polite');
|
||||
liveRegion.setAttribute('aria-atomic', 'true');
|
||||
liveRegion.className = 'sr-only';
|
||||
liveRegion.textContent = announcement;
|
||||
document.body.appendChild(liveRegion);
|
||||
setTimeout(() => document.body.removeChild(liveRegion), 1000);
|
||||
};
|
||||
|
||||
// Debounced search
|
||||
useEffect(() => {
|
||||
if (searchTimeout.current) {
|
||||
clearTimeout(searchTimeout.current);
|
||||
}
|
||||
searchTimeout.current = setTimeout(() => {
|
||||
// Search logic would trigger here
|
||||
}, 300);
|
||||
|
||||
return () => {
|
||||
if (searchTimeout.current) {
|
||||
clearTimeout(searchTimeout.current);
|
||||
}
|
||||
};
|
||||
}, [searchQuery]);
|
||||
|
||||
// Filter webinars based on current filters
|
||||
const getFilteredWebinars = (tab = activeTab) => {
|
||||
let webinars = mockWebinars[tab] || [];
|
||||
|
||||
if (searchQuery) {
|
||||
webinars = webinars.filter(webinar =>
|
||||
webinar.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
webinar.abstract.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
webinar.speaker.name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
if (selectedTheme !== 'All Themes') {
|
||||
webinars = webinars.filter(webinar => webinar.theme === selectedTheme);
|
||||
}
|
||||
|
||||
if (selectedSpeaker !== 'All Speakers') {
|
||||
webinars = webinars.filter(webinar => webinar.speaker.name === selectedSpeaker);
|
||||
}
|
||||
|
||||
return webinars;
|
||||
};
|
||||
|
||||
// Handle newsletter subscription
|
||||
const handleSubscribe = async (e) => {
|
||||
e.preventDefault();
|
||||
if (!emailInput || isSubscribing) return;
|
||||
|
||||
setIsSubscribing(true);
|
||||
// Simulate API call
|
||||
await new Promise(resolve => setTimeout(resolve, 1500));
|
||||
setIsSubscribing(false);
|
||||
setEmailInput('');
|
||||
alert('Successfully subscribed to webinar reminders!');
|
||||
};
|
||||
|
||||
// Handle load more for past webinars
|
||||
const handleLoadMore = async () => {
|
||||
setLoadingMore(true);
|
||||
// Simulate API call
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
setPage(prev => prev + 1);
|
||||
setLoadingMore(false);
|
||||
};
|
||||
|
||||
// Clear all filters
|
||||
const clearFilters = () => {
|
||||
setSearchQuery('');
|
||||
setSelectedTheme('All Themes');
|
||||
setSelectedSpeaker('All Speakers');
|
||||
};
|
||||
|
||||
const hasActiveFilters = searchQuery || selectedTheme !== 'All Themes' || selectedSpeaker !== 'All Speakers';
|
||||
const filteredWebinars = getFilteredWebinars();
|
||||
|
||||
return (
|
||||
<div className="min-h-screen" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
{/* Hero Banner */}
|
||||
<section className="relative py-20 overflow-hidden">
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-[#04045B] via-[#04045B] to-[#030359]" />
|
||||
<div className="absolute inset-0 bg-[url('https://images.unsplash.com/photo-1580863651276-3a1669b2e65a?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHx3ZWJpbmFyJTIwaGVybyUyMGJhY2tncm91bmQlMjBwcmVzZW50YXRpb258ZW58MXx8fHwxNzU2MTE0ODA0fDA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral')] bg-cover bg-center opacity-10" />
|
||||
|
||||
<div className="relative z-10 section-margin-x">
|
||||
<div className="max-w-4xl">
|
||||
<BrandedTag text="LEARN & GROW" variant="white" />
|
||||
<h1 className="text-h1-white mb-6">
|
||||
Expert-Led Webinars for <br />
|
||||
Leadership Excellence
|
||||
</h1>
|
||||
<p className="text-body-lg-white mb-8 max-w-2xl">
|
||||
Join industry experts and thought leaders as they share cutting-edge insights,
|
||||
proven strategies, and practical tools to elevate your leadership journey.
|
||||
</p>
|
||||
|
||||
{/* Hero Stats */}
|
||||
<div className="grid grid-cols-3 gap-8 max-w-lg">
|
||||
<div className="text-center">
|
||||
<div className="text-h2-white">50+</div>
|
||||
<div className="text-small-white">Expert Sessions</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-h2-white">12K+</div>
|
||||
<div className="text-small-white">Global Attendees</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-h2-white">95%</div>
|
||||
<div className="text-small-white">Satisfaction Rate</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Main Content */}
|
||||
<section className="py-16 section-margin-x">
|
||||
{/* Tab Switcher */}
|
||||
<Tabs value={activeTab} onValueChange={handleTabChange} className="w-full">
|
||||
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between mb-8">
|
||||
<TabsList className="grid w-full lg:w-auto grid-cols-2 mb-4 lg:mb-0">
|
||||
<TabsTrigger
|
||||
value="upcoming"
|
||||
className="data-[state=active]:bg-[#04045B] data-[state=active]:text-white"
|
||||
>
|
||||
Upcoming Webinars
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="past"
|
||||
className="data-[state=active]:bg-[#04045B] data-[state=active]:text-white"
|
||||
>
|
||||
Past Webinars
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
{/* Results Count */}
|
||||
<div className="text-small text-muted">
|
||||
Showing {filteredWebinars.length} result{filteredWebinars.length !== 1 ? 's' : ''}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Filter & Search Bar */}
|
||||
<Card className="mb-8">
|
||||
<CardContent className="p-6">
|
||||
{/* Mobile Filter Toggle */}
|
||||
<div className="lg:hidden mb-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setFiltersOpen(!filtersOpen)}
|
||||
className="w-full justify-between"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Filter className="w-4 h-4" />
|
||||
Filters & Search
|
||||
</div>
|
||||
<ChevronDown className={`w-4 h-4 transition-transform ${filtersOpen ? 'rotate-180' : ''}`} />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Filter Content */}
|
||||
<div className={`space-y-4 lg:space-y-0 lg:grid lg:grid-cols-12 lg:gap-4 lg:items-end ${!filtersOpen ? 'hidden lg:grid' : ''}`}>
|
||||
{/* Search Input */}
|
||||
<div className="lg:col-span-4">
|
||||
<label className="block text-small font-medium mb-2">Search Webinars</label>
|
||||
<div className="relative">
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Search by title, speaker, or topic..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="pl-10"
|
||||
/>
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-muted" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Theme Filter */}
|
||||
<div className="lg:col-span-3">
|
||||
<label className="block text-small font-medium mb-2">Theme</label>
|
||||
<select
|
||||
value={selectedTheme}
|
||||
onChange={(e) => setSelectedTheme(e.target.value)}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#04045B] focus:border-transparent"
|
||||
>
|
||||
{themes.map(theme => (
|
||||
<option key={theme} value={theme}>{theme}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Speaker Filter */}
|
||||
<div className="lg:col-span-3">
|
||||
<label className="block text-small font-medium mb-2">Speaker</label>
|
||||
<select
|
||||
value={selectedSpeaker}
|
||||
onChange={(e) => setSelectedSpeaker(e.target.value)}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#04045B] focus:border-transparent"
|
||||
>
|
||||
{speakers.map(speaker => (
|
||||
<option key={speaker} value={speaker}>{speaker}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Clear Filters */}
|
||||
<div className="lg:col-span-2">
|
||||
{hasActiveFilters && (
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={clearFilters}
|
||||
className="w-full"
|
||||
>
|
||||
<X className="w-4 h-4 mr-2" />
|
||||
Clear
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Tab Content */}
|
||||
<TabsContent value="upcoming" className="mt-0">
|
||||
<WebinarGrid webinars={filteredWebinars} type="upcoming" />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="past" className="mt-0">
|
||||
<WebinarGrid webinars={filteredWebinars} type="past" />
|
||||
|
||||
{/* Load More Button for Past Webinars */}
|
||||
{filteredWebinars.length > 0 && (
|
||||
<div className="text-center mt-8">
|
||||
<Button
|
||||
onClick={handleLoadMore}
|
||||
disabled={loadingMore}
|
||||
className="bg-[#04045B] hover:bg-[#030359] text-white"
|
||||
>
|
||||
{loadingMore ? (
|
||||
<>
|
||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
||||
Loading More...
|
||||
</>
|
||||
) : (
|
||||
'Load More Webinars'
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
{/* Empty State */}
|
||||
{filteredWebinars.length === 0 && (
|
||||
<EmptyState
|
||||
type={activeTab}
|
||||
hasFilters={hasActiveFilters}
|
||||
onClearFilters={clearFilters}
|
||||
onSwitchTab={() => handleTabChange(activeTab === 'upcoming' ? 'past' : 'upcoming')}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
|
||||
{/* Newsletter CTA Panel */}
|
||||
<section className="py-16 bg-gray-50">
|
||||
<div className="section-margin-x">
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<div className="mb-8">
|
||||
<h2 className="text-h2 mb-4">Never Miss a Session</h2>
|
||||
<p className="text-body-lg text-muted max-w-2xl mx-auto">
|
||||
Get notified about upcoming webinars, exclusive leadership insights, and
|
||||
early access to replay content. Join our community of 12,000+ leaders.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubscribe} className="max-w-md mx-auto">
|
||||
<div className="flex gap-3">
|
||||
<div className="flex-1">
|
||||
<Input
|
||||
type="email"
|
||||
placeholder="Enter your email address"
|
||||
value={emailInput}
|
||||
onChange={(e) => setEmailInput(e.target.value)}
|
||||
required
|
||||
className="h-12"
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={isSubscribing || !emailInput}
|
||||
className="bg-[#04045B] hover:bg-[#030359] text-white h-12 px-6"
|
||||
>
|
||||
{isSubscribing ? (
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
) : (
|
||||
<>
|
||||
<Mail className="w-4 h-4 mr-2" />
|
||||
Subscribe
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-small text-muted mt-3">
|
||||
No spam, unsubscribe at any time. We respect your privacy.
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Webinar Grid Component
|
||||
function WebinarGrid({ webinars, type }) {
|
||||
if (webinars.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{webinars.map((webinar, index) => (
|
||||
<WebinarCard key={webinar.id} webinar={webinar} type={type} index={index} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Individual Webinar Card Component
|
||||
function WebinarCard({ webinar, type, index }) {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const [imageError, setImageError] = useState(false);
|
||||
|
||||
const formatDate = (dateString) => {
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric'
|
||||
});
|
||||
};
|
||||
|
||||
const getStatusBadge = () => {
|
||||
switch (webinar.status) {
|
||||
case 'live':
|
||||
return <Badge className="bg-red-500 text-white">LIVE</Badge>;
|
||||
case 'upcoming':
|
||||
return <Badge className="bg-[#04045B] text-white">Upcoming</Badge>;
|
||||
case 'replay':
|
||||
return <Badge className="bg-green-600 text-white">Replay</Badge>;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const handleCardClick = () => {
|
||||
// Navigate to webinar detail page
|
||||
navigateTo(`/webinar/${webinar.id}`);
|
||||
};
|
||||
|
||||
const handleCTAClick = (e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
if (webinar.status === 'upcoming') {
|
||||
if (webinar.registrationOpen) {
|
||||
// Handle registration
|
||||
alert('Registration functionality would be implemented here');
|
||||
} else {
|
||||
// Show tooltip or message
|
||||
alert('Registration is closed for this webinar');
|
||||
}
|
||||
} else if (webinar.status === 'replay') {
|
||||
// Navigate to replay
|
||||
navigateTo(`/webinar/${webinar.id}`);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<motion.article
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||
className="group cursor-pointer"
|
||||
onClick={handleCardClick}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
role="article"
|
||||
tabIndex={0}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
handleCardClick();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Card className="overflow-hidden transition-all duration-300 hover:shadow-lg hover:-translate-y-1 focus-within:shadow-lg focus-within:-translate-y-1">
|
||||
<div className="relative">
|
||||
{/* Status Badge */}
|
||||
<div className="absolute top-3 left-3 z-10">
|
||||
{getStatusBadge()}
|
||||
</div>
|
||||
|
||||
{/* Cover Image */}
|
||||
<div className="relative h-48 overflow-hidden">
|
||||
<ImageWithFallback
|
||||
src={webinar.coverImage}
|
||||
alt={webinar.title}
|
||||
className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
|
||||
onError={() => setImageError(true)}
|
||||
/>
|
||||
{imageError && (
|
||||
<div className="absolute inset-0 bg-gray-200 flex items-center justify-center">
|
||||
<Play className="w-12 h-12 text-gray-400" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Hover Overlay */}
|
||||
<AnimatePresence>
|
||||
{isHovered && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="absolute inset-0 bg-black bg-opacity-50 flex items-center justify-center"
|
||||
>
|
||||
<div className="text-center text-white p-4">
|
||||
<Play className="w-12 h-12 mx-auto mb-2" />
|
||||
<p className="text-small">{webinar.abstract}</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
|
||||
<CardContent className="p-6">
|
||||
{/* Theme Badge */}
|
||||
<Badge variant="secondary" className="mb-3 bg-[#F8C301] bg-opacity-10 text-[#04045B]">
|
||||
{webinar.theme}
|
||||
</Badge>
|
||||
|
||||
{/* Title */}
|
||||
<h3 className="text-h4 mb-3 line-clamp-2 group-hover:text-[#04045B] transition-colors">
|
||||
{webinar.title}
|
||||
</h3>
|
||||
|
||||
{/* Speaker */}
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<Avatar className="w-10 h-10">
|
||||
<AvatarImage src={webinar.speaker.avatar} alt={webinar.speaker.name} />
|
||||
<AvatarFallback>{webinar.speaker.name.split(' ').map(n => n[0]).join('')}</AvatarFallback>
|
||||
</Avatar>
|
||||
<div>
|
||||
<div className="text-body font-medium">{webinar.speaker.name}</div>
|
||||
<div className="text-small text-muted">{webinar.speaker.title}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Meta Information */}
|
||||
<div className="flex items-center gap-4 mb-4 text-small text-muted">
|
||||
<div className="flex items-center gap-1">
|
||||
<Calendar className="w-4 h-4" />
|
||||
{formatDate(webinar.date)}
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Clock className="w-4 h-4" />
|
||||
{webinar.duration}
|
||||
</div>
|
||||
{type === 'upcoming' && (
|
||||
<div className="flex items-center gap-1">
|
||||
<Users className="w-4 h-4" />
|
||||
{webinar.attendees} registered
|
||||
</div>
|
||||
)}
|
||||
{type === 'past' && (
|
||||
<div className="flex items-center gap-1">
|
||||
<Play className="w-4 h-4" />
|
||||
{webinar.views} views
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* CTA Button */}
|
||||
<Button
|
||||
onClick={handleCTAClick}
|
||||
disabled={webinar.status === 'upcoming' && !webinar.registrationOpen}
|
||||
className={`w-full ${
|
||||
webinar.status === 'upcoming' && !webinar.registrationOpen
|
||||
? 'bg-gray-300 text-gray-500 cursor-not-allowed'
|
||||
: 'bg-[#04045B] hover:bg-[#030359] text-white'
|
||||
}`}
|
||||
>
|
||||
{webinar.status === 'upcoming' && webinar.registrationOpen && 'Register Now'}
|
||||
{webinar.status === 'upcoming' && !webinar.registrationOpen && 'Registration Closed'}
|
||||
{webinar.status === 'replay' && (
|
||||
<>
|
||||
Watch Replay
|
||||
<ExternalLink className="w-4 h-4 ml-2" />
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.article>
|
||||
);
|
||||
}
|
||||
|
||||
// Empty State Component
|
||||
function EmptyState({ type, hasFilters, onClearFilters, onSwitchTab }) {
|
||||
const isUpcoming = type === 'upcoming';
|
||||
|
||||
return (
|
||||
<div className="text-center py-16">
|
||||
<div className="max-w-md mx-auto">
|
||||
<div className="w-20 h-20 mx-auto mb-6 bg-gray-100 rounded-full flex items-center justify-center">
|
||||
<Calendar className="w-10 h-10 text-gray-400" />
|
||||
</div>
|
||||
|
||||
<h3 className="text-h3 mb-4">
|
||||
{hasFilters ? 'No Results Found' :
|
||||
isUpcoming ? 'No Upcoming Webinars' : 'No Past Webinars'}
|
||||
</h3>
|
||||
|
||||
<p className="text-body text-muted mb-6">
|
||||
{hasFilters ?
|
||||
'Try adjusting your search criteria or clear filters to see more results.' :
|
||||
isUpcoming ?
|
||||
'We\'re planning exciting sessions. Check back soon or explore our past webinars.' :
|
||||
'Our webinar archive is growing. Check out our upcoming sessions.'
|
||||
}
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-3 justify-center">
|
||||
{hasFilters && (
|
||||
<Button onClick={onClearFilters} variant="outline">
|
||||
Clear Filters
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button onClick={onSwitchTab} className="bg-[#04045B] hover:bg-[#030359] text-white">
|
||||
{isUpcoming ? 'View Past Webinars' : 'View Upcoming Webinars'}
|
||||
<ArrowRight className="w-4 h-4 ml-2" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default WebinarsListing;
|
||||
883
src/components/WebinarsPage.tsx
Normal file
@@ -0,0 +1,883 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { Button } from './ui/button';
|
||||
import { Card, CardContent } from './ui/card';
|
||||
import { Badge } from './ui/badge';
|
||||
import { Input } from './ui/input';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
|
||||
import { ImageWithFallback } from './figma/ImageWithFallback';
|
||||
import { PrimaryCTAButton } from './PrimaryCTAButton';
|
||||
import { navigateTo } from './Router';
|
||||
import {
|
||||
Search,
|
||||
Calendar,
|
||||
Clock,
|
||||
Users,
|
||||
Play,
|
||||
Filter,
|
||||
Grid,
|
||||
List,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
X,
|
||||
TrendingUp,
|
||||
Award,
|
||||
Globe
|
||||
} from 'lucide-react';
|
||||
|
||||
// Comprehensive webinar data
|
||||
const webinarsData = [
|
||||
{
|
||||
id: '1',
|
||||
slug: 'leadership-in-digital-age',
|
||||
title: 'Leadership in the Digital Age: Navigating Change and Innovation',
|
||||
description: 'Discover how modern leaders can adapt and thrive in an increasingly digital world. This comprehensive webcast covers digital transformation strategies, leading remote teams, and fostering innovation.',
|
||||
presenter: 'Dr. Sarah Mitchell',
|
||||
presenterTitle: 'Chief Digital Transformation Officer',
|
||||
company: 'Global Leadership Institute',
|
||||
date: '2024-02-15',
|
||||
time: '2:00 PM EST',
|
||||
duration: '90 minutes',
|
||||
attendees: '2,400+',
|
||||
category: 'Digital Transformation',
|
||||
tags: ['Leadership', 'Digital Strategy', 'Innovation', 'Change Management'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1560472355-536de3962603?w=600&h=400&fit=crop',
|
||||
status: 'Available',
|
||||
featured: true,
|
||||
level: 'Advanced',
|
||||
format: 'Hybrid',
|
||||
rating: 4.8,
|
||||
price: 'Free'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
slug: 'building-resilient-teams',
|
||||
title: 'Building Resilient Teams: Strategies for Sustainable Performance',
|
||||
description: 'Learn proven methodologies for creating teams that can withstand challenges and maintain high performance under pressure.',
|
||||
presenter: 'Marcus Rodriguez',
|
||||
presenterTitle: 'Director of Organizational Development',
|
||||
company: 'Excellence Consulting',
|
||||
date: '2024-02-08',
|
||||
time: '1:00 PM EST',
|
||||
duration: '75 minutes',
|
||||
attendees: '1,850+',
|
||||
category: 'Team Development',
|
||||
tags: ['Team Building', 'Resilience', 'Performance', 'Leadership'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=600&h=400&fit=crop',
|
||||
status: 'Available',
|
||||
featured: true,
|
||||
level: 'Intermediate',
|
||||
format: 'In Person',
|
||||
rating: 4.9,
|
||||
price: 'Free'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
slug: 'strategic-decision-making',
|
||||
title: 'Strategic Decision Making: Tools and Frameworks for Leaders',
|
||||
description: 'Master the art of strategic thinking and decision-making with proven frameworks and methodologies used by top executives.',
|
||||
presenter: 'Dr. Emily Chen',
|
||||
presenterTitle: 'Strategic Leadership Consultant',
|
||||
company: 'Strategy First',
|
||||
date: '2024-01-25',
|
||||
time: '3:00 PM EST',
|
||||
duration: '60 minutes',
|
||||
attendees: '3,200+',
|
||||
category: 'Strategy',
|
||||
tags: ['Strategic Thinking', 'Decision Making', 'Leadership', 'Planning'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=600&h=400&fit=crop',
|
||||
status: 'Available',
|
||||
featured: false,
|
||||
level: 'Advanced',
|
||||
format: 'Virtual',
|
||||
rating: 4.7,
|
||||
price: 'Free'
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
slug: 'emotional-intelligence-workplace',
|
||||
title: 'Emotional Intelligence in the Workplace: Leading with Empathy',
|
||||
description: 'Explore the critical role of emotional intelligence in effective leadership and learn practical techniques for developing empathy and emotional awareness.',
|
||||
presenter: 'Jennifer Adams',
|
||||
presenterTitle: 'Executive Coach & EQ Specialist',
|
||||
company: 'Empathy Leadership Group',
|
||||
date: '2024-01-18',
|
||||
time: '2:30 PM EST',
|
||||
duration: '85 minutes',
|
||||
attendees: '2,100+',
|
||||
category: 'Personal Development',
|
||||
tags: ['Emotional Intelligence', 'Empathy', 'Communication', 'Leadership'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1559027615-cd4628902d4a?w=600&h=400&fit=crop',
|
||||
status: 'Recorded',
|
||||
featured: false,
|
||||
level: 'Intermediate',
|
||||
format: 'Hybrid',
|
||||
rating: 4.6,
|
||||
price: 'Free'
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
slug: 'crisis-leadership-management',
|
||||
title: 'Crisis Leadership: Managing Through Uncertainty and Change',
|
||||
description: 'Learn essential crisis leadership skills and strategies for guiding organizations through challenging times and uncertainty.',
|
||||
presenter: 'David Park',
|
||||
presenterTitle: 'Crisis Management Expert',
|
||||
company: 'Risk Management Solutions',
|
||||
date: '2024-01-10',
|
||||
time: '1:30 PM EST',
|
||||
duration: '70 minutes',
|
||||
attendees: '2,800+',
|
||||
category: 'Crisis Management',
|
||||
tags: ['Crisis Leadership', 'Change Management', 'Communication', 'Strategy'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1552664730-d307ca884978?w=600&h=400&fit=crop',
|
||||
status: 'Live',
|
||||
featured: false,
|
||||
level: 'Advanced',
|
||||
format: 'Virtual',
|
||||
rating: 4.8,
|
||||
price: 'Free'
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
slug: 'future-workplace-trends',
|
||||
title: 'The Future of Work: Preparing for Tomorrow\'s Workplace',
|
||||
description: 'Discover emerging workplace trends and learn how to prepare your organization and teams for the future of work.',
|
||||
presenter: 'Lisa Thompson',
|
||||
presenterTitle: 'Future of Work Analyst',
|
||||
company: 'Tomorrow Institute',
|
||||
date: '2024-01-05',
|
||||
time: '4:00 PM EST',
|
||||
duration: '80 minutes',
|
||||
attendees: '1,950+',
|
||||
category: 'Future of Work',
|
||||
tags: ['Future Trends', 'Workplace Evolution', 'Technology', 'Leadership'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1600880292203-757bb62b4baf?w=600&h=400&fit=crop',
|
||||
status: 'Upcoming',
|
||||
featured: false,
|
||||
level: 'Beginner',
|
||||
format: 'Hybrid',
|
||||
rating: 4.5,
|
||||
price: 'Free'
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
slug: 'ai-leadership-transformation',
|
||||
title: 'AI-Powered Leadership: Transforming Decision Making',
|
||||
description: 'Explore how artificial intelligence is reshaping leadership practices and learn to leverage AI tools for better decision-making.',
|
||||
presenter: 'Dr. Alex Kim',
|
||||
presenterTitle: 'AI Strategy Director',
|
||||
company: 'AI Leadership Labs',
|
||||
date: '2024-03-15',
|
||||
time: '2:00 PM EST',
|
||||
duration: '75 minutes',
|
||||
attendees: '3,500+',
|
||||
category: 'Technology Leadership',
|
||||
tags: ['Artificial Intelligence', 'Decision Making', 'Technology', 'Innovation'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1677442136019-21780ecad995?w=600&h=400&fit=crop',
|
||||
status: 'Upcoming',
|
||||
featured: true,
|
||||
level: 'Advanced',
|
||||
format: 'Virtual',
|
||||
rating: 4.9,
|
||||
price: 'Free'
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
slug: 'sustainable-leadership-live',
|
||||
title: 'Sustainable Leadership: Building for the Future - LIVE SESSION',
|
||||
description: 'Join us LIVE as we discuss sustainable leadership practices and environmental responsibility in business leadership.',
|
||||
presenter: 'Maria Santos',
|
||||
presenterTitle: 'Sustainability Leadership Expert',
|
||||
company: 'Green Leadership Council',
|
||||
date: '2024-02-20',
|
||||
time: '1:00 PM EST',
|
||||
duration: '90 minutes',
|
||||
attendees: '2,200+',
|
||||
category: 'Sustainable Leadership',
|
||||
tags: ['Sustainability', 'Environmental Leadership', 'Future Business', 'Corporate Responsibility'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1542601906990-b4d3fb778b09?w=600&h=400&fit=crop',
|
||||
status: 'Live',
|
||||
featured: true,
|
||||
level: 'Intermediate',
|
||||
format: 'Virtual',
|
||||
rating: 4.7,
|
||||
price: 'Free'
|
||||
},
|
||||
{
|
||||
id: '9',
|
||||
slug: 'cross-cultural-leadership',
|
||||
title: 'Cross-Cultural Leadership: Leading Global Teams Effectively',
|
||||
description: 'Develop the skills needed to lead diverse, multicultural teams across different time zones and cultural contexts.',
|
||||
presenter: 'Dr. Raj Patel',
|
||||
presenterTitle: 'Global Leadership Specialist',
|
||||
company: 'International Business Academy',
|
||||
date: '2024-02-25',
|
||||
time: '11:00 AM EST',
|
||||
duration: '65 minutes',
|
||||
attendees: '1,750+',
|
||||
category: 'Global Leadership',
|
||||
tags: ['Cross-Cultural', 'Global Teams', 'Diversity', 'Communication'],
|
||||
thumbnail: 'https://images.unsplash.com/photo-1573164713714-d95e436ab8d6?w=600&h=400&fit=crop',
|
||||
status: 'Available',
|
||||
featured: false,
|
||||
level: 'Intermediate',
|
||||
format: 'Virtual',
|
||||
rating: 4.6,
|
||||
price: 'Free'
|
||||
}
|
||||
];
|
||||
|
||||
// Filter configurations
|
||||
const categories = ['All Categories', 'Digital Transformation', 'Team Development', 'Strategy', 'Personal Development', 'Crisis Management', 'Future of Work', 'Technology Leadership', 'Sustainable Leadership', 'Global Leadership'];
|
||||
const formats = ['All Formats', 'Virtual', 'Hybrid', 'In Person'];
|
||||
const levels = ['All Levels', 'Beginner', 'Intermediate', 'Advanced'];
|
||||
const statuses = ['All Status', 'Available', 'Live', 'Upcoming', 'Recorded', 'Featured'];
|
||||
const sortOptions = [
|
||||
{ value: 'Most Popular', label: 'Most Popular' },
|
||||
{ value: 'newest', label: 'Newest First' },
|
||||
{ value: 'oldest', label: 'Oldest First' },
|
||||
{ value: 'title', label: 'Title A-Z' },
|
||||
{ value: 'duration', label: 'Duration' },
|
||||
{ value: 'rating', label: 'Highest Rated' }
|
||||
];
|
||||
|
||||
export function WebinarsPage() {
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [selectedCategory, setSelectedCategory] = useState('All Categories');
|
||||
const [selectedFormat, setSelectedFormat] = useState('All Formats');
|
||||
const [selectedLevel, setSelectedLevel] = useState('All Levels');
|
||||
const [selectedStatus, setSelectedStatus] = useState('All Status');
|
||||
const [sortBy, setSortBy] = useState('Most Popular');
|
||||
const [viewType, setViewType] = useState<'grid' | 'list'>('grid');
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const webinarsPerPage = 9;
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Filter and sort webinars
|
||||
const filteredWebinars = webinarsData.filter(webinar => {
|
||||
const matchesSearch = webinar.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
webinar.description.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
webinar.presenter.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
webinar.tags.some(tag => tag.toLowerCase().includes(searchTerm.toLowerCase()));
|
||||
const matchesCategory = selectedCategory === 'All Categories' || webinar.category === selectedCategory;
|
||||
const matchesFormat = selectedFormat === 'All Formats' || webinar.format === selectedFormat;
|
||||
const matchesLevel = selectedLevel === 'All Levels' || webinar.level === selectedLevel;
|
||||
const matchesStatus = selectedStatus === 'All Status' ||
|
||||
webinar.status === selectedStatus ||
|
||||
(selectedStatus === 'Featured' && webinar.featured);
|
||||
|
||||
return matchesSearch && matchesCategory && matchesFormat && matchesLevel && matchesStatus;
|
||||
}).sort((a, b) => {
|
||||
switch (sortBy) {
|
||||
case 'newest':
|
||||
return new Date(b.date).getTime() - new Date(a.date).getTime();
|
||||
case 'oldest':
|
||||
return new Date(a.date).getTime() - new Date(b.date).getTime();
|
||||
case 'title':
|
||||
return a.title.localeCompare(b.title);
|
||||
case 'duration':
|
||||
return parseInt(b.duration) - parseInt(a.duration);
|
||||
case 'rating':
|
||||
return b.rating - a.rating;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
// Statistics
|
||||
const stats = {
|
||||
total: webinarsData.length,
|
||||
available: webinarsData.filter(w => w.status === 'Available').length,
|
||||
live: webinarsData.filter(w => w.status === 'Live').length,
|
||||
upcoming: webinarsData.filter(w => w.status === 'Upcoming').length,
|
||||
recorded: webinarsData.filter(w => w.status === 'Recorded').length,
|
||||
featured: webinarsData.filter(w => w.featured).length,
|
||||
avgRating: (webinarsData.reduce((sum, w) => sum + w.rating, 0) / webinarsData.length).toFixed(1)
|
||||
};
|
||||
|
||||
// Paginate results
|
||||
const totalPages = Math.ceil(filteredWebinars.length / webinarsPerPage);
|
||||
const currentWebinars = filteredWebinars.slice((currentPage - 1) * webinarsPerPage, currentPage * webinarsPerPage);
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
return new Date(dateString).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
});
|
||||
};
|
||||
|
||||
const clearAllFilters = () => {
|
||||
setSearchTerm('');
|
||||
setSelectedCategory('All Categories');
|
||||
setSelectedFormat('All Formats');
|
||||
setSelectedLevel('All Levels');
|
||||
setSelectedStatus('All Status');
|
||||
setSortBy('Most Popular');
|
||||
};
|
||||
|
||||
const hasActiveFilters = searchTerm ||
|
||||
selectedCategory !== 'All Categories' ||
|
||||
selectedFormat !== 'All Formats' ||
|
||||
selectedLevel !== 'All Levels' ||
|
||||
selectedStatus !== 'All Status';
|
||||
|
||||
const renderStars = (rating: number) => {
|
||||
return Array.from({ length: 5 }, (_, i) => (
|
||||
<span key={i} className={`text-sm ${i < Math.floor(rating) ? 'text-yellow-400' : 'text-gray-300'}`}>★</span>
|
||||
));
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ backgroundColor: '#FFFFFF' }}>
|
||||
{/* Hero Section with Background Image */}
|
||||
<section className="relative h-[500px] overflow-hidden">
|
||||
{/* Background Image */}
|
||||
<div className="absolute inset-0">
|
||||
<ImageWithFallback
|
||||
src="https://images.unsplash.com/photo-1552664730-d307ca884978?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxidXNpbmVzcyUyMHByZXNlbnRhdGlvbiUyMHdlYmluYXIlMjBjb25mZXJlbmNlfGVufDF8fHx8MTc1NTg1NDI3MHww&ixlib=rb-4.1.0&q=80&w=1080"
|
||||
alt="Professional webinar and conference presentation"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-black/60" />
|
||||
</div>
|
||||
|
||||
{/* Hero Content */}
|
||||
<div className="relative h-full flex flex-col justify-center section-margin-x">
|
||||
<div className="text-center max-w-4xl mx-auto">
|
||||
<div className="branded-tag-system-white mb-6">
|
||||
<span className="dot"></span>
|
||||
<span className="text">Expert Leadership Insights</span>
|
||||
</div>
|
||||
|
||||
<h1 className="text-h1-white mb-6">
|
||||
Transform Your Leadership<br />
|
||||
Through Expert Webcasts
|
||||
</h1>
|
||||
|
||||
<p className="text-body-lg-white max-w-3xl mx-auto mb-8">
|
||||
Access world-class leadership development content from industry experts.
|
||||
Join thousands of leaders who are advancing their skills through our
|
||||
comprehensive webcast library.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
|
||||
<PrimaryCTAButton
|
||||
text="Browse All Webcasts"
|
||||
onClick={() => {
|
||||
const filtersSection = document.getElementById('webinars-filters');
|
||||
if (filtersSection) {
|
||||
filtersSection.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
}}
|
||||
className="cta-text-black"
|
||||
/>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="lg"
|
||||
className="bg-white/10 text-white border-white/30 hover:bg-white/20 backdrop-blur-sm"
|
||||
onClick={() => navigateTo('/contact?topic=webinars')}
|
||||
>
|
||||
<Calendar className="w-5 h-5 mr-2" />
|
||||
Schedule a Session
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Statistics Strip at Bottom */}
|
||||
<div className="absolute bottom-0 left-0 right-0">
|
||||
<div className="bg-black/80 backdrop-blur-sm px-8 py-6">
|
||||
<div className="section-margin-x">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-8 text-center">
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">{stats.total}+</div>
|
||||
<div className="text-small-white">Expert Webcasts</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">{stats.live + stats.upcoming}</div>
|
||||
<div className="text-small-white">Live & Upcoming</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">25K+</div>
|
||||
<div className="text-small-white">Total Attendees</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">{stats.avgRating}/5</div>
|
||||
<div className="text-small-white">Average Rating</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Search and Controls Section */}
|
||||
<section id="webinars-filters" className="py-8" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="section-margin-x">
|
||||
{/* Search and View Controls */}
|
||||
<div className="flex flex-col lg:flex-row gap-6 mb-8">
|
||||
{/* Search Bar */}
|
||||
<div className="relative max-w-md flex-1">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Search webcasts, presenters, topics..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="pl-10 pr-4 py-3 text-body rounded-lg border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 transition-all duration-200 w-full bg-gray-50"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
height: '48px'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Controls */}
|
||||
<div className="flex items-center gap-4">
|
||||
{/* View Toggle */}
|
||||
<div className="flex items-center border border-gray-300 rounded-lg overflow-hidden">
|
||||
<button
|
||||
onClick={() => setViewType('grid')}
|
||||
className={`p-2 transition-colors ${
|
||||
viewType === 'grid'
|
||||
? 'text-white'
|
||||
: 'bg-white text-gray-600 hover:bg-gray-50'
|
||||
}`}
|
||||
style={{
|
||||
backgroundColor: viewType === 'grid' ? 'var(--color-primary)' : undefined
|
||||
}}
|
||||
aria-label="Grid view"
|
||||
>
|
||||
<Grid className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setViewType('list')}
|
||||
className={`p-2 transition-colors ${
|
||||
viewType === 'list'
|
||||
? 'text-white'
|
||||
: 'bg-white text-gray-600 hover:bg-gray-50'
|
||||
}`}
|
||||
style={{
|
||||
backgroundColor: viewType === 'list' ? 'var(--color-primary)' : undefined
|
||||
}}
|
||||
aria-label="List view"
|
||||
>
|
||||
<List className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Sort */}
|
||||
<Select value={sortBy} onValueChange={setSortBy}>
|
||||
<SelectTrigger className="w-48 text-body">
|
||||
<SelectValue placeholder="Sort by" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{sortOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-5 gap-4 mb-6">
|
||||
<Select value={selectedCategory} onValueChange={setSelectedCategory}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Category" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{categories.map((category) => (
|
||||
<SelectItem key={category} value={category}>
|
||||
{category}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<Select value={selectedFormat} onValueChange={setSelectedFormat}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Format" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{formats.map((format) => (
|
||||
<SelectItem key={format} value={format}>
|
||||
{format}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<Select value={selectedLevel} onValueChange={setSelectedLevel}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Level" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{levels.map((level) => (
|
||||
<SelectItem key={level} value={level}>
|
||||
{level}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<Select value={selectedStatus} onValueChange={setSelectedStatus}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Status" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{statuses.map((status) => (
|
||||
<SelectItem key={status} value={status}>
|
||||
{status}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
{hasActiveFilters && (
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={clearAllFilters}
|
||||
className="text-gray-600 hover:text-gray-800"
|
||||
>
|
||||
<X className="w-4 h-4 mr-2" />
|
||||
Clear All
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="text-small text-gray-600 mb-8">
|
||||
Showing {currentWebinars.length} of {filteredWebinars.length} webcasts
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Webinars Content */}
|
||||
<section className="pb-16" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="section-margin-x">
|
||||
{currentWebinars.length === 0 ? (
|
||||
<div className="text-center py-16">
|
||||
<div className="w-16 h-16 mx-auto mb-4 bg-gray-100 rounded-full flex items-center justify-center">
|
||||
<Search className="w-8 h-8 text-gray-400" />
|
||||
</div>
|
||||
<h3 className="text-h4 mb-4">No webcasts found</h3>
|
||||
<p className="text-body text-gray-600 mb-6">
|
||||
Try adjusting your filters or search terms to find relevant content.
|
||||
</p>
|
||||
<Button onClick={clearAllFilters} variant="outline">
|
||||
Clear All Filters
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{/* Grid View */}
|
||||
{viewType === 'grid' && (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{currentWebinars.map((webinar) => (
|
||||
<Card
|
||||
key={webinar.id}
|
||||
className="overflow-hidden hover:shadow-xl transition-all duration-300 cursor-pointer group border-0 shadow-lg"
|
||||
onClick={() => navigateTo(`/webinar/${webinar.slug}`)}
|
||||
>
|
||||
<div className="aspect-video w-full bg-gray-100 overflow-hidden relative">
|
||||
<ImageWithFallback
|
||||
src={webinar.thumbnail}
|
||||
alt={webinar.title}
|
||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
||||
/>
|
||||
|
||||
{/* Status Badge */}
|
||||
<div className="absolute top-4 left-4">
|
||||
{webinar.status === 'Live' && (
|
||||
<Badge className="bg-red-600 text-white border-red-600 animate-pulse">
|
||||
LIVE
|
||||
</Badge>
|
||||
)}
|
||||
{webinar.status === 'Upcoming' && (
|
||||
<Badge className="bg-blue-600 text-white border-blue-600">
|
||||
Upcoming
|
||||
</Badge>
|
||||
)}
|
||||
{webinar.status === 'Recorded' && (
|
||||
<Badge className="bg-green-600 text-white border-green-600">
|
||||
Replay Available
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{webinar.featured && (
|
||||
<div className="absolute top-4 right-4">
|
||||
<Badge className="bg-yellow-500 text-white border-yellow-500">
|
||||
<Award className="w-3 h-3 mr-1" />
|
||||
Featured
|
||||
</Badge>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Play Button Overlay */}
|
||||
<div className="absolute inset-0 bg-black bg-opacity-40 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-300">
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="lg"
|
||||
className="bg-white text-black hover:bg-gray-100"
|
||||
>
|
||||
<Play className="w-5 h-5 mr-2" />
|
||||
{webinar.status === 'Live' ? 'Join Live' : webinar.status === 'Upcoming' ? 'Register' : 'Watch'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CardContent className="p-6">
|
||||
{/* Meta Info */}
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<Badge variant="outline" className="text-xs">
|
||||
{webinar.format}
|
||||
</Badge>
|
||||
<Badge variant="outline" className="text-xs">
|
||||
{webinar.level}
|
||||
</Badge>
|
||||
<div className="flex items-center gap-1 ml-auto">
|
||||
{renderStars(webinar.rating)}
|
||||
<span className="text-xs text-gray-600 ml-1">{webinar.rating}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 className="text-h4 mb-3 group-hover:text-blue-600 transition-colors line-clamp-2">
|
||||
{webinar.title}
|
||||
</h3>
|
||||
|
||||
<p className="text-small text-gray-600 mb-4 line-clamp-2">
|
||||
{webinar.description}
|
||||
</p>
|
||||
|
||||
{/* Presenter Info */}
|
||||
<div className="flex items-center mb-4">
|
||||
<div className="w-10 h-10 bg-gray-200 rounded-full flex items-center justify-center mr-3">
|
||||
<Users className="w-5 h-5 text-gray-400" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-small font-medium">{webinar.presenter}</div>
|
||||
<div className="text-xs text-gray-500">{webinar.presenterTitle}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Details */}
|
||||
<div className="flex items-center justify-between text-small text-gray-500 mb-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-1">
|
||||
<Clock className="w-4 h-4" />
|
||||
<span>{webinar.duration}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Users className="w-4 h-4" />
|
||||
<span>{webinar.attendees}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Globe className="w-4 h-4" />
|
||||
<span>{webinar.format}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Date and Price */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="text-small text-gray-600">
|
||||
{formatDate(webinar.date)}
|
||||
</div>
|
||||
<div className="text-small font-medium text-green-600">
|
||||
{webinar.price}
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* List View */}
|
||||
{viewType === 'list' && (
|
||||
<div className="space-y-6">
|
||||
{currentWebinars.map((webinar) => (
|
||||
<Card
|
||||
key={webinar.id}
|
||||
className="overflow-hidden hover:shadow-xl transition-all duration-300 cursor-pointer border-0 shadow-lg"
|
||||
onClick={() => navigateTo(`/webinar/${webinar.slug}`)}
|
||||
>
|
||||
<div className="flex">
|
||||
<div className="w-80 h-48 bg-gray-100 overflow-hidden relative flex-shrink-0">
|
||||
<ImageWithFallback
|
||||
src={webinar.thumbnail}
|
||||
alt={webinar.title}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
{webinar.featured && (
|
||||
<div className="absolute top-4 right-4">
|
||||
<Badge className="bg-yellow-500 text-white border-yellow-500">
|
||||
<Award className="w-3 h-3 mr-1" />
|
||||
Featured
|
||||
</Badge>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<CardContent className="flex-1 p-6">
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="outline" className="text-xs">
|
||||
{webinar.format}
|
||||
</Badge>
|
||||
<Badge variant="outline" className="text-xs">
|
||||
{webinar.level}
|
||||
</Badge>
|
||||
{webinar.status === 'Live' && (
|
||||
<Badge className="bg-red-600 text-white border-red-600 animate-pulse">
|
||||
LIVE
|
||||
</Badge>
|
||||
)}
|
||||
{webinar.status === 'Upcoming' && (
|
||||
<Badge className="bg-blue-600 text-white border-blue-600">
|
||||
Upcoming
|
||||
</Badge>
|
||||
)}
|
||||
{webinar.status === 'Recorded' && (
|
||||
<Badge className="bg-green-600 text-white border-green-600">
|
||||
Replay Available
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
{renderStars(webinar.rating)}
|
||||
<span className="text-xs text-gray-600 ml-1">{webinar.rating}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 className="text-h4 mb-3 hover:text-blue-600 transition-colors">
|
||||
{webinar.title}
|
||||
</h3>
|
||||
|
||||
<p className="text-small text-gray-600 mb-4">
|
||||
{webinar.description}
|
||||
</p>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-6 text-small text-gray-500">
|
||||
<div className="flex items-center gap-1">
|
||||
<Clock className="w-4 h-4" />
|
||||
<span>{webinar.duration}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Users className="w-4 h-4" />
|
||||
<span>{webinar.attendees}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span>By {webinar.presenter}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="text-small text-gray-600">
|
||||
{formatDate(webinar.date)}
|
||||
</div>
|
||||
<div className="text-small font-medium text-green-600">
|
||||
{webinar.price}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Pagination */}
|
||||
{totalPages > 1 && (
|
||||
<div className="flex items-center justify-center gap-2 mt-12">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setCurrentPage(prev => Math.max(1, prev - 1))}
|
||||
disabled={currentPage === 1}
|
||||
>
|
||||
<ChevronLeft className="w-4 h-4" />
|
||||
Previous
|
||||
</Button>
|
||||
|
||||
<div className="flex items-center gap-1">
|
||||
{Array.from({ length: Math.min(totalPages, 7) }, (_, i) => {
|
||||
let page;
|
||||
if (totalPages <= 7) {
|
||||
page = i + 1;
|
||||
} else if (currentPage <= 4) {
|
||||
page = i + 1;
|
||||
} else if (currentPage >= totalPages - 3) {
|
||||
page = totalPages - 6 + i;
|
||||
} else {
|
||||
page = currentPage - 3 + i;
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
key={page}
|
||||
variant={currentPage === page ? "default" : "outline"}
|
||||
size="sm"
|
||||
onClick={() => setCurrentPage(page)}
|
||||
className="w-10 h-10 p-0"
|
||||
style={{
|
||||
backgroundColor: currentPage === page ? 'var(--color-primary)' : undefined,
|
||||
color: currentPage === page ? 'white' : undefined
|
||||
}}
|
||||
>
|
||||
{page}
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setCurrentPage(prev => Math.min(totalPages, prev + 1))}
|
||||
disabled={currentPage === totalPages}
|
||||
>
|
||||
Next
|
||||
<ChevronRight className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA Section */}
|
||||
<section className="py-16 bg-gray-50">
|
||||
<div className="section-margin-x text-center">
|
||||
<div className="max-w-3xl mx-auto">
|
||||
<h2 className="text-h2 mb-6">Ready to Transform Your Leadership?</h2>
|
||||
<p className="text-body-lg text-gray-600 mb-8">
|
||||
Join thousands of professionals who are advancing their careers through our expert-led webcasts.
|
||||
Start your leadership transformation today.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
|
||||
<PrimaryCTAButton
|
||||
text="Get Started Today"
|
||||
onClick={() => navigateTo('/contact?topic=leadership-development')}
|
||||
className="cta-text-black"
|
||||
/>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="lg"
|
||||
onClick={() => navigateTo('/about/our-expertise')}
|
||||
>
|
||||
<TrendingUp className="w-5 h-5 mr-2" />
|
||||
Learn More About Us
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
221
src/components/WhitepapersSection.tsx
Normal file
@@ -0,0 +1,221 @@
|
||||
import { motion } from "motion/react";
|
||||
import { FileText, Target, BarChart3, Download, Eye } from "lucide-react";
|
||||
import { BrandedTag } from "./about/BrandedTag";
|
||||
import { PrimaryCTAButton } from "./PrimaryCTAButton";
|
||||
import { navigateTo } from "./Router";
|
||||
|
||||
interface Resource {
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
type: string;
|
||||
category: string;
|
||||
icon: any;
|
||||
color: string;
|
||||
}
|
||||
|
||||
const resources: Resource[] = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Leadership Assessment Framework",
|
||||
description: "Comprehensive self-leadership evaluation tool with detailed scoring methodology.",
|
||||
type: "PDF Guide",
|
||||
category: "Stage",
|
||||
icon: FileText,
|
||||
color: "#8B5CF6"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Executive Decision Making Toolkit",
|
||||
description: "Strategic frameworks and templates for complex organizational decision making.",
|
||||
type: "Toolkit",
|
||||
category: "B Frame",
|
||||
icon: Target,
|
||||
color: "#3B82F6"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Team Performance Metrics Dashboard",
|
||||
description: "Ready-to-use Excel template for tracking and improving team performance indicators.",
|
||||
type: "Excel Template",
|
||||
category: "Dashboard",
|
||||
icon: BarChart3,
|
||||
color: "#10B981"
|
||||
}
|
||||
];
|
||||
|
||||
interface ResourceCardProps {
|
||||
resource: Resource;
|
||||
index: number;
|
||||
}
|
||||
|
||||
function ResourceCard({ resource, index }: ResourceCardProps) {
|
||||
const IconComponent = resource.icon;
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
className="bg-white rounded-xl border border-gray-100 p-8 hover:shadow-lg transition-all duration-300 group"
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: index * 0.1 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
{/* Icon and Category */}
|
||||
<div className="flex items-center justify-center mb-6">
|
||||
<div
|
||||
className="w-16 h-16 rounded-xl flex items-center justify-center"
|
||||
style={{ backgroundColor: `${resource.color}20` }}
|
||||
>
|
||||
<IconComponent
|
||||
className="w-8 h-8"
|
||||
style={{ color: resource.color }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Type and Category Tags */}
|
||||
<div className="flex items-center justify-center gap-2 mb-4">
|
||||
<span
|
||||
className="px-3 py-1 text-xs font-semibold rounded-full"
|
||||
style={{
|
||||
backgroundColor: `${resource.color}15`,
|
||||
color: resource.color
|
||||
}}
|
||||
>
|
||||
{resource.type}
|
||||
</span>
|
||||
<span
|
||||
className="px-3 py-1 text-xs font-semibold rounded-full"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-brand-primary)',
|
||||
color: 'white'
|
||||
}}
|
||||
>
|
||||
{resource.category}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<h3
|
||||
className="text-xl font-bold text-center mb-4 leading-tight"
|
||||
style={{
|
||||
color: 'var(--color-brand-black)',
|
||||
display: '-webkit-box',
|
||||
WebkitLineClamp: 2,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
overflow: 'hidden',
|
||||
minHeight: '3.5rem'
|
||||
}}
|
||||
>
|
||||
{resource.title}
|
||||
</h3>
|
||||
|
||||
{/* Description */}
|
||||
<p
|
||||
className="text-sm text-center leading-relaxed mb-8"
|
||||
style={{ color: '#6B7280' }}
|
||||
>
|
||||
{resource.description}
|
||||
</p>
|
||||
|
||||
{/* Download Button - Updated to redirect to learning resources */}
|
||||
<button
|
||||
className="w-full flex items-center justify-center space-x-2 px-6 py-3 font-semibold text-sm transition-all duration-300 hover:shadow-lg hover:-translate-y-1"
|
||||
style={{
|
||||
borderRadius: '10px',
|
||||
backgroundColor: 'var(--color-brand-primary)',
|
||||
color: 'white'
|
||||
}}
|
||||
onClick={() => navigateTo('/learning/articles')}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.backgroundColor = 'var(--color-brand-accent)';
|
||||
e.currentTarget.style.color = 'var(--color-brand-black)';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.backgroundColor = 'var(--color-brand-primary)';
|
||||
e.currentTarget.style.color = 'white';
|
||||
}}
|
||||
>
|
||||
<Download className="w-4 h-4" />
|
||||
<span>Free Download</span>
|
||||
</button>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
export function WhitepapersSection() {
|
||||
return (
|
||||
<section
|
||||
className="py-20"
|
||||
style={{ backgroundColor: 'var(--color-brand-bg-light)' }}
|
||||
>
|
||||
<div className="max-w-7xl mx-auto section-margin-x">
|
||||
{/* Section Header */}
|
||||
<div className="mb-16">
|
||||
{/* Branded Tag */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<BrandedTag text="Free Leadership Resources" />
|
||||
</motion.div>
|
||||
|
||||
{/* Heading and CTA Container */}
|
||||
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between mb-6 gap-6">
|
||||
{/* Main Heading */}
|
||||
<motion.h2
|
||||
className="text-5xl font-bold leading-tight max-lg:text-4xl max-md:text-3xl"
|
||||
style={{ color: 'var(--color-brand-black)' }}
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.7, delay: 0.2 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
Free Leadership Downloads
|
||||
</motion.h2>
|
||||
|
||||
{/* Browse All Resources Button - Updated to redirect to articles */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.4 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<PrimaryCTAButton
|
||||
text="Browse All Resources"
|
||||
onClick={() => navigateTo('/learning/articles')}
|
||||
ariaLabel="Browse all leadership resources"
|
||||
className="browse-resources-cta-override"
|
||||
/>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<motion.p
|
||||
className="text-lg leading-relaxed max-w-2xl"
|
||||
style={{ color: '#6B7280' }}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.6 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
Access our collection of premium leadership tools, frameworks, and templates at no cost. Start implementing proven leadership strategies today.
|
||||
</motion.p>
|
||||
</div>
|
||||
|
||||
{/* Resources Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{resources.map((resource, index) => (
|
||||
<ResourceCard
|
||||
key={resource.id}
|
||||
resource={resource}
|
||||
index={index}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
42
src/components/about/BrandedTag.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
interface BrandedTagProps {
|
||||
text: string;
|
||||
className?: string;
|
||||
textColor?: string;
|
||||
variant?: "default" | "white" | "next-steps";
|
||||
}
|
||||
|
||||
export function BrandedTag({
|
||||
text,
|
||||
className = "",
|
||||
textColor,
|
||||
variant = "default",
|
||||
}: BrandedTagProps) {
|
||||
let baseClass = "branded-tag-system";
|
||||
let defaultTextColor = "#26231A"; // Default to black
|
||||
|
||||
if (variant === "white") {
|
||||
baseClass = "branded-tag-system-white";
|
||||
defaultTextColor = "#ffffff"; // White text for dark backgrounds
|
||||
} else if (variant === "next-steps") {
|
||||
baseClass = "branded-tag-system-next-steps";
|
||||
defaultTextColor = "#26231A"; // Brand black for NEXT STEPS
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${baseClass} ${className}`}
|
||||
style={{ marginBottom: "20px" }}
|
||||
>
|
||||
<div className="dot" />
|
||||
<span
|
||||
className="text"
|
||||
style={{
|
||||
color: textColor || defaultTextColor,
|
||||
fontFamily: "var(--font-family-base)",
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
1080
src/components/about/OurExpertise.tsx
Normal file
739
src/components/about/OurImpact.tsx
Normal file
@@ -0,0 +1,739 @@
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { Button } from '../ui/button';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../ui/card';
|
||||
import { Badge } from '../ui/badge';
|
||||
import { ImageWithFallback } from '../figma/ImageWithFallback';
|
||||
import { TestimonialsSection } from '../TestimonialsSection';
|
||||
import { navigateTo } from '../Router';
|
||||
import { PrimaryCTAButton } from '../PrimaryCTAButton';
|
||||
import {
|
||||
TrendingUp,
|
||||
Users,
|
||||
Award,
|
||||
ArrowRight,
|
||||
Star,
|
||||
Quote,
|
||||
Building,
|
||||
Target,
|
||||
Globe,
|
||||
Sparkles,
|
||||
CheckCircle,
|
||||
BarChart3,
|
||||
PieChart,
|
||||
Activity
|
||||
} from 'lucide-react';
|
||||
// Import the actual logos from Figma assets (same as LogosSection)
|
||||
import imgImage36 from "figma:asset/6bdf8056f51bbdc6dd9dab9044a6579a254bd02c.png";
|
||||
import imgImage39 from "figma:asset/037c4659b7b0bf15b1dfdcd4868cb42e8257e838.png";
|
||||
import imgImage43 from "figma:asset/c57ec1f4466f68e607139a3cd6d52f7e2f372408.png";
|
||||
import imgImage45 from "figma:asset/4833274f0a593cd31fdefe553b70bb016de281af.png";
|
||||
import imgImage38 from "figma:asset/d5bab6ea4f3d8cef3b0425c45cfee7faea19fdbc.png";
|
||||
import imgImage47 from "figma:asset/e8fad960112d5eba554c3969d08891ebe4d4b9c7.png";
|
||||
|
||||
const impactStats = [
|
||||
{
|
||||
number: 26643,
|
||||
suffix: "",
|
||||
label: "PARTICIPANTS",
|
||||
description: "Leaders developed across all programs",
|
||||
icon: Users,
|
||||
bgColor: "bg-primary/10"
|
||||
},
|
||||
{
|
||||
number: 1220,
|
||||
suffix: "",
|
||||
label: "PROGRAMS",
|
||||
description: "Successfully delivered initiatives",
|
||||
icon: Target,
|
||||
bgColor: "bg-primary/15"
|
||||
},
|
||||
{
|
||||
number: 152,
|
||||
suffix: "",
|
||||
label: "CLIENTS",
|
||||
description: "Organizations transformed",
|
||||
icon: Building,
|
||||
bgColor: "bg-primary/20"
|
||||
}
|
||||
];
|
||||
|
||||
const clientLogos = [
|
||||
{
|
||||
name: "CANMOOR",
|
||||
logo: imgImage36,
|
||||
sector: "Financial Services",
|
||||
width: 302,
|
||||
height: 54
|
||||
},
|
||||
{
|
||||
name: "BlackRock",
|
||||
logo: imgImage45,
|
||||
sector: "Investment Management",
|
||||
width: 210,
|
||||
height: 54
|
||||
},
|
||||
{
|
||||
name: "Royal London",
|
||||
logo: imgImage38,
|
||||
sector: "Insurance",
|
||||
width: 145,
|
||||
height: 54
|
||||
},
|
||||
{
|
||||
name: "Abstract",
|
||||
logo: imgImage39,
|
||||
sector: "Technology",
|
||||
width: 172,
|
||||
height: 54
|
||||
},
|
||||
{
|
||||
name: "ARES",
|
||||
logo: imgImage47,
|
||||
sector: "Asset Management",
|
||||
width: 163,
|
||||
height: 54
|
||||
},
|
||||
{
|
||||
name: "KADANS",
|
||||
logo: imgImage43,
|
||||
sector: "Real Estate",
|
||||
width: 206,
|
||||
height: 54
|
||||
},
|
||||
// Add repeats for more variety
|
||||
{
|
||||
name: "CANMOOR",
|
||||
logo: imgImage36,
|
||||
sector: "Financial Services",
|
||||
width: 302,
|
||||
height: 54
|
||||
},
|
||||
{
|
||||
name: "BlackRock",
|
||||
logo: imgImage45,
|
||||
sector: "Investment Management",
|
||||
width: 210,
|
||||
height: 54
|
||||
},
|
||||
{
|
||||
name: "Royal London",
|
||||
logo: imgImage38,
|
||||
sector: "Insurance",
|
||||
width: 145,
|
||||
height: 54
|
||||
},
|
||||
{
|
||||
name: "Abstract",
|
||||
logo: imgImage39,
|
||||
sector: "Technology",
|
||||
width: 172,
|
||||
height: 54
|
||||
},
|
||||
{
|
||||
name: "ARES",
|
||||
logo: imgImage47,
|
||||
sector: "Asset Management",
|
||||
width: 163,
|
||||
height: 54
|
||||
},
|
||||
{
|
||||
name: "KADANS",
|
||||
logo: imgImage43,
|
||||
sector: "Real Estate",
|
||||
width: 206,
|
||||
height: 54
|
||||
},
|
||||
{
|
||||
name: "CANMOOR",
|
||||
logo: imgImage36,
|
||||
sector: "Financial Services",
|
||||
width: 302,
|
||||
height: 54
|
||||
},
|
||||
{
|
||||
name: "BlackRock",
|
||||
logo: imgImage45,
|
||||
sector: "Investment Management",
|
||||
width: 210,
|
||||
height: 54
|
||||
},
|
||||
{
|
||||
name: "Royal London",
|
||||
logo: imgImage38,
|
||||
sector: "Insurance",
|
||||
width: 145,
|
||||
height: 54
|
||||
}
|
||||
];
|
||||
|
||||
const testimonialHighlights = [
|
||||
{
|
||||
quote: "KLC's leadership program transformed our entire management approach. The ROI was evident within 6 months.",
|
||||
author: "Rajesh Kumar",
|
||||
position: "CEO, TechVision Ltd",
|
||||
program: "Executive Leadership Program",
|
||||
image: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=150&h=150&fit=crop",
|
||||
rating: 5
|
||||
},
|
||||
{
|
||||
quote: "The practical frameworks and peer learning experience exceeded our expectations. Highly recommend KLC.",
|
||||
author: "Priya Sharma",
|
||||
position: "VP Operations, Global Manufacturing",
|
||||
program: "Team Leadership Intensive",
|
||||
image: "https://images.unsplash.com/photo-1494790108755-2616b612b47c?w=150&h=150&fit=crop",
|
||||
rating: 5
|
||||
},
|
||||
{
|
||||
quote: "KLC's consulting approach helped us navigate complex organizational change with remarkable success.",
|
||||
author: "Dr. Amit Patel",
|
||||
position: "Director, Healthcare Innovation",
|
||||
program: "Organizational Consulting",
|
||||
image: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=150&h=150&fit=crop",
|
||||
rating: 5
|
||||
}
|
||||
];
|
||||
|
||||
// Animated counter component
|
||||
function AnimatedCounter({
|
||||
targetValue,
|
||||
suffix = "",
|
||||
duration = 2000,
|
||||
isVisible = false
|
||||
}: {
|
||||
targetValue: number;
|
||||
suffix?: string;
|
||||
duration?: number;
|
||||
isVisible?: boolean;
|
||||
}) {
|
||||
const [currentValue, setCurrentValue] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isVisible) return;
|
||||
|
||||
const startTime = Date.now();
|
||||
const animate = () => {
|
||||
const elapsed = Date.now() - startTime;
|
||||
const progress = Math.min(elapsed / duration, 1);
|
||||
|
||||
// Easing function for smooth animation
|
||||
const easeOutExpo = progress === 1 ? 1 : 1 - Math.pow(2, -10 * progress);
|
||||
const newValue = Math.floor(targetValue * easeOutExpo);
|
||||
|
||||
setCurrentValue(newValue);
|
||||
|
||||
if (progress < 1) {
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
};
|
||||
|
||||
requestAnimationFrame(animate);
|
||||
}, [isVisible, targetValue, duration]);
|
||||
|
||||
return (
|
||||
<span>
|
||||
{currentValue.toLocaleString()}{suffix}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
// Intersection Observer hook for triggering animations
|
||||
function useIntersectionObserver(options = {}) {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const elementRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
if (entry.isIntersecting) {
|
||||
setIsVisible(true);
|
||||
}
|
||||
},
|
||||
{ threshold: 0.3, ...options }
|
||||
);
|
||||
|
||||
if (elementRef.current) {
|
||||
observer.observe(elementRef.current);
|
||||
}
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
return [elementRef, isVisible] as const;
|
||||
}
|
||||
|
||||
export function OurImpact() {
|
||||
const [statsRef, statsVisible] = useIntersectionObserver();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = 'Our Impact - About Us | KLC';
|
||||
window.scrollTo(0, 0);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
{/* Hero Section - Services Page Style */}
|
||||
<section className="relative min-h-[90vh] flex flex-col">
|
||||
<div className="absolute inset-0 z-0">
|
||||
<ImageWithFallback
|
||||
src="https://images.unsplash.com/photo-1460472178825-e5240623afd5?w=1920&h=1080&fit=crop"
|
||||
alt="Measuring leadership impact and organizational transformation"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-black/80 via-black/70 to-black/60"></div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 flex-1 flex items-center">
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="text-center max-w-5xl mx-auto">
|
||||
<div className="branded-tag-system-white mb-8 justify-center">
|
||||
<div className="dot"></div>
|
||||
<span className="text">Knowledge Leadership Centre</span>
|
||||
</div>
|
||||
|
||||
<h1 className="text-h1-white mb-6">
|
||||
Our Impact
|
||||
</h1>
|
||||
|
||||
<p className="text-body-lg-white mb-8 max-w-2xl mx-auto">
|
||||
Real numbers. Real transformation. Discover the measurable difference we've made
|
||||
across industries and organizations worldwide through our leadership development expertise.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4 mb-12 justify-center">
|
||||
<PrimaryCTAButton
|
||||
text="View Success Stories"
|
||||
onClick={() => document.getElementById('testimonials')?.scrollIntoView({ behavior: 'smooth' })}
|
||||
className="management-dev-primary-cta"
|
||||
/>
|
||||
|
||||
<Button
|
||||
onClick={() => navigateTo('/contact')}
|
||||
className="management-dev-glassmorphic-btn text-body px-8 py-4 min-h-[52px] border text-white transition-all duration-300"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
<ArrowRight className="w-5 h-5 mr-2" />
|
||||
Get Started
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Statistics Section - Embedded in Hero */}
|
||||
<div className="relative z-10 border-t border-white/20 backdrop-blur-sm bg-black/20" ref={statsRef}>
|
||||
<div className="container mx-auto section-margin-x py-12">
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-8 text-center max-w-5xl mx-auto">
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">
|
||||
<AnimatedCounter
|
||||
targetValue={26643}
|
||||
isVisible={statsVisible}
|
||||
/>
|
||||
</div>
|
||||
<div className="text-small-white opacity-90">Participants Developed</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">
|
||||
<AnimatedCounter
|
||||
targetValue={1220}
|
||||
isVisible={statsVisible}
|
||||
/>
|
||||
</div>
|
||||
<div className="text-small-white opacity-90">Programs Delivered</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">
|
||||
<AnimatedCounter
|
||||
targetValue={152}
|
||||
isVisible={statsVisible}
|
||||
/>
|
||||
+
|
||||
</div>
|
||||
<div className="text-small-white opacity-90">Organizations Transformed</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">96%</div>
|
||||
<div className="text-small-white opacity-90">Client Satisfaction</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Partners Section - Redesigned According to JSON Config */}
|
||||
<section className="py-20" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="container mx-auto section-margin-x">
|
||||
|
||||
|
||||
{/* Statistics Section - Landing Page Style */}
|
||||
<div className="mb-20">
|
||||
<div className="grid lg:grid-cols-2 gap-16 items-center max-w-7xl mx-auto">
|
||||
|
||||
{/* Left Column - Descriptive Content */}
|
||||
<div>
|
||||
<h2 className="text-h2 mb-6 text-black">
|
||||
Trusted by Industry Leaders
|
||||
</h2>
|
||||
|
||||
|
||||
|
||||
{/* Industries We Serve */}
|
||||
<div className="mb-6">
|
||||
<h3 className="text-h3 mb-6">Industries We Serve</h3>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{[
|
||||
"Financial Services", "Investment Banking", "Asset Management", "Technology",
|
||||
"Healthcare", "Manufacturing", "Real Estate", "Consulting"
|
||||
].map((sector) => (
|
||||
<span
|
||||
key={sector}
|
||||
className="px-6 py-3 text-body text-gray-700 border border-primary rounded-full transition-all duration-300 hover:bg-primary hover:text-white hover:shadow-lg hover:scale-105 cursor-default group"
|
||||
style={{
|
||||
backgroundColor: '#FFFFFF',
|
||||
borderColor: 'var(--color-primary)',
|
||||
}}
|
||||
>
|
||||
<span className="group-hover:animate-pulse">{sector}</span>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Column - Statistics with Yellow Bullets */}
|
||||
<div className="space-y-8">
|
||||
{[
|
||||
{ value: "27,000+", label: "Leaders Developed" },
|
||||
{ value: "150+", label: "Corporate Clients" },
|
||||
{ value: "20+", label: "Countries Served" }
|
||||
].map((stat, i) => (
|
||||
<div
|
||||
key={stat.label}
|
||||
className={`flex items-center gap-6 ${i < 2 ? 'border-b border-gray-200 pb-8' : ''}`}
|
||||
style={{ animation: `fade-in-up 0.6s ease-out ${i * 0.1}s both` }}
|
||||
>
|
||||
{/* Yellow Bullet Point */}
|
||||
<div
|
||||
className="w-3 h-3 rounded-full flex-shrink-0"
|
||||
style={{ backgroundColor: 'var(--color-accent)' }}
|
||||
></div>
|
||||
|
||||
{/* Stats Content */}
|
||||
<div>
|
||||
<div className="stats-number text-primary mb-1">{stat.value}</div>
|
||||
<p className="text-body font-semibold text-black uppercase tracking-wide">
|
||||
{stat.label}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{/* Client Logos Grid */}
|
||||
<div className="mb-20">
|
||||
<h3 className="text-h3 text-center mb-12">Our Valued Partners</h3>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-8 max-w-6xl mx-auto">
|
||||
{clientLogos.slice(0, 16).map((client, index) => {
|
||||
// Calculate appropriate width based on original logo dimensions
|
||||
const scaledWidth = client.width > 200 ? Math.round(client.width * 0.7) : client.width * 1.1;
|
||||
const displayWidth = Math.max(120, Math.min(scaledWidth, 200));
|
||||
|
||||
return (
|
||||
<div
|
||||
key={`${client.name}-${index}`}
|
||||
className="group p-6 flex items-center justify-center h-24 transition-all duration-300 cursor-pointer border border-gray-100 rounded-xl hover:border-primary/30 hover:shadow-lg hover:-translate-y-1"
|
||||
>
|
||||
<img
|
||||
src={client.logo}
|
||||
alt={client.name}
|
||||
style={{
|
||||
width: `${displayWidth}px`,
|
||||
height: 'auto',
|
||||
maxHeight: '50px',
|
||||
objectFit: 'contain',
|
||||
filter: 'grayscale(100%) opacity(0.7)',
|
||||
transition: 'all 0.3s ease'
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.filter = 'grayscale(0%) opacity(1)';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.filter = 'grayscale(100%) opacity(0.7)';
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Partnership CTA Section - Two Column Layout */}
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<Card className="border-0 rounded-2xl overflow-hidden card-shadow-dramatic" style={{ backgroundColor: 'rgb(249, 250, 251)' }}>
|
||||
<CardContent className="p-0">
|
||||
<div className="grid lg:grid-cols-2 gap-0 items-center min-h-[400px]">
|
||||
|
||||
{/* Left Column - Partnership Graphic */}
|
||||
<div className="relative h-full min-h-[400px] overflow-hidden">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1739285452621-59d92842fcc8?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxidXNpbmVzcyUyMGhhbmRzaGFrZSUyMHBhcnRuZXJzaGlwJTIwcHJvZmVzc2lvbmFsJTIwbWVldGluZ3xlbnwxfHx8fDE3NTU1OTg0NTV8MA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral"
|
||||
alt="Partnership collaboration"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-primary/80 to-primary/40"></div>
|
||||
</div>
|
||||
|
||||
{/* Right Column - CTA Content */}
|
||||
<div className="p-12 flex flex-col justify-center bg-gray-50">
|
||||
|
||||
{/* Branded Tag */}
|
||||
<div className="mb-6">
|
||||
<div className="branded-tag-system">
|
||||
<div className="dot"></div>
|
||||
<span className="text">Partner With Us</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Heading */}
|
||||
<h3 className="text-h2 mb-6 text-black">
|
||||
Ready to Transform Your Leadership?
|
||||
</h3>
|
||||
|
||||
{/* Description */}
|
||||
<p className="text-body-lg text-muted mb-8 leading-relaxed">
|
||||
Join our network of successful organizations and unlock your team's full potential.
|
||||
<span className="text-primary font-semibold"> Get in touch</span> to start your development journey today.
|
||||
</p>
|
||||
|
||||
{/* Sticky CTA Button */}
|
||||
<div className="sticky top-8">
|
||||
<button
|
||||
onClick={() => navigateTo('/contact')}
|
||||
className="w-full px-8 py-4 rounded-lg font-semibold transition-all duration-300 hover:shadow-xl mb-4 group"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-primary)',
|
||||
color: '#FFFFFF',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
fontSize: '1.125rem',
|
||||
fontWeight: '600',
|
||||
border: 'none'
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.backgroundColor = '#030359';
|
||||
e.currentTarget.style.transform = 'translateY(-2px)';
|
||||
e.currentTarget.style.boxShadow = '0 12px 24px rgba(4, 4, 91, 0.3)';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.backgroundColor = 'var(--color-primary)';
|
||||
e.currentTarget.style.transform = 'translateY(0)';
|
||||
e.currentTarget.style.boxShadow = 'none';
|
||||
}}
|
||||
>
|
||||
<span className="flex items-center justify-center gap-2">
|
||||
Schedule a Partnership Discussion
|
||||
<ArrowRight className="w-5 h-5 group-hover:translate-x-1 transition-transform duration-300" />
|
||||
</span>
|
||||
</button>
|
||||
|
||||
{/* Supporting Information */}
|
||||
<div className="bg-white/80 backdrop-blur-sm rounded-lg p-4 border border-primary/20">
|
||||
<p className="text-small text-muted text-center leading-relaxed">
|
||||
Connect with our partnership specialists to explore collaboration opportunities
|
||||
and discuss your organization's specific leadership development needs.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Success Stories Section - Branded Testimonials */}
|
||||
<div id="testimonials">
|
||||
<TestimonialsSection
|
||||
customTestimonials={testimonialHighlights.map(testimonial => ({
|
||||
name: testimonial.author,
|
||||
role: testimonial.position,
|
||||
company: testimonial.program,
|
||||
avatar: testimonial.image,
|
||||
quote: testimonial.quote,
|
||||
rating: testimonial.rating,
|
||||
isVideo: false
|
||||
}))}
|
||||
title="What Our Clients Say"
|
||||
subtitle="Don't just take our word for it. Here's what leaders from our partner organizations have to say about their transformational experiences with KLC."
|
||||
tagText="Client Success"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Additional Metrics Section - Redesigned */}
|
||||
<section className="py-20" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="text-center mb-16">
|
||||
<Badge
|
||||
className="mb-6 text-body px-4 py-2"
|
||||
style={{
|
||||
backgroundColor: 'rgba(4, 4, 91, 0.1)',
|
||||
color: 'var(--color-primary)',
|
||||
border: 'none'
|
||||
}}
|
||||
>
|
||||
Transformation Metrics
|
||||
</Badge>
|
||||
<h2 className="text-h2 mb-6">
|
||||
Measuring Real Transformation
|
||||
</h2>
|
||||
<p className="text-body-lg text-muted max-w-3xl mx-auto leading-relaxed">
|
||||
Our impact extends far beyond statistics. We measure success through the
|
||||
lasting transformation we create in leadership capabilities, organizational
|
||||
culture, and sustainable business outcomes.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid lg:grid-cols-3 gap-8 max-w-6xl mx-auto">
|
||||
{[
|
||||
{
|
||||
icon: TrendingUp,
|
||||
metric: "85%",
|
||||
label: "Leadership Effectiveness",
|
||||
description: "Average improvement in leadership effectiveness scores within 6 months",
|
||||
details: ["Decision-making quality", "Team engagement", "Strategic thinking", "Change management"]
|
||||
},
|
||||
{
|
||||
icon: Award,
|
||||
metric: "92%",
|
||||
label: "Client Satisfaction",
|
||||
description: "Client satisfaction and program completion rate across all services",
|
||||
details: ["Program quality", "Facilitator expertise", "Content relevance", "Measurable outcomes"]
|
||||
},
|
||||
{
|
||||
icon: Globe,
|
||||
metric: "15+",
|
||||
label: "Global Reach",
|
||||
description: "Countries where our alumni now lead organizations and drive change",
|
||||
details: ["Global network", "Cultural diversity", "Best practices", "Knowledge transfer"]
|
||||
}
|
||||
].map((stat, index) => {
|
||||
const Icon = stat.icon;
|
||||
return (
|
||||
<Card
|
||||
key={index}
|
||||
className="border-0 shadow-sm hover:shadow-lg transition-all duration-300 group h-full"
|
||||
style={{ backgroundColor: '#FFFFFF' }}
|
||||
>
|
||||
<CardContent className="p-8">
|
||||
{/* Icon and Metric */}
|
||||
<div className="text-center mb-6">
|
||||
<div
|
||||
className="w-20 h-20 rounded-2xl flex items-center justify-center mx-auto mb-4 group-hover:scale-110 transition-transform duration-300"
|
||||
style={{ backgroundColor: 'rgba(4, 4, 91, 0.1)' }}
|
||||
>
|
||||
<Icon className="w-10 h-10 text-primary" />
|
||||
</div>
|
||||
<div className="text-4xl font-bold text-primary mb-2">
|
||||
{stat.metric}
|
||||
</div>
|
||||
<h3 className="text-h4 text-black font-semibold">
|
||||
{stat.label}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<p className="text-body text-muted leading-relaxed mb-6 text-center">
|
||||
{stat.description}
|
||||
</p>
|
||||
|
||||
{/* Details */}
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-subhead text-black font-medium">Key Areas:</h4>
|
||||
{stat.details.map((detail, detailIndex) => (
|
||||
<div key={detailIndex} className="flex items-center gap-3">
|
||||
<CheckCircle className="w-4 h-4 text-primary flex-shrink-0" />
|
||||
<span className="text-small text-muted">{detail}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA Section - Branded Banner Style */}
|
||||
<section className="relative h-[700px] overflow-hidden">
|
||||
{/* Background Image */}
|
||||
<div className="absolute inset-0">
|
||||
<ImageWithFallback
|
||||
src="https://images.unsplash.com/photo-1552664730-d307ca884978?w=2940&h=1200&fit=crop"
|
||||
alt="Professional team collaborating in modern office"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
|
||||
{/* Subtle dark overlay for overall image */}
|
||||
<div className="absolute inset-0 bg-black/30" />
|
||||
|
||||
{/* Gradient overlay for better text readability */}
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-black/20 via-transparent to-black/60" />
|
||||
</div>
|
||||
|
||||
{/* Content Container */}
|
||||
<div className="relative h-full flex items-center justify-end section-margin-x">
|
||||
{/* CTA Content Block */}
|
||||
<div
|
||||
className="bg-opacity-95 backdrop-blur-sm rounded-lg p-16 max-w-2xl"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-primary)'
|
||||
}}
|
||||
>
|
||||
{/* Branded Tag */}
|
||||
<div className="mb-6">
|
||||
<div className="branded-tag-system-white">
|
||||
<div className="dot"></div>
|
||||
<span className="text">
|
||||
Next Steps
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Headline */}
|
||||
<h2 className="text-h2-white leading-tight mb-8">
|
||||
Ready to Create Your Own Success Story?
|
||||
<span
|
||||
className="italic"
|
||||
style={{ color: 'var(--color-accent)' }}
|
||||
>
|
||||
{" "}Join the leaders{" "}
|
||||
</span>
|
||||
transforming their organizations.
|
||||
</h2>
|
||||
|
||||
{/* CTA Button */}
|
||||
<PrimaryCTAButton
|
||||
text="Explore Our Services"
|
||||
onClick={() => navigateTo('/services/leadership-development')}
|
||||
ariaLabel="Explore our leadership development services"
|
||||
className="cta-banner-yellow"
|
||||
/>
|
||||
|
||||
{/* Supporting Text */}
|
||||
<p className="text-body-white mt-6 opacity-90">
|
||||
Connect with our leadership experts to discover how KLC's proven methodologies
|
||||
can transform your organization's performance and culture.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
997
src/components/about/OurVision.tsx
Normal file
@@ -0,0 +1,997 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { navigateTo } from '../Router';
|
||||
import { PrimaryCTAButton } from '../PrimaryCTAButton';
|
||||
import { Button } from '../ui/button';
|
||||
import { Card, CardContent } from '../ui/card';
|
||||
import { Badge } from '../ui/badge';
|
||||
import { ImageWithFallback } from '../figma/ImageWithFallback';
|
||||
import { BrandedTag } from './BrandedTag';
|
||||
import {
|
||||
Eye,
|
||||
Target,
|
||||
Users,
|
||||
Brain,
|
||||
TrendingUp,
|
||||
Lightbulb,
|
||||
ArrowRight,
|
||||
CheckCircle,
|
||||
Globe,
|
||||
BarChart3,
|
||||
Layers,
|
||||
Compass
|
||||
} from 'lucide-react';
|
||||
|
||||
// Vision pillars data
|
||||
const visionPillars = [
|
||||
{
|
||||
title: "World-Class Institution in Leadership",
|
||||
description: "Establishing ourselves as a premier global institution recognized for excellence in leadership thought and practice, setting industry standards for development methodologies.",
|
||||
icon: Globe,
|
||||
gradient: "from-blue-600 to-blue-700",
|
||||
mainStat: {
|
||||
label: "Global Recognition",
|
||||
value: "+96%",
|
||||
color: "text-primary"
|
||||
},
|
||||
chartData: [
|
||||
{ label: "2020", value: 20 },
|
||||
{ label: "2021", value: 45 },
|
||||
{ label: "2022", value: 70 },
|
||||
{ label: "2023", value: 85 },
|
||||
{ label: "2024", value: 96 }
|
||||
],
|
||||
metrics: [
|
||||
{ label: "Excellence", value: "94%", color: "text-primary" },
|
||||
{ label: "Recognition", value: "91%", color: "text-primary" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Partnership-Driven Capacity Building",
|
||||
description: "Collaborating with institutions to build sustainable leadership capacity and capability that drives organizational transformation and long-term success.",
|
||||
icon: Users,
|
||||
gradient: "from-yellow-500 to-yellow-600",
|
||||
mainStat: {
|
||||
label: "Partnership Growth",
|
||||
value: "+78%",
|
||||
color: "text-primary"
|
||||
},
|
||||
progressMetrics: [
|
||||
{ label: "Partner Satisfaction", value: 82, color: "#F8C301" },
|
||||
{ label: "Capacity Building", value: 76, color: "#04045B" },
|
||||
{ label: "Retention Rate", value: 84, color: "#F8C301" }
|
||||
],
|
||||
metrics: []
|
||||
},
|
||||
{
|
||||
title: "Individual Leadership Discovery",
|
||||
description: "Facilitating individuals to discover and develop their personal leadership resources, unlocking their unique potential to create meaningful impact.",
|
||||
icon: Compass,
|
||||
gradient: "from-purple-600 to-indigo-600",
|
||||
mainStat: {
|
||||
label: "Development Speed",
|
||||
value: "65%",
|
||||
color: "text-primary"
|
||||
},
|
||||
topMetrics: [
|
||||
{ label: "Speed", value: "65%", color: "text-primary" },
|
||||
{ label: "Alignment", value: "89%", color: "text-primary" }
|
||||
],
|
||||
bottomMetrics: [
|
||||
{ label: "Quality", value: "91%", color: "text-primary" },
|
||||
{ label: "Time Reduction", value: "-65%", color: "text-primary" },
|
||||
{ label: "Impact", value: "89%", color: "text-primary" }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// Approach timeline data
|
||||
const approachSteps = [
|
||||
{
|
||||
title: "Client Context Alignment",
|
||||
description: "Deep understanding of unique organizational challenges, culture, and strategic objectives through comprehensive assessment and stakeholder engagement.",
|
||||
details: [
|
||||
"Organizational diagnostic and culture assessment",
|
||||
"Leadership capability gap analysis",
|
||||
"Strategic business alignment review",
|
||||
"Stakeholder needs analysis"
|
||||
],
|
||||
icon: Target
|
||||
},
|
||||
{
|
||||
title: "Research Anchored Methodology",
|
||||
description: "Evidence-based methodologies and frameworks grounded in behavioral science, leadership psychology, and proven best practices from global research.",
|
||||
details: [
|
||||
"Latest research in leadership effectiveness",
|
||||
"Behavioral science applications",
|
||||
"Validated assessment tools and frameworks",
|
||||
"Global best practice integration"
|
||||
],
|
||||
icon: Brain
|
||||
},
|
||||
{
|
||||
title: "Co-Created Program Design",
|
||||
description: "Collaborative program development with clients, ensuring solutions are tailored, practical, and aligned with specific organizational needs and constraints.",
|
||||
details: [
|
||||
"Joint design workshops with key stakeholders",
|
||||
"Customized learning journeys and pathways",
|
||||
"Practical application focus",
|
||||
"Measurable outcome definition"
|
||||
],
|
||||
icon: Lightbulb
|
||||
}
|
||||
];
|
||||
|
||||
// Leadership orientations preview
|
||||
const leadershipOrientations = [
|
||||
{
|
||||
orientation: "Thinking",
|
||||
description: "How leaders process information, make decisions, and approach complex challenges",
|
||||
aspects: ["Strategic thinking", "Systems thinking", "Critical analysis", "Creative problem-solving"]
|
||||
},
|
||||
{
|
||||
orientation: "Risk Appetite",
|
||||
description: "Leaders' comfort with uncertainty, innovation, and calculated risk-taking",
|
||||
aspects: ["Innovation tolerance", "Change agility", "Calculated risk-taking", "Uncertainty navigation"]
|
||||
},
|
||||
{
|
||||
orientation: "Power",
|
||||
description: "How leaders use influence, authority, and positional power to drive results",
|
||||
aspects: ["Influence strategies", "Authority dynamics", "Empowerment approaches", "Decision rights"]
|
||||
},
|
||||
{
|
||||
orientation: "Interpersonal/Political",
|
||||
description: "Leaders' ability to navigate relationships, coalitions, and organizational dynamics",
|
||||
aspects: ["Relationship building", "Coalition management", "Stakeholder engagement", "Political savvy"]
|
||||
}
|
||||
];
|
||||
|
||||
export function OurVision() {
|
||||
const [activeStep, setActiveStep] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
document.title = 'Our Vision - About Us | KLC';
|
||||
window.scrollTo(0, 0);
|
||||
}, []);
|
||||
|
||||
// Auto-advance timeline steps
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setActiveStep((prev) => (prev + 1) % approachSteps.length);
|
||||
}, 4000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
{/* Hero Section - Our Impact Page Style */}
|
||||
<section className="relative min-h-[90vh] flex flex-col">
|
||||
<div className="absolute inset-0 z-0">
|
||||
<ImageWithFallback
|
||||
src="https://images.unsplash.com/photo-1552664730-d307ca884978?w=1920&h=1080&fit=crop"
|
||||
alt="Leadership vision and transformation - Our Vision"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-black/80 via-black/70 to-black/60"></div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 flex-1 flex items-center">
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="text-center max-w-5xl mx-auto">
|
||||
<div className="branded-tag-system-white mb-8 justify-center">
|
||||
<div className="dot"></div>
|
||||
<span className="text">Knowledge Leadership Centre</span>
|
||||
</div>
|
||||
|
||||
<h1 className="text-h1-white mb-6">
|
||||
Our Vision
|
||||
</h1>
|
||||
|
||||
<p className="text-body-lg-white mb-8 max-w-2xl mx-auto">
|
||||
Building the next generation of transformational leaders who drive organizational
|
||||
success and create lasting positive impact worldwide through excellence in leadership development.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4 mb-12 justify-center">
|
||||
<PrimaryCTAButton
|
||||
text="Explore Our Approach"
|
||||
onClick={() => document.getElementById('vision-pillars')?.scrollIntoView({ behavior: 'smooth' })}
|
||||
className="management-dev-primary-cta"
|
||||
/>
|
||||
|
||||
<Button
|
||||
onClick={() => navigateTo('/contact')}
|
||||
className="management-dev-glassmorphic-btn text-body px-8 py-4 min-h-[52px] border text-white transition-all duration-300"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
<ArrowRight className="w-5 h-5 mr-2" />
|
||||
Get Started
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Vision Statistics Section - Embedded in Hero */}
|
||||
<div className="relative z-10 border-t border-white/20 backdrop-blur-sm bg-black/20">
|
||||
<div className="container mx-auto section-margin-x py-8">
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-8 text-center max-w-5xl mx-auto">
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">3</div>
|
||||
<div className="text-small-white opacity-90">Vision Pillars</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">96%</div>
|
||||
<div className="text-small-white opacity-90">Client Success Rate</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">25+</div>
|
||||
<div className="text-small-white opacity-90">Years Experience</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">Global</div>
|
||||
<div className="text-small-white opacity-90">Impact Reach</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Vision Pillars - Redesigned */}
|
||||
<section id="vision-pillars" className="py-20" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="text-center mb-16">
|
||||
<Badge
|
||||
className="mb-6 text-body px-4 py-2"
|
||||
style={{
|
||||
backgroundColor: 'rgba(4, 4, 91, 0.1)',
|
||||
color: 'var(--color-primary)',
|
||||
border: 'none'
|
||||
}}
|
||||
>
|
||||
Foundation
|
||||
</Badge>
|
||||
<h2 className="text-h2 mb-6">Three Pillars of Our Vision</h2>
|
||||
<p className="text-body-lg text-muted max-w-3xl mx-auto leading-relaxed">
|
||||
Our comprehensive approach to leadership development is built on three foundational pillars
|
||||
that guide everything we do and drive measurable transformation.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid lg:grid-cols-3 gap-12 max-w-7xl mx-auto">
|
||||
{visionPillars.map((pillar, index) => (
|
||||
<div key={pillar.title} className="group">
|
||||
<Card
|
||||
className="border-0 shadow-sm hover:shadow-xl transition-all duration-300 h-full bg-white p-0"
|
||||
>
|
||||
<CardContent className="p-8">
|
||||
{/* Icon */}
|
||||
<div
|
||||
className="w-16 h-16 rounded-xl flex items-center justify-center mb-6 group-hover:scale-110 transition-transform duration-300"
|
||||
style={{ backgroundColor: 'rgba(4, 4, 91, 0.08)' }}
|
||||
>
|
||||
{React.createElement(pillar.icon, {
|
||||
className: "w-8 h-8 text-primary"
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<h3 className="text-h4 mb-4 text-black leading-tight">
|
||||
{pillar.title}
|
||||
</h3>
|
||||
|
||||
{/* Description */}
|
||||
<p className="text-body text-muted leading-relaxed mb-6">
|
||||
{pillar.description}
|
||||
</p>
|
||||
|
||||
{/* Main Stat */}
|
||||
<div className="mb-6">
|
||||
<div className="text-small text-muted mb-1">{pillar.mainStat.label}</div>
|
||||
<div className={`text-3xl font-bold ${pillar.mainStat.color}`}>
|
||||
{pillar.mainStat.value}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Data Visualization Section */}
|
||||
{/* First Pillar: Bar Chart */}
|
||||
{index === 0 && (
|
||||
<>
|
||||
<div className="mb-6">
|
||||
<div className="flex items-end justify-between gap-1 h-20 mb-2">
|
||||
{pillar.chartData.map((item, barIndex) => (
|
||||
<div key={item.label} className="flex flex-col items-center flex-1">
|
||||
<div
|
||||
className="w-full rounded-t-sm transition-all duration-1000 delay-300"
|
||||
style={{
|
||||
height: `${item.value}%`,
|
||||
backgroundColor: barIndex >= 3 ? '#04045B' : '#E5E7EB',
|
||||
minHeight: '8px'
|
||||
}}
|
||||
></div>
|
||||
<div className="text-xs text-muted mt-1">{item.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{pillar.metrics.map((metric, metricIndex) => (
|
||||
<div key={metric.label} className="text-center">
|
||||
<div className={`text-2xl font-bold ${metric.color}`}>
|
||||
{metric.value}
|
||||
</div>
|
||||
<div className="text-small text-muted">
|
||||
{metric.label}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Second Pillar: Progress Bars */}
|
||||
{index === 1 && (
|
||||
<div className="space-y-4">
|
||||
{pillar.progressMetrics.map((metric, metricIndex) => (
|
||||
<div key={metric.label}>
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<span className="text-small text-muted">{metric.label}</span>
|
||||
<span className="text-small font-medium text-black">{metric.value}%</span>
|
||||
</div>
|
||||
<div className="w-full bg-gray-200 rounded-full h-2">
|
||||
<div
|
||||
className="h-2 rounded-full transition-all duration-1000"
|
||||
style={{
|
||||
width: `${metric.value}%`,
|
||||
backgroundColor: metric.color,
|
||||
animationDelay: `${(metricIndex + 1) * 200}ms`
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Third Pillar: Multiple Metrics Grid */}
|
||||
{index === 2 && (
|
||||
<>
|
||||
{/* Top Metrics */}
|
||||
<div className="grid grid-cols-2 gap-4 mb-6">
|
||||
{pillar.topMetrics.map((metric, metricIndex) => (
|
||||
<div key={metric.label} className="text-center">
|
||||
<div className="text-small text-muted mb-1">{metric.label}</div>
|
||||
<div className={`text-2xl font-bold ${metric.color}`}>
|
||||
{metric.value}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Bottom Metrics */}
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
{pillar.bottomMetrics.map((metric, metricIndex) => (
|
||||
<div key={metric.label} className="text-center">
|
||||
<div className={`text-lg font-bold ${metric.color}`}>
|
||||
{metric.value}
|
||||
</div>
|
||||
<div className="text-xs text-muted">
|
||||
{metric.label}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* How Our Work Aligns - Redesigned Interactive Timeline */}
|
||||
<section className="py-20" style={{ background: 'linear-gradient(135deg, rgba(247, 247, 253, 0.3) 0%, rgba(248, 195, 1, 0.05) 100%)' }}>
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="text-center mb-16">
|
||||
<Badge
|
||||
className="mb-6 text-body px-4 py-2"
|
||||
style={{
|
||||
backgroundColor: 'rgba(4, 4, 91, 0.1)',
|
||||
color: 'var(--color-primary)',
|
||||
border: 'none'
|
||||
}}
|
||||
>
|
||||
Methodology
|
||||
</Badge>
|
||||
<h2 className="text-h2 mb-6">Our Strategic Approach</h2>
|
||||
<p className="text-body-lg text-muted max-w-3xl mx-auto leading-relaxed">
|
||||
A clear step-by-step process that drives measurable impact and lasting transformation.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col lg:flex-row gap-12 max-w-6xl mx-auto">
|
||||
{/* Timeline Steps - Left Side */}
|
||||
<div className="flex lg:flex-col gap-6 lg:w-1/3">
|
||||
{/* Progress Indicator */}
|
||||
<div className="hidden lg:block text-center mb-6">
|
||||
<div className="text-small text-muted font-medium">
|
||||
Step {activeStep + 1} of {approachSteps.length}
|
||||
</div>
|
||||
<div className="w-full rounded-full h-2 mt-2" style={{ backgroundColor: 'rgba(4, 4, 91, 0.1)' }}>
|
||||
<div
|
||||
className="h-2 rounded-full transition-all duration-500 ease-out"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-primary)',
|
||||
width: `${((activeStep + 1) / approachSteps.length) * 100}%`
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Step Buttons */}
|
||||
{approachSteps.map((step, index) => (
|
||||
<button
|
||||
key={step.title}
|
||||
onClick={() => setActiveStep(index)}
|
||||
className={`group flex items-center gap-4 px-6 py-4 rounded-xl border-2 transition-all duration-300 text-left w-full
|
||||
${activeStep === index
|
||||
? 'bg-primary text-white border-primary shadow-lg'
|
||||
: 'bg-white text-black hover:shadow-md hover:scale-105'
|
||||
}
|
||||
`}
|
||||
style={{
|
||||
borderColor: activeStep === index ? 'var(--color-primary)' : 'rgba(4, 4, 91, 0.2)',
|
||||
backgroundColor: activeStep === index ? 'var(--color-primary)' : '#FFFFFF'
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
if (activeStep !== index) {
|
||||
e.currentTarget.style.borderColor = 'var(--color-primary)';
|
||||
}
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (activeStep !== index) {
|
||||
e.currentTarget.style.borderColor = 'rgba(4, 4, 91, 0.2)';
|
||||
}
|
||||
}}
|
||||
>
|
||||
{/* Step Number */}
|
||||
<div
|
||||
className={`w-10 h-10 flex items-center justify-center rounded-full font-bold text-lg transition-all duration-300
|
||||
${activeStep === index
|
||||
? 'bg-white text-primary'
|
||||
: 'bg-primary text-white group-hover:scale-110'
|
||||
}
|
||||
`}
|
||||
>
|
||||
{index + 1}
|
||||
</div>
|
||||
|
||||
{/* Step Info */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<div
|
||||
className={`font-medium text-body leading-tight transition-colors duration-300
|
||||
${activeStep === index ? 'text-white' : 'text-black'}
|
||||
`}
|
||||
>
|
||||
{step.title}
|
||||
</div>
|
||||
|
||||
{/* Step Icon */}
|
||||
<div className="mt-2">
|
||||
{React.createElement(step.icon, {
|
||||
className: `w-5 h-5 transition-all duration-300 ${
|
||||
activeStep === index ? 'text-white' : 'text-primary'
|
||||
}`
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Active Step Content - Right Side */}
|
||||
<div className="flex-1 lg:w-2/3">
|
||||
<Card
|
||||
className="border-0 shadow-xl transition-all duration-500 ease-out bg-white"
|
||||
style={{ minHeight: '500px' }}
|
||||
>
|
||||
<CardContent className="p-8 lg:p-10">
|
||||
{/* Mobile Progress Indicator */}
|
||||
<div className="lg:hidden text-center mb-6">
|
||||
<div className="text-small text-muted font-medium">
|
||||
Step {activeStep + 1} of {approachSteps.length}
|
||||
</div>
|
||||
<div className="w-full rounded-full h-2 mt-2" style={{ backgroundColor: 'rgba(4, 4, 91, 0.1)' }}>
|
||||
<div
|
||||
className="h-2 rounded-full transition-all duration-500 ease-out"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-primary)',
|
||||
width: `${((activeStep + 1) / approachSteps.length) * 100}%`
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Step Content */}
|
||||
<div className="mb-8 fade-in animate-in duration-700">
|
||||
<div className="flex items-center gap-4 mb-6">
|
||||
<div
|
||||
className="w-16 h-16 rounded-xl flex items-center justify-center transition-transform duration-300 hover:scale-110"
|
||||
style={{ backgroundColor: 'rgba(4, 4, 91, 0.1)' }}
|
||||
>
|
||||
{React.createElement(approachSteps[activeStep].icon, {
|
||||
className: "w-8 h-8 text-primary"
|
||||
})}
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-h3 text-black leading-tight">
|
||||
{approachSteps[activeStep].title}
|
||||
</h3>
|
||||
<div className="text-small text-primary font-medium mt-1">
|
||||
Step {activeStep + 1} of {approachSteps.length}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-body-lg text-muted leading-relaxed mb-8">
|
||||
{approachSteps[activeStep].description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Key Components Accordion-Style */}
|
||||
<div className="mb-8">
|
||||
<h4 className="text-h4 text-black mb-6">Key Components</h4>
|
||||
<div className="space-y-3">
|
||||
{approachSteps[activeStep].details.map((detail, index) => (
|
||||
<div key={index} className="group">
|
||||
<div
|
||||
className="flex items-start gap-3 p-4 rounded-lg transition-all duration-300 cursor-pointer hover:scale-102"
|
||||
style={{
|
||||
backgroundColor: 'rgba(4, 4, 91, 0.05)',
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.backgroundColor = 'rgba(4, 4, 91, 0.08)';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.backgroundColor = 'rgba(4, 4, 91, 0.05)';
|
||||
}}
|
||||
>
|
||||
<div className="w-6 h-6 rounded-full bg-primary flex items-center justify-center flex-shrink-0 mt-0.5 group-hover:scale-110 transition-transform duration-300">
|
||||
<CheckCircle className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<span className="text-body text-black leading-relaxed">{detail}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CTA Button - Always Visible */}
|
||||
<div className="pt-6" style={{ borderTop: '1px solid rgba(4, 4, 91, 0.1)' }}>
|
||||
<PrimaryCTAButton
|
||||
text="Learn More About Our Approach"
|
||||
onClick={() => navigateTo('/services/consulting')}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Leadership Framework CTA - Redesigned */}
|
||||
<section className="py-20" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<div className="grid lg:grid-cols-2 gap-16 items-center">
|
||||
{/* Content Side */}
|
||||
<div>
|
||||
{/* Modern Pill Badge */}
|
||||
<div className="inline-flex items-center gap-3 px-6 py-3 rounded-full mb-8 shadow-sm transition-all duration-300 hover:shadow-md"
|
||||
style={{
|
||||
backgroundColor: 'rgba(248, 195, 1, 0.12)',
|
||||
border: '1px solid rgba(248, 195, 1, 0.2)'
|
||||
}}>
|
||||
<div className="w-3 h-3 rounded-full animate-pulse"
|
||||
style={{ backgroundColor: 'var(--color-accent)' }} />
|
||||
<span className="text-small font-semibold text-black uppercase tracking-wide">
|
||||
Proprietary Framework
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h3 className="text-h2 mb-8 text-black leading-tight">
|
||||
Leadership Orientations Framework
|
||||
</h3>
|
||||
|
||||
<p className="text-body-lg text-muted mb-10 leading-relaxed">
|
||||
Our research-backed framework examines four critical dimensions that define
|
||||
leadership effectiveness and drive organizational results. This proprietary
|
||||
methodology helps leaders understand their natural orientations and develop
|
||||
adaptive capabilities.
|
||||
</p>
|
||||
|
||||
{/* Enhanced Framework Items */}
|
||||
<div className="grid grid-cols-2 gap-6 mb-12">
|
||||
{[
|
||||
{
|
||||
label: 'Thinking',
|
||||
desc: 'Strategic & analytical approaches',
|
||||
icon: Brain,
|
||||
color: '#FFFFFF',
|
||||
accentColor: 'var(--color-primary)'
|
||||
},
|
||||
{
|
||||
label: 'Risk Appetite',
|
||||
desc: 'Innovation & change tolerance',
|
||||
icon: TrendingUp,
|
||||
color: '#FFFFFF',
|
||||
accentColor: 'var(--color-primary)'
|
||||
},
|
||||
{
|
||||
label: 'Power',
|
||||
desc: 'Influence & authority usage',
|
||||
icon: Target,
|
||||
color: '#FFFFFF',
|
||||
accentColor: 'var(--color-primary)'
|
||||
},
|
||||
{
|
||||
label: 'Interpersonal',
|
||||
desc: 'Relationship & political navigation',
|
||||
icon: Users,
|
||||
color: '#FFFFFF',
|
||||
accentColor: 'var(--color-primary)'
|
||||
}
|
||||
].map((item, index) => {
|
||||
const Icon = item.icon;
|
||||
return (
|
||||
<Card
|
||||
key={item.label}
|
||||
className="border border-gray-200 shadow-md hover:shadow-lg transition-all duration-300 hover:-translate-y-2 cursor-pointer group overflow-hidden"
|
||||
style={{ backgroundColor: item.color }}
|
||||
>
|
||||
<CardContent className="p-6 relative">
|
||||
{/* Subtle background pattern */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-5 transition-opacity duration-300 group-hover:opacity-10"
|
||||
style={{
|
||||
backgroundImage: 'radial-gradient(circle at 80% 20%, rgba(4,4,91,0.1) 0%, transparent 50%)'
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="relative z-10">
|
||||
<div className="flex items-start gap-4 mb-4">
|
||||
<div
|
||||
className="w-12 h-12 rounded-xl flex items-center justify-center transition-all duration-300 group-hover:scale-110 group-hover:rotate-3"
|
||||
style={{
|
||||
backgroundColor: item.accentColor,
|
||||
boxShadow: '0 4px 12px rgba(4,4,91,0.25)'
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
className="w-6 h-6 text-white"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex-1">
|
||||
<h4 className="text-subhead text-black mb-2 font-semibold group-hover:text-primary transition-colors">
|
||||
{item.label}
|
||||
</h4>
|
||||
<p className="text-small text-muted leading-relaxed">
|
||||
{item.desc}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Enhanced progress indicator */}
|
||||
<div className="mt-4">
|
||||
<div className="h-1.5 bg-gray-100 rounded-full overflow-hidden shadow-inner">
|
||||
<div
|
||||
className="h-full rounded-full transition-all duration-1000 group-hover:w-full"
|
||||
style={{
|
||||
width: '60%',
|
||||
backgroundColor: item.accentColor,
|
||||
boxShadow: '0 0 8px rgba(4,4,91,0.3)'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Floating accent */}
|
||||
<div
|
||||
className="absolute top-4 right-4 w-2 h-2 rounded-full opacity-60 group-hover:opacity-100 transition-opacity duration-300"
|
||||
style={{ backgroundColor: item.accentColor }}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<PrimaryCTAButton
|
||||
text="Explore Our Framework"
|
||||
onClick={() => navigateTo('/about/our-expertise')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Enhanced Visual Side */}
|
||||
<div className="relative">
|
||||
<Card
|
||||
className="border border-gray-200 shadow-2xl overflow-hidden"
|
||||
style={{
|
||||
background: '#FFFFFF'
|
||||
}}
|
||||
>
|
||||
<CardContent className="p-10 relative">
|
||||
{/* Sophisticated Background Pattern */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-5"
|
||||
style={{
|
||||
backgroundImage: `
|
||||
radial-gradient(circle at 25% 25%, rgba(4,4,91,0.3) 0%, transparent 40%),
|
||||
radial-gradient(circle at 75% 75%, rgba(4,4,91,0.1) 0%, transparent 40%),
|
||||
linear-gradient(45deg, rgba(4,4,91,0.05) 0%, transparent 30%, rgba(4,4,91,0.05) 70%, transparent 100%)
|
||||
`
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Geometric accent elements */}
|
||||
<div className="absolute top-8 right-8 w-16 h-16 border border-blue-300 opacity-30 rounded-lg rotate-12" />
|
||||
<div className="absolute bottom-8 left-8 w-12 h-12 bg-blue-300 opacity-15 rounded-full" />
|
||||
|
||||
{/* Content */}
|
||||
<div className="relative z-10">
|
||||
{/* Header */}
|
||||
<div className="text-center mb-10">
|
||||
<div
|
||||
className="w-20 h-20 rounded-2xl flex items-center justify-center mx-auto mb-6 relative overflow-hidden"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-primary)',
|
||||
border: '1px solid rgba(4, 4, 91, 0.3)'
|
||||
}}
|
||||
>
|
||||
{/* Inner glow */}
|
||||
<div className="absolute inset-2 rounded-xl bg-gradient-to-br from-white/20 to-transparent" />
|
||||
<BarChart3 className="w-10 h-10 text-white relative z-10" />
|
||||
</div>
|
||||
<h4 className="text-h4 text-black mb-3 font-semibold">Framework Impact</h4>
|
||||
<p className="text-body text-muted">
|
||||
Proven results across organizations
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Enhanced Infographic Stats */}
|
||||
<div className="space-y-8">
|
||||
{/* Leadership Effectiveness */}
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="relative">
|
||||
{/* Circular progress ring */}
|
||||
<svg className="w-20 h-20 transform -rotate-90" viewBox="0 0 80 80">
|
||||
<circle
|
||||
cx="40"
|
||||
cy="40"
|
||||
r="30"
|
||||
stroke="rgba(4, 4, 91, 0.2)"
|
||||
strokeWidth="4"
|
||||
fill="none"
|
||||
/>
|
||||
<circle
|
||||
cx="40"
|
||||
cy="40"
|
||||
r="30"
|
||||
stroke="var(--color-primary)"
|
||||
strokeWidth="4"
|
||||
fill="none"
|
||||
strokeDasharray={`${2 * Math.PI * 30}`}
|
||||
strokeDashoffset={`${2 * Math.PI * 30 * (1 - 89 / 100)}`}
|
||||
className="transition-all duration-2000 ease-out"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
</svg>
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<span className="text-xl font-bold text-blue-600">89%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="text-body-lg text-black font-semibold mb-2">Leadership Effectiveness</div>
|
||||
<div className="text-small text-muted mb-3">Improvement in 6 months</div>
|
||||
{/* Horizontal progress bar */}
|
||||
<div className="h-2 bg-gray-200 rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full rounded-full transition-all duration-2000 ease-out"
|
||||
style={{
|
||||
width: '89%',
|
||||
background: 'linear-gradient(90deg, var(--color-primary) 0%, rgba(4, 4, 91, 0.8) 100%)'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Self-Awareness Growth */}
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="relative">
|
||||
{/* Circular progress ring */}
|
||||
<svg className="w-20 h-20 transform -rotate-90" viewBox="0 0 80 80">
|
||||
<circle
|
||||
cx="40"
|
||||
cy="40"
|
||||
r="30"
|
||||
stroke="rgba(4, 4, 91, 0.2)"
|
||||
strokeWidth="4"
|
||||
fill="none"
|
||||
/>
|
||||
<circle
|
||||
cx="40"
|
||||
cy="40"
|
||||
r="30"
|
||||
stroke="var(--color-primary)"
|
||||
strokeWidth="4"
|
||||
fill="none"
|
||||
strokeDasharray={`${2 * Math.PI * 30}`}
|
||||
strokeDashoffset={`${2 * Math.PI * 30 * (1 - 92 / 100)}`}
|
||||
className="transition-all duration-2000 ease-out"
|
||||
strokeLinecap="round"
|
||||
style={{ transitionDelay: '0.5s' }}
|
||||
/>
|
||||
</svg>
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<span className="text-xl font-bold text-blue-600">92%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="text-body-lg text-black font-semibold mb-2">Self-Awareness Growth</div>
|
||||
<div className="text-small text-muted mb-3">Better decision making</div>
|
||||
{/* Horizontal progress bar */}
|
||||
<div className="h-2 bg-gray-200 rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full rounded-full transition-all duration-2000 ease-out"
|
||||
style={{
|
||||
width: '92%',
|
||||
background: 'linear-gradient(90deg, var(--color-primary) 0%, rgba(4, 4, 91, 0.8) 100%)',
|
||||
transitionDelay: '0.5s'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Team Performance */}
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="relative">
|
||||
{/* Circular progress ring */}
|
||||
<svg className="w-20 h-20 transform -rotate-90" viewBox="0 0 80 80">
|
||||
<circle
|
||||
cx="40"
|
||||
cy="40"
|
||||
r="30"
|
||||
stroke="rgba(4, 4, 91, 0.2)"
|
||||
strokeWidth="4"
|
||||
fill="none"
|
||||
/>
|
||||
<circle
|
||||
cx="40"
|
||||
cy="40"
|
||||
r="30"
|
||||
stroke="var(--color-primary)"
|
||||
strokeWidth="4"
|
||||
fill="none"
|
||||
strokeDasharray={`${2 * Math.PI * 30}`}
|
||||
strokeDashoffset={`${2 * Math.PI * 30 * (1 - 76 / 100)}`}
|
||||
className="transition-all duration-2000 ease-out"
|
||||
strokeLinecap="round"
|
||||
style={{ transitionDelay: '1s' }}
|
||||
/>
|
||||
</svg>
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<span className="text-xl font-bold text-blue-600">76%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="text-body-lg text-black font-semibold mb-2">Team Performance</div>
|
||||
<div className="text-small text-muted mb-3">Enhanced collaboration</div>
|
||||
{/* Horizontal progress bar */}
|
||||
<div className="h-2 bg-gray-200 rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full rounded-full transition-all duration-2000 ease-out"
|
||||
style={{
|
||||
width: '76%',
|
||||
background: 'linear-gradient(90deg, var(--color-primary) 0%, rgba(4, 4, 91, 0.8) 100%)',
|
||||
transitionDelay: '1s'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom accent with trust indicator */}
|
||||
<div className="flex items-center justify-center mt-10 pt-8 border-t border-gray-200">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<CheckCircle className="w-5 h-5 text-blue-500" />
|
||||
<span className="text-small text-black font-medium">500+ Organizations</span>
|
||||
</div>
|
||||
<div className="w-1 h-1 bg-blue-500 rounded-full" />
|
||||
<div className="flex items-center gap-2">
|
||||
<CheckCircle className="w-5 h-5 text-blue-500" />
|
||||
<span className="text-small text-black font-medium">Proven Results</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/* CTA Section - Vision Banner Style */}
|
||||
<section className="relative h-[700px] overflow-hidden">
|
||||
{/* Background Image */}
|
||||
<div className="absolute inset-0">
|
||||
<ImageWithFallback
|
||||
src="https://images.unsplash.com/photo-1600880292203-757bb62b4baf?w=2940&h=1200&fit=crop"
|
||||
alt="Visionary leadership team planning strategy"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
|
||||
{/* Subtle dark overlay for overall image */}
|
||||
<div className="absolute inset-0 bg-black/30" />
|
||||
|
||||
{/* Gradient overlay for better text readability */}
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-black/20 via-transparent to-black/60" />
|
||||
</div>
|
||||
|
||||
{/* Content Container */}
|
||||
<div className="relative h-full flex items-center justify-end section-margin-x">
|
||||
{/* CTA Content Block */}
|
||||
<div
|
||||
className="bg-opacity-95 backdrop-blur-sm rounded-lg p-16 max-w-2xl"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-primary)'
|
||||
}}
|
||||
>
|
||||
{/* Branded Tag */}
|
||||
<div className="mb-6">
|
||||
<div className="branded-tag-system-white">
|
||||
<div className="dot"></div>
|
||||
<span className="text">
|
||||
Transform Your Vision
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Headline */}
|
||||
<h2 className="text-h2-white leading-tight mb-8">
|
||||
Ready to Build Your Leadership Vision?
|
||||
<span
|
||||
className="italic"
|
||||
style={{ color: 'var(--color-accent)' }}
|
||||
>
|
||||
{" "}Start your transformation{" "}
|
||||
</span>
|
||||
journey today.
|
||||
</h2>
|
||||
|
||||
{/* CTA Button */}
|
||||
<PrimaryCTAButton
|
||||
text="Explore Our Services"
|
||||
onClick={() => navigateTo('/services/leadership-development')}
|
||||
ariaLabel="Explore our leadership development services"
|
||||
className="cta-banner-yellow"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
1
src/components/about/PageHeader.tsx
Normal file
@@ -0,0 +1 @@
|
||||
// This file has been removed
|
||||
1
src/components/about/StatCard.tsx
Normal file
@@ -0,0 +1 @@
|
||||
// This file has been removed
|
||||
1
src/components/about/TeamMemberCard.tsx
Normal file
@@ -0,0 +1 @@
|
||||
// This file has been removed
|
||||
27
src/components/figma/ImageWithFallback.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import React, { useState } from 'react'
|
||||
|
||||
const ERROR_IMG_SRC =
|
||||
'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODgiIGhlaWdodD0iODgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBvcGFjaXR5PSIuMyIgZmlsbD0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIzLjciPjxyZWN0IHg9IjE2IiB5PSIxNiIgd2lkdGg9IjU2IiBoZWlnaHQ9IjU2IiByeD0iNiIvPjxwYXRoIGQ9Im0xNiA1OCAxNi0xOCAzMiAzMiIvPjxjaXJjbGUgY3g9IjUzIiBjeT0iMzUiIHI9IjciLz48L3N2Zz4KCg=='
|
||||
|
||||
export function ImageWithFallback(props: React.ImgHTMLAttributes<HTMLImageElement>) {
|
||||
const [didError, setDidError] = useState(false)
|
||||
|
||||
const handleError = () => {
|
||||
setDidError(true)
|
||||
}
|
||||
|
||||
const { src, alt, style, className, ...rest } = props
|
||||
|
||||
return didError ? (
|
||||
<div
|
||||
className={`inline-block bg-gray-100 text-center align-middle ${className ?? ''}`}
|
||||
style={style}
|
||||
>
|
||||
<div className="flex items-center justify-center w-full h-full">
|
||||
<img src={ERROR_IMG_SRC} alt="Error loading image" {...rest} data-original-url={src} />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<img src={src} alt={alt} className={className} style={style} {...rest} onError={handleError} />
|
||||
)
|
||||
}
|
||||
78
src/components/hooks/useAnimatedCounter.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
|
||||
interface UseAnimatedCounterOptions {
|
||||
start?: number;
|
||||
end: number;
|
||||
duration?: number;
|
||||
decimals?: number;
|
||||
suffix?: string;
|
||||
}
|
||||
|
||||
export function useAnimatedCounter({
|
||||
start = 0,
|
||||
end,
|
||||
duration = 2000,
|
||||
decimals = 0,
|
||||
suffix = ''
|
||||
}: UseAnimatedCounterOptions) {
|
||||
const [count, setCount] = useState(start);
|
||||
const [isInView, setIsInView] = useState(false);
|
||||
const countRef = useRef<HTMLSpanElement>(null);
|
||||
const frameRef = useRef<number>();
|
||||
const startTimeRef = useRef<number>();
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
if (entry.isIntersecting && !isInView) {
|
||||
setIsInView(true);
|
||||
}
|
||||
},
|
||||
{ threshold: 0.1 }
|
||||
);
|
||||
|
||||
if (countRef.current) {
|
||||
observer.observe(countRef.current);
|
||||
}
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, [isInView]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isInView) return;
|
||||
|
||||
const animate = (currentTime: number) => {
|
||||
if (!startTimeRef.current) startTimeRef.current = currentTime;
|
||||
|
||||
const elapsed = currentTime - startTimeRef.current;
|
||||
const progress = Math.min(elapsed / duration, 1);
|
||||
|
||||
// Easing function for smooth animation
|
||||
const easeOutQuart = 1 - Math.pow(1 - progress, 4);
|
||||
const currentCount = start + (end - start) * easeOutQuart;
|
||||
|
||||
setCount(currentCount);
|
||||
|
||||
if (progress < 1) {
|
||||
frameRef.current = requestAnimationFrame(animate);
|
||||
}
|
||||
};
|
||||
|
||||
frameRef.current = requestAnimationFrame(animate);
|
||||
|
||||
return () => {
|
||||
if (frameRef.current) {
|
||||
cancelAnimationFrame(frameRef.current);
|
||||
}
|
||||
};
|
||||
}, [isInView, start, end, duration]);
|
||||
|
||||
const formattedCount = decimals > 0
|
||||
? count.toFixed(decimals)
|
||||
: Math.floor(count).toLocaleString();
|
||||
|
||||
return {
|
||||
count: formattedCount + suffix,
|
||||
ref: countRef
|
||||
};
|
||||
}
|
||||
693
src/components/services/Consulting.tsx
Normal file
@@ -0,0 +1,693 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Button } from '../ui/button';
|
||||
import { Card, CardContent } from '../ui/card';
|
||||
import { Badge } from '../ui/badge';
|
||||
import { ImageWithFallback } from '../figma/ImageWithFallback';
|
||||
import { navigateTo } from '../Router';
|
||||
import { BrandedTag } from '../about/BrandedTag';
|
||||
import { TestimonialsSection } from '../TestimonialsSection';
|
||||
import { CTABannerSection } from '../CTABannerSection';
|
||||
import StackedOfferSection from '../StackedOfferSection';
|
||||
import {
|
||||
Users,
|
||||
Target,
|
||||
ArrowRight,
|
||||
CheckCircle,
|
||||
Lightbulb,
|
||||
Brain,
|
||||
Eye,
|
||||
TrendingUp,
|
||||
MessageCircle,
|
||||
BarChart3,
|
||||
Compass,
|
||||
Shield,
|
||||
Network,
|
||||
Zap,
|
||||
Settings,
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
ArrowLeft,
|
||||
Download,
|
||||
Crown
|
||||
} from 'lucide-react';
|
||||
import { PrimaryCTAButton } from '../PrimaryCTAButton';
|
||||
|
||||
// Strategic consulting cards data for the stacking section
|
||||
const consultingCards = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Organizational Structure Design",
|
||||
subtitle: "Strategic Design & Optimization",
|
||||
description:
|
||||
"Comprehensive organizational design services that optimize structure, roles, and reporting relationships for enhanced performance and strategic alignment across all business functions.",
|
||||
badge: "Featured",
|
||||
color: "from-blue-600 to-blue-700",
|
||||
icon: Network,
|
||||
features: ["Organizational Assessment", "Structure Optimization", "Role Clarification", "Governance Design"],
|
||||
stats: [
|
||||
{ label: "Success Rate", value: "96%" },
|
||||
{ label: "Organizations", value: "250+" },
|
||||
{ label: "Duration", value: "3-6 Mo" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Vision & Mission Redefinition",
|
||||
subtitle: "Purpose-Driven Strategic Alignment",
|
||||
description:
|
||||
"Collaborative development of inspiring vision and mission statements that drive organizational alignment, stakeholder engagement, and sustainable competitive advantage.",
|
||||
badge: "Strategic",
|
||||
color: "from-purple-600 to-purple-700",
|
||||
icon: Compass,
|
||||
features: ["Stakeholder Engagement", "Values Articulation", "Purpose Definition", "Strategic Narrative"],
|
||||
stats: [
|
||||
{ label: "Alignment Rate", value: "92%" },
|
||||
{ label: "Leaders Engaged", value: "500+" },
|
||||
{ label: "Duration", value: "2-4 Mo" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Leadership Capability Building",
|
||||
subtitle: "Strategic Leadership Development",
|
||||
description:
|
||||
"Systematic development of leadership capabilities aligned with organizational strategy, including succession planning, talent development, and leadership pipeline optimization.",
|
||||
badge: "Popular",
|
||||
color: "from-green-600 to-green-700",
|
||||
icon: TrendingUp,
|
||||
features: ["Capability Frameworks", "Leadership Pipeline", "Succession Planning", "Talent Development"],
|
||||
stats: [
|
||||
{ label: "Development Rate", value: "89%" },
|
||||
{ label: "Leaders Developed", value: "1,000+" },
|
||||
{ label: "Duration", value: "6-12 Mo" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "Strategic Transformation",
|
||||
subtitle: "End-to-End Change Management",
|
||||
description:
|
||||
"Complete transformation support for major organizational change initiatives, including change strategy, stakeholder alignment, and cultural integration for sustainable success.",
|
||||
badge: "Comprehensive",
|
||||
color: "from-orange-600 to-orange-700",
|
||||
icon: Zap,
|
||||
features: ["Change Strategy", "Stakeholder Alignment", "Implementation Roadmap", "Culture Integration"],
|
||||
stats: [
|
||||
{ label: "Success Rate", value: "91%" },
|
||||
{ label: "Transformations", value: "180+" },
|
||||
{ label: "Duration", value: "12-24 Mo" }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const targetAudience = [
|
||||
{
|
||||
title: "Founders & CXOs",
|
||||
description: "Visionary leaders driving organizational transformation and strategic growth",
|
||||
icon: Lightbulb,
|
||||
characteristics: ["Strategic vision", "Growth focus", "Change leadership", "Stakeholder management"]
|
||||
},
|
||||
{
|
||||
title: "Boards",
|
||||
description: "Board members seeking governance excellence and strategic oversight capabilities",
|
||||
icon: Shield,
|
||||
characteristics: ["Governance focus", "Risk oversight", "Strategic guidance", "Performance monitoring"]
|
||||
},
|
||||
{
|
||||
title: "OD Leaders",
|
||||
description: "Organizational development professionals implementing systematic change initiatives",
|
||||
icon: Settings,
|
||||
characteristics: ["Change expertise", "System thinking", "Process optimization", "Culture development"]
|
||||
}
|
||||
];
|
||||
|
||||
const outcomes = [
|
||||
{
|
||||
title: "Clarity in Leadership Direction",
|
||||
description: "Clear strategic direction with aligned leadership behaviors and decision-making",
|
||||
icon: Compass,
|
||||
metrics: "96% of leadership teams report improved strategic clarity and alignment"
|
||||
},
|
||||
{
|
||||
title: "Scalable Org Models",
|
||||
description: "Organizational structures that support growth while maintaining operational efficiency",
|
||||
icon: Network,
|
||||
metrics: "78% improvement in organizational agility and decision-making speed"
|
||||
},
|
||||
{
|
||||
title: "Faster Decision-Making & Alignment",
|
||||
description: "Streamlined processes that enable rapid, informed decision-making across the organization",
|
||||
icon: Zap,
|
||||
metrics: "65% reduction in decision cycle time with improved cross-functional alignment"
|
||||
}
|
||||
];
|
||||
|
||||
const approachSteps = [
|
||||
{
|
||||
step: "01",
|
||||
title: "Strategic Assessment",
|
||||
description: "Comprehensive analysis of organizational current state, strategic challenges, and transformation opportunities. Together, we make your vision manifest through proven assessment methodologies and frameworks.",
|
||||
details: ["Organizational scan", "Stakeholder interviews", "Performance analysis", "Gap assessment"],
|
||||
icon: Eye
|
||||
},
|
||||
{
|
||||
step: "02",
|
||||
title: "Co-Creation Process",
|
||||
description: "Collaborative design sessions with leadership to develop tailored strategic solutions and alignment. Together, we make your vision manifest through structured co-creation methodologies and proven frameworks.",
|
||||
details: ["Leadership workshops", "Co-design sessions", "Stakeholder alignment", "Solution development"],
|
||||
icon: Users
|
||||
},
|
||||
{
|
||||
step: "03",
|
||||
title: "Implementation Support",
|
||||
description: "Hands-on guidance and support throughout the strategic transformation implementation journey. Together, we make your vision manifest through comprehensive support methodologies and proven frameworks.",
|
||||
details: ["Change management", "Project oversight", "Training delivery", "Progress monitoring"],
|
||||
icon: TrendingUp
|
||||
},
|
||||
{
|
||||
step: "04",
|
||||
title: "Continuous Optimization",
|
||||
description: "Ongoing refinement and optimization to ensure sustainable transformation success and impact. Together, we make your vision manifest through continuous improvement methodologies and proven frameworks.",
|
||||
details: ["Performance tracking", "Feedback integration", "Process refinement", "Capability building"],
|
||||
icon: BarChart3
|
||||
}
|
||||
];
|
||||
|
||||
export function Consulting() {
|
||||
const [expandedAudienceIndex, setExpandedAudienceIndex] = useState<number | null>(0);
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, []);
|
||||
|
||||
const toggleAudienceExpanded = (index: number) => {
|
||||
setExpandedAudienceIndex(expandedAudienceIndex === index ? null : index);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
{/* Hero Section */}
|
||||
<section className="relative min-h-[90vh] flex flex-col">
|
||||
<div className="absolute inset-0 z-0">
|
||||
<ImageWithFallback
|
||||
src="https://images.unsplash.com/photo-1560472354-b33ff0c44a43?w=1920&h=1080&fit=crop"
|
||||
alt="Strategic consulting meeting - business transformation"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-black/80 via-black/70 to-black/60"></div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 flex-1 flex items-center">
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="text-center max-w-5xl mx-auto">
|
||||
<div className="branded-tag-system-white mb-8 justify-center">
|
||||
<div className="dot"></div>
|
||||
<span className="text">Strategic Advisory</span>
|
||||
</div>
|
||||
|
||||
<h1 className="text-h1-white mb-6">
|
||||
Strategic Consulting Services
|
||||
</h1>
|
||||
|
||||
<p className="text-body-lg-white mb-8 max-w-2xl mx-auto">
|
||||
Expert strategic guidance for organizational transformation. Drive clarity,
|
||||
alignment, and sustainable business growth through proven consulting methodologies.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4 mb-12 justify-center">
|
||||
<PrimaryCTAButton
|
||||
text="Book a consultation"
|
||||
onClick={() => navigateTo('/contact')}
|
||||
className="management-dev-primary-cta"
|
||||
/>
|
||||
|
||||
<Button
|
||||
onClick={() => window.open('/strategic-overview', '_blank')}
|
||||
className="management-dev-glassmorphic-btn text-body px-8 py-4 min-h-[52px] border text-white transition-all duration-300"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
<Download className="w-5 h-5 mr-2" />
|
||||
Download Overview
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 border-t border-white/20 backdrop-blur-sm bg-black/20">
|
||||
<div className="container mx-auto section-margin-x py-8">
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-8 text-center max-w-5xl mx-auto">
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">96%</div>
|
||||
<div className="text-small-white opacity-90">Strategic alignment improvement</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">250+</div>
|
||||
<div className="text-small-white opacity-90">Organizations transformed</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">78%</div>
|
||||
<div className="text-small-white opacity-90">Faster decision-making</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">91%</div>
|
||||
<div className="text-small-white opacity-90">Client satisfaction rating</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Stacked Offer Section */}
|
||||
<StackedOfferSection cards={consultingCards} />
|
||||
|
||||
{/* Who It's For Section */}
|
||||
<section className="py-20" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="grid lg:grid-cols-2 gap-16 items-start max-w-7xl mx-auto">
|
||||
<div className="lg:pr-8">
|
||||
<h2 className="text-h2 mb-6 text-black leading-tight">
|
||||
Who It's <span className="text-primary">For</span>
|
||||
</h2>
|
||||
<p className="text-body-lg text-muted leading-relaxed">
|
||||
Our strategic consulting services are designed for leaders and organizations
|
||||
committed to transformation, growth, and sustainable competitive advantage.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
{targetAudience.map((audience, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="border-b border-gray-400 last:border-b-0"
|
||||
>
|
||||
<button
|
||||
onClick={() => toggleAudienceExpanded(index)}
|
||||
className="w-full py-6 px-0 flex items-center justify-between text-left hover:bg-transparent focus:outline-none group transition-all duration-200"
|
||||
style={{ backgroundColor: 'transparent' }}
|
||||
>
|
||||
<span className="text-h4 text-black group-hover:text-primary transition-colors duration-200 pr-4">
|
||||
{audience.title}
|
||||
</span>
|
||||
<div className="flex-shrink-0 ml-4">
|
||||
{expandedAudienceIndex === index ? (
|
||||
<ChevronUp className="w-6 h-6 text-primary transition-transform duration-200" />
|
||||
) : (
|
||||
<ChevronDown className="w-6 h-6 text-gray-400 group-hover:text-primary transition-all duration-200" />
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div className={`overflow-hidden transition-all duration-300 ease-in-out ${
|
||||
expandedAudienceIndex === index ? 'max-h-96 opacity-100' : 'max-h-0 opacity-0'
|
||||
}`}>
|
||||
<div className="pb-6 pr-8">
|
||||
<p className="text-body text-muted leading-relaxed mb-4">
|
||||
{audience.description}
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{audience.characteristics.map((characteristic, charIndex) => (
|
||||
<Badge key={charIndex} variant="outline" className="text-small" style={{ fontFamily: 'var(--font-family-base)' }}>
|
||||
{characteristic}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Expected Outcomes Section */}
|
||||
<section className="py-20" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="text-center mb-16 max-w-4xl mx-auto">
|
||||
<h2 className="text-h2 mb-6">
|
||||
Expected <span className="text-primary">Outcomes</span>
|
||||
</h2>
|
||||
<p className="text-body-lg text-muted">
|
||||
Our strategic consulting delivers measurable improvements in organizational
|
||||
clarity, agility, and decision-making effectiveness across all business functions.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Three-Column Layout - Reference Design Style */}
|
||||
<div className="grid lg:grid-cols-3 gap-8 max-w-7xl mx-auto">
|
||||
|
||||
{/* Clarity in Leadership Direction */}
|
||||
<div className="bg-white p-10 rounded-2xl border border-gray-100 shadow-sm hover:shadow-lg transition-all duration-300">
|
||||
<div className="mb-8">
|
||||
<div className="w-20 h-20 rounded-2xl bg-gradient-to-br from-blue-50 to-blue-100 flex items-center justify-center mb-6">
|
||||
<Compass className="w-10 h-10 text-primary" />
|
||||
</div>
|
||||
|
||||
<h3 className="text-h3 text-black mb-4">
|
||||
Clarity in Leadership Direction
|
||||
</h3>
|
||||
|
||||
<p className="text-body text-muted leading-relaxed mb-8">
|
||||
Clear strategic direction with aligned leadership behaviors and
|
||||
decision-making frameworks for enhanced organizational effectiveness.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Strategic Leadership Visualization */}
|
||||
<div className="space-y-6">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-small text-muted font-medium">Strategic Clarity</span>
|
||||
<span className="text-h4 text-green-600 font-bold">+96%</span>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<div className="flex items-end space-x-1 h-24 mb-4">
|
||||
<div className="bg-gray-200 h-8 w-4 rounded-sm"></div>
|
||||
<div className="bg-gray-300 h-12 w-4 rounded-sm"></div>
|
||||
<div className="bg-gray-200 h-16 w-4 rounded-sm"></div>
|
||||
<div className="bg-gray-300 h-10 w-4 rounded-sm"></div>
|
||||
<div className="bg-green-400 h-20 w-4 rounded-sm"></div>
|
||||
<div className="bg-green-500 h-24 w-4 rounded-sm"></div>
|
||||
<div className="bg-green-600 h-22 w-4 rounded-sm"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="text-center p-3 bg-blue-50 rounded-lg border border-blue-100">
|
||||
<div className="text-xl font-bold text-primary">94%</div>
|
||||
<div className="text-small text-muted">Alignment</div>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-green-50 rounded-lg border border-green-100">
|
||||
<div className="text-xl font-bold text-green-600">91%</div>
|
||||
<div className="text-small text-muted">Direction</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Scalable Org Models */}
|
||||
<div className="bg-white p-10 rounded-2xl border border-gray-100 shadow-sm hover:shadow-lg transition-all duration-300">
|
||||
<div className="mb-8">
|
||||
<div className="w-20 h-20 rounded-2xl bg-gradient-to-br from-yellow-50 to-yellow-100 flex items-center justify-center mb-6">
|
||||
<Network className="w-10 h-10 text-accent" />
|
||||
</div>
|
||||
|
||||
<h3 className="text-h3 text-black mb-4">
|
||||
Scalable Org Models
|
||||
</h3>
|
||||
|
||||
<p className="text-body text-muted leading-relaxed mb-8">
|
||||
Organizational structures that support growth while maintaining
|
||||
operational efficiency and strategic flexibility.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Scalable Organization Metrics */}
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<span className="text-small text-muted font-medium">Organizational Agility</span>
|
||||
<div className="flex items-center">
|
||||
<TrendingUp className="w-4 h-4 text-green-500 mr-1" />
|
||||
<span className="text-small text-green-600 font-medium">+78%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative bg-gray-100 rounded-full h-3 mb-6">
|
||||
<div
|
||||
className="bg-gradient-to-r from-yellow-400 to-yellow-500 h-3 rounded-full transition-all duration-1000"
|
||||
style={{ width: '78%' }}
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-small text-muted">Decision Speed</span>
|
||||
<div className="bg-yellow-100 text-yellow-700 px-2 py-1 rounded text-small font-medium">
|
||||
82%
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-small text-muted">Structure Efficiency</span>
|
||||
<div className="bg-yellow-100 text-yellow-700 px-2 py-1 rounded text-small font-medium">
|
||||
76%
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-small text-muted">Growth Readiness</span>
|
||||
<div className="bg-yellow-100 text-yellow-700 px-2 py-1 rounded text-small font-medium">
|
||||
84%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Faster Decision-Making & Alignment */}
|
||||
<div className="bg-white p-10 rounded-2xl border border-gray-100 shadow-sm hover:shadow-lg transition-all duration-300">
|
||||
<div className="mb-8">
|
||||
<div className="w-20 h-20 rounded-2xl bg-gradient-to-br from-purple-50 to-purple-100 flex items-center justify-center mb-6">
|
||||
<Zap className="w-10 h-10 text-primary" />
|
||||
</div>
|
||||
|
||||
<h3 className="text-h3 text-black mb-4">
|
||||
Faster Decision-Making & Alignment
|
||||
</h3>
|
||||
|
||||
<p className="text-body text-muted leading-relaxed mb-8">
|
||||
Streamlined processes that enable rapid, informed decision-making
|
||||
with improved cross-functional alignment.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Decision-Making KPIs */}
|
||||
<div className="space-y-6">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="text-center p-3 bg-blue-50 rounded-lg border border-blue-100">
|
||||
<div className="flex items-center justify-center mb-1">
|
||||
<span className="text-small text-muted mr-2">Speed</span>
|
||||
<TrendingUp className="w-3 h-3 text-green-500" />
|
||||
</div>
|
||||
<div className="text-xl font-bold text-primary">65%</div>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-green-50 rounded-lg border border-green-100">
|
||||
<div className="flex items-center justify-center mb-1">
|
||||
<span className="text-small text-muted mr-2">Alignment</span>
|
||||
<div className="w-3 h-3 flex items-center justify-center">
|
||||
<div className="w-0 h-0 border-l-2 border-r-2 border-b-3 border-transparent border-b-green-500"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xl font-bold text-green-500">89%</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-50 rounded-lg p-4">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<span className="text-small text-muted font-medium">Decision Impact</span>
|
||||
<span className="text-small text-primary font-bold">Cycle Time</span>
|
||||
</div>
|
||||
<div className="flex items-end justify-between">
|
||||
<div className="text-2xl font-bold text-primary">91%</div>
|
||||
<div className="text-2xl font-bold text-green-600">-65%</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
<div className="text-center">
|
||||
<div className="text-lg font-bold text-primary">91%</div>
|
||||
<div className="text-xs text-muted">Quality</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-lg font-bold text-green-600">-65%</div>
|
||||
<div className="text-xs text-muted">Time Reduction</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-lg font-bold text-blue-500">89%</div>
|
||||
<div className="text-xs text-muted">Alignment</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Results Confirmation Banner */}
|
||||
<div className="mt-16 max-w-4xl mx-auto">
|
||||
<div className="bg-gradient-to-r from-green-50 to-blue-50 rounded-xl p-6 border border-green-200">
|
||||
<div className="flex items-center justify-center gap-3">
|
||||
<div className="w-8 h-8 rounded-full bg-green-100 flex items-center justify-center">
|
||||
<CheckCircle className="w-5 h-5 text-green-600" />
|
||||
</div>
|
||||
<p className="text-body text-center" style={{ fontWeight: 'var(--font-weight-subhead)', color: '#059669' }}>
|
||||
All outcomes measured within 4-6 months of engagement completion
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Our Approach Section - Carousel Design */}
|
||||
<section className="py-20" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12 items-start max-w-7xl mx-auto">
|
||||
|
||||
{/* Left Side - Title, Description & Navigation */}
|
||||
<div className="lg:col-span-5 lg:pr-8">
|
||||
<h2 className="text-h2 mb-6 leading-tight">
|
||||
Our <span className="text-primary">Approach</span>
|
||||
</h2>
|
||||
<p className="text-body-lg text-muted leading-relaxed mb-8">
|
||||
Co-creation model with leadership using proven strategic methodologies: organizational
|
||||
assessments, stakeholder interviews, and collaborative consulting for maximum impact.
|
||||
</p>
|
||||
|
||||
{/* Navigation Controls */}
|
||||
<div className="flex gap-3">
|
||||
<button
|
||||
onClick={() => {
|
||||
const container = document.getElementById('consulting-approach-carousel');
|
||||
if (container) {
|
||||
container.scrollBy({ left: -384, behavior: 'smooth' });
|
||||
}
|
||||
}}
|
||||
className="w-12 h-12 rounded-full bg-gray-100 hover:bg-primary hover:text-white transition-all duration-300 flex items-center justify-center border border-gray-200 hover:border-primary"
|
||||
aria-label="Previous approach step"
|
||||
>
|
||||
<ArrowLeft className="w-5 h-5" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
const container = document.getElementById('consulting-approach-carousel');
|
||||
if (container) {
|
||||
container.scrollBy({ left: 384, behavior: 'smooth' });
|
||||
}
|
||||
}}
|
||||
className="w-12 h-12 rounded-full bg-gray-100 hover:bg-primary hover:text-white transition-all duration-300 flex items-center justify-center border border-gray-200 hover:border-primary"
|
||||
aria-label="Next approach step"
|
||||
>
|
||||
<ArrowRight className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Side - Scrollable Approach Cards */}
|
||||
<div className="lg:col-span-7 relative">
|
||||
<div
|
||||
id="consulting-approach-carousel"
|
||||
className="flex gap-6 overflow-x-auto pb-4"
|
||||
style={{
|
||||
scrollSnapType: 'x mandatory',
|
||||
WebkitOverflowScrolling: 'touch',
|
||||
msOverflowStyle: 'none',
|
||||
scrollbarWidth: 'none'
|
||||
}}
|
||||
>
|
||||
{approachSteps.map((step, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex-shrink-0 w-96 bg-white rounded-2xl border border-gray-200 p-8 shadow-md hover:shadow-2xl transition-all duration-300 hover:transform hover:scale-[1.02]"
|
||||
style={{ scrollSnapAlign: 'start' }}
|
||||
>
|
||||
{/* Step Number */}
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div
|
||||
className="leading-none font-medium"
|
||||
style={{
|
||||
fontSize: 'var(--font-h2)',
|
||||
fontWeight: '500',
|
||||
lineHeight: 'var(--line-height-h2)',
|
||||
color: 'var(--color-accent)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{step.step}
|
||||
</div>
|
||||
<div className="w-16 h-0.5 bg-gradient-to-r from-accent to-transparent opacity-40"></div>
|
||||
</div>
|
||||
|
||||
{/* Step Title */}
|
||||
<h3
|
||||
className="leading-tight mb-6"
|
||||
style={{
|
||||
fontSize: 'var(--font-h3)',
|
||||
fontWeight: 'var(--font-weight-h3)',
|
||||
lineHeight: 'var(--line-height-h3)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-black)'
|
||||
}}
|
||||
>
|
||||
{step.title}
|
||||
</h3>
|
||||
|
||||
{/* Step Description */}
|
||||
<p
|
||||
className="leading-relaxed mb-6"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
lineHeight: 'var(--line-height-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-gray-muted)'
|
||||
}}
|
||||
>
|
||||
{step.description}
|
||||
</p>
|
||||
|
||||
{/* Bullet Points */}
|
||||
<div className="space-y-3">
|
||||
{step.details.map((detail, detailIndex) => (
|
||||
<div key={detailIndex} className="flex items-start gap-3">
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-primary flex-shrink-0 mt-2"></div>
|
||||
<span
|
||||
className="leading-relaxed"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
lineHeight: 'var(--line-height-small)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-black)'
|
||||
}}
|
||||
>
|
||||
{detail}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Right Side Fade Overlay */}
|
||||
<div
|
||||
className="absolute top-0 right-0 h-full w-16 pointer-events-none z-10"
|
||||
style={{
|
||||
background: 'linear-gradient(to left, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0.8) 40%, rgba(255, 255, 255, 0) 100%)'
|
||||
}}
|
||||
></div>
|
||||
|
||||
{/* Hide scrollbar with CSS */}
|
||||
<style>{`
|
||||
#consulting-approach-carousel::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Testimonials Section - Using home page testimonials with custom headers */}
|
||||
<TestimonialsSection
|
||||
title="What Our Clients Say"
|
||||
subtitle="Success stories from CXOs, founders, and board members who have achieved transformational results through our strategic consulting services."
|
||||
tagText="Client Success Stories"
|
||||
/>
|
||||
|
||||
{/* CTA Section - Using standardized home page CTA */}
|
||||
<CTABannerSection />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
695
src/components/services/CultureCompetence.tsx
Normal file
@@ -0,0 +1,695 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Button } from '../ui/button';
|
||||
import { Badge } from '../ui/badge';
|
||||
import { Card, CardContent } from '../ui/card';
|
||||
import { ImageWithFallback } from '../figma/ImageWithFallback';
|
||||
import { navigateTo } from '../Router';
|
||||
import { BrandedTag } from '../about/BrandedTag';
|
||||
import { TestimonialsSection } from '../TestimonialsSection';
|
||||
import { CTABannerSection } from '../CTABannerSection';
|
||||
import StackedOfferSection from '../StackedOfferSection';
|
||||
import {
|
||||
ArrowRight,
|
||||
CheckCircle,
|
||||
Settings,
|
||||
Calendar,
|
||||
Download,
|
||||
Network,
|
||||
Users,
|
||||
Target,
|
||||
Brain,
|
||||
Eye,
|
||||
TrendingUp,
|
||||
BarChart3,
|
||||
Award,
|
||||
Lightbulb,
|
||||
Shield,
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
ArrowLeft,
|
||||
Star,
|
||||
Zap,
|
||||
Globe,
|
||||
Heart,
|
||||
Users2,
|
||||
Sparkles
|
||||
} from 'lucide-react';
|
||||
import { PrimaryCTAButton } from '../PrimaryCTAButton';
|
||||
|
||||
// Culture competence cards data for the stacking section
|
||||
const cultureCards = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Inclusive Leadership Development",
|
||||
subtitle: "Building Inclusive Excellence",
|
||||
description:
|
||||
"Comprehensive inclusive leadership development that creates belonging, drives performance, and transforms organizational culture through proven methodologies and sustainable practices.",
|
||||
badge: "Featured",
|
||||
color: "from-blue-600 to-blue-700",
|
||||
icon: Heart,
|
||||
features: ["Inclusive Leadership", "Cultural Intelligence", "Bias Mitigation", "Equity Practices"],
|
||||
stats: [
|
||||
{ label: "Success Rate", value: "87%" },
|
||||
{ label: "Leaders Trained", value: "2,000+" },
|
||||
{ label: "Duration", value: "6-12 Mo" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Cultural Transformation",
|
||||
subtitle: "Comprehensive Culture Change",
|
||||
description:
|
||||
"End-to-end organizational culture transformation initiatives that create sustainable change, enhanced engagement, and measurable business impact through strategic implementation.",
|
||||
badge: "Popular",
|
||||
color: "from-purple-600 to-purple-700",
|
||||
icon: Sparkles,
|
||||
features: ["Culture Assessment", "Change Strategy", "Implementation Support", "Sustainability Planning"],
|
||||
stats: [
|
||||
{ label: "Transformation Rate", value: "92%" },
|
||||
{ label: "Organizations", value: "300+" },
|
||||
{ label: "Duration", value: "12-18 Mo" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Diversity & Inclusion Programs",
|
||||
subtitle: "Strategic D&I Excellence",
|
||||
description:
|
||||
"Strategic diversity and inclusion initiatives that create measurable organizational impact, enhanced representation, and inclusive cultures that drive innovation and performance.",
|
||||
badge: "Strategic",
|
||||
color: "from-green-600 to-green-700",
|
||||
icon: Users2,
|
||||
features: ["D&I Strategy", "Program Development", "Metrics & Measurement", "Stakeholder Engagement"],
|
||||
stats: [
|
||||
{ label: "Inclusion Score", value: "+79%" },
|
||||
{ label: "Programs Delivered", value: "500+" },
|
||||
{ label: "Duration", value: "8-15 Mo" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "High-Performance Culture Building",
|
||||
subtitle: "Excellence-Driven Cultures",
|
||||
description:
|
||||
"Create high-performance cultures that drive exceptional results, employee engagement, and organizational excellence through strategic culture design and implementation.",
|
||||
badge: "Comprehensive",
|
||||
color: "from-orange-600 to-orange-700",
|
||||
icon: Target,
|
||||
features: ["Performance Culture", "Engagement Strategies", "Recognition Systems", "Values Integration"],
|
||||
stats: [
|
||||
{ label: "Performance Lift", value: "+68%" },
|
||||
{ label: "Engagement Increase", value: "+79%" },
|
||||
{ label: "Duration", value: "9-18 Mo" }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const targetAudience = [
|
||||
{
|
||||
title: "Chief Diversity Officers",
|
||||
description: "CDOs and diversity leaders responsible for creating inclusive organizational cultures and D&I strategy",
|
||||
icon: Heart,
|
||||
characteristics: ["D&I strategy", "Cultural transformation", "Inclusive leadership", "Organizational change"]
|
||||
},
|
||||
{
|
||||
title: "HR Leadership",
|
||||
description: "CHROs and HR leaders driving culture change, employee engagement, and organizational development",
|
||||
icon: Users2,
|
||||
characteristics: ["Culture development", "Employee engagement", "Organizational design", "Change management"]
|
||||
},
|
||||
{
|
||||
title: "Executive Teams",
|
||||
description: "Senior leadership teams committed to building inclusive, high-performance organizational cultures",
|
||||
icon: Star,
|
||||
characteristics: ["Executive commitment", "Culture strategy", "Performance excellence", "Inclusive leadership"]
|
||||
}
|
||||
];
|
||||
|
||||
const outcomes = [
|
||||
{
|
||||
title: "Inclusive Culture Excellence",
|
||||
description: "Enhanced inclusive leadership capabilities and organizational belonging measures",
|
||||
icon: Heart,
|
||||
metrics: "87% improvement in inclusion scores and employee belonging metrics within 12 months"
|
||||
},
|
||||
{
|
||||
title: "Enhanced Employee Engagement",
|
||||
description: "Significant improvements in employee engagement, retention, and performance outcomes",
|
||||
icon: Sparkles,
|
||||
metrics: "79% increase in employee engagement scores and 23% improvement in retention rates"
|
||||
},
|
||||
{
|
||||
title: "Organizational Performance",
|
||||
description: "Measurable business impact through improved culture, innovation, and team effectiveness",
|
||||
icon: TrendingUp,
|
||||
metrics: "68% improvement in team performance and 31% increase in innovation metrics"
|
||||
}
|
||||
];
|
||||
|
||||
const approachSteps = [
|
||||
{
|
||||
step: "01",
|
||||
title: "Culture Assessment",
|
||||
description: "Comprehensive organizational culture and inclusion assessment using proven methodologies. Together, we make your vision manifest through comprehensive assessment methodologies and proven frameworks.",
|
||||
details: ["Culture diagnostic", "Inclusion assessment", "Employee insights", "Gap analysis"],
|
||||
icon: Eye
|
||||
},
|
||||
{
|
||||
step: "02",
|
||||
title: "Strategy Development",
|
||||
description: "Customized culture and inclusion strategy aligned with organizational goals and values. Together, we make your vision manifest through comprehensive design methodologies and proven frameworks.",
|
||||
details: ["Culture strategy", "D&I roadmap", "Leadership alignment", "Success metrics"],
|
||||
icon: Brain
|
||||
},
|
||||
{
|
||||
step: "03",
|
||||
title: "Implementation & Training",
|
||||
description: "Comprehensive implementation with leadership development, training, and change support. Together, we make your vision manifest through comprehensive learning methodologies and proven frameworks.",
|
||||
details: ["Leadership development", "Culture training", "Change support", "Skills building"],
|
||||
icon: Network
|
||||
},
|
||||
{
|
||||
step: "04",
|
||||
title: "Measurement & Sustainability",
|
||||
description: "Ongoing measurement, reinforcement, and continuous improvement for sustained culture change. Together, we make your vision manifest through comprehensive review methodologies and proven frameworks.",
|
||||
details: ["Progress tracking", "Impact measurement", "Continuous improvement", "Sustainability planning"],
|
||||
icon: BarChart3
|
||||
}
|
||||
];
|
||||
|
||||
export function CultureCompetence() {
|
||||
const [expandedAudienceIndex, setExpandedAudienceIndex] = useState<number | null>(0);
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, []);
|
||||
|
||||
const toggleAudienceExpanded = (index: number) => {
|
||||
setExpandedAudienceIndex(expandedAudienceIndex === index ? null : index);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
{/* Hero Section */}
|
||||
<section className="relative min-h-[90vh] flex flex-col">
|
||||
<div className="absolute inset-0 z-0">
|
||||
<ImageWithFallback
|
||||
src="https://images.unsplash.com/photo-1522202176988-66273c2fd55f?w=1920&h=1080&fit=crop"
|
||||
alt="Diverse team collaboration - culture competence"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-black/80 via-black/70 to-black/60"></div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 flex-1 flex items-center">
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="text-center max-w-5xl mx-auto">
|
||||
<div className="branded-tag-system-white mb-8 justify-center">
|
||||
<div className="dot"></div>
|
||||
<span className="text">Culture Excellence</span>
|
||||
</div>
|
||||
|
||||
<h1 className="text-h1-white mb-6">
|
||||
Culture Competence Services
|
||||
</h1>
|
||||
|
||||
<p className="text-body-lg-white mb-8 max-w-2xl mx-auto">
|
||||
Build inclusive, high-performance cultures that drive organizational excellence,
|
||||
employee engagement, and sustainable business success.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4 mb-12 justify-center">
|
||||
<PrimaryCTAButton
|
||||
text="Start culture assessment"
|
||||
onClick={() => navigateTo('/contact')}
|
||||
className="management-dev-primary-cta"
|
||||
/>
|
||||
|
||||
<Button
|
||||
onClick={() => window.open('/culture-guide', '_blank')}
|
||||
className="management-dev-glassmorphic-btn text-body px-8 py-4 min-h-[52px] border text-white transition-all duration-300"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
<Download className="w-5 h-5 mr-2" />
|
||||
Download Culture Guide
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 border-t border-white/20 backdrop-blur-sm bg-black/20">
|
||||
<div className="container mx-auto section-margin-x py-8">
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-8 text-center max-w-5xl mx-auto">
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">87%</div>
|
||||
<div className="text-small-white opacity-90">Inclusion score improvement</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">300+</div>
|
||||
<div className="text-small-white opacity-90">Organizations transformed</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">79%</div>
|
||||
<div className="text-small-white opacity-90">Engagement increase</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">92%</div>
|
||||
<div className="text-small-white opacity-90">Culture satisfaction rating</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Stacked Offer Section */}
|
||||
<StackedOfferSection cards={cultureCards} />
|
||||
|
||||
{/* Who It's For Section */}
|
||||
<section className="py-20" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="grid lg:grid-cols-2 gap-16 items-start max-w-7xl mx-auto">
|
||||
<div className="lg:pr-8">
|
||||
<h2 className="text-h2 mb-6 text-black leading-tight">
|
||||
Who It's <span className="text-primary">For</span>
|
||||
</h2>
|
||||
<p className="text-body-lg text-muted leading-relaxed">
|
||||
Our culture competence programs are designed for diversity leaders, HR executives,
|
||||
and senior leadership teams committed to building inclusive, high-performance cultures.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
{targetAudience.map((audience, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="border-b border-gray-400 last:border-b-0"
|
||||
>
|
||||
<button
|
||||
onClick={() => toggleAudienceExpanded(index)}
|
||||
className="w-full py-6 px-0 flex items-center justify-between text-left hover:bg-transparent focus:outline-none group transition-all duration-200"
|
||||
style={{ backgroundColor: 'transparent' }}
|
||||
>
|
||||
<span className="text-h4 text-black group-hover:text-primary transition-colors duration-200 pr-4">
|
||||
{audience.title}
|
||||
</span>
|
||||
<div className="flex-shrink-0 ml-4">
|
||||
{expandedAudienceIndex === index ? (
|
||||
<ChevronUp className="w-6 h-6 text-primary transition-transform duration-200" />
|
||||
) : (
|
||||
<ChevronDown className="w-6 h-6 text-gray-400 group-hover:text-primary transition-all duration-200" />
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div className={`overflow-hidden transition-all duration-300 ease-in-out ${
|
||||
expandedAudienceIndex === index ? 'max-h-96 opacity-100' : 'max-h-0 opacity-0'
|
||||
}`}>
|
||||
<div className="pb-6 pr-8">
|
||||
<p className="text-body text-muted leading-relaxed mb-4">
|
||||
{audience.description}
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{audience.characteristics.map((characteristic, charIndex) => (
|
||||
<Badge key={charIndex} variant="outline" className="text-small" style={{ fontFamily: 'var(--font-family-base)' }}>
|
||||
{characteristic}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Expected Outcomes Section */}
|
||||
<section className="py-20" style={{ backgroundColor: 'rgba(247, 247, 253, 0.5)' }}>
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="text-center mb-16 max-w-4xl mx-auto">
|
||||
<h2 className="text-h2 mb-6">
|
||||
Expected <span className="text-primary">Outcomes</span>
|
||||
</h2>
|
||||
<p className="text-body-lg text-muted">
|
||||
Our culture competence programs deliver measurable improvements in inclusion,
|
||||
employee engagement, and organizational performance outcomes.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Three-Column Layout - Reference Design Style */}
|
||||
<div className="grid lg:grid-cols-3 gap-8 max-w-7xl mx-auto">
|
||||
|
||||
{/* Inclusive Culture Excellence */}
|
||||
<div className="bg-white p-10 rounded-2xl border border-gray-100 shadow-sm hover:shadow-lg transition-all duration-300">
|
||||
<div className="mb-8">
|
||||
<div className="w-20 h-20 rounded-2xl bg-gradient-to-br from-blue-50 to-blue-100 flex items-center justify-center mb-6">
|
||||
<Heart className="w-10 h-10 text-primary" />
|
||||
</div>
|
||||
|
||||
<h3 className="text-h3 text-black mb-4">
|
||||
Inclusive Culture Excellence
|
||||
</h3>
|
||||
|
||||
<p className="text-body text-muted leading-relaxed mb-8">
|
||||
Build inclusive leadership capabilities and organizational belonging measures
|
||||
that drive engagement and performance excellence.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Culture Excellence Visualization */}
|
||||
<div className="space-y-6">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-small text-muted font-medium">Inclusion Score</span>
|
||||
<span className="text-h4 text-green-600 font-bold">+87%</span>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<div className="flex items-end space-x-1 h-24 mb-4">
|
||||
<div className="bg-gray-200 h-8 w-4 rounded-sm"></div>
|
||||
<div className="bg-gray-300 h-12 w-4 rounded-sm"></div>
|
||||
<div className="bg-gray-200 h-16 w-4 rounded-sm"></div>
|
||||
<div className="bg-gray-300 h-10 w-4 rounded-sm"></div>
|
||||
<div className="bg-green-400 h-20 w-4 rounded-sm"></div>
|
||||
<div className="bg-green-500 h-24 w-4 rounded-sm"></div>
|
||||
<div className="bg-green-600 h-22 w-4 rounded-sm"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="text-center p-3 bg-green-50 rounded-lg border border-green-100">
|
||||
<div className="text-xl font-bold text-green-600">89%</div>
|
||||
<div className="text-small text-muted">Belonging</div>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-blue-50 rounded-lg border border-blue-100">
|
||||
<div className="text-xl font-bold text-primary">84%</div>
|
||||
<div className="text-small text-muted">Inclusion</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Enhanced Employee Engagement */}
|
||||
<div className="bg-white p-10 rounded-2xl border border-gray-100 shadow-sm hover:shadow-lg transition-all duration-300">
|
||||
<div className="mb-8">
|
||||
<div className="w-20 h-20 rounded-2xl bg-gradient-to-br from-yellow-50 to-yellow-100 flex items-center justify-center mb-6">
|
||||
<Sparkles className="w-10 h-10 text-accent" />
|
||||
</div>
|
||||
|
||||
<h3 className="text-h3 text-black mb-4">
|
||||
Enhanced Employee Engagement
|
||||
</h3>
|
||||
|
||||
<p className="text-body text-muted leading-relaxed mb-8">
|
||||
Significant improvements in employee engagement, retention rates,
|
||||
and overall organizational satisfaction metrics.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Engagement Metrics */}
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<span className="text-small text-muted font-medium">Engagement Level</span>
|
||||
<div className="flex items-center">
|
||||
<TrendingUp className="w-4 h-4 text-green-500 mr-1" />
|
||||
<span className="text-small text-green-600 font-medium">+79%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative bg-gray-100 rounded-full h-3 mb-6">
|
||||
<div
|
||||
className="bg-gradient-to-r from-yellow-400 to-yellow-500 h-3 rounded-full transition-all duration-1000"
|
||||
style={{ width: '79%' }}
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-small text-muted">Employee Satisfaction</span>
|
||||
<div className="bg-yellow-100 text-yellow-700 px-2 py-1 rounded text-small font-medium">
|
||||
82%
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-small text-muted">Retention Rate</span>
|
||||
<div className="bg-yellow-100 text-yellow-700 px-2 py-1 rounded text-small font-medium">
|
||||
77%
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-small text-muted">Culture Rating</span>
|
||||
<div className="bg-yellow-100 text-yellow-700 px-2 py-1 rounded text-small font-medium">
|
||||
85%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Organizational Performance */}
|
||||
<div className="bg-white p-10 rounded-2xl border border-gray-100 shadow-sm hover:shadow-lg transition-all duration-300">
|
||||
<div className="mb-8">
|
||||
<div className="w-20 h-20 rounded-2xl bg-gradient-to-br from-purple-50 to-purple-100 flex items-center justify-center mb-6">
|
||||
<TrendingUp className="w-10 h-10 text-primary" />
|
||||
</div>
|
||||
|
||||
<h3 className="text-h3 text-black mb-4">
|
||||
Organizational Performance
|
||||
</h3>
|
||||
|
||||
<p className="text-body text-muted leading-relaxed mb-8">
|
||||
Measurable business impact through improved culture, innovation metrics,
|
||||
and team effectiveness outcomes.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Performance KPIs */}
|
||||
<div className="space-y-6">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="text-center p-3 bg-blue-50 rounded-lg border border-blue-100">
|
||||
<div className="flex items-center justify-center mb-1">
|
||||
<span className="text-small text-muted mr-2">Performance</span>
|
||||
<TrendingUp className="w-3 h-3 text-green-500" />
|
||||
</div>
|
||||
<div className="text-xl font-bold text-primary">68%</div>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-purple-50 rounded-lg border border-purple-100">
|
||||
<div className="flex items-center justify-center mb-1">
|
||||
<span className="text-small text-muted mr-2">Innovation</span>
|
||||
<div className="w-3 h-3 flex items-center justify-center">
|
||||
<div className="w-0 h-0 border-l-2 border-r-2 border-b-3 border-transparent border-b-purple-500"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xl font-bold text-purple-500">31%</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-50 rounded-lg p-4">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<span className="text-small text-muted font-medium">Culture Impact</span>
|
||||
<span className="text-small text-primary font-bold">Business ROI</span>
|
||||
</div>
|
||||
<div className="flex items-end justify-between">
|
||||
<div className="text-2xl font-bold text-primary">74%</div>
|
||||
<div className="text-2xl font-bold text-green-600">$1.8M</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
<div className="text-center">
|
||||
<div className="text-lg font-bold text-primary">68%</div>
|
||||
<div className="text-xs text-muted">Performance</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-lg font-bold text-green-600">+31%</div>
|
||||
<div className="text-xs text-muted">Innovation</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-lg font-bold text-purple-500">23%</div>
|
||||
<div className="text-xs text-muted">Retention</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Results Confirmation Banner */}
|
||||
<div className="mt-16 max-w-4xl mx-auto">
|
||||
<div className="bg-gradient-to-r from-green-50 to-blue-50 rounded-xl p-6 border border-green-200">
|
||||
<div className="flex items-center justify-center gap-3">
|
||||
<div className="w-8 h-8 rounded-full bg-green-100 flex items-center justify-center">
|
||||
<CheckCircle className="w-5 h-5 text-green-600" />
|
||||
</div>
|
||||
<p className="text-body text-center" style={{ fontWeight: 'var(--font-weight-subhead)', color: '#059669' }}>
|
||||
All outcomes measured within 12 months of program implementation
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Our Approach Section - Carousel Design */}
|
||||
<section className="py-20" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12 items-start max-w-7xl mx-auto">
|
||||
|
||||
{/* Left Side - Title, Description & Navigation */}
|
||||
<div className="lg:col-span-5 lg:pr-8">
|
||||
<h2 className="text-h2 mb-6 leading-tight">
|
||||
Our <span className="text-primary">Approach</span>
|
||||
</h2>
|
||||
<p className="text-body-lg text-muted leading-relaxed mb-8">
|
||||
Comprehensive culture transformation journey combining assessment, strategic planning,
|
||||
implementation support, and measurement for sustainable organizational change.
|
||||
</p>
|
||||
|
||||
{/* Navigation Controls */}
|
||||
<div className="flex gap-3">
|
||||
<button
|
||||
onClick={() => {
|
||||
const container = document.getElementById('approach-carousel');
|
||||
if (container) {
|
||||
container.scrollBy({ left: -320, behavior: 'smooth' });
|
||||
}
|
||||
}}
|
||||
className="w-12 h-12 rounded-full bg-gray-100 hover:bg-primary hover:text-white transition-all duration-300 flex items-center justify-center border border-gray-200 hover:border-primary"
|
||||
aria-label="Previous approach step"
|
||||
>
|
||||
<ArrowLeft className="w-5 h-5" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
const container = document.getElementById('approach-carousel');
|
||||
if (container) {
|
||||
container.scrollBy({ left: 320, behavior: 'smooth' });
|
||||
}
|
||||
}}
|
||||
className="w-12 h-12 rounded-full bg-gray-100 hover:bg-primary hover:text-white transition-all duration-300 flex items-center justify-center border border-gray-200 hover:border-primary"
|
||||
aria-label="Next approach step"
|
||||
>
|
||||
<ArrowRight className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Side - Scrollable Approach Cards */}
|
||||
<div className="lg:col-span-7 relative">
|
||||
<div
|
||||
id="approach-carousel"
|
||||
className="flex gap-6 overflow-x-auto pb-4"
|
||||
style={{
|
||||
scrollSnapType: 'x mandatory',
|
||||
WebkitOverflowScrolling: 'touch',
|
||||
msOverflowStyle: 'none',
|
||||
scrollbarWidth: 'none'
|
||||
}}
|
||||
>
|
||||
{approachSteps.map((step, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex-shrink-0 w-80 bg-white rounded-2xl border border-gray-100 p-8 hover:shadow-lg transition-all duration-300 hover:transform hover:scale-[1.02]"
|
||||
style={{ scrollSnapAlign: 'start' }}
|
||||
>
|
||||
{/* Step Number */}
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div
|
||||
className="leading-none font-medium"
|
||||
style={{
|
||||
fontSize: 'var(--font-h2)',
|
||||
fontWeight: '500',
|
||||
lineHeight: 'var(--line-height-h2)',
|
||||
color: 'var(--color-accent)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{step.step}
|
||||
</div>
|
||||
<div className="w-16 h-0.5 bg-gradient-to-r from-accent to-transparent opacity-40"></div>
|
||||
</div>
|
||||
|
||||
{/* Step Title */}
|
||||
<h3
|
||||
className="leading-tight mb-6"
|
||||
style={{
|
||||
fontSize: 'var(--font-h3)',
|
||||
fontWeight: 'var(--font-weight-h3)',
|
||||
lineHeight: 'var(--line-height-h3)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-black)'
|
||||
}}
|
||||
>
|
||||
{step.title}
|
||||
</h3>
|
||||
|
||||
{/* Step Description */}
|
||||
<p
|
||||
className="leading-relaxed mb-6"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
lineHeight: 'var(--line-height-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-gray-muted)'
|
||||
}}
|
||||
>
|
||||
{step.description}
|
||||
</p>
|
||||
|
||||
{/* Bullet Points */}
|
||||
<div className="space-y-3">
|
||||
{step.details.map((detail, detailIndex) => (
|
||||
<div key={detailIndex} className="flex items-start gap-3">
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-primary flex-shrink-0 mt-2"></div>
|
||||
<span
|
||||
className="leading-relaxed"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
lineHeight: 'var(--line-height-small)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-black)'
|
||||
}}
|
||||
>
|
||||
{detail}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Right Side Fade Overlay */}
|
||||
<div
|
||||
className="absolute top-0 right-0 h-full w-16 pointer-events-none z-10"
|
||||
style={{
|
||||
background: 'linear-gradient(to left, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0.8) 40%, rgba(255, 255, 255, 0) 100%)'
|
||||
}}
|
||||
></div>
|
||||
|
||||
{/* Hide scrollbar with CSS */}
|
||||
<style>{`
|
||||
#approach-carousel::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<TestimonialsSection
|
||||
title="What Our Culture Leaders Say"
|
||||
subtitle="Hear from diversity officers and HR leaders who have transformed their organizational cultures through our comprehensive programs."
|
||||
tagText="Culture Success"
|
||||
/>
|
||||
|
||||
<CTABannerSection />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
693
src/components/services/ExecutiveCoaching.tsx
Normal file
@@ -0,0 +1,693 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Button } from '../ui/button';
|
||||
import { Badge } from '../ui/badge';
|
||||
import { Card, CardContent } from '../ui/card';
|
||||
import { ImageWithFallback } from '../figma/ImageWithFallback';
|
||||
import { navigateTo } from '../Router';
|
||||
import { BrandedTag } from '../about/BrandedTag';
|
||||
import { TestimonialsSection } from '../TestimonialsSection';
|
||||
import { CTABannerSection } from '../CTABannerSection';
|
||||
import StackedOfferSection from '../StackedOfferSection';
|
||||
import {
|
||||
Users,
|
||||
Target,
|
||||
ArrowRight,
|
||||
CheckCircle,
|
||||
Award,
|
||||
Lightbulb,
|
||||
Brain,
|
||||
UserCheck,
|
||||
Eye,
|
||||
TrendingUp,
|
||||
MessageCircle,
|
||||
Calendar,
|
||||
BarChart3,
|
||||
Shield,
|
||||
Star,
|
||||
User,
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
ArrowLeft,
|
||||
Download
|
||||
} from 'lucide-react';
|
||||
import { PrimaryCTAButton } from '../PrimaryCTAButton';
|
||||
|
||||
// Executive coaching cards data for the stacking section
|
||||
const coachingCards = [
|
||||
{
|
||||
id: 1,
|
||||
title: "1:1 Coaching Programs",
|
||||
subtitle: "Personalized Executive Development",
|
||||
description:
|
||||
"Confidential, personalized executive coaching programs tailored to individual leadership challenges and growth goals, designed for maximum impact and sustainable change.",
|
||||
badge: "Featured",
|
||||
color: "from-blue-600 to-blue-700",
|
||||
icon: User,
|
||||
features: ["Individual Assessment", "Customized Development Plan", "Regular Coaching Sessions", "Progress Tracking"],
|
||||
stats: [
|
||||
{ label: "Success Rate", value: "91%" },
|
||||
{ label: "Executives Coached", value: "450+" },
|
||||
{ label: "Duration", value: "3-6 Mo" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "360° Feedback Debriefs",
|
||||
subtitle: "Comprehensive Leadership Assessment",
|
||||
description:
|
||||
"Expert-facilitated 360-degree feedback analysis and development planning based on multi-source insights that drive meaningful behavioral change and leadership effectiveness.",
|
||||
badge: "Popular",
|
||||
color: "from-purple-600 to-purple-700",
|
||||
icon: Eye,
|
||||
features: ["Multi-source Feedback", "Behavioral Analysis", "Strengths Identification", "Development Prioritization"],
|
||||
stats: [
|
||||
{ label: "Awareness Increase", value: "+88%" },
|
||||
{ label: "Leaders Assessed", value: "800+" },
|
||||
{ label: "Duration", value: "4-8 Wks" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Executive Presence & Transition Support",
|
||||
subtitle: "Leadership Transition Excellence",
|
||||
description:
|
||||
"Specialized coaching for leadership transitions and executive presence enhancement that ensures successful role transitions and enhanced stakeholder confidence.",
|
||||
badge: "Strategic",
|
||||
color: "from-green-600 to-green-700",
|
||||
icon: Award,
|
||||
features: ["Presence Development", "Transition Planning", "Stakeholder Management", "Communication Skills"],
|
||||
stats: [
|
||||
{ label: "Transition Success", value: "96%" },
|
||||
{ label: "Leaders Transitioned", value: "300+" },
|
||||
{ label: "Duration", value: "6-12 Mo" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "Strategic Leadership Coaching",
|
||||
subtitle: "Complex Challenge Navigation",
|
||||
description:
|
||||
"Advanced coaching for complex strategic challenges and organizational transformation that develops exceptional strategic thinking and decision-making capabilities.",
|
||||
badge: "Comprehensive",
|
||||
color: "from-orange-600 to-orange-700",
|
||||
icon: Target,
|
||||
features: ["Strategic Thinking", "Decision-Making", "Change Leadership", "Organizational Impact"],
|
||||
stats: [
|
||||
{ label: "Strategic Impact", value: "+79%" },
|
||||
{ label: "Strategic Leaders", value: "250+" },
|
||||
{ label: "Duration", value: "6-18 Mo" }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const targetAudience = [
|
||||
{
|
||||
title: "CXOs",
|
||||
description: "Chief executives seeking to enhance their leadership effectiveness and organizational impact",
|
||||
icon: Award,
|
||||
characteristics: ["Strategic leadership", "Organizational vision", "Stakeholder management", "Performance accountability"]
|
||||
},
|
||||
{
|
||||
title: "Senior Leaders Taking on New Roles",
|
||||
description: "Executives transitioning into expanded responsibilities or new organizational contexts",
|
||||
icon: TrendingUp,
|
||||
characteristics: ["Role transition", "New challenges", "Expanded scope", "Stakeholder expectations"]
|
||||
},
|
||||
{
|
||||
title: "High-Potential Successors",
|
||||
description: "Emerging leaders being prepared for senior executive roles and strategic responsibilities",
|
||||
icon: Star,
|
||||
characteristics: ["Leadership potential", "Career acceleration", "Strategic development", "Future leadership"]
|
||||
}
|
||||
];
|
||||
|
||||
const outcomes = [
|
||||
{
|
||||
title: "Heightened Self-Awareness",
|
||||
description: "Deep understanding of leadership strengths, blind spots, and impact on others",
|
||||
icon: Brain,
|
||||
metrics: "91% of executives report significantly increased self-awareness and emotional intelligence"
|
||||
},
|
||||
{
|
||||
title: "Enhanced Leadership Confidence",
|
||||
description: "Increased confidence in leadership abilities and decision-making in complex situations",
|
||||
icon: Shield,
|
||||
metrics: "88% improvement in leadership confidence and presence in challenging situations"
|
||||
},
|
||||
{
|
||||
title: "Clearer Strategic Execution",
|
||||
description: "Improved ability to translate strategy into action and drive organizational results",
|
||||
icon: Target,
|
||||
metrics: "79% better strategic execution effectiveness and organizational impact"
|
||||
}
|
||||
];
|
||||
|
||||
const approachSteps = [
|
||||
{
|
||||
step: "01",
|
||||
title: "Assessment & Goal Setting",
|
||||
description: "Comprehensive leadership assessment to understand current state and define specific coaching objectives. Together, we make your vision manifest through proven assessment methodologies and frameworks.",
|
||||
details: ["Leadership assessment", "360-degree feedback", "Goal clarification", "Success metrics"],
|
||||
icon: Eye
|
||||
},
|
||||
{
|
||||
step: "02",
|
||||
title: "Coach Matching",
|
||||
description: "Careful matching with ICF-certified executive coaches based on industry experience and leadership style compatibility. Together, we make your vision manifest through proven matching methodologies and frameworks.",
|
||||
details: ["Coach selection", "Chemistry session", "Approach alignment", "Relationship building"],
|
||||
icon: UserCheck
|
||||
},
|
||||
{
|
||||
step: "03",
|
||||
title: "Coaching Journey",
|
||||
description: "Confidential, goal-oriented coaching engagement over 3–6 months with regular sessions and ongoing support. Together, we make your vision manifest through proven coaching methodologies and frameworks.",
|
||||
details: ["Regular sessions", "Action planning", "Skill development", "Progress monitoring"],
|
||||
icon: TrendingUp
|
||||
},
|
||||
{
|
||||
step: "04",
|
||||
title: "Impact Measurement",
|
||||
description: "Comprehensive evaluation of coaching impact and integration of learning into ongoing leadership practice. Together, we make your vision manifest through proven measurement methodologies and frameworks.",
|
||||
details: ["Progress review", "Impact assessment", "Learning integration", "Sustained development"],
|
||||
icon: BarChart3
|
||||
}
|
||||
];
|
||||
|
||||
export function ExecutiveCoaching() {
|
||||
const [expandedAudienceIndex, setExpandedAudienceIndex] = useState<number | null>(0);
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, []);
|
||||
|
||||
const toggleAudienceExpanded = (index: number) => {
|
||||
setExpandedAudienceIndex(expandedAudienceIndex === index ? null : index);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
{/* Hero Section */}
|
||||
<section className="relative min-h-[90vh] flex flex-col">
|
||||
<div className="absolute inset-0 z-0">
|
||||
<ImageWithFallback
|
||||
src="https://images.unsplash.com/photo-1573164713714-d95e436ab8d6?w=1920&h=1080&fit=crop"
|
||||
alt="Executive coaching session - leadership development"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-black/80 via-black/70 to-black/60"></div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 flex-1 flex items-center">
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="text-center max-w-5xl mx-auto">
|
||||
<div className="branded-tag-system-white mb-8 justify-center">
|
||||
<div className="dot"></div>
|
||||
<span className="text">Executive Excellence</span>
|
||||
</div>
|
||||
|
||||
<h1 className="text-h1-white mb-6">
|
||||
Executive Coaching Services
|
||||
</h1>
|
||||
|
||||
<p className="text-body-lg-white mb-8 max-w-2xl mx-auto">
|
||||
Transform your leadership effectiveness through confidential, personalized coaching
|
||||
tailored to your unique challenges and growth objectives.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4 mb-12 justify-center">
|
||||
<PrimaryCTAButton
|
||||
text="Match me with a coach"
|
||||
onClick={() => navigateTo('/contact')}
|
||||
className="management-dev-primary-cta"
|
||||
/>
|
||||
|
||||
<Button
|
||||
onClick={() => window.open('/coaching-guide', '_blank')}
|
||||
className="management-dev-glassmorphic-btn text-body px-8 py-4 min-h-[52px] border text-white transition-all duration-300"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
<Download className="w-5 h-5 mr-2" />
|
||||
Download Coaching Guide
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 border-t border-white/20 backdrop-blur-sm bg-black/20">
|
||||
<div className="container mx-auto section-margin-x py-8">
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-8 text-center max-w-5xl mx-auto">
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">91%</div>
|
||||
<div className="text-small-white opacity-90">Self-awareness improvement</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">450+</div>
|
||||
<div className="text-small-white opacity-90">Executives coached</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">88%</div>
|
||||
<div className="text-small-white opacity-90">Leadership confidence boost</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">96%</div>
|
||||
<div className="text-small-white opacity-90">Client satisfaction rating</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Stacked Offer Section */}
|
||||
<StackedOfferSection cards={coachingCards} />
|
||||
|
||||
{/* Who It's For Section */}
|
||||
<section className="py-20" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="grid lg:grid-cols-2 gap-16 items-start max-w-7xl mx-auto">
|
||||
<div className="lg:pr-8">
|
||||
<h2 className="text-h2 mb-6 text-black leading-tight">
|
||||
Who It's <span className="text-primary">For</span>
|
||||
</h2>
|
||||
<p className="text-body-lg text-muted leading-relaxed">
|
||||
Our executive coaching programs serve senior leaders and high-potential executives
|
||||
who are committed to accelerating their leadership impact and effectiveness.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
{targetAudience.map((audience, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="border-b border-gray-400 last:border-b-0"
|
||||
>
|
||||
<button
|
||||
onClick={() => toggleAudienceExpanded(index)}
|
||||
className="w-full py-6 px-0 flex items-center justify-between text-left hover:bg-transparent focus:outline-none group transition-all duration-200"
|
||||
style={{ backgroundColor: 'transparent' }}
|
||||
>
|
||||
<span className="text-h4 text-black group-hover:text-primary transition-colors duration-200 pr-4">
|
||||
{audience.title}
|
||||
</span>
|
||||
<div className="flex-shrink-0 ml-4">
|
||||
{expandedAudienceIndex === index ? (
|
||||
<ChevronUp className="w-6 h-6 text-primary transition-transform duration-200" />
|
||||
) : (
|
||||
<ChevronDown className="w-6 h-6 text-gray-400 group-hover:text-primary transition-all duration-200" />
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div className={`overflow-hidden transition-all duration-300 ease-in-out ${
|
||||
expandedAudienceIndex === index ? 'max-h-96 opacity-100' : 'max-h-0 opacity-0'
|
||||
}`}>
|
||||
<div className="pb-6 pr-8">
|
||||
<p className="text-body text-muted leading-relaxed mb-4">
|
||||
{audience.description}
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{audience.characteristics.map((characteristic, charIndex) => (
|
||||
<Badge key={charIndex} variant="outline" className="text-small" style={{ fontFamily: 'var(--font-family-base)' }}>
|
||||
{characteristic}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Expected Outcomes Section */}
|
||||
<section className="py-20" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="text-center mb-16 max-w-4xl mx-auto">
|
||||
<h2 className="text-h2 mb-6">
|
||||
Expected <span className="text-primary">Outcomes</span>
|
||||
</h2>
|
||||
<p className="text-body-lg text-muted">
|
||||
Our executive coaching delivers transformational results in leadership effectiveness,
|
||||
confidence, and strategic execution capabilities for sustained success.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Three-Column Layout - Reference Design Style */}
|
||||
<div className="grid lg:grid-cols-3 gap-8 max-w-7xl mx-auto">
|
||||
|
||||
{/* Heightened Self-Awareness */}
|
||||
<div className="bg-white p-10 rounded-2xl border border-gray-100 shadow-sm hover:shadow-lg transition-all duration-300">
|
||||
<div className="mb-8">
|
||||
<div className="w-20 h-20 rounded-2xl bg-gradient-to-br from-blue-50 to-blue-100 flex items-center justify-center mb-6">
|
||||
<Brain className="w-10 h-10 text-primary" />
|
||||
</div>
|
||||
|
||||
<h3 className="text-h3 text-black mb-4">
|
||||
Heightened Self-Awareness
|
||||
</h3>
|
||||
|
||||
<p className="text-body text-muted leading-relaxed mb-8">
|
||||
Deep understanding of leadership strengths, blind spots, and impact on
|
||||
others for enhanced emotional intelligence and effectiveness.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Self-Awareness Visualization */}
|
||||
<div className="space-y-6">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-small text-muted font-medium">Self-Awareness Growth</span>
|
||||
<span className="text-h4 text-green-600 font-bold">+91%</span>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<div className="flex items-end space-x-1 h-24 mb-4">
|
||||
<div className="bg-gray-200 h-8 w-4 rounded-sm"></div>
|
||||
<div className="bg-gray-300 h-12 w-4 rounded-sm"></div>
|
||||
<div className="bg-gray-200 h-16 w-4 rounded-sm"></div>
|
||||
<div className="bg-gray-300 h-10 w-4 rounded-sm"></div>
|
||||
<div className="bg-green-400 h-20 w-4 rounded-sm"></div>
|
||||
<div className="bg-green-500 h-24 w-4 rounded-sm"></div>
|
||||
<div className="bg-green-600 h-22 w-4 rounded-sm"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="text-center p-3 bg-blue-50 rounded-lg border border-blue-100">
|
||||
<div className="text-xl font-bold text-primary">88%</div>
|
||||
<div className="text-small text-muted">EQ Improvement</div>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-green-50 rounded-lg border border-green-100">
|
||||
<div className="text-xl font-bold text-green-600">94%</div>
|
||||
<div className="text-small text-muted">Impact Clarity</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Enhanced Leadership Confidence */}
|
||||
<div className="bg-white p-10 rounded-2xl border border-gray-100 shadow-sm hover:shadow-lg transition-all duration-300">
|
||||
<div className="mb-8">
|
||||
<div className="w-20 h-20 rounded-2xl bg-gradient-to-br from-yellow-50 to-yellow-100 flex items-center justify-center mb-6">
|
||||
<Shield className="w-10 h-10 text-accent" />
|
||||
</div>
|
||||
|
||||
<h3 className="text-h3 text-black mb-4">
|
||||
Enhanced Leadership Confidence
|
||||
</h3>
|
||||
|
||||
<p className="text-body text-muted leading-relaxed mb-8">
|
||||
Increased confidence in leadership abilities and decision-making
|
||||
in complex and challenging situations.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Leadership Confidence Metrics */}
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<span className="text-small text-muted font-medium">Leadership Confidence</span>
|
||||
<div className="flex items-center">
|
||||
<TrendingUp className="w-4 h-4 text-green-500 mr-1" />
|
||||
<span className="text-small text-green-600 font-medium">+88%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative bg-gray-100 rounded-full h-3 mb-6">
|
||||
<div
|
||||
className="bg-gradient-to-r from-yellow-400 to-yellow-500 h-3 rounded-full transition-all duration-1000"
|
||||
style={{ width: '88%' }}
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-small text-muted">Decision Making</span>
|
||||
<div className="bg-yellow-100 text-yellow-700 px-2 py-1 rounded text-small font-medium">
|
||||
91%
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-small text-muted">Complex Situations</span>
|
||||
<div className="bg-yellow-100 text-yellow-700 px-2 py-1 rounded text-small font-medium">
|
||||
85%
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-small text-muted">Executive Presence</span>
|
||||
<div className="bg-yellow-100 text-yellow-700 px-2 py-1 rounded text-small font-medium">
|
||||
89%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Clearer Strategic Execution */}
|
||||
<div className="bg-white p-10 rounded-2xl border border-gray-100 shadow-sm hover:shadow-lg transition-all duration-300">
|
||||
<div className="mb-8">
|
||||
<div className="w-20 h-20 rounded-2xl bg-gradient-to-br from-purple-50 to-purple-100 flex items-center justify-center mb-6">
|
||||
<Target className="w-10 h-10 text-primary" />
|
||||
</div>
|
||||
|
||||
<h3 className="text-h3 text-black mb-4">
|
||||
Clearer Strategic Execution
|
||||
</h3>
|
||||
|
||||
<p className="text-body text-muted leading-relaxed mb-8">
|
||||
Improved ability to translate strategy into action and drive
|
||||
measurable organizational results and impact.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Strategic Execution KPIs */}
|
||||
<div className="space-y-6">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="text-center p-3 bg-blue-50 rounded-lg border border-blue-100">
|
||||
<div className="flex items-center justify-center mb-1">
|
||||
<span className="text-small text-muted mr-2">Strategy</span>
|
||||
<TrendingUp className="w-3 h-3 text-green-500" />
|
||||
</div>
|
||||
<div className="text-xl font-bold text-primary">79%</div>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-green-50 rounded-lg border border-green-100">
|
||||
<div className="flex items-center justify-center mb-1">
|
||||
<span className="text-small text-muted mr-2">Execution</span>
|
||||
<div className="w-3 h-3 flex items-center justify-center">
|
||||
<div className="w-0 h-0 border-l-2 border-r-2 border-b-3 border-transparent border-b-green-500"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xl font-bold text-green-500">86%</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-50 rounded-lg p-4">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<span className="text-small text-muted font-medium">Strategic Impact</span>
|
||||
<span className="text-small text-primary font-bold">Result Quality</span>
|
||||
</div>
|
||||
<div className="flex items-end justify-between">
|
||||
<div className="text-2xl font-bold text-primary">83%</div>
|
||||
<div className="text-2xl font-bold text-green-600">92%</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
<div className="text-center">
|
||||
<div className="text-lg font-bold text-primary">79%</div>
|
||||
<div className="text-xs text-muted">Execution</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-lg font-bold text-green-600">+24%</div>
|
||||
<div className="text-xs text-muted">Results</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-lg font-bold text-blue-500">92%</div>
|
||||
<div className="text-xs text-muted">Impact</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Results Confirmation Banner */}
|
||||
<div className="mt-16 max-w-4xl mx-auto">
|
||||
<div className="bg-gradient-to-r from-green-50 to-blue-50 rounded-xl p-6 border border-green-200">
|
||||
<div className="flex items-center justify-center gap-3">
|
||||
<div className="w-8 h-8 rounded-full bg-green-100 flex items-center justify-center">
|
||||
<CheckCircle className="w-5 h-5 text-green-600" />
|
||||
</div>
|
||||
<p className="text-body text-center" style={{ fontWeight: 'var(--font-weight-subhead)', color: '#059669' }}>
|
||||
All outcomes measured within 3-6 months of coaching engagement
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Our Approach Section - Carousel Design */}
|
||||
<section className="py-20" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12 items-start max-w-7xl mx-auto">
|
||||
|
||||
{/* Left Side - Title, Description & Navigation */}
|
||||
<div className="lg:col-span-5 lg:pr-8">
|
||||
<h2 className="text-h2 mb-6 leading-tight">
|
||||
Our <span className="text-primary">Approach</span>
|
||||
</h2>
|
||||
<p className="text-body-lg text-muted leading-relaxed mb-8">
|
||||
Confidential, goal-oriented coaching over 3–6 months matched with ICF-certified
|
||||
executive coaches for maximum leadership impact and sustainable transformation.
|
||||
</p>
|
||||
|
||||
{/* Navigation Controls */}
|
||||
<div className="flex gap-3">
|
||||
<button
|
||||
onClick={() => {
|
||||
const container = document.getElementById('coaching-approach-carousel');
|
||||
if (container) {
|
||||
container.scrollBy({ left: -384, behavior: 'smooth' });
|
||||
}
|
||||
}}
|
||||
className="w-12 h-12 rounded-full bg-gray-100 hover:bg-primary hover:text-white transition-all duration-300 flex items-center justify-center border border-gray-200 hover:border-primary"
|
||||
aria-label="Previous approach step"
|
||||
>
|
||||
<ArrowLeft className="w-5 h-5" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
const container = document.getElementById('coaching-approach-carousel');
|
||||
if (container) {
|
||||
container.scrollBy({ left: 384, behavior: 'smooth' });
|
||||
}
|
||||
}}
|
||||
className="w-12 h-12 rounded-full bg-gray-100 hover:bg-primary hover:text-white transition-all duration-300 flex items-center justify-center border border-gray-200 hover:border-primary"
|
||||
aria-label="Next approach step"
|
||||
>
|
||||
<ArrowRight className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Side - Scrollable Approach Cards */}
|
||||
<div className="lg:col-span-7 relative">
|
||||
<div
|
||||
id="coaching-approach-carousel"
|
||||
className="flex gap-6 overflow-x-auto pb-4"
|
||||
style={{
|
||||
scrollSnapType: 'x mandatory',
|
||||
WebkitOverflowScrolling: 'touch',
|
||||
msOverflowStyle: 'none',
|
||||
scrollbarWidth: 'none'
|
||||
}}
|
||||
>
|
||||
{approachSteps.map((step, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex-shrink-0 w-96 bg-white rounded-2xl border border-gray-200 p-8 shadow-md hover:shadow-2xl transition-all duration-300 hover:transform hover:scale-[1.02]"
|
||||
style={{ scrollSnapAlign: 'start' }}
|
||||
>
|
||||
{/* Step Number */}
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div
|
||||
className="leading-none font-medium"
|
||||
style={{
|
||||
fontSize: 'var(--font-h2)',
|
||||
fontWeight: '500',
|
||||
lineHeight: 'var(--line-height-h2)',
|
||||
color: 'var(--color-accent)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{step.step}
|
||||
</div>
|
||||
<div className="w-16 h-0.5 bg-gradient-to-r from-accent to-transparent opacity-40"></div>
|
||||
</div>
|
||||
|
||||
{/* Step Title */}
|
||||
<h3
|
||||
className="leading-tight mb-6"
|
||||
style={{
|
||||
fontSize: 'var(--font-h3)',
|
||||
fontWeight: 'var(--font-weight-h3)',
|
||||
lineHeight: 'var(--line-height-h3)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-black)'
|
||||
}}
|
||||
>
|
||||
{step.title}
|
||||
</h3>
|
||||
|
||||
{/* Step Description */}
|
||||
<p
|
||||
className="leading-relaxed mb-6"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
lineHeight: 'var(--line-height-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-gray-muted)'
|
||||
}}
|
||||
>
|
||||
{step.description}
|
||||
</p>
|
||||
|
||||
{/* Bullet Points */}
|
||||
<div className="space-y-3">
|
||||
{step.details.map((detail, detailIndex) => (
|
||||
<div key={detailIndex} className="flex items-start gap-3">
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-primary flex-shrink-0 mt-2"></div>
|
||||
<span
|
||||
className="leading-relaxed"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
lineHeight: 'var(--line-height-small)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-black)'
|
||||
}}
|
||||
>
|
||||
{detail}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Right Side Fade Overlay */}
|
||||
<div
|
||||
className="absolute top-0 right-0 h-full w-16 pointer-events-none z-10"
|
||||
style={{
|
||||
background: 'linear-gradient(to left, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0.8) 40%, rgba(255, 255, 255, 0) 100%)'
|
||||
}}
|
||||
></div>
|
||||
|
||||
{/* Hide scrollbar with CSS */}
|
||||
<style>{`
|
||||
#coaching-approach-carousel::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Testimonials Section - Using home page testimonials with custom headers */}
|
||||
<TestimonialsSection
|
||||
title="Success Stories"
|
||||
subtitle="Private success stories from executives who have achieved breakthrough results through our confidential coaching programs."
|
||||
tagText="Executive Success"
|
||||
/>
|
||||
|
||||
{/* CTA Section - Using standardized home page CTA */}
|
||||
<CTABannerSection />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
695
src/components/services/LeadershipDevelopment.tsx
Normal file
@@ -0,0 +1,695 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Button } from '../ui/button';
|
||||
import { Badge } from '../ui/badge';
|
||||
import { Card, CardContent } from '../ui/card';
|
||||
import { ImageWithFallback } from '../figma/ImageWithFallback';
|
||||
import { navigateTo } from '../Router';
|
||||
import { BrandedTag } from '../about/BrandedTag';
|
||||
import { TestimonialsSection } from '../TestimonialsSection';
|
||||
import { CTABannerSection } from '../CTABannerSection';
|
||||
import StackedOfferSection from '../StackedOfferSection';
|
||||
import {
|
||||
ArrowRight,
|
||||
CheckCircle,
|
||||
Settings,
|
||||
Calendar,
|
||||
Download,
|
||||
Network,
|
||||
Users,
|
||||
Target,
|
||||
Brain,
|
||||
Eye,
|
||||
TrendingUp,
|
||||
BarChart3,
|
||||
Award,
|
||||
Lightbulb,
|
||||
Shield,
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
ArrowLeft,
|
||||
Star,
|
||||
Zap,
|
||||
Globe,
|
||||
Crown,
|
||||
Compass,
|
||||
Users2
|
||||
} from 'lucide-react';
|
||||
import { PrimaryCTAButton } from '../PrimaryCTAButton';
|
||||
|
||||
const targetAudience = [
|
||||
{
|
||||
title: "C-Suite Executives",
|
||||
description: "CEOs, Presidents, and C-level executives leading organizational transformation and strategic initiatives",
|
||||
icon: Crown,
|
||||
characteristics: ["Strategic leadership", "Organizational vision", "Board governance", "Executive presence"]
|
||||
},
|
||||
{
|
||||
title: "Senior Directors",
|
||||
description: "Directors and Vice Presidents responsible for major business units and strategic functions",
|
||||
icon: Users2,
|
||||
characteristics: ["Business unit leadership", "Strategic execution", "Cross-functional leadership", "P&L responsibility"]
|
||||
},
|
||||
{
|
||||
title: "High-Potential Leaders",
|
||||
description: "Emerging leaders identified for accelerated development and future executive roles",
|
||||
icon: Star,
|
||||
characteristics: ["Leadership potential", "Career advancement", "Executive readiness", "Strategic capability"]
|
||||
}
|
||||
];
|
||||
|
||||
const outcomes = [
|
||||
{
|
||||
title: "Strategic Leadership Excellence",
|
||||
description: "Enhanced strategic thinking, vision development, and organizational leadership capabilities",
|
||||
icon: Compass,
|
||||
metrics: "94% of executives report improved strategic leadership effectiveness within 6 months"
|
||||
},
|
||||
{
|
||||
title: "Enhanced Executive Presence",
|
||||
description: "Strengthened executive presence, communication skills, and stakeholder influence",
|
||||
icon: Crown,
|
||||
metrics: "91% improvement in executive presence and stakeholder confidence ratings"
|
||||
},
|
||||
{
|
||||
title: "Organizational Impact",
|
||||
description: "Measurable improvements in organizational performance, culture, and strategic outcomes",
|
||||
icon: TrendingUp,
|
||||
metrics: "83% improvement in organizational performance metrics and strategic goal achievement"
|
||||
}
|
||||
];
|
||||
|
||||
const approachSteps = [
|
||||
{
|
||||
step: "01",
|
||||
title: "Leadership Assessment",
|
||||
description: "Comprehensive 360-degree assessment of current leadership capabilities and strategic readiness. Together, we make your vision manifest through comprehensive assessment methodologies and proven frameworks.",
|
||||
details: ["Executive assessment", "Leadership competency analysis", "Strategic readiness evaluation", "Development priorities"],
|
||||
icon: Eye
|
||||
},
|
||||
{
|
||||
step: "02",
|
||||
title: "Strategic Program Design",
|
||||
description: "Customized executive development curriculum aligned with organizational strategy and leadership needs. Together, we make your vision manifest through comprehensive design methodologies and proven frameworks.",
|
||||
details: ["Executive curriculum", "Strategic alignment", "Leadership development", "Program customization"],
|
||||
icon: Brain
|
||||
},
|
||||
{
|
||||
step: "03",
|
||||
title: "Executive Learning",
|
||||
description: "High-impact leadership development through executive coaching, peer learning, and strategic simulations. Together, we make your vision manifest through comprehensive learning methodologies and proven frameworks.",
|
||||
details: ["Executive workshops", "Strategic simulations", "Peer learning groups", "Leadership coaching"],
|
||||
icon: Network
|
||||
},
|
||||
{
|
||||
step: "04",
|
||||
title: "Impact & Sustainability",
|
||||
description: "Ongoing support and measurement to ensure leadership transformation and organizational impact. Together, we make your vision manifest through comprehensive review methodologies and proven frameworks.",
|
||||
details: ["Leadership application", "Impact measurement", "Ongoing coaching", "Sustainability planning"],
|
||||
icon: BarChart3
|
||||
}
|
||||
];
|
||||
|
||||
// Leadership development cards data for the stacking section
|
||||
const leadershipCards = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Executive Leadership Program",
|
||||
subtitle: "C-Suite & Senior Executive Development",
|
||||
description:
|
||||
"Transform senior executives into visionary leaders who drive organizational transformation, strategic excellence, and sustainable business growth through proven executive development methodologies.",
|
||||
badge: "Featured",
|
||||
color: "from-blue-600 to-blue-700",
|
||||
icon: Crown,
|
||||
features: ["Strategic Vision", "Executive Presence", "Organizational Impact", "Crisis Leadership"],
|
||||
stats: [
|
||||
{ label: "Success Rate", value: "94%" },
|
||||
{ label: "Executives Trained", value: "500+" },
|
||||
{ label: "Duration", value: "6-12 Mo" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Senior Management Development",
|
||||
subtitle: "Advanced Leadership for Directors & VPs",
|
||||
description:
|
||||
"Comprehensive leadership development for directors and vice presidents leading major business units and strategic functions across the organization.",
|
||||
badge: "Popular",
|
||||
color: "from-purple-600 to-purple-700",
|
||||
icon: Users2,
|
||||
features: ["Change Management", "Strategic Execution", "Cross-functional Leadership", "P&L Responsibility"],
|
||||
stats: [
|
||||
{ label: "Success Rate", value: "91%" },
|
||||
{ label: "Managers Trained", value: "800+" },
|
||||
{ label: "Duration", value: "4-8 Mo" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Leadership Pipeline Building",
|
||||
subtitle: "Succession Planning & Development",
|
||||
description:
|
||||
"Systematic development of future leaders and comprehensive succession planning initiatives to build organizational leadership capacity.",
|
||||
badge: "Strategic",
|
||||
color: "from-green-600 to-green-700",
|
||||
icon: TrendingUp,
|
||||
features: ["Succession Planning", "High-potential Development", "Leadership Assessment", "Career Progression"],
|
||||
stats: [
|
||||
{ label: "Pipeline Success", value: "87%" },
|
||||
{ label: "Leaders Identified", value: "1,200+" },
|
||||
{ label: "Duration", value: "12-18 Mo" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "Leadership Transformation",
|
||||
subtitle: "Organizational Culture Change",
|
||||
description:
|
||||
"Comprehensive organizational leadership culture transformation initiatives that create lasting change and sustainable business performance.",
|
||||
badge: "Comprehensive",
|
||||
color: "from-orange-600 to-orange-700",
|
||||
icon: Target,
|
||||
features: ["Culture Change", "Leadership Alignment", "Transformation Strategy", "Capability Building"],
|
||||
stats: [
|
||||
{ label: "Transformation Rate", value: "89%" },
|
||||
{ label: "Organizations", value: "150+" },
|
||||
{ label: "Duration", value: "6-24 Mo" }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
export function LeadershipDevelopment() {
|
||||
const [expandedAudienceIndex, setExpandedAudienceIndex] = useState<number | null>(0);
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, []);
|
||||
|
||||
const toggleAudienceExpanded = (index: number) => {
|
||||
setExpandedAudienceIndex(expandedAudienceIndex === index ? null : index);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen" style={{ backgroundColor: '#F7F7FD' }}>
|
||||
{/* Hero Section */}
|
||||
<section className="relative min-h-[90vh] flex flex-col">
|
||||
<div className="absolute inset-0 z-0">
|
||||
<ImageWithFallback
|
||||
src="https://images.unsplash.com/photo-1560472354-b33ff0c44a43?w=1920&h=1080&fit=crop"
|
||||
alt="Executive leadership meeting - leadership development"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-black/80 via-black/70 to-black/60"></div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 flex-1 flex items-center">
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="text-center max-w-5xl mx-auto">
|
||||
<div className="branded-tag-system-white mb-8 justify-center">
|
||||
<div className="dot"></div>
|
||||
<span className="text">Executive Excellence</span>
|
||||
</div>
|
||||
|
||||
<h1 className="text-h1-white mb-6">
|
||||
Leadership Development Services
|
||||
</h1>
|
||||
|
||||
<p className="text-body-lg-white mb-8 max-w-2xl mx-auto">
|
||||
Transform your executive leadership capabilities and organizational impact through
|
||||
comprehensive leadership development programs designed for senior leaders.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4 mb-12 justify-center">
|
||||
<PrimaryCTAButton
|
||||
text="Book a consultation"
|
||||
onClick={() => navigateTo('/contact')}
|
||||
className="management-dev-primary-cta"
|
||||
/>
|
||||
|
||||
<Button
|
||||
onClick={() => window.open('/executive-overview', '_blank')}
|
||||
className="management-dev-glassmorphic-btn text-body px-8 py-4 min-h-[52px] border text-white transition-all duration-300"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
<Download className="w-5 h-5 mr-2" />
|
||||
Download Overview
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 border-t border-white/20 backdrop-blur-sm bg-black/20">
|
||||
<div className="container mx-auto section-margin-x py-8">
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-8 text-center max-w-5xl mx-auto">
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">94%</div>
|
||||
<div className="text-small-white opacity-90">Leadership effectiveness improvement</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">500+</div>
|
||||
<div className="text-small-white opacity-90">Executives developed</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">65%</div>
|
||||
<div className="text-small-white opacity-90">Faster strategic execution</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">89%</div>
|
||||
<div className="text-small-white opacity-90">Organizational impact rating</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Stacked Offer Section */}
|
||||
<StackedOfferSection cards={leadershipCards} />
|
||||
|
||||
{/* Who It's For Section */}
|
||||
<section className="py-20" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="grid lg:grid-cols-2 gap-16 items-start max-w-7xl mx-auto">
|
||||
<div className="lg:pr-8">
|
||||
<h2 className="text-h2 mb-6 text-black leading-tight">
|
||||
Who It's <span className="text-primary">For</span>
|
||||
</h2>
|
||||
<p className="text-body-lg text-muted leading-relaxed">
|
||||
Our leadership development programs are designed for senior executives, directors, and
|
||||
high-potential leaders who drive organizational strategy and transformation.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
{targetAudience.map((audience, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="border-b border-gray-400 last:border-b-0"
|
||||
>
|
||||
<button
|
||||
onClick={() => toggleAudienceExpanded(index)}
|
||||
className="w-full py-6 px-0 flex items-center justify-between text-left hover:bg-transparent focus:outline-none group transition-all duration-200"
|
||||
style={{ backgroundColor: 'transparent' }}
|
||||
>
|
||||
<span className="text-h4 text-black group-hover:text-primary transition-colors duration-200 pr-4">
|
||||
{audience.title}
|
||||
</span>
|
||||
<div className="flex-shrink-0 ml-4">
|
||||
{expandedAudienceIndex === index ? (
|
||||
<ChevronUp className="w-6 h-6 text-primary transition-transform duration-200" />
|
||||
) : (
|
||||
<ChevronDown className="w-6 h-6 text-gray-400 group-hover:text-primary transition-all duration-200" />
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div className={`overflow-hidden transition-all duration-300 ease-in-out ${
|
||||
expandedAudienceIndex === index ? 'max-h-96 opacity-100' : 'max-h-0 opacity-0'
|
||||
}`}>
|
||||
<div className="pb-6 pr-8">
|
||||
<p className="text-body text-muted leading-relaxed mb-4">
|
||||
{audience.description}
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{audience.characteristics.map((characteristic, charIndex) => (
|
||||
<Badge key={charIndex} variant="outline" className="text-small" style={{ fontFamily: 'var(--font-family-base)' }}>
|
||||
{characteristic}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Expected Outcomes Section */}
|
||||
<section className="py-20" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="text-center mb-16 max-w-4xl mx-auto">
|
||||
<h2 className="text-h2 mb-6">
|
||||
Expected <span className="text-primary">Outcomes</span>
|
||||
</h2>
|
||||
<p className="text-body-lg text-muted">
|
||||
Our leadership development programs deliver measurable improvements in strategic leadership,
|
||||
executive effectiveness, and organizational performance.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Three-Column Layout - Reference Design Style */}
|
||||
<div className="grid lg:grid-cols-3 gap-8 max-w-7xl mx-auto">
|
||||
|
||||
{/* Strategic Leadership Excellence */}
|
||||
<div className="bg-white p-10 rounded-2xl border border-gray-100 shadow-sm hover:shadow-lg transition-all duration-300">
|
||||
<div className="mb-8">
|
||||
<div className="w-20 h-20 rounded-2xl bg-gradient-to-br from-blue-50 to-blue-100 flex items-center justify-center mb-6">
|
||||
<Compass className="w-10 h-10 text-primary" />
|
||||
</div>
|
||||
|
||||
<h3 className="text-h3 text-black mb-4">
|
||||
Strategic Leadership Excellence
|
||||
</h3>
|
||||
|
||||
<p className="text-body text-muted leading-relaxed mb-8">
|
||||
Enhance strategic thinking capabilities, vision development, and organizational
|
||||
leadership effectiveness where it matters most.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Strategic Leadership Visualization */}
|
||||
<div className="space-y-6">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-small text-muted font-medium">Strategic Effectiveness</span>
|
||||
<span className="text-h4 text-green-600 font-bold">+94%</span>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<div className="flex items-end space-x-1 h-24 mb-4">
|
||||
<div className="bg-gray-200 h-8 w-4 rounded-sm"></div>
|
||||
<div className="bg-gray-300 h-12 w-4 rounded-sm"></div>
|
||||
<div className="bg-gray-200 h-16 w-4 rounded-sm"></div>
|
||||
<div className="bg-gray-300 h-10 w-4 rounded-sm"></div>
|
||||
<div className="bg-green-400 h-20 w-4 rounded-sm"></div>
|
||||
<div className="bg-green-500 h-24 w-4 rounded-sm"></div>
|
||||
<div className="bg-green-600 h-22 w-4 rounded-sm"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="text-center p-3 bg-green-50 rounded-lg border border-green-100">
|
||||
<div className="text-xl font-bold text-green-600">91%</div>
|
||||
<div className="text-small text-muted">Vision Clarity</div>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-blue-50 rounded-lg border border-blue-100">
|
||||
<div className="text-xl font-bold text-primary">88%</div>
|
||||
<div className="text-small text-muted">Strategic Impact</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Enhanced Executive Presence */}
|
||||
<div className="bg-white p-10 rounded-2xl border border-gray-100 shadow-sm hover:shadow-lg transition-all duration-300">
|
||||
<div className="mb-8">
|
||||
<div className="w-20 h-20 rounded-2xl bg-gradient-to-br from-yellow-50 to-yellow-100 flex items-center justify-center mb-6">
|
||||
<Crown className="w-10 h-10 text-accent" />
|
||||
</div>
|
||||
|
||||
<h3 className="text-h3 text-black mb-4">
|
||||
Enhanced Executive Presence
|
||||
</h3>
|
||||
|
||||
<p className="text-body text-muted leading-relaxed mb-8">
|
||||
Strengthen executive presence, stakeholder influence, and communication
|
||||
effectiveness for enhanced leadership impact.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Executive Presence Metrics */}
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<span className="text-small text-muted font-medium">Executive Presence</span>
|
||||
<div className="flex items-center">
|
||||
<TrendingUp className="w-4 h-4 text-green-500 mr-1" />
|
||||
<span className="text-small text-green-600 font-medium">+91%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative bg-gray-100 rounded-full h-3 mb-6">
|
||||
<div
|
||||
className="bg-gradient-to-r from-yellow-400 to-yellow-500 h-3 rounded-full transition-all duration-1000"
|
||||
style={{ width: '91%' }}
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-small text-muted">Communication Impact</span>
|
||||
<div className="bg-yellow-100 text-yellow-700 px-2 py-1 rounded text-small font-medium">
|
||||
93%
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-small text-muted">Stakeholder Confidence</span>
|
||||
<div className="bg-yellow-100 text-yellow-700 px-2 py-1 rounded text-small font-medium">
|
||||
89%
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-small text-muted">Leadership Influence</span>
|
||||
<div className="bg-yellow-100 text-yellow-700 px-2 py-1 rounded text-small font-medium">
|
||||
91%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Organizational Impact */}
|
||||
<div className="bg-white p-10 rounded-2xl border border-gray-100 shadow-sm hover:shadow-lg transition-all duration-300">
|
||||
<div className="mb-8">
|
||||
<div className="w-20 h-20 rounded-2xl bg-gradient-to-br from-purple-50 to-purple-100 flex items-center justify-center mb-6">
|
||||
<TrendingUp className="w-10 h-10 text-primary" />
|
||||
</div>
|
||||
|
||||
<h3 className="text-h3 text-black mb-4">
|
||||
Organizational Impact
|
||||
</h3>
|
||||
|
||||
<p className="text-body text-muted leading-relaxed mb-8">
|
||||
Drive measurable organizational performance improvements and strategic
|
||||
goal achievement through enhanced leadership effectiveness.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Organizational Impact KPIs */}
|
||||
<div className="space-y-6">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="text-center p-3 bg-blue-50 rounded-lg border border-blue-100">
|
||||
<div className="flex items-center justify-center mb-1">
|
||||
<span className="text-small text-muted mr-2">Performance</span>
|
||||
<TrendingUp className="w-3 h-3 text-green-500" />
|
||||
</div>
|
||||
<div className="text-xl font-bold text-primary">83%</div>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-red-50 rounded-lg border border-red-100">
|
||||
<div className="flex items-center justify-center mb-1">
|
||||
<span className="text-small text-muted mr-2">Goal Achievement</span>
|
||||
<div className="w-3 h-3 flex items-center justify-center">
|
||||
<div className="w-0 h-0 border-l-2 border-r-2 border-b-3 border-transparent border-b-green-500"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xl font-bold text-green-500">89%</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-50 rounded-lg p-4">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<span className="text-small text-muted font-medium">Leadership Impact</span>
|
||||
<span className="text-small text-primary font-bold">Annual ROI</span>
|
||||
</div>
|
||||
<div className="flex items-end justify-between">
|
||||
<div className="text-2xl font-bold text-primary">87%</div>
|
||||
<div className="text-2xl font-bold text-green-600">$2.3M</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
<div className="text-center">
|
||||
<div className="text-lg font-bold text-primary">83%</div>
|
||||
<div className="text-xs text-muted">Performance</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-lg font-bold text-green-600">+28%</div>
|
||||
<div className="text-xs text-muted">Revenue</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-lg font-bold text-blue-500">89%</div>
|
||||
<div className="text-xs text-muted">Goals</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Results Confirmation Banner */}
|
||||
<div className="mt-16 max-w-4xl mx-auto">
|
||||
<div className="bg-gradient-to-r from-green-50 to-blue-50 rounded-xl p-6 border border-green-200">
|
||||
<div className="flex items-center justify-center gap-3">
|
||||
<div className="w-8 h-8 rounded-full bg-green-100 flex items-center justify-center">
|
||||
<CheckCircle className="w-5 h-5 text-green-600" />
|
||||
</div>
|
||||
<p className="text-body text-center" style={{ fontWeight: 'var(--font-weight-subhead)', color: '#059669' }}>
|
||||
All outcomes measured within 6 months of program completion
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Our Approach Section - Carousel Design */}
|
||||
<section className="py-20" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12 items-start max-w-7xl mx-auto">
|
||||
|
||||
{/* Left Side - Title, Description & Navigation */}
|
||||
<div className="lg:col-span-5 lg:pr-8">
|
||||
<h2 className="text-h2 mb-6 leading-tight">
|
||||
Our <span className="text-primary">Approach</span>
|
||||
</h2>
|
||||
<p className="text-body-lg text-muted leading-relaxed mb-8">
|
||||
Comprehensive leadership development journey combining strategic assessment, customized
|
||||
program design, and ongoing support for sustained executive excellence.
|
||||
</p>
|
||||
|
||||
{/* Navigation Controls */}
|
||||
<div className="flex gap-3">
|
||||
<button
|
||||
onClick={() => {
|
||||
const container = document.getElementById('approach-carousel');
|
||||
if (container) {
|
||||
container.scrollBy({ left: -384, behavior: 'smooth' });
|
||||
}
|
||||
}}
|
||||
className="w-12 h-12 rounded-full bg-gray-100 hover:bg-primary hover:text-white transition-all duration-300 flex items-center justify-center border border-gray-200 hover:border-primary"
|
||||
aria-label="Previous approach step"
|
||||
>
|
||||
<ArrowLeft className="w-5 h-5" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
const container = document.getElementById('approach-carousel');
|
||||
if (container) {
|
||||
container.scrollBy({ left: 384, behavior: 'smooth' });
|
||||
}
|
||||
}}
|
||||
className="w-12 h-12 rounded-full bg-gray-100 hover:bg-primary hover:text-white transition-all duration-300 flex items-center justify-center border border-gray-200 hover:border-primary"
|
||||
aria-label="Next approach step"
|
||||
>
|
||||
<ArrowRight className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Side - Scrollable Approach Cards */}
|
||||
<div className="lg:col-span-7 relative">
|
||||
<div
|
||||
id="approach-carousel"
|
||||
className="flex gap-6 overflow-x-auto pb-4"
|
||||
style={{
|
||||
scrollSnapType: 'x mandatory',
|
||||
WebkitOverflowScrolling: 'touch',
|
||||
msOverflowStyle: 'none',
|
||||
scrollbarWidth: 'none'
|
||||
}}
|
||||
>
|
||||
{approachSteps.map((step, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex-shrink-0 w-96 bg-white rounded-2xl border border-gray-200 p-8 shadow-md hover:shadow-2xl transition-all duration-300 hover:transform hover:scale-[1.02]"
|
||||
style={{ scrollSnapAlign: 'start' }}
|
||||
>
|
||||
{/* Step Number */}
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div
|
||||
className="leading-none font-medium"
|
||||
style={{
|
||||
fontSize: 'var(--font-h2)',
|
||||
fontWeight: '500',
|
||||
lineHeight: 'var(--line-height-h2)',
|
||||
color: 'var(--color-accent)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{step.step}
|
||||
</div>
|
||||
<div className="w-16 h-0.5 bg-gradient-to-r from-accent to-transparent opacity-40"></div>
|
||||
</div>
|
||||
|
||||
{/* Step Title */}
|
||||
<h3
|
||||
className="leading-tight mb-6"
|
||||
style={{
|
||||
fontSize: 'var(--font-h3)',
|
||||
fontWeight: 'var(--font-weight-h3)',
|
||||
lineHeight: 'var(--line-height-h3)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-black)'
|
||||
}}
|
||||
>
|
||||
{step.title}
|
||||
</h3>
|
||||
|
||||
{/* Step Description */}
|
||||
<p
|
||||
className="leading-relaxed mb-6"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
lineHeight: 'var(--line-height-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-gray-muted)'
|
||||
}}
|
||||
>
|
||||
{step.description}
|
||||
</p>
|
||||
|
||||
{/* Bullet Points */}
|
||||
<div className="space-y-3">
|
||||
{step.details.map((detail, detailIndex) => (
|
||||
<div key={detailIndex} className="flex items-start gap-3">
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-primary flex-shrink-0 mt-2"></div>
|
||||
<span
|
||||
className="leading-relaxed"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
lineHeight: 'var(--line-height-small)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-black)'
|
||||
}}
|
||||
>
|
||||
{detail}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Right Side Fade Overlay */}
|
||||
<div
|
||||
className="absolute top-0 right-0 h-full w-16 pointer-events-none z-10"
|
||||
style={{
|
||||
background: 'linear-gradient(to left, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0.8) 40%, rgba(255, 255, 255, 0) 100%)'
|
||||
}}
|
||||
></div>
|
||||
|
||||
{/* Hide scrollbar with CSS */}
|
||||
<style>{`
|
||||
#approach-carousel::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<TestimonialsSection
|
||||
title="What Our Executives Say"
|
||||
subtitle="Hear from senior leaders who have transformed their leadership effectiveness and organizational impact through our development programs."
|
||||
tagText="Executive Success"
|
||||
/>
|
||||
|
||||
<CTABannerSection />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
1577
src/components/services/LearningFacility.tsx
Normal file
692
src/components/services/ManagementDevelopment.tsx
Normal file
@@ -0,0 +1,692 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Button } from '../ui/button';
|
||||
import { Badge } from '../ui/badge';
|
||||
import { Card, CardContent } from '../ui/card';
|
||||
import { ImageWithFallback } from '../figma/ImageWithFallback';
|
||||
import { navigateTo } from '../Router';
|
||||
import { BrandedTag } from '../about/BrandedTag';
|
||||
import { TestimonialsSection } from '../TestimonialsSection';
|
||||
import { CTABannerSection } from '../CTABannerSection';
|
||||
import StackedOfferSection from '../StackedOfferSection';
|
||||
import {
|
||||
ArrowRight,
|
||||
CheckCircle,
|
||||
Settings,
|
||||
Calendar,
|
||||
Download,
|
||||
Network,
|
||||
Users,
|
||||
Target,
|
||||
Brain,
|
||||
Eye,
|
||||
TrendingUp,
|
||||
BarChart3,
|
||||
Award,
|
||||
Lightbulb,
|
||||
Shield,
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
ArrowLeft,
|
||||
Star,
|
||||
Zap,
|
||||
Globe
|
||||
} from 'lucide-react';
|
||||
import { PrimaryCTAButton } from '../PrimaryCTAButton';
|
||||
|
||||
// Management development cards data for the stacking section
|
||||
const managementCards = [
|
||||
{
|
||||
id: 1,
|
||||
title: "First-Time Manager Programs",
|
||||
subtitle: "Essential Leadership Transition",
|
||||
description:
|
||||
"Comprehensive programs for new supervisors transitioning from individual contributor roles to leadership positions, building essential management skills and confidence.",
|
||||
badge: "Featured",
|
||||
color: "from-blue-600 to-blue-700",
|
||||
icon: Users,
|
||||
features: ["Team Leadership Basics", "Delegation Skills", "Performance Conversations", "Time Management"],
|
||||
stats: [
|
||||
{ label: "Success Rate", value: "92%" },
|
||||
{ label: "Managers Trained", value: "1,800+" },
|
||||
{ label: "Duration", value: "6-12 Wks" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Operational Excellence Training",
|
||||
subtitle: "Process & Quality Management",
|
||||
description:
|
||||
"Advanced training in process optimization, quality management, and operational efficiency that drives measurable business results and team performance improvements.",
|
||||
badge: "Popular",
|
||||
color: "from-purple-600 to-purple-700",
|
||||
icon: Settings,
|
||||
features: ["Process Improvement", "Quality Systems", "Lean Methodologies", "Performance Metrics"],
|
||||
stats: [
|
||||
{ label: "Efficiency Gain", value: "+76%" },
|
||||
{ label: "Teams Trained", value: "900+" },
|
||||
{ label: "Duration", value: "8-16 Wks" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Team Development & Coaching",
|
||||
subtitle: "High-Performance Team Building",
|
||||
description:
|
||||
"Develop skills to build high-performing teams through effective coaching, feedback delivery, and conflict resolution that creates lasting team effectiveness.",
|
||||
badge: "Strategic",
|
||||
color: "from-green-600 to-green-700",
|
||||
icon: Network,
|
||||
features: ["Team Dynamics", "Coaching Skills", "Feedback Delivery", "Conflict Resolution"],
|
||||
stats: [
|
||||
{ label: "Team Performance", value: "+89%" },
|
||||
{ label: "Coaches Trained", value: "1,200+" },
|
||||
{ label: "Duration", value: "10-20 Wks" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "Performance Management Systems",
|
||||
subtitle: "Goal-Driven Excellence",
|
||||
description:
|
||||
"Comprehensive training in goal setting, performance tracking, and accountability frameworks that drive consistent results and organizational success.",
|
||||
badge: "Comprehensive",
|
||||
color: "from-orange-600 to-orange-700",
|
||||
icon: Target,
|
||||
features: ["Goal Setting", "KPI Development", "Performance Reviews", "Accountability Systems"],
|
||||
stats: [
|
||||
{ label: "Goal Achievement", value: "+93%" },
|
||||
{ label: "Systems Deployed", value: "650+" },
|
||||
{ label: "Duration", value: "4-12 Wks" }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const targetAudience = [
|
||||
{
|
||||
title: "First-Time Managers",
|
||||
description: "New supervisors and team leaders learning essential management skills and responsibilities",
|
||||
icon: Users,
|
||||
characteristics: ["New to management", "Team oversight", "Skill development", "Leadership transition"]
|
||||
},
|
||||
{
|
||||
title: "Operations Managers",
|
||||
description: "Managers focused on operational efficiency, process improvement, and team performance",
|
||||
icon: Settings,
|
||||
characteristics: ["Operational focus", "Process optimization", "Team performance", "Quality management"]
|
||||
},
|
||||
{
|
||||
title: "Department Heads",
|
||||
description: "Mid-level managers responsible for multiple teams and cross-functional collaboration",
|
||||
icon: Award,
|
||||
characteristics: ["Multi-team leadership", "Cross-functional work", "Strategic execution", "Resource management"]
|
||||
}
|
||||
];
|
||||
|
||||
const outcomes = [
|
||||
{
|
||||
title: "Improved Team Performance",
|
||||
description: "Measurable improvements in team productivity, engagement, and operational effectiveness",
|
||||
icon: TrendingUp,
|
||||
metrics: "89% of managers report improved team performance and productivity within 90 days"
|
||||
},
|
||||
{
|
||||
title: "Enhanced Management Confidence",
|
||||
description: "Increased confidence in handling management responsibilities and challenging situations",
|
||||
icon: Shield,
|
||||
metrics: "92% improvement in management confidence and decision-making effectiveness"
|
||||
},
|
||||
{
|
||||
title: "Better Operational Results",
|
||||
description: "Improved operational metrics, process efficiency, and quality outcomes",
|
||||
icon: BarChart3,
|
||||
metrics: "76% improvement in operational KPIs and process efficiency measures"
|
||||
}
|
||||
];
|
||||
|
||||
const approachSteps = [
|
||||
{
|
||||
step: "01",
|
||||
title: "Skills Assessment",
|
||||
description: "Comprehensive assessment of current management skills and development needs. Together, we make your vision manifest through comprehensive assessment methodologies and proven frameworks.",
|
||||
details: ["Management assessment", "Skills gap analysis", "360-degree feedback", "Development priorities"],
|
||||
icon: Eye
|
||||
},
|
||||
{
|
||||
step: "02",
|
||||
title: "Program Design",
|
||||
description: "Customized curriculum design based on assessment results and operational context. Together, we make your vision manifest through comprehensive design methodologies and proven frameworks.",
|
||||
details: ["Curriculum development", "Learning objectives", "Practical application", "Timeline planning"],
|
||||
icon: Brain
|
||||
},
|
||||
{
|
||||
step: "03",
|
||||
title: "Interactive Learning",
|
||||
description: "Hands-on workshops and peer learning sessions with real-world application. Together, we make your vision manifest through comprehensive learning methodologies and proven frameworks.",
|
||||
details: ["Management workshops", "Peer collaboration", "Case studies", "Skills practice"],
|
||||
icon: Network
|
||||
},
|
||||
{
|
||||
step: "04",
|
||||
title: "Application & Review",
|
||||
description: "Ongoing support and progress tracking to ensure skills transfer and improvement. Together, we make your vision manifest through comprehensive review methodologies and proven frameworks.",
|
||||
details: ["Skills application", "Progress tracking", "Feedback sessions", "Continuous improvement"],
|
||||
icon: BarChart3
|
||||
}
|
||||
];
|
||||
|
||||
export function ManagementDevelopment() {
|
||||
const [expandedAudienceIndex, setExpandedAudienceIndex] = useState<number | null>(0);
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, []);
|
||||
|
||||
const toggleAudienceExpanded = (index: number) => {
|
||||
setExpandedAudienceIndex(expandedAudienceIndex === index ? null : index);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
{/* Hero Section */}
|
||||
<section className="relative min-h-[90vh] flex flex-col">
|
||||
<div className="absolute inset-0 z-0">
|
||||
<ImageWithFallback
|
||||
src="https://images.unsplash.com/photo-1521737711867-e3b97375f902?w=1920&h=1080&fit=crop"
|
||||
alt="Professional business meeting - management development"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-black/80 via-black/70 to-black/60"></div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 flex-1 flex items-center">
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="text-center max-w-5xl mx-auto">
|
||||
<div className="branded-tag-system-white mb-8 justify-center">
|
||||
<div className="dot"></div>
|
||||
<span className="text">Management Excellence</span>
|
||||
</div>
|
||||
|
||||
<h1 className="text-h1-white mb-6">
|
||||
Management Development Services
|
||||
</h1>
|
||||
|
||||
<p className="text-body-lg-white mb-8 max-w-2xl mx-auto">
|
||||
As your trusted management development partner, we bring the expertise and
|
||||
experience to ensure your leadership workforce aligns seamlessly with your goals.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4 mb-12 justify-center">
|
||||
<PrimaryCTAButton
|
||||
text="Book a demo"
|
||||
onClick={() => navigateTo('/contact')}
|
||||
className="management-dev-primary-cta"
|
||||
/>
|
||||
|
||||
<Button
|
||||
onClick={() => window.open('/curriculum-download', '_blank')}
|
||||
className="management-dev-glassmorphic-btn text-body px-8 py-4 min-h-[52px] border text-white transition-all duration-300"
|
||||
style={{
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
<Download className="w-5 h-5 mr-2" />
|
||||
Download Curriculum
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 border-t border-white/20 backdrop-blur-sm bg-black/20">
|
||||
<div className="container mx-auto section-margin-x py-8">
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-8 text-center max-w-5xl mx-auto">
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">85%</div>
|
||||
<div className="text-small-white opacity-90">Improvement in team performance</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">1,800+</div>
|
||||
<div className="text-small-white opacity-90">Managers trained</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">40%</div>
|
||||
<div className="text-small-white opacity-90">Faster decision making</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-h2-white mb-2">93%</div>
|
||||
<div className="text-small-white opacity-90">Project completion rate</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Stacked Offer Section */}
|
||||
<StackedOfferSection cards={managementCards} />
|
||||
|
||||
{/* Who It's For Section */}
|
||||
<section className="py-20" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="grid lg:grid-cols-2 gap-16 items-start max-w-7xl mx-auto">
|
||||
<div className="lg:pr-8">
|
||||
<h2 className="text-h2 mb-6 text-black leading-tight">
|
||||
Who It's <span className="text-primary">For</span>
|
||||
</h2>
|
||||
<p className="text-body-lg text-muted leading-relaxed">
|
||||
Our management development programs cater to professionals at various stages of their
|
||||
management journey, from first-time supervisors to experienced leaders.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
{targetAudience.map((audience, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="border-b border-gray-400 last:border-b-0"
|
||||
>
|
||||
<button
|
||||
onClick={() => toggleAudienceExpanded(index)}
|
||||
className="w-full py-6 px-0 flex items-center justify-between text-left hover:bg-transparent focus:outline-none group transition-all duration-200"
|
||||
style={{ backgroundColor: 'transparent' }}
|
||||
>
|
||||
<span className="text-h4 text-black group-hover:text-primary transition-colors duration-200 pr-4">
|
||||
{audience.title}
|
||||
</span>
|
||||
<div className="flex-shrink-0 ml-4">
|
||||
{expandedAudienceIndex === index ? (
|
||||
<ChevronUp className="w-6 h-6 text-primary transition-transform duration-200" />
|
||||
) : (
|
||||
<ChevronDown className="w-6 h-6 text-gray-400 group-hover:text-primary transition-all duration-200" />
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div className={`overflow-hidden transition-all duration-300 ease-in-out ${
|
||||
expandedAudienceIndex === index ? 'max-h-96 opacity-100' : 'max-h-0 opacity-0'
|
||||
}`}>
|
||||
<div className="pb-6 pr-8">
|
||||
<p className="text-body text-muted leading-relaxed mb-4">
|
||||
{audience.description}
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{audience.characteristics.map((characteristic, charIndex) => (
|
||||
<Badge key={charIndex} variant="outline" className="text-small" style={{ fontFamily: 'var(--font-family-base)' }}>
|
||||
{characteristic}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Expected Outcomes Section - Redesigned to Match Reference */}
|
||||
<section className="py-20" style={{ backgroundColor: 'rgba(247, 247, 253, 0.5)' }}>
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="text-center mb-16 max-w-4xl mx-auto">
|
||||
<h2 className="text-h2 mb-6">
|
||||
Expected <span className="text-primary">Outcomes</span>
|
||||
</h2>
|
||||
<p className="text-body-lg text-muted">
|
||||
Our management development programs deliver tangible improvements in team performance,
|
||||
operational efficiency, and leadership effectiveness.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Three-Column Layout - Reference Design Style */}
|
||||
<div className="grid lg:grid-cols-3 gap-8 max-w-7xl mx-auto">
|
||||
|
||||
{/* Improved Team Performance */}
|
||||
<div className="bg-white p-10 rounded-2xl border border-gray-100 shadow-sm hover:shadow-lg transition-all duration-300">
|
||||
<div className="mb-8">
|
||||
<div className="w-20 h-20 rounded-2xl bg-gradient-to-br from-blue-50 to-blue-100 flex items-center justify-center mb-6">
|
||||
<TrendingUp className="w-10 h-10 text-primary" />
|
||||
</div>
|
||||
|
||||
<h3 className="text-h3 text-black mb-4">
|
||||
Improved Team Performance
|
||||
</h3>
|
||||
|
||||
<p className="text-body text-muted leading-relaxed mb-8">
|
||||
Smooth out team inefficiencies, conserve leadership capital, and invest
|
||||
in your management capabilities where it matters most.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Performance Visualization */}
|
||||
<div className="space-y-6">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-small text-muted font-medium">Team Productivity</span>
|
||||
<span className="text-h4 text-green-600 font-bold">+89%</span>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<div className="flex items-end space-x-1 h-24 mb-4">
|
||||
<div className="bg-gray-200 h-8 w-4 rounded-sm"></div>
|
||||
<div className="bg-gray-300 h-12 w-4 rounded-sm"></div>
|
||||
<div className="bg-gray-200 h-16 w-4 rounded-sm"></div>
|
||||
<div className="bg-gray-300 h-10 w-4 rounded-sm"></div>
|
||||
<div className="bg-green-400 h-20 w-4 rounded-sm"></div>
|
||||
<div className="bg-green-500 h-24 w-4 rounded-sm"></div>
|
||||
<div className="bg-green-600 h-22 w-4 rounded-sm"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="text-center p-3 bg-green-50 rounded-lg border border-green-100">
|
||||
<div className="text-xl font-bold text-green-600">85%</div>
|
||||
<div className="text-small text-muted">Engagement</div>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-blue-50 rounded-lg border border-blue-100">
|
||||
<div className="text-xl font-bold text-primary">92%</div>
|
||||
<div className="text-small text-muted">Retention</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Enhanced Management Confidence */}
|
||||
<div className="bg-white p-10 rounded-2xl border border-gray-100 shadow-sm hover:shadow-lg transition-all duration-300">
|
||||
<div className="mb-8">
|
||||
<div className="w-20 h-20 rounded-2xl bg-gradient-to-br from-yellow-50 to-yellow-100 flex items-center justify-center mb-6">
|
||||
<Shield className="w-10 h-10 text-accent" />
|
||||
</div>
|
||||
|
||||
<h3 className="text-h3 text-black mb-4">
|
||||
Enhanced Management Confidence
|
||||
</h3>
|
||||
|
||||
<p className="text-body text-muted leading-relaxed mb-8">
|
||||
Close confidence gaps and streamline decision-making processes
|
||||
without compromising leadership relationships.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Confidence Metrics */}
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<span className="text-small text-muted font-medium">Confidence Level</span>
|
||||
<div className="flex items-center">
|
||||
<TrendingUp className="w-4 h-4 text-green-500 mr-1" />
|
||||
<span className="text-small text-green-600 font-medium">+92%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative bg-gray-100 rounded-full h-3 mb-6">
|
||||
<div
|
||||
className="bg-gradient-to-r from-yellow-400 to-yellow-500 h-3 rounded-full transition-all duration-1000"
|
||||
style={{ width: '92%' }}
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-small text-muted">Decision Making</span>
|
||||
<div className="bg-yellow-100 text-yellow-700 px-2 py-1 rounded text-small font-medium">
|
||||
95%
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-small text-muted">Team Leadership</span>
|
||||
<div className="bg-yellow-100 text-yellow-700 px-2 py-1 rounded text-small font-medium">
|
||||
89%
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-small text-muted">Conflict Resolution</span>
|
||||
<div className="bg-yellow-100 text-yellow-700 px-2 py-1 rounded text-small font-medium">
|
||||
87%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Better Operational Results */}
|
||||
<div className="bg-white p-10 rounded-2xl border border-gray-100 shadow-sm hover:shadow-lg transition-all duration-300">
|
||||
<div className="mb-8">
|
||||
<div className="w-20 h-20 rounded-2xl bg-gradient-to-br from-purple-50 to-purple-100 flex items-center justify-center mb-6">
|
||||
<BarChart3 className="w-10 h-10 text-primary" />
|
||||
</div>
|
||||
|
||||
<h3 className="text-h3 text-black mb-4">
|
||||
Better Operational Results
|
||||
</h3>
|
||||
|
||||
<p className="text-body text-muted leading-relaxed mb-8">
|
||||
Seize control of your operational KPIs and build towards your
|
||||
performance goals without barriers.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Operational KPIs */}
|
||||
<div className="space-y-6">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="text-center p-3 bg-blue-50 rounded-lg border border-blue-100">
|
||||
<div className="flex items-center justify-center mb-1">
|
||||
<span className="text-small text-muted mr-2">Process Efficiency</span>
|
||||
<TrendingUp className="w-3 h-3 text-green-500" />
|
||||
</div>
|
||||
<div className="text-xl font-bold text-primary">76%</div>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-red-50 rounded-lg border border-red-100">
|
||||
<div className="flex items-center justify-center mb-1">
|
||||
<span className="text-small text-muted mr-2">Cost Reduction</span>
|
||||
<div className="w-3 h-3 flex items-center justify-center">
|
||||
<div className="w-0 h-0 border-l-2 border-r-2 border-b-3 border-transparent border-b-red-500"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xl font-bold text-red-500">12%</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-50 rounded-lg p-4">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<span className="text-small text-muted font-medium">Quality Metrics</span>
|
||||
<span className="text-small text-primary font-bold">Annual Revenue</span>
|
||||
</div>
|
||||
<div className="flex items-end justify-between">
|
||||
<div className="text-2xl font-bold text-primary">82%</div>
|
||||
<div className="text-2xl font-bold text-green-600">$1.7M</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
<div className="text-center">
|
||||
<div className="text-lg font-bold text-primary">76%</div>
|
||||
<div className="text-xs text-muted">Efficiency</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-lg font-bold text-green-600">+25%</div>
|
||||
<div className="text-xs text-muted">Revenue</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-lg font-bold text-red-500">-12%</div>
|
||||
<div className="text-xs text-muted">Costs</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Results Confirmation Banner */}
|
||||
<div className="mt-16 max-w-4xl mx-auto">
|
||||
<div className="bg-gradient-to-r from-green-50 to-blue-50 rounded-xl p-6 border border-green-200">
|
||||
<div className="flex items-center justify-center gap-3">
|
||||
<div className="w-8 h-8 rounded-full bg-green-100 flex items-center justify-center">
|
||||
<CheckCircle className="w-5 h-5 text-green-600" />
|
||||
</div>
|
||||
<p className="text-body text-center" style={{ fontWeight: 'var(--font-weight-subhead)', color: '#059669' }}>
|
||||
All outcomes measured within 6 months of program completion
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Our Approach Section - Carousel Design */}
|
||||
<section className="py-20" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="container mx-auto section-margin-x">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12 items-start max-w-7xl mx-auto">
|
||||
|
||||
{/* Left Side - Title, Description & Navigation */}
|
||||
<div className="lg:col-span-5 lg:pr-8">
|
||||
<h2 className="text-h2 mb-6 leading-tight">
|
||||
Our <span className="text-primary">Approach</span>
|
||||
</h2>
|
||||
<p className="text-body-lg text-muted leading-relaxed mb-8">
|
||||
Programmatic journey over 6–12 weeks combining practical application with peer learning
|
||||
for maximum management effectiveness.
|
||||
</p>
|
||||
|
||||
{/* Navigation Controls */}
|
||||
<div className="flex gap-3">
|
||||
<button
|
||||
onClick={() => {
|
||||
const container = document.getElementById('approach-carousel');
|
||||
if (container) {
|
||||
container.scrollBy({ left: -320, behavior: 'smooth' });
|
||||
}
|
||||
}}
|
||||
className="w-12 h-12 rounded-full bg-gray-100 hover:bg-primary hover:text-white transition-all duration-300 flex items-center justify-center border border-gray-200 hover:border-primary"
|
||||
aria-label="Previous approach step"
|
||||
>
|
||||
<ArrowLeft className="w-5 h-5" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
const container = document.getElementById('approach-carousel');
|
||||
if (container) {
|
||||
container.scrollBy({ left: 320, behavior: 'smooth' });
|
||||
}
|
||||
}}
|
||||
className="w-12 h-12 rounded-full bg-gray-100 hover:bg-primary hover:text-white transition-all duration-300 flex items-center justify-center border border-gray-200 hover:border-primary"
|
||||
aria-label="Next approach step"
|
||||
>
|
||||
<ArrowRight className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Side - Scrollable Approach Cards */}
|
||||
<div className="lg:col-span-7 relative">
|
||||
<div
|
||||
id="approach-carousel"
|
||||
className="flex gap-6 overflow-x-auto pb-4"
|
||||
style={{
|
||||
scrollSnapType: 'x mandatory',
|
||||
WebkitOverflowScrolling: 'touch',
|
||||
msOverflowStyle: 'none',
|
||||
scrollbarWidth: 'none'
|
||||
}}
|
||||
>
|
||||
{approachSteps.map((step, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex-shrink-0 w-80 bg-white rounded-2xl border border-gray-100 p-8 hover:shadow-lg transition-all duration-300 hover:transform hover:scale-[1.02]"
|
||||
style={{ scrollSnapAlign: 'start' }}
|
||||
>
|
||||
{/* Step Number */}
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div
|
||||
className="leading-none font-medium"
|
||||
style={{
|
||||
fontSize: 'var(--font-h2)',
|
||||
fontWeight: '500',
|
||||
lineHeight: 'var(--line-height-h2)',
|
||||
color: 'var(--color-accent)',
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{step.step}
|
||||
</div>
|
||||
<div className="w-16 h-0.5 bg-gradient-to-r from-accent to-transparent opacity-40"></div>
|
||||
</div>
|
||||
|
||||
{/* Step Title */}
|
||||
<h3
|
||||
className="leading-tight mb-6"
|
||||
style={{
|
||||
fontSize: 'var(--font-h3)',
|
||||
fontWeight: 'var(--font-weight-h3)',
|
||||
lineHeight: 'var(--line-height-h3)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-black)'
|
||||
}}
|
||||
>
|
||||
{step.title}
|
||||
</h3>
|
||||
|
||||
{/* Step Description */}
|
||||
<p
|
||||
className="leading-relaxed mb-6"
|
||||
style={{
|
||||
fontSize: 'var(--font-body)',
|
||||
lineHeight: 'var(--line-height-body)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-gray-muted)'
|
||||
}}
|
||||
>
|
||||
{step.description}
|
||||
</p>
|
||||
|
||||
{/* Bullet Points */}
|
||||
<div className="space-y-3">
|
||||
{step.details.map((detail, detailIndex) => (
|
||||
<div key={detailIndex} className="flex items-start gap-3">
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-primary flex-shrink-0 mt-2"></div>
|
||||
<span
|
||||
className="leading-relaxed"
|
||||
style={{
|
||||
fontSize: 'var(--font-small)',
|
||||
lineHeight: 'var(--line-height-small)',
|
||||
fontFamily: 'var(--font-family-base)',
|
||||
color: 'var(--color-black)'
|
||||
}}
|
||||
>
|
||||
{detail}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Right Side Fade Overlay */}
|
||||
<div
|
||||
className="absolute top-0 right-0 h-full w-16 pointer-events-none z-10"
|
||||
style={{
|
||||
background: 'linear-gradient(to left, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0.8) 40%, rgba(255, 255, 255, 0) 100%)'
|
||||
}}
|
||||
></div>
|
||||
|
||||
{/* Hide scrollbar with CSS */}
|
||||
<style>{`
|
||||
#approach-carousel::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<TestimonialsSection
|
||||
title="What Our Managers Say"
|
||||
subtitle="Hear from participants who have enhanced their management effectiveness and operational results through our development programs."
|
||||
tagText="Management Success"
|
||||
/>
|
||||
|
||||
<CTABannerSection />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
66
src/components/ui/accordion.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as AccordionPrimitive from "@radix-ui/react-accordion@1.2.3";
|
||||
import { ChevronDownIcon } from "lucide-react@0.487.0";
|
||||
|
||||
import { cn } from "./utils";
|
||||
|
||||
function Accordion({
|
||||
...props
|
||||
}: React.ComponentProps<typeof AccordionPrimitive.Root>) {
|
||||
return <AccordionPrimitive.Root data-slot="accordion" {...props} />;
|
||||
}
|
||||
|
||||
function AccordionItem({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AccordionPrimitive.Item>) {
|
||||
return (
|
||||
<AccordionPrimitive.Item
|
||||
data-slot="accordion-item"
|
||||
className={cn("border-b last:border-b-0", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function AccordionTrigger({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
|
||||
return (
|
||||
<AccordionPrimitive.Header className="flex">
|
||||
<AccordionPrimitive.Trigger
|
||||
data-slot="accordion-trigger"
|
||||
className={cn(
|
||||
"focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronDownIcon className="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200" />
|
||||
</AccordionPrimitive.Trigger>
|
||||
</AccordionPrimitive.Header>
|
||||
);
|
||||
}
|
||||
|
||||
function AccordionContent({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AccordionPrimitive.Content>) {
|
||||
return (
|
||||
<AccordionPrimitive.Content
|
||||
data-slot="accordion-content"
|
||||
className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
|
||||
{...props}
|
||||
>
|
||||
<div className={cn("pt-0 pb-4", className)}>{children}</div>
|
||||
</AccordionPrimitive.Content>
|
||||
);
|
||||
}
|
||||
|
||||
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
|
||||
157
src/components/ui/alert-dialog.tsx
Normal file
@@ -0,0 +1,157 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog@1.1.6";
|
||||
|
||||
import { cn } from "./utils";
|
||||
import { buttonVariants } from "./button";
|
||||
|
||||
function AlertDialog({
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
|
||||
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />;
|
||||
}
|
||||
|
||||
function AlertDialogTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
|
||||
);
|
||||
}
|
||||
|
||||
function AlertDialogPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
|
||||
);
|
||||
}
|
||||
|
||||
function AlertDialogOverlay({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Overlay
|
||||
data-slot="alert-dialog-overlay"
|
||||
className={cn(
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function AlertDialogContent({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Content>) {
|
||||
return (
|
||||
<AlertDialogPortal>
|
||||
<AlertDialogOverlay />
|
||||
<AlertDialogPrimitive.Content
|
||||
data-slot="alert-dialog-content"
|
||||
className={cn(
|
||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</AlertDialogPortal>
|
||||
);
|
||||
}
|
||||
|
||||
function AlertDialogHeader({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert-dialog-header"
|
||||
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function AlertDialogFooter({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert-dialog-footer"
|
||||
className={cn(
|
||||
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function AlertDialogTitle({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Title
|
||||
data-slot="alert-dialog-title"
|
||||
className={cn("text-lg font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function AlertDialogDescription({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Description
|
||||
data-slot="alert-dialog-description"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function AlertDialogAction({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Action>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Action
|
||||
className={cn(buttonVariants(), className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function AlertDialogCancel({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Cancel
|
||||
className={cn(buttonVariants({ variant: "outline" }), className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
AlertDialog,
|
||||
AlertDialogPortal,
|
||||
AlertDialogOverlay,
|
||||
AlertDialogTrigger,
|
||||
AlertDialogContent,
|
||||
AlertDialogHeader,
|
||||
AlertDialogFooter,
|
||||
AlertDialogTitle,
|
||||
AlertDialogDescription,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
};
|
||||
66
src/components/ui/alert.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import * as React from "react";
|
||||
import { cva, type VariantProps } from "class-variance-authority@0.7.1";
|
||||
|
||||
import { cn } from "./utils";
|
||||
|
||||
const alertVariants = cva(
|
||||
"relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-card text-card-foreground",
|
||||
destructive:
|
||||
"text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
function Alert({
|
||||
className,
|
||||
variant,
|
||||
...props
|
||||
}: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert"
|
||||
role="alert"
|
||||
className={cn(alertVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert-title"
|
||||
className={cn(
|
||||
"col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function AlertDescription({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert-description"
|
||||
className={cn(
|
||||
"text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { Alert, AlertTitle, AlertDescription };
|
||||
11
src/components/ui/aspect-ratio.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio@1.1.2";
|
||||
|
||||
function AspectRatio({
|
||||
...props
|
||||
}: React.ComponentProps<typeof AspectRatioPrimitive.Root>) {
|
||||
return <AspectRatioPrimitive.Root data-slot="aspect-ratio" {...props} />;
|
||||
}
|
||||
|
||||
export { AspectRatio };
|
||||
53
src/components/ui/avatar.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as AvatarPrimitive from "@radix-ui/react-avatar@1.1.3";
|
||||
|
||||
import { cn } from "./utils";
|
||||
|
||||
function Avatar({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Root>) {
|
||||
return (
|
||||
<AvatarPrimitive.Root
|
||||
data-slot="avatar"
|
||||
className={cn(
|
||||
"relative flex size-10 shrink-0 overflow-hidden rounded-full",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function AvatarImage({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
|
||||
return (
|
||||
<AvatarPrimitive.Image
|
||||
data-slot="avatar-image"
|
||||
className={cn("aspect-square size-full", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function AvatarFallback({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
|
||||
return (
|
||||
<AvatarPrimitive.Fallback
|
||||
data-slot="avatar-fallback"
|
||||
className={cn(
|
||||
"bg-muted flex size-full items-center justify-center rounded-full",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { Avatar, AvatarImage, AvatarFallback };
|
||||
46
src/components/ui/badge.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import * as React from "react";
|
||||
import { Slot } from "@radix-ui/react-slot@1.1.2";
|
||||
import { cva, type VariantProps } from "class-variance-authority@0.7.1";
|
||||
|
||||
import { cn } from "./utils";
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
|
||||
secondary:
|
||||
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
||||
destructive:
|
||||
"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
outline:
|
||||
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
function Badge({
|
||||
className,
|
||||
variant,
|
||||
asChild = false,
|
||||
...props
|
||||
}: React.ComponentProps<"span"> &
|
||||
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
|
||||
const Comp = asChild ? Slot : "span";
|
||||
|
||||
return (
|
||||
<Comp
|
||||
data-slot="badge"
|
||||
className={cn(badgeVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { Badge, badgeVariants };
|
||||