first commit
All checks were successful
CodeAnt AI Review - Stage 1 / codeant-review (push) Successful in 58s

This commit is contained in:
WDI-Ideas
2026-03-30 01:02:06 +05:30
parent 8b4c79d8f7
commit 9c679f896f
258 changed files with 59055 additions and 0 deletions

32
.gitea/workflows/.gitignore vendored Normal file
View File

@@ -0,0 +1,32 @@
# Node modules
node_modules/
# Logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# Environment files
.env
.env.local
.env.*.local
# Build output
dist/
build/
.next/
out/
# IDE files
.vscode/
.idea/
*.swp
# Mac / Linux / Windows system files
.DS_Store
Thumbs.db
# Temporary
*.tmp

View File

@@ -0,0 +1,11 @@
# KLC Public website
This is a code bundle for KLC Public website. The original project is available at https://www.figma.com/design/HKsCupXQAAtHdsmq0gcuEE/KLC-Public-website.
## Running the code
Run `npm i` to install the dependencies.
Run `npm run dev` to start the development server.

View File

@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>KLC Public website</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View File

@@ -0,0 +1,73 @@
{
"name": "KLC Public website",
"version": "0.1.0",
"private": true,
"dependencies": {
"@radix-ui/react-accordion": "^1.2.3",
"@radix-ui/react-alert-dialog": "^1.1.6",
"@radix-ui/react-aspect-ratio": "^1.1.2",
"@radix-ui/react-avatar": "^1.1.3",
"@radix-ui/react-checkbox": "^1.1.4",
"@radix-ui/react-collapsible": "^1.1.3",
"@radix-ui/react-context-menu": "^2.2.6",
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-dropdown-menu": "^2.1.6",
"@radix-ui/react-hover-card": "^1.1.6",
"@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-menubar": "^1.1.6",
"@radix-ui/react-navigation-menu": "^1.2.5",
"@radix-ui/react-popover": "^1.1.6",
"@radix-ui/react-progress": "^1.1.2",
"@radix-ui/react-radio-group": "^1.2.3",
"@radix-ui/react-scroll-area": "^1.2.3",
"@radix-ui/react-select": "^2.1.6",
"@radix-ui/react-separator": "^1.1.2",
"@radix-ui/react-slider": "^1.2.3",
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-switch": "^1.1.3",
"@radix-ui/react-tabs": "^1.1.3",
"@radix-ui/react-toggle": "^1.1.2",
"@radix-ui/react-toggle-group": "^1.1.2",
"@radix-ui/react-tooltip": "^1.1.8",
"@reduxjs/toolkit": "^2.9.0",
"@tailwindcss/postcss": "^4.1.12",
"class-variance-authority": "^0.7.1",
"clsx": "*",
"cmdk": "^1.1.1",
"embla-carousel-react": "^8.6.0",
"input-otp": "^1.4.2",
"lucide-react": "^0.487.0",
"motion": "*",
"next-themes": "^0.4.6",
"postcss": "^8.5.6",
"react": "^18.3.1",
"react-day-picker": "^8.10.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.55.0",
"react-redux": "^9.2.0",
"react-resizable-panels": "^2.1.7",
"react-router-dom": "^7.8.2",
"react-slick": "^0.31.0",
"recharts": "^2.15.2",
"slick-carousel": "^1.8.1",
"sonner": "^2.0.3",
"tailwind-merge": "*",
"tailwindcss": "^4.1.12",
"three": "^0.183.2",
"vaul": "^1.1.2"
},
"devDependencies": {
"@types/node": "^20.10.0",
"@types/react": "^19.1.12",
"@types/react-dom": "^19.1.8",
"@types/react-slick": "^0.23.13",
"@types/three": "^0.183.1",
"@vitejs/plugin-react": "^5.0.2",
"@vitejs/plugin-react-swc": "^3.10.2",
"vite": "^6.3.5"
},
"scripts": {
"dev": "vite",
"build": "vite build"
}
}

View File

@@ -0,0 +1,5 @@
export default {
plugins: {
"@tailwindcss/postcss": {},
}
}

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View 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>
);
}

View 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 612 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>
);
}

View 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",
}

View 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",
}

View 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",
}

View File

@@ -0,0 +1,165 @@
import { Route, Routes, useNavigate } from "react-router-dom";
import { OurExpertise } from "./components/about/OurExpertise";
import { OurImpact } from "./components/about/OurImpact";
import { OurVision } from "./components/about/OurVision";
import { AIChatbot } from "./components/AIChatbot";
import { Articles } from "./components/Articles";
import { AuthProvider } from "./components/AuthContext";
import { BlogDetail } from "./components/BlogDetail";
import { Blogs } from "./components/Blogs";
import { Cart } from "./components/Cart";
import { CartProvider } from "./components/CartContext";
import { Contact } from "./components/Contact";
import { CorporateSignIn } from "./components/CorporateSignIn";
import { CorporateSignUp } from "./components/CorporateSignUp";
import { Footer } from "./components/Footer";
import { LeadershipJourneyPage } from "./components/LeadershipJourneyPage";
import { LearningOnline } from "./components/LearningOnline";
import { Navigation } from "./components/Navigation";
import { ProgrammeDetail } from "./components/ProgrammeDetail";
import { SelfLearnerSignIn } from "./components/SelfLearnerSignIn";
import { SelfLearnerSignUp } from "./components/SelfLearnerSignUp";
import { Consulting } from "./components/services/Consulting";
import { CultureCompetence } from "./components/services/CultureCompetence";
import { ExecutiveCoaching } from "./components/services/ExecutiveCoaching";
import { LearningFacility } from "./components/services/LearningFacility";
import { ManagementDevelopment } from "./components/services/ManagementDevelopment";
import { Terms } from "./components/Terms";
import { Webinars } from "./components/Webinars";
import WebinarsListing from "./components/WebinarsListing";
import { WebinarsPage } from "./components/WebinarsPage";
import HomePage from './pages/HomePage';
import { AboutUs } from './components/AboutUs';
import { Services } from './components/Services';
import { LearningFacilityNew } from './components/LearningFacilityNew';
import { FooterNew } from './components/FooterNew';
import { Privacy } from "./pages/Privacy";
import { TermsCondition } from "./pages/TermsCondition";
import { FAQ } from "./pages/FAQ";
import { LeadershipPipelineDevelopment } from "./components/services/LeadershipPipelineDevelopment";
import { LeadershipDevelopment } from "./components/services/LeadershipDevelopment";
import { KautilyaFacility } from "./components/KautilyaFacility";
// 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' }}
>
<Navigation />
<Routes>
{/* Home Page */}
<Route path="/" element={<HomePage />} />
{/* Leadership Journey */}
<Route path="/leadership-journey" element={<LeadershipJourneyPage />} />
{/* Services Pages */}
<Route path="/services/leadership-pipeline-development" element={<LeadershipPipelineDevelopment />} />
<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 />} />
<Route path="/services" element={<Services />} />
<Route path="/services/kautilya-facility" element={<KautilyaFacility />} />
{/* About Us Pages */}
<Route path="/about/our-vision" element={<OurVision />} />
<Route path="/about/our-expertise" element={<OurExpertise />} />
<Route path="/about/our-impact" element={<OurImpact />} />
<Route path="/about-us" element={<AboutUs />} />
{/* 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/:slugAndId" 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 />} />
{/* Learning Facility */}
<Route path="/learning-facility" element={<LearningFacilityNew />} />
{/* Privacy policy */}
<Route path="/privacy-policy" element={<Privacy />} />
<Route path="/term-condition" element={<TermsCondition />} />
<Route path="/faq" element={<FAQ />} />
{/* 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>
<FooterNew />
{/* Add AIChatbot to all pages */}
<AIChatbot />
</div>
</CartProvider>
</AuthProvider>
);
}
// 404 Not Found Component
function NotFound() {
const navigate = useNavigate();
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>
<div className="flex items-center justify-center">
<button
onClick={() => navigate('/')}
className="brand-button-system"
>
Back to Home
</button>
</div>
</div>
</div>
);
}

View 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).

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -0,0 +1 @@
<svg height="658" viewBox="0 0 163.4 43" width="2500" xmlns="http://www.w3.org/2000/svg"><path d="m95.1 12 9.4-3.5-9.4-3.6v-4.9l16.1 6.5v4l-16.1 6.5z" fill="#a100ff"/><path d="m6.2 43c-3.4 0-6.2-1.7-6.2-5.5v-.2c0-4.6 4-6.2 8.9-6.2h2.3v-.9c0-1.9-.8-3-2.8-3-1.8 0-2.7 1-2.8 2.4h-5c.4-4.2 3.7-6.2 8.1-6.2 4.5 0 7.8 1.9 7.8 6.6v12.6h-5.1v-2.2c-1 1.4-2.7 2.6-5.2 2.6zm5-6.6v-1.8h-2.1c-2.6 0-3.9.7-3.9 2.4v.2c0 1.3.8 2.2 2.6 2.2 1.8-.1 3.4-1.1 3.4-3zm17.2 6.6c-5.2 0-9-3.2-9-9.6v-.3c0-6.4 4-9.8 9-9.8 4.3 0 7.8 2.2 8.2 7.1h-5c-.3-1.8-1.3-3-3.1-3-2.2 0-3.8 1.8-3.8 5.5v.6c0 3.8 1.4 5.5 3.8 5.5 1.8 0 3.1-1.3 3.4-3.4h4.8c-.3 4.4-3.2 7.4-8.3 7.4zm19.6 0c-5.2 0-9-3.2-9-9.6v-.3c0-6.4 4-9.8 9-9.8 4.3 0 7.8 2.2 8.2 7.1h-5c-.3-1.8-1.3-3-3.1-3-2.2 0-3.8 1.8-3.8 5.5v.6c0 3.8 1.4 5.5 3.8 5.5 1.8 0 3.1-1.3 3.4-3.4h4.8c-.3 4.4-3.2 7.4-8.3 7.4zm19.7 0c-5.4 0-9.1-3.2-9.1-9.5v-.4c0-6.3 3.9-9.8 9-9.8 4.7 0 8.6 2.6 8.6 8.9v2.3h-12.3c.2 3.4 1.7 4.7 3.9 4.7 2 0 3.1-1.1 3.5-2.4h4.9c-.6 3.5-3.6 6.2-8.5 6.2zm-3.7-12h7c-.1-2.8-1.4-4-3.5-4-1.6.1-3.1 1-3.5 4zm15.4-7.2h5.3v2.8c.9-1.8 2.8-3.2 5.7-3.2 3.4 0 5.7 2.1 5.7 6.6v12.6h-5.3v-11.8c0-2.2-.9-3.2-2.8-3.2-1.8 0-3.3 1.1-3.3 3.5v11.5h-5.3zm26.4-5.7v5.7h3.6v3.9h-3.6v8.9c0 1.4.6 2.1 1.9 2.1.8 0 1.3-.1 1.8-.3v4.1c-.6.2-1.7.4-3 .4-4.1 0-6-1.9-6-5.7v-9.5h-2.2v-3.9h2.2v-3.5zm23.4 24.5h-5.2v-2.8c-.9 1.8-2.7 3.2-5.5 3.2-3.4 0-5.9-2.1-5.9-6.5v-12.7h5.3v12c0 2.2.9 3.2 2.7 3.2s3.3-1.2 3.3-3.5v-11.7h5.3zm3.9-18.8h5.3v3.5c1.1-2.5 2.9-3.7 5.7-3.7v5.2c-3.6 0-5.7 1.1-5.7 4.2v9.7h-5.3zm21.7 19.2c-5.4 0-9.1-3.2-9.1-9.5v-.4c0-6.3 3.9-9.8 9-9.8 4.7 0 8.6 2.6 8.6 8.9v2.3h-12.2c.2 3.4 1.7 4.7 3.9 4.7 2 0 3.1-1.1 3.5-2.4h4.9c-.8 3.5-3.7 6.2-8.6 6.2zm-3.8-12h7.1c-.1-2.8-1.4-4-3.5-4-1.6.1-3.1 1-3.6 4z"/></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1 @@
<svg height="789" width="2500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496.68700000000007 156.71"><linearGradient id="a" x2="1" y1=".5" y2=".5"><stop offset="0" stop-color="#1094c4"/><stop offset=".447" stop-color="#473aff"/><stop offset=".725" stop-color="#7a00fd"/><stop offset="1" stop-color="#bf1969"/></linearGradient><path d="M433.387 12.698c0 7.068 5.4 13.114 12.9 13.114 7.127 0 12.713-6.046 12.713-13.114C459 5.576 453.412 0 446.288 0c-7.5 0-12.9 5.576-12.9 12.698zm-252.9-.03v32.235c-7.087-3.206-17.812-6.214-28.162-6.214-27.713 0-43.613 16.53-45.975 43.2-.638 7.755-.638 23.003 0 30.533 2.362 26.437 19.988 44.288 49.013 44.288 20.85 0 38.25-9.252 43.836-13.35 3.452-2.349 4.089-4.733 4.089-8.6V12.668c0-6.436-4.951-11.606-11.401-11.606-6.225 0-11.4 5.17-11.4 11.606zm-51.375 96.949c-.6-6.672-.412-19.34 0-25.144.863-15.69 8.625-23.861 24.938-23.861 11.212 0 21.937 3.866 26.438 6.667v59.741c-4.276 3.244-15.451 7.74-24.713 7.74-16.536 0-25.35-8.602-26.663-25.143zM328.125 50.49c-3.225 2.168-4.539 4.324-4.539 8.81l.001 84.483c0 6.445 5.4 11.633 11.437 11.633 6.413-.001 11.363-5.188 11.363-11.633l.001-77.145c4.913-3.026 15.036-6.026 25.574-6.026 13.35 0 24.975 7.308 24.975 24.506v58.665c0 6.445 5.325 11.633 11.362 11.633 6.676-.001 11.4-5.188 11.4-11.633V84.049c0-29.036-15.9-45.36-47.062-45.36-22.575 0-38.288 7.113-44.513 11.801h.001zm-97.124-5.38c-5.814 2.594-7.539 6.892-7.539 10.566 0 6.657 6.225 11.603 15.712 8.58 6.002-2.137 14.587-4.71 23.401-4.71 16.762 0 23.888 6.214 23.888 20.847v7.072c-9.9-2.347-17.25-3.618-27.339-3.618-30.074 0-43.612 16.988-43.612 35.666.002 21.063 14.587 37.197 44.063 37.197 22.35 0 38.7-7.973 44.737-12.054 3.225-2.16 4.05-4.496 4.05-8.366V80.393c0-29.047-16.95-41.704-44.474-41.704-13.124 0-26.663 3.428-32.888 6.42zm6.862 73.56c0-10.321 9.45-16.164 22.574-16.164 9.677 0 16.537 1.309 26.026 3.665v23.875c-2.174 1.71-10.125 6.669-24.975 6.669-15.226 0-23.627-6.45-23.627-18.046h.001zM15.413 45.11c-5.776 2.594-7.538 6.892-7.538 10.566 0 6.657 6.263 11.603 15.711 8.58 6.04-2.144 14.664-4.717 23.439-4.717 16.764 0 23.85 6.221 23.85 20.854v7.072c-9.9-2.347-17.176-3.618-27.3-3.618C14.55 83.847.899 99.687 0 117.589v4.016c.938 20.03 15.525 35.105 43.988 35.105 22.387 0 38.699-7.973 44.737-12.054 3.226-2.16 4.087-4.496 4.087-8.366l.002-55.897c0-29.047-16.99-41.704-44.514-41.704-13.087 0-26.626 3.428-32.888 6.42zm6.9 73.56c0-10.329 9.45-16.164 22.537-16.164 9.675 0 16.575 1.309 26.025 3.665v23.875c-2.138 1.71-10.088 6.669-24.938 6.669-15.261 0-23.625-6.45-23.625-18.046zm412.576-66.428v91.987c0 6.458 5.398 11.644 11.399 11.644 6.45 0 11.436-5.186 11.436-11.644l.001-91.987c0-6.44-5.175-11.6-11.438-11.6-6.45 0-11.4 5.16-11.4 11.6h.002z" fill="url(#a)"/><path d="M486.075 7.46l2.776 10.536 2.774-10.537c.225-.717.674-1.272 1.425-1.272.864 0 1.462.5 1.612 1.44l1.913 10.812c.112.498-.15 1.05-.675 1.17-.563.113-.938-.282-1.012-.776l-1.763-10.878-2.736 10.162c-.225.876-.751 1.44-1.54 1.44-.823 0-1.35-.503-1.612-1.44L484.5 8.014l-1.761 10.876c-.039.501-.452.832-1.052.72-.487-.121-.712-.617-.6-1.11l1.839-10.872c.188-.942.824-1.44 1.574-1.44s1.35.439 1.574 1.271h.001zm-9.711 11.207c0 .503-.452.89-.827.89-.523 0-.9-.45-.9-.889l.001-10.654h-3.824a.806.806 0 0 1-.827-.832c0-.44.375-.837.825-.837h9.264c.488 0 .899.397.899.837 0 .503-.413.832-.9.832h-3.711v10.653z"/></svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -0,0 +1 @@
<svg height="-358" viewBox="-.04 195.13 511.99 121.87" width="2500" xmlns="http://www.w3.org/2000/svg"><path d="m70.91 195.13c7.29 12.48 14.58 24.82 21.6 37.3-11.5 20.33-23 40.81-34.92 61-4.63 7.71-8.98 15.57-13.6 23.28-14.72.14-29.45.14-44.03 0 .42-.98.84-1.96 1.4-2.94 23.27-39.55 46.55-78.95 69.55-118.64zm319.74 30.57c8.13 15.15 16.27 30.29 24.4 45.44-2.8 0-5.75-.14-8.55 0-1.96-3.37-3.65-6.87-5.47-10.24-7.15-.28-14.44 0-21.6-.14-1.68 3.51-3.65 6.87-5.47 10.24h-8.41c8.13-15.15 16.4-30.29 25.1-45.3m-7.72 28.75c4.91 0 9.82 0 14.72.14-2.52-4.63-4.63-9.26-7.29-13.74a316.29 316.29 0 0 0 -7.43 13.6zm36.74-28.89c12.2 10.66 24.54 21.32 36.6 32.11.28-9.82 0-19.49.28-29.31 2.66 0 5.19.14 7.85.14-.14 15.01 0 30.15 0 45.16-7.99-6.73-15.85-13.74-23.7-20.61-4.49-3.79-8.83-7.57-13.04-11.64-.14 9.96 0 19.77-.14 29.73h-7.85c0-15.29-.14-30.43 0-45.58zm-259.15 45.44c8.41-15.15 16.69-30.43 25.38-45.44 8.13 15.15 16.13 30.29 24.4 45.44h-8.69a233.5 233.5 0 0 1 -5.19-10.24c-7.29-.14-14.44 0-21.74 0-1.82 3.37-3.65 6.87-5.61 10.24-2.94.14-5.75.14-8.55 0m17.67-16.55h14.86c-2.52-4.63-4.63-9.26-7.29-13.74-2.66 4.62-5.05 9.11-7.57 13.74zm102.93-24.82c4.21-2.1 8.98-2.24 13.46-1.4 3.93.84 7.01 3.65 9.4 6.59-2.1 1.12-4.21 2.1-6.31 3.23-2.8-4.35-9.4-5.75-13.46-2.38-2.1 1.54-1.96 5.33.42 6.45 5.47 2.94 12.06 3.65 16.83 7.71 4.91 4.21 4.77 12.48.14 16.83-4.91 4.91-12.62 6.03-19.07 4.21-5.47-1.4-9.54-6.31-10.24-11.78 2.66-.56 5.33-.98 7.99-1.4.28 2.52 1.26 5.47 3.79 6.73 3.79 2.1 9.4 1.54 12.06-2.24 1.82-2.52 1.26-6.45-1.26-8.27-5.33-3.51-12.34-3.79-17.25-8.27-2.52-2.1-3.51-5.75-2.66-8.98.69-3.25 3.36-5.63 6.16-7.03zm-68.29-.98c2.94-.42 6.03-.28 8.98-.28 3.23 4.77 6.45 9.54 9.54 14.3 3.37-4.77 6.59-9.54 9.96-14.16 3.09-.14 6.17-.14 9.26 0-4.91 6.73-9.82 13.46-14.58 20.33 4.91 7.57 10.38 14.72 15.29 22.44h-8.83c-3.79-5.33-7.43-10.8-11.08-16.13-3.79 5.33-7.71 10.66-11.5 16.13h-8.83c5.19-7.57 10.52-14.86 15.99-22.3-4.81-6.87-9.57-13.6-14.2-20.33zm45.01-.15a73.4 73.4 0 0 1 7.85 0v42.63h-7.99c.14-14.3 0-28.46.14-42.63zm76.99-.14c6.73.28 14.02-.98 20.33 2.38 6.03 3.23 7.43 12.9 1.82 17.25 3.51 1.4 7.01 4.07 7.85 7.85 1.54 4.77-1.12 9.96-5.33 12.48-7.57 4.21-16.41 2.24-24.68 2.8-.13-14.15.01-28.45.01-42.76m7.86 6.18v12.2c2.94-.14 6.17 0 8.69-1.82 2.52-2.24 2.52-7.01-.42-8.98-2.38-1.55-5.47-1.4-8.27-1.4m0 17.8v12.48c3.93-.14 8.13.56 11.78-1.4 3.23-1.54 3.79-6.73.84-8.83-3.51-2.95-8.28-2.25-12.62-2.25zm130.98-23.98h7.85v16.83c6.45-5.75 13.04-11.22 19.63-16.97h10.8c-7.71 6.31-15.43 12.76-23.14 19.35 7.57 7.99 15.29 15.57 23 23.28-3.51 0-7.15.28-10.66-.14-6.17-6.17-12.2-12.48-18.23-18.79l-1.26 1.68c-.14 5.75 0 11.5-.14 17.25h-7.99c0-14.02 0-28.18.14-42.49zm-402.76 42.5c14.72 0 29.45-.14 44.17.14 8.83 15.29 17.81 30.43 26.5 45.86-14.72.14-29.45 0-44.17 0-8.69-15.43-17.81-30.58-26.5-46z" fill="#ae285d"/></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 MiB

View File

@@ -0,0 +1 @@
<svg enable-background="new 0 0 438 151" viewBox="0 0 438 151" xmlns="http://www.w3.org/2000/svg"><path d="m0 0h438v151h-438zm45.2 23.3c-14.2 6.9-25.7 19.5-30.2 34.8-4.4 15.5-3.4 32.7 3.6 47.2 7.5 15 21.9 26.3 38.3 30 12.2 2.7 25.1 2.5 37.1-1.1 12.4-3.7 22.4-12.2 31.4-21.1-6.2-6.5-12.5-13-18.8-19.5-7.3 7.5-15.8 15.1-26.7 16.6-11.6 2-24.5-1.8-32.3-10.8-11.4-13.4-10.5-36 3.1-47.6 8-7.5 19.7-9.9 30.2-7.7 10.2 1.9 18.6 8.7 25.4 16.1 6.5-6 12.8-12.2 19.3-18.2-8.5-10.7-20.2-18.9-33.4-22.6-15.3-4.7-32.5-3.4-47 3.9m91-4.8c-.4 8.8-.1 17.7-.2 26.5h83.7c-.1-8.8.1-17.7-.1-26.5-27.8 0-55.6 0-83.4 0m136.9.1c-.3 1.1-.7 2.3-1 3.4-15.1 37.7-30.3 75.4-45.1 113.2h30.3c8.4-21.2 17-42.3 25.5-63.4 1.6-3.7 2.8-7.6 4.8-11 9.9 24.8 20 49.6 29.9 74.4 10.1.1 20.2 0 30.3 0-15.2-39-31.1-77.7-46.4-116.6-9.6 0-18.9-.1-28.3 0m57.3 27.8c10.6-.1 21.1 0 31.7 0v88.2h27.9c.2-29.4-.1-58.8.1-88.2h31.6c0-9.3 0-18.6 0-27.9-30.5.1-60.9 0-91.3 0-.1 9.4-.1 18.7 0 27.9m-194.5 16.9c.3 8.8-.2 17.5.3 26.3 27.8-.2 55.6-.1 83.4 0 .1-8.8 0-17.6 0-26.3-27.8 0-55.8-.2-83.7 0m.1 45.5c0 8.8-.2 17.7.1 26.5 27.8-.1 55.6-.1 83.4 0 .4-8.9.1-17.7.2-26.6-27.9 0-55.8-.1-83.7.1z" fill="#203f99"/><path d="m45.2 23.3c14.5-7.3 31.7-8.6 47.1-3.9 13.2 3.8 24.9 12 33.4 22.7-6.5 6-12.8 12.2-19.3 18.2-6.8-7.5-15.1-14.3-25.3-16.2-10.6-2.2-22.3.2-30.2 7.7-13.7 11.5-14.6 34.2-3.2 47.6 7.9 9 20.7 12.8 32.3 10.8 10.9-1.4 19.3-9 26.7-16.6 6.2 6.5 12.5 13 18.8 19.5-9 8.8-19.1 17.4-31.4 21.1-12 3.5-24.9 3.8-37 1.1-16.4-3.6-30.8-15-38.3-30-7.1-14.5-8.1-31.8-3.6-47.2 4.2-15.3 15.8-27.9 30-34.8zm227.9-4.7c9.4-.1 18.7 0 28.1 0 15.2 39 31.1 77.7 46.4 116.6-10.1-.1-20.2.1-30.3 0-9.9-24.9-20-49.6-29.9-74.4-2 3.5-3.2 7.4-4.8 11-8.5 21.2-17 42.3-25.5 63.4-10.1 0-20.2 0-30.3 0 15-37.8 30.2-75.5 45.3-113.2.3-1.1.7-2.2 1-3.4zm57.3 27.9c-.1-9.3 0-18.6 0-27.9 30.4 0 60.9.1 91.3 0v27.9c-10.5-.1-21.1 0-31.6 0-.2 29.4 0 58.8-.1 88.2-9.3 0-18.6 0-27.9 0 0-29.4 0-58.8 0-88.2-10.6-.1-21.2-.1-31.7 0z" fill="#fff"/><path d="m136.2 18.5c27.8 0 55.6.1 83.4 0 .3 8.8.1 17.7.1 26.5-27.9 0-55.8 0-83.7 0 .1-8.9-.2-17.7.2-26.5zm-.3 44.8c27.9-.2 55.9 0 83.8-.1 0 8.8.1 17.5 0 26.3-27.8-.1-55.6-.1-83.4 0-.6-8.7-.1-17.4-.4-26.2zm.1 45.5c27.9-.3 55.8-.2 83.7-.1-.1 8.9.2 17.7-.2 26.6-27.8-.1-55.6-.1-83.4 0-.3-8.7 0-17.6-.1-26.5z" fill="#f04f23"/></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 315.9 85.1" xmlns="http://www.w3.org/2000/svg" width="2500" height="673"><path d="M42.6 0h85v85h-85z" fill="#fff"/><g fill="#db0011"><path d="M170.1 42.6L127.6 0v85.1zM85.1 42.6L127.6 0h-85z"/><path d="M0 42.6l42.6 42.5V0zM85.1 42.6L42.6 85.1h85z"/></g><path d="M207.4 45.1H192v15.2h-7.7V24.7h7.7v14.6h15.4V24.7h7.7v35.6h-7.7zM233.7 61c-7.7 0-14-3.1-14.1-11.6h7.7c.1 3.8 2.3 6.1 6.5 6.1 3.1 0 6.7-1.6 6.7-5.1 0-2.8-2.4-3.6-6.4-4.8l-2.6-.7c-5.6-1.6-11.2-3.8-11.2-10.2 0-7.9 7.4-10.6 14.1-10.6 6.9 0 12.9 2.4 13 10.3h-7.7c-.3-3.2-2.2-5.1-5.8-5.1-2.9 0-5.7 1.5-5.7 4.7 0 2.6 2.4 3.4 7.4 5l3 .9c6.1 1.9 10 4 10 10-.1 8-7.9 11.1-14.9 11.1zM252.9 24.8h12.4c2.3-.1 4.7 0 7 .4 4.3 1 7.6 3.8 7.6 8.6 0 4.6-2.9 6.9-7.1 8 4.8.9 8.4 3.3 8.4 8.6 0 8.1-8 9.9-14.2 9.9h-14zm12.4 14.8c3.4 0 6.9-.7 6.9-4.8 0-3.7-3.2-4.7-6.4-4.7h-5.4v9.5zM266 55c3.6 0 7.1-.8 7.1-5.2s-3-5.2-6.7-5.2h-6.1V55zM301.2 61c-11.5 0-16.6-7.3-16.6-18.2s5.7-18.8 17-18.8c7.1 0 14 3.2 14.2 11.2h-8c-.4-3.6-2.8-5.4-6.2-5.4-7 0-9.1 7.5-9.1 13.2s2.1 12.3 8.8 12.3c3.5 0 6.1-1.9 6.6-5.5h8c-.8 8.2-7.3 11.2-14.7 11.2z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -0,0 +1 @@
<svg height="50.25mm" viewBox="0.15 0.15 50 50" width="50.25mm" xmlns="http://www.w3.org/2000/svg"><path d="M25.141.15c13.74 0 25.009 11.217 25.009 24.974 0 13.791-11.27 25.026-25.009 25.026C11.367 50.15.15 38.915.15 25.124.15 11.367 11.367.15 25.141.15m-3.457 46.596l10.67-18.5h-7.213l3.58-6.209 16.932-.018-3.58 6.244-6.138-.018L25.14 47.028c12.028 0 21.887-9.859 21.887-21.905 0-11.992-9.859-21.834-21.887-21.834-.882 0-1.763.053-2.575.159l-10.758 18.59H25.14l-3.615 6.207H4.612l13.792-23.88C9.639 7.205 3.272 15.46 3.272 25.124c0 10.881 7.99 19.947 18.412 21.622" fill="#004f8a" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 610 B

View File

@@ -0,0 +1 @@
<svg height="1025" width="2500" xmlns="http://www.w3.org/2000/svg" viewBox="0 -1.4210854715202004e-14 283.464 116.18800000000002"><path d="M250.094 7.094c.71-.05 1.341-.196 1.896-.439.552-.24.997-.61 1.323-1.117.331-.502.496-1.206.496-2.104 0-.759-.151-1.366-.441-1.819a3.04 3.04 0 0 0-1.145-1.04 4.978 4.978 0 0 0-1.611-.489c-.261-.04-.52-.065-.777-.086h-5.688v7.17h3.688c.797 0 1.549-.024 2.259-.076zm-13.348 13.168c1.437 1.489 3.135 2.656 5.092 3.506 1.955.849 4.076 1.271 6.358 1.271 2.255 0 4.347-.423 6.287-1.271a15.75 15.75 0 0 0 5.066-3.506c1.435-1.487 2.562-3.246 3.377-5.271.812-2.028 1.222-4.235 1.222-6.626 0-2.317-.41-4.482-1.222-6.491A17.011 17.011 0 0 0 262.014 0h-5.527c.564.958.854 2.15.854 3.588 0 2.043-.571 3.543-1.716 4.491-1.142.953-2.559 1.517-4.259 1.691l6.495 10.024h-3.795l-6.178-9.765h-3.74v9.765h-3.529V0h-6.338a16.93 16.93 0 0 0-.91 1.875c-.812 2.008-1.221 4.173-1.221 6.491 0 2.392.408 4.599 1.221 6.626.812 2.024 1.939 3.783 3.375 5.27zM99.877 38.848c-4.923 0-7.531 3.845-7.586 7.781h15.825c0-4.64-2.936-7.781-8.239-7.781zM265.756 0c.119.245.251.479.362.731 1.037 2.356 1.559 4.901 1.559 7.634 0 2.811-.521 5.396-1.559 7.771-1.037 2.365-2.438 4.427-4.207 6.175a18.945 18.945 0 0 1-6.18 4.079c-2.354.969-4.865 1.455-7.536 1.455-2.665 0-5.185-.485-7.553-1.455a19.146 19.146 0 0 1-6.235-4.079c-1.783-1.749-3.193-3.811-4.23-6.175-1.039-2.375-1.561-4.96-1.561-7.771 0-2.733.521-5.278 1.561-7.634.111-.252.244-.486.363-.731H0l27.257 116.188c57.227-36.803 112.227-1.435 114.439-.086h.006a.095.095 0 0 1 .017-.01h.028c.004.003.012.006.016.01h.009c2.208-1.349 57.211-36.717 114.437.086L283.464 0zM74.901 70.539H37.644V31.733h16.75V59.98h20.507zm50.111-16.1H92.32c0 5.051 4.276 8.861 9.975 8.861 3.389 0 6.476-1.121 8.657-4.154h13.037c-4.261 9.791-12.526 12.575-23.794 12.575-14.654 0-25.015-7.935-25.015-20.504 0-13.424 10.206-20.506 25.015-20.506 16.568 0 24.875 10.736 24.875 21.07-.001.862.066 1.723-.058 2.658zm30.326 16.162h-14.116l-21.076-38.9h19.02l12.094 23.646.333.713.303-.713L163.207 31.7h11.421zm37.224-.076h-16.156V31.628h16.156zm12.137-34.022l-4.867 5.294h-2.024l2.231-5.772h-4.459v-7.697h9.119zm21.203 35.13c-17.264 0-20.406-9.974-20.406-12.445h12.501c0 1.452 1.967 3.193 3.627 3.901 1.366.585 2.888.7 4.312.7 3.52 0 6.408-.854 6.408-2.898 0-2.489-2.957-3.041-6.219-3.509-8.877-1.28-21.318-2.771-21.318-12.701 0-9.262 10.113-13.976 20.849-13.976 17.187 0 21.743 9.521 21.743 12.278h-12.705c0-.823-.627-2.403-2.359-3.307-1.285-.671-3.174-1.214-5-1.214-4.747 0-7.418.988-7.418 3.163 0 1.777 2.408 2.658 5.865 3.251 9.023 1.549 22.879 2.085 22.879 13.066-.001 5.578-4.831 13.691-22.759 13.691z" fill="#a8112e"/></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 778 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

View File

@@ -0,0 +1 @@
<svg height="387" viewBox="17.717 17.72 566.913 57.891" width="2500" xmlns="http://www.w3.org/2000/svg"><path d="m515.958 17.72c-5.909 0-11.618.477-15.586.977v55.82h10.156v-23.123h5.196c1.245 0 2.488.019 3.476.508 3.43 1.025 5.173 3.499 6.407 9.296 1.488 7.032 3.028 11.807 3.75 13.32h10.39c-1.001-2.258-2.499-8.539-4.258-15.546-1.477-5.543-3.692-9.605-7.89-11.094v-.234c5.432-1.745 10.195-6.769 10.195-14.063 0-4.791-1.8-8.84-4.766-11.367-3.188-2.765-7.639-4.255-13.828-4.492h-3.242zm53.125 0c-12.121 0-19.805 7.02-19.805 16.016 0 8.234 5.947 13.21 15.079 16.446 7.19 2.484 9.921 4.982 9.921 9.219 0 4.492-3.698 7.734-10.156 7.734-5.187 0-10.109-1.746-13.32-3.711l-2.227 8.437c2.967 2.003 9.146 3.75 15.078 3.75 14.344 0 20.977-7.969 20.977-16.953 0-7.977-4.64-12.932-14.297-16.68-7.397-2.777-10.664-4.769-10.664-9.023 0-3.479 2.72-6.992 9.14-6.992 5.188 0 9.195 1.777 11.172 2.734l2.461-8.203c-2.99-1.458-7.414-2.773-13.359-2.773zm-203.71.079c-15.454.22-26.094 12.29-26.094 29.336 0 16.309 10.002 28.36 25.664 28.36h.429c15.235-.233 26.368-10.914 26.368-29.298 0-16.12-9.568-28.398-25.938-28.398h-.43zm99.726 0c-15.479.22-26.133 12.29-26.133 29.336 0 16.309 9.978 28.36 25.664 28.36h.47c15.185-.233 26.289-10.914 26.289-29.298 0-16.12-9.5-28.398-25.82-28.398zm-447.382.742v16.406h15.937v39.688h22.617v-39.688h15.469v-16.406zm69.765 0-21.406 56.094h21.64l11.407-33.32 11.64 33.32h21.875l-21.64-56.094zm40.156 0v16.406h15.977v39.688h22.578v-39.688h15.507v-16.406zm69.57 0-21.21 56.094h21.64l11.406-33.32 11.641 33.32h21.914l-21.679-56.095zm76.524 0-3.477 56.094h9.57l1.172-22.383c.288-7.886.585-16.896.86-24.14h.273c1.453 6.964 3.502 14.808 5.82 21.796l7.266 24.14h7.852l8.437-24.413c2.32-6.976 4.629-14.56 6.68-21.524 0 7.557.554 16.268.86 23.555l.898 22.969h10.195l-3.203-56.094h-13.399l-7.851 22.344c-2.026 6.695-4.043 13.65-5.508 19.766h-.312c-1.43-6.384-3.176-13.051-5.196-19.454l-7.304-22.656zm120.703.156v8.633h16.133v47.227h10.195v-47.227h15.898v-8.633zm122.539 6.836h2.226c5.188.72 8.399 3.484 8.399 9.063 0 4.736-3.21 8.272-8.399 9.023-.988 0-1.744.235-2.734.235h-5.938v-17.814c.977-.262 3.248-.508 6.446-.508zm-151.602.43c10.23 0 15.43 10.02 15.43 20.469 0 11.792-5.432 20.86-15.43 20.86-9.753 0-15.468-9.31-15.468-20.43 0-11.116 5.25-20.899 15.468-20.899zm99.727 0c10.192 0 15.39 10.02 15.39 20.469 0 11.792-5.429 20.86-15.39 20.86-9.803 0-15.469-9.31-15.469-20.43 0-11.116 5.226-20.899 15.469-20.899z" fill="#5982c4"/></svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View 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 | null>(null);
// 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] overflow-y-auto">
{/* 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:React.MouseEvent<HTMLButtonElement>) => {
e.currentTarget.style.backgroundColor = 'var(--color-primary)';
e.currentTarget.style.borderColor = 'var(--color-primary)';
}}
onMouseLeave={(e:React.MouseEvent<HTMLButtonElement>) => {
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:React.MouseEvent<HTMLButtonElement>) => {
if (!e.currentTarget.disabled) {
e.currentTarget.style.backgroundColor = '#030359';
}
}}
onMouseLeave={(e:React.MouseEvent<HTMLButtonElement>) => {
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>
)}
</>
);
}

View 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>
);
}

View File

@@ -0,0 +1,996 @@
import {
BookOpen,
Brain,
CheckCircle,
Heart,
Puzzle,
Shield,
Target,
TrendingUp,
Users,
Zap
} from 'lucide-react';
import { motion } from 'motion/react';
import { useEffect, useState } from 'react';
import Aparna from '../assets/Aparna-Nair.png';
import Balaji from '../assets/Balaji-Chandrakumar.jpeg';
import Diju from '../assets/Diju.jpeg';
import Ramkumar from '../assets/K-Ramkumar.png';
import Muralidharan from '../assets/R-Muralidharan.png';
import Ramesh from '../assets/Ramesh-Padmanabhan.jpeg';
import Swaminathan from '../assets/v-Swaminathan.jpg';
import svgPaths from '../imports/svg-kw7r0ellyk';
import { useGetAboutUsQuery } from '../redux/services/aboutUsApi';
import { BrandedTag } from './about/BrandedTag';
import { CTABannerSection } from './CTABannerSection';
import { PrimaryCTAButton } from './PrimaryCTAButton';
import { navigateTo } from './Router';
import { TeamMemberModal } from './TeamMemberModal';
import { TestimonialsSection } from './TestimonialsSection';
import { Button } from './ui/button';
import { FullScreenLoader } from './FullScreenLoader';
// Static detailed team member data for modal
const staticTeamMembersDetails = {
'Mr. K Ramkumar': {
name: 'Mr. K Ramkumar',
role: 'Managing Director',
image: Ramkumar,
experience: 'Over 40 years of experience in HR, Business, and Leadership Development.',
fullBio: `Mr. K. Ramkumar is the founder of Kautilya Leadership Centre, an organisation dedicated to building world-class thought and practice in leadership consulting, research and development. He retired as Executive Director on the Board of ICICI Bank and was also on the Boards of ICICI Prudential Life Insurance and ICICI Ventures. Prior to his retirement, he was President of ICICI Foundation, the CSR arm of ICICI Group.
Over a 40-year career, Ram worked with HAL, Hindustan Unilever, ICI and ICICI Bank. His passion has always been leadership development, with a focus on enabling individuals to discover and expand their leadership resources. He played a key role in making ICICI Group a recognized Leadership Factory.
He co-created the ICICI Manipal Academy for Banking and Insurance, which inducted over 12,000 young leaders, and envisioned the ICICI Academy for Skills, now skilling 35,000 youth annually across 23 centers.`,
expertise: [
'Leadership Development',
'HR & Business Strategy',
'Organizational Capability Building',
'CSR Initiatives',
'Talent Pipeline Development'
],
education: 'Graduate in Science; further leadership and management exposure through senior executive roles.',
achievements: [
'Founded Kautilya Leadership Centre',
'Executive Director on ICICI Bank Board',
'Created ICICI Manipal Academy (12,000 leaders trained)',
'Founded ICICI Academy for Skills (35,000+ youth skilled)',
'Author of "Leveraging Human Capital" (McGraw Hill)'
],
clientWork: 'Guided leadership development across ICICI Group and worked with Manipal Global Education to groom future banking leaders.',
boardRoles: 'Former Board Member of ICICI Prudential Life, ICICI Ventures; served on CSR and leadership committees.'
},
'Mr. R. Muralidharan': {
name: 'Mr. R. Muralidharan',
role: 'Practice Head Leadership Development',
image: Muralidharan,
experience: 'Over 33 years in Banking, Financial Services, and Leadership roles.',
fullBio: `Muralidharan (Murali) has 33+ years of experience in BFSI. He has held top leadership roles including CEO/COO positions at ICICI Bank, Dhanlaxmi Bank, and L&T Financial Services. His experience spans corporate and retail banking, strategy, technology, operations, and transformational projects.
At ICICI Bank, he was part of the founding team in 1994 and rose to become GM Global Operations Group. His work at Dhanlaxmi Bank as COO won The Asian Banker Award (2012). At L&T Financial Services, he led operations, technology, credit, and CSR across retail, rural, and infrastructure finance.`,
expertise: [
'Banking & Financial Services',
'Transformational Projects',
'Customer Experience',
'Leadership Development',
'Strategy & Operations'
],
education: 'B.Sc. (Mathematics, Statistics, Physics), M.A. in Economics (Gold Medallist), Executive Education at Wharton & Stanford GSB.',
achievements: [
'Helped set up ICICI Bank in 1994',
'Asian Banker Award (2012) for transformation',
'Group Head at L&T Financial Services',
'Held Board and leadership committee roles',
'Speaker at NIBM, TMTC, and industry fora'
],
clientWork: 'Worked with public and private banks, financial services firms, and non-profits on leadership and customer service transformation.',
boardRoles: 'Held board positions in business and non-profits; Vice-Chair, Customer Service Excellence Foundation.'
},
'Ms. Aparna Nair': {
name: 'Ms. Aparna Nair',
role: 'Practice Head Leadership Development',
image: Aparna,
experience: 'Over 25 years in leadership consulting, HR, and organizational development.',
fullBio: `Aparna is a founding member of Kautilya Leadership Centre and has been instrumental in building it since 2016. She advises Boards and Management Committees on leadership bench strength, designs CXO-level interventions, and consults on culture, performance, and talent management.
She is certified in MBTI and OPQ, has applied Balanced Scorecard frameworks, and built proprietary psychometric tools for KLC. Her prior roles include ICICI Bank and Blue Dart-FedEx, and she has engaged with leading organizations across multiple industries.`,
expertise: [
'Leadership Consulting',
'Psychometric Tools (MBTI, OPQ)',
'Culture & Strategy Alignment',
'Performance Management',
'HR Mentorship'
],
education: 'MBA in Marketing, Bachelors in Commerce; Accredited facilitator from SMR, Malaysia.',
achievements: [
'Co-founded KLC',
'Developed proprietary psychometric tools',
'Advised Boards across industries',
'Mentored HR professionals',
'Independent Director on a startup board'
],
clientWork: 'Worked with Godrej & Boyce, ICICI Prudential, Citi WAI, WNS, ThyssenKrupp, and others across pharma, BFSI, retail, auto, and private equity.',
boardRoles: 'Independent Woman Director; held leadership positions at ICICI Bank and Blue Dart-FedEx.'
},
'Mr. V. Swaminathan': {
name: 'Mr. V. Swaminathan',
role: 'Practice Head Leadership Development',
image: Swaminathan,
experience: 'Over 31 years in BFSI and office automation; 26 years in leadership at Kotak Group.',
fullBio: `Swami spent 26 years at Kotak Group in leadership roles across consumer banking, investment banking, asset management, and asset finance. He has built businesses from concept to scale and handled P&L responsibilities across divisions.
He stepped down as Joint President of Kotak Mahindra Bank in 2021 before joining KLC. He was on Kotaks Management Committee and led strategic initiatives, talent development, and capability building across the group.`,
expertise: [
'Banking & Financial Services',
'P&L Management',
'Change Management',
'Leadership Pipeline Development',
'Business Strategy'
],
education: 'PGDBM (Finance & Marketing), Gujarat University; Bachelor of Commerce; Senior Management programs at IIM Ahmedabad, IIM Bangalore, ISB Hyderabad.',
achievements: [
'Joint President at Kotak Mahindra Bank',
'Built and scaled multiple Kotak business units',
'Led strategic and leadership initiatives',
'Mentored talent pipelines across Kotak',
'Joined KLC as Practice Head in 2021'
],
clientWork: 'Extensive work with BFSI organizations and leadership development initiatives at scale.',
boardRoles: 'Key member of Kotaks Management Committee; contributor to strategic boards within Kotak divisions.'
},
'Mr. Balaji Chandrakumar': {
name: 'Mr. Balaji Chandrakumar',
role: 'Practice Head Leadership Development',
image: Balaji,
experience: '25+ years across Sales, HR Consulting, and HR leadership in India and SE Asia.',
fullBio: `Balaji has led HR leadership across India and SE Asia, including roles at Etisalat (India) and Goodhope Asia Holdings (Singapore). He has expertise in organization design, succession management, change management, and policy development.
Earlier, he worked in consulting with top Indian firms and began his HR journey with NIS Sparta after regional sales roles at Sandvik Asia.`,
expertise: [
'HR Leadership & Consulting',
'Organization Design',
'Succession Management',
'Change Management',
'Leadership Hiring'
],
education: 'Bachelors in Engineering, Masters in HR, Postgraduate in Management; Chartered Fellow of CIPD.',
achievements: [
'HR Leadership roles at Etisalat and Goodhope Asia',
'Consulted for top Indian companies',
'Built people and performance initiatives across industries',
'Certified in multiple HR programs',
'CIPD Chartered Fellow'
],
clientWork: 'Worked with leading companies in telecom, food, and HR consulting sectors across India and SE Asia.',
boardRoles: 'Advisor in HR capability building across organizations.'
},
'Mr. Ramesh Padmanabhan': {
name: 'Mr. Ramesh Padmanabhan',
role: 'Practice Head Leadership Development',
image: Ramesh,
experience: '30+ years in BFSI, with leadership roles at ICICI Bank, Dhanlaxmi Bank, and ADCB India.',
fullBio: `Ramesh has worked with SBI, ICICI Bank, Dhanlaxmi Bank, and ADCB India across corporate credit, retail banking, operations, and training. He has built leadership pipelines among branch and middle managers, and at ADCB India created products for NRIs and centralized operations.
He has consistently worked on capability building and leadership development alongside business roles.`,
expertise: [
'Banking & Financial Services',
'Leadership Development',
'Capability Building',
'Operations & Process Design',
'Retail & Corporate Banking'
],
education: 'Bachelors in Commerce; senior leadership roles provided extensive global exposure.',
achievements: [
'Built leadership pipelines in ICICI and ADCB',
'Part of ICICI for 13 years',
'Created NRI-focused products at ADCB India',
'Set up centralized operations at ADCB',
'30 years in BFSI leadership'
],
clientWork: 'Significant work in BFSI sector; tailored leadership development for managers and executives.',
boardRoles: 'Served on MANCOM and strategic boards in ICICI, Dhanlaxmi, and ADCB India.'
},
'Ms. Diju S': {
name: 'Ms. Diju S',
role: 'Practice Head Leadership Development',
image: Diju,
experience: '15+ years in leadership development, banking, and people-centric initiatives.',
fullBio: `Diju began at ICICI Bank, leading initiatives in customer service, Six Sigma, workplace induction (SWIFT), and branch operations. She drove business growth, mentored teams, and implemented technology projects.
After a career break, she joined KLC as Practice Head. She now co-creates leadership program designs with clients and manages knowledge business and learning facilities.`,
expertise: [
'Leadership Program Design',
'Customer Experience',
'Operational Excellence',
'Women Leadership & Diversity',
'Project Management'
],
education: 'B.Com (Honours), Delhi University; MBA (Finance & Marketing), Symbiosis Institute of Management Studies.',
achievements: [
'Led Six Sigma rollout at ICICI Bank branches',
'Part of SWIFT trainer team',
'Head of Customer Service at ICICI',
'Built leadership learning facility at KLC',
'Special focus on diversity & inclusion'
],
clientWork: 'Worked with BFSI clients and KLC partners to create custom leadership programs.',
boardRoles: 'Active in leadership forums and project management at KLC.'
}
};
// Helper function to get member details by name
const getMemberDetails = (nameRole: string) => {
// Extract the name from "Name - Role" format
const name = nameRole.split(' - ')[0];
return staticTeamMembersDetails[name as keyof typeof staticTeamMembersDetails] || null;
};
export function AboutUs() {
const [isVisible, setIsVisible] = useState(false);
const [expandedValue, setExpandedValue] = useState<string | null>('context');
const [selectedMember, setSelectedMember] = useState<any>(null);
const [isModalOpen, setIsModalOpen] = useState(false);
// Fetch About Us data from API
const { data: aboutUsData, isLoading, isError, error } = useGetAboutUsQuery();
const transformTestimonials = (apiTestimonials: any[]) => {
if (!apiTestimonials || apiTestimonials.length === 0) return [];
return apiTestimonials.map((testimonial, index) => ({
id: testimonial.id || index,
name: testimonial.name || "Anonymous",
role: testimonial.designation || "Client",
company: undefined,
avatar: testimonial.profile_photo_url || undefined,
image: testimonial.profile_photo_url || undefined,
quote: testimonial.content || "",
rating: 5,
isVideo: !!testimonial.video_url,
videoThumbnail: testimonial.video_thumbnail_url || testimonial.profile_photo_url,
videoUrl: testimonial.video_url || undefined
}));
};
// Transform the testimonials
const testimonialsData = transformTestimonials(aboutUsData?.testimonials || []);
// Get team members from API
const apiTeamMembers = aboutUsData?.our_team || [];
const handleMemberClick = (member: any) => {
// Get detailed static data for the clicked member
const memberDetails = getMemberDetails(member.name_role);
if (memberDetails) {
setSelectedMember(memberDetails);
} else {
// Fallback to API data if no static details found
setSelectedMember({
name: member.name_role.split(' - ')[0],
role: member.name_role.split(' - ')[1] || 'Team Member',
image: member.photo_url,
experience: member.bio,
fullBio: member.bio,
expertise: [],
education: '',
achievements: [],
clientWork: '',
boardRoles: ''
});
}
setIsModalOpen(true);
};
const handleCloseModal = () => {
setIsModalOpen(false);
setSelectedMember(null);
};
// Prevent background scroll when modal is open
useEffect(() => {
if (isModalOpen) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = '';
}
return () => {
document.body.style.overflow = '';
};
}, [isModalOpen]);
useEffect(() => {
setIsVisible(true);
const handleScroll = () => {
const timelineSection = document.querySelector('#timeline-fill-line') as HTMLElement | null;
const timelineContainer = timelineSection?.parentElement;
if (!timelineSection || !timelineContainer) return;
const rect = timelineContainer.getBoundingClientRect();
const windowHeight = window.innerHeight;
const sectionTop = rect.top;
const sectionHeight = rect.height;
const visibleTop = Math.max(0, windowHeight - sectionTop);
const visibleHeight = Math.min(visibleTop, sectionHeight);
const scrollProgress = Math.max(0, Math.min(1, visibleHeight / sectionHeight));
const maxFillHeight = 'calc(100% - 1rem)';
if (scrollProgress >= 0.9) {
timelineSection.style.height = maxFillHeight;
} else {
timelineSection.style.height = `${scrollProgress * 90}%`;
}
};
window.addEventListener('scroll', handleScroll);
handleScroll();
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []);
if (isLoading) {
return (
<div className="min-h-screen flex items-center justify-center bg-white">
<FullScreenLoader text="Loading articles..." />
</div>
);
}
if (isError) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="text-center">
<h2 className="text-2xl font-bold text-red-600 mb-4">Error Loading Page</h2>
<p className="text-gray-600 mb-4">Failed to load About Us content. Please try again later.</p>
<Button onClick={() => window.location.reload()}>Retry</Button>
</div>
</div>
);
}
return (
<div style={{ backgroundColor: '#FFFFFF', fontFamily: 'var(--font-family-base)' }}>
{/* Hero Section - Dynamic from API */}
<section className="relative min-h-[85vh] flex flex-col">
<div className="absolute inset-0 z-0">
<div
className="w-full h-full bg-cover bg-center bg-no-repeat"
style={{
backgroundImage: `url('${aboutUsData?.hero_section?.background_image_url || 'https://images.unsplash.com/photo-1552664730-d307ca884978?w=1920&h=1080&fit=crop'}')`
}}
/>
<div className="absolute inset-0 bg-gradient-to-r from-black/85 via-black/75 to-black/65"></div>
</div>
<div className="relative z-10 flex-1 flex items-center">
<div className="w-full section-margin-x">
<div className="max-w-6xl">
<div className="mb-8">
<h1 className="text-h1-white">
{aboutUsData?.hero_section?.headline || "Advancing Leadership Through Insight"}
</h1>
</div>
<p className="text-body-lg-white mb-8 max-w-3xl">
<strong>
{aboutUsData?.hero_section?.subtext || "Founded in 2016 with the vision of being a world class institution in the thought and practice of leadership. We facilitate institutions to build Leadership capacity and capability while helping individuals unleash their potential."}
</strong>
</p>
<div className="flex justify-start">
<PrimaryCTAButton
text={aboutUsData?.hero_section?.cta_text || "Talk to Us"}
onClick={() => navigateTo(aboutUsData?.hero_section?.cta_destination || '/contact?topic=management-development')}
ariaLabel="Talk to us about management development"
className="primary-cta-button-blue cta-text-white"
/>
</div>
</div>
</div>
</div>
</section>
{/* Section 1: Our Promise - Dynamic from API */}
<section className="py-24 lg:py-32" style={{ backgroundColor: '#FFFFFF' }}>
<div className="section-margin-x">
<div className="max-w-6xl mx-auto">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
viewport={{ once: true }}
className="text-center"
>
<BrandedTag text={aboutUsData?.our_promise_title || "Our Promise"} />
<h2 className="text-h1 mb-8" style={{
fontSize: 'clamp(2.5rem, 5vw, 4rem)',
lineHeight: '1.1',
color: 'var(--color-black)'
}}>
We build leaders who deliver business results.
</h2>
</motion.div>
</div>
</div>
</section>
{/* Section 2: How We Work - Dynamic from API */}
<section className="py-24 lg:py-32" style={{ backgroundColor: '#F9F9F9' }}>
<div className="section-margin-x">
<div className="max-w-6xl mx-auto">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
viewport={{ once: true }}
className="text-center mb-16"
>
<BrandedTag text={aboutUsData?.how_we_work_title || "How We Work"} />
<h2 className="text-h2 mb-8">{aboutUsData?.how_we_work_title || "How We Work"}</h2>
</motion.div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 lg:gap-12">
{(aboutUsData?.how_we_work && aboutUsData.how_we_work.length > 0) ? (
aboutUsData.how_we_work.map((item, index) => (
<motion.div
key={item.id}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.1 * (index + 1) }}
viewport={{ once: true }}
className="bg-white p-8 rounded-2xl shadow-lg border border-gray-100"
>
<div className="flex items-start gap-4">
<div
className="w-12 h-12 rounded-xl flex items-center justify-center flex-shrink-0"
style={{ backgroundColor: 'var(--color-primary)' }}
>
{index === 0 && <Puzzle className="w-6 h-6 text-white" />}
{index === 1 && <Target className="w-6 h-6 text-white" />}
{index === 2 && <BookOpen className="w-6 h-6 text-white" />}
{index === 3 && <Zap className="w-6 h-6 text-white" />}
</div>
<div>
<h3 className="text-h4 mb-4">{item.title}</h3>
<p className="text-body text-muted leading-relaxed">
{item.description}
</p>
</div>
</div>
</motion.div>
))
) : (
<>
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.1 }}
viewport={{ once: true }}
className="bg-white p-8 rounded-2xl shadow-lg border border-gray-100"
>
<div className="flex items-start gap-4">
<div
className="w-12 h-12 rounded-xl flex items-center justify-center flex-shrink-0"
style={{ backgroundColor: 'var(--color-primary)' }}
>
<Puzzle className="w-6 h-6 text-white" />
</div>
<div>
<h3 className="text-h4 mb-4">Co-created interventions</h3>
<p className="text-body text-muted leading-relaxed">
We collaborate with you to design solutions that fit your unique organizational context and strategic objectives.
</p>
</div>
</div>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
viewport={{ once: true }}
className="bg-white p-8 rounded-2xl shadow-lg border border-gray-100"
>
<div className="flex items-start gap-4">
<div
className="w-12 h-12 rounded-xl flex items-center justify-center flex-shrink-0"
style={{ backgroundColor: 'var(--color-primary)' }}
>
<Target className="w-6 h-6 text-white" />
</div>
<div>
<h3 className="text-h4 mb-4">Grounded in business context</h3>
<p className="text-body text-muted leading-relaxed">
Every solution is tailored to your specific business environment, challenges, and growth objectives.
</p>
</div>
</div>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 }}
viewport={{ once: true }}
className="bg-white p-8 rounded-2xl shadow-lg border border-gray-100"
>
<div className="flex items-start gap-4">
<div
className="w-12 h-12 rounded-xl flex items-center justify-center flex-shrink-0"
style={{ backgroundColor: 'var(--color-primary)' }}
>
<BookOpen className="w-6 h-6 text-white" />
</div>
<div>
<h3 className="text-h4 mb-4">Research-backed, behaviour-anchored</h3>
<p className="text-body text-muted leading-relaxed">
Our methodologies are rooted in rigorous research and focused on sustainable behavioral transformation.
</p>
</div>
</div>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.4 }}
viewport={{ once: true }}
className="bg-white p-8 rounded-2xl shadow-lg border border-gray-100"
>
<div className="flex items-start gap-4">
<div
className="w-12 h-12 rounded-xl flex items-center justify-center flex-shrink-0"
style={{ backgroundColor: 'var(--color-primary)' }}
>
<Zap className="w-6 h-6 text-white" />
</div>
<div>
<h3 className="text-h4 mb-4">Delivered through immersive formats</h3>
<p className="text-body text-muted leading-relaxed">
Interactive, experiential learning approaches that engage participants and drive lasting impact.
</p>
</div>
</div>
</motion.div>
</>
)}
</div>
</div>
</div>
</section>
{/* Section 3: Who We Are - Updated Statistics - Dynamic from API */}
<section className="py-24 lg:py-32" style={{ backgroundColor: '#FFFFFF' }}>
<div className="section-margin-x">
<div className="max-w-6xl mx-auto">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
viewport={{ once: true }}
>
<div className="grid grid-cols-1 lg:grid-cols-5 gap-8 lg:gap-12 mb-16">
<div className="lg:col-span-1">
<div className="branded-tag-system">
<div className="dot"></div>
<span className="text">{aboutUsData?.who_we_are_title || "Who we are"}</span>
</div>
</div>
<div className="lg:col-span-4">
<h2 className="text-h1 leading-tight" style={{
fontSize: 'clamp(2.5rem, 5vw, 4rem)',
lineHeight: '1.1',
color: 'var(--color-black)'
}}>
A premier Indian leadership development institution shaping India's most transformational leaders
</h2>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-4 gap-8 lg:gap-12 pt-12 border-t border-gray-200">
{(aboutUsData?.stat_section && aboutUsData.stat_section.length > 0) ? (
aboutUsData.stat_section.map((stat, index) => (
<motion.div
key={stat.id}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.1 * (index + 1) }}
viewport={{ once: true }}
className="text-center lg:text-left"
>
<div className="text-5xl lg:text-6xl font-medium mb-2" style={{
fontFamily: 'var(--font-family-base)',
lineHeight: '1',
color: 'var(--color-primary)'
}}>
{stat.number}{stat.suffix}
</div>
<div className="flex items-center justify-center lg:justify-start gap-2 text-body text-muted" style={{ fontFamily: 'var(--font-family-base)' }}>
<div className="w-2 h-2 rounded-sm" style={{ backgroundColor: 'var(--color-accent)' }}></div>
<span style={{ color: 'var(--color-black)', fontWeight: '500' }}>{stat.label.toUpperCase()}</span>
</div>
</motion.div>
))
) : (
<>
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.1 }}
viewport={{ once: true }}
className="text-center lg:text-left"
>
<div className="text-5xl lg:text-6xl font-medium mb-2" style={{
fontFamily: 'var(--font-family-base)',
lineHeight: '1',
color: 'var(--color-primary)'
}}>
150+
</div>
<div className="flex items-center justify-center lg:justify-start gap-2 text-body text-muted" style={{ fontFamily: 'var(--font-family-base)' }}>
<div className="w-2 h-2 rounded-sm" style={{ backgroundColor: 'var(--color-accent)' }}></div>
<span style={{ color: 'var(--color-black)', fontWeight: '500' }}>CORPORATES</span>
</div>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
viewport={{ once: true }}
className="text-center lg:text-left"
>
<div className="text-5xl lg:text-6xl font-medium mb-2" style={{
fontFamily: 'var(--font-family-base)',
lineHeight: '1',
color: 'var(--color-primary)'
}}>
27,000+
</div>
<div className="flex items-center justify-center lg:justify-start gap-2 text-body text-muted" style={{ fontFamily: 'var(--font-family-base)' }}>
<div className="w-2 h-2 rounded-sm" style={{ backgroundColor: 'var(--color-accent)' }}></div>
<span style={{ color: 'var(--color-black)', fontWeight: '500' }}>LEADERS</span>
</div>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 }}
viewport={{ once: true }}
className="text-center lg:text-left"
>
<div className="text-5xl lg:text-6xl font-medium mb-2" style={{
fontFamily: 'var(--font-family-base)',
lineHeight: '1',
color: 'var(--color-primary)'
}}>
5,000+
</div>
<div className="flex items-center justify-center lg:justify-start gap-2 text-body text-muted" style={{ fontFamily: 'var(--font-family-base)' }}>
<div className="w-2 h-2 rounded-sm" style={{ backgroundColor: 'var(--color-accent)' }}></div>
<span style={{ color: 'var(--color-black)', fontWeight: '500' }}>ROOM NIGHTS</span>
</div>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.4 }}
viewport={{ once: true }}
className="text-center lg:text-left"
>
<div className="text-3xl lg:text-4xl font-medium mb-2" style={{
fontFamily: 'var(--font-family-base)',
lineHeight: '1',
color: 'var(--color-primary)'
}}>
India & APAC
</div>
<div className="flex items-center justify-center lg:justify-start gap-2 text-body text-muted" style={{ fontFamily: 'var(--font-family-base)' }}>
<div className="w-2 h-2 rounded-sm" style={{ backgroundColor: 'var(--color-accent)' }}></div>
<span style={{ color: 'var(--color-black)', fontWeight: '500' }}>PRESENCE</span>
</div>
</motion.div>
</>
)}
</div>
</motion.div>
</div>
</div>
</section>
{/* Section 4: Our Team - Dynamic from API (outer grid from API, modal from static) */}
<section className="py-24 lg:py-32" style={{ backgroundColor: '#F9F9F9' }}>
<div className="section-margin-x">
<div className="max-w-6xl mx-auto">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
viewport={{ once: true }}
className="text-center mb-16"
>
<BrandedTag text={aboutUsData?.our_team_title || "Our Team"} />
<h2 className="text-h2 mb-8">{aboutUsData?.our_team_title || "Our Team"}</h2>
<div className="max-w-4xl mx-auto text-center space-y-6">
<p className="text-body-lg text-muted leading-relaxed">
{aboutUsData?.our_team_description || "We have a team of 7 consultants and 4 young consultants. All our senior Consultants are ex-business professionals with experience ranging from 15-30 years in varied business functions and carry a deep understanding of the area they are engaging in. Two of them bring in Board room experience. Meet them"}
</p>
</div>
</motion.div>
{/* Team Members Grid - Using API data for outer display */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8 lg:gap-12">
{apiTeamMembers.map((member, index) => {
const name = member.name_role.split(' - ')[0];
const role = member.name_role.split(' - ')[1] || 'Team Member';
return (
<motion.div
key={member.id || index}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: index * 0.1 }}
viewport={{ once: true }}
className="text-left cursor-pointer"
onClick={() => handleMemberClick(member)}
>
<div className="relative mb-6 group">
<div className="aspect-square rounded-2xl overflow-hidden bg-gray-100 shadow-lg group-hover:shadow-xl transition-all duration-300">
<img
src={member.photo_url}
alt={member.alt_text || name}
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
onError={(e) => {
(e.target as HTMLImageElement).src = 'https://ui-avatars.com/api/?name=' + encodeURIComponent(name) + '&background=04045B&color=fff&size=200';
}}
/>
</div>
<div className="absolute inset-0 bg-black/0 group-hover:bg-black/10 transition-all duration-300 rounded-2xl flex items-center justify-center">
<div className="opacity-0 group-hover:opacity-100 transition-opacity duration-300">
<div
className="px-4 py-2 rounded-lg text-white text-small"
style={{
backgroundColor: '#04045B',
fontFamily: 'var(--font-family-base)',
fontWeight: '500'
}}
>
View Profile
</div>
</div>
</div>
</div>
<div className="space-y-2">
<h3 className="text-h4 text-black group-hover:text-primary transition-colors duration-300">{name}</h3>
<p className="text-body text-muted leading-relaxed font-medium">
{role}
</p>
{/* Bio Section */}
<p className="text-small text-muted leading-relaxed mt-2 line-clamp-3">
{member.bio || "No bio available"}
</p>
</div>
</motion.div>
);
})}
</div>
</div>
</div>
</section>
{/* Section 5: Our Methodology - Dynamic from API */}
{aboutUsData?.methodology && (
<section className="py-16 lg:py-20" style={{ backgroundColor: '#FFFFFF' }}>
<div className="section-margin-x">
<div className="max-w-6xl mx-auto">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
viewport={{ once: true }}
className="text-center mb-16"
>
<BrandedTag text={aboutUsData.methodology.title || "Our Methodology"} />
{aboutUsData.methodology.subtitle && (
<h2 className="text-h2 mb-8">{aboutUsData.methodology.subtitle}</h2>
)}
</motion.div>
<div className="relative max-w-6xl mx-auto">
<div
className="absolute left-4 top-0 w-0.5 bg-gray-300"
style={{
height: 'calc(100% - 1rem)',
zIndex: 1
}}
></div>
<div
id="timeline-fill-line"
className="absolute left-4 top-0 w-0.5 transition-all duration-1000 ease-out"
style={{
backgroundColor: 'var(--color-primary)',
height: '0%',
maxHeight: 'calc(100% - 1rem)',
zIndex: 2
}}
></div>
{[...(aboutUsData.methodology.phases || [])]
.sort((a, b) => (a.display_order || 0) - (b.display_order || 0))
.map((phase, phaseIndex) => (
<div key={phase.id || phaseIndex} className="relative pb-20">
<div className="grid lg:grid-cols-12 gap-8 lg:gap-12 pl-12">
<div
className="absolute left-3 top-1 w-2.5 h-2.5 rounded-full bg-white border-2 z-10"
style={{
borderColor: 'var(--color-primary)',
}}
></div>
<div className="lg:col-span-2">
<div className="branded-tag-system">
<div className="dot"></div>
<span className="text">{phase.phase_label || `Phase ${phase.phase_number}`}</span>
</div>
</div>
<div className="lg:col-span-3">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: phaseIndex * 0.2 }}
viewport={{ once: true }}
>
<h3 className="text-h3">{phase.title}</h3>
</motion.div>
</div>
<div className="lg:col-span-7">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: phaseIndex * 0.2 }}
viewport={{ once: true }}
>
<p className="text-body-lg text-muted leading-relaxed mb-6">
{phase.description}
</p>
{phase.bullet_title && phase.bullets && phase.bullets.length > 0 && (
<div className="mb-8">
<h4 className="text-h4 mb-4">{phase.bullet_title}</h4>
<div className="space-y-3">
{phase.bullets.map((bullet, bulletIndex) => (
<div key={bulletIndex} className="flex items-start gap-3">
<div
className="w-1.5 h-1.5 rounded-full mt-2 flex-shrink-0"
style={{
backgroundColor: 'var(--color-primary)',
}}
></div>
<span className="text-body text-muted">
{bullet}
</span>
</div>
))}
</div>
</div>
)}
</motion.div>
</div>
</div>
</div>
))}
{aboutUsData?.philosophy && (
<div className="relative pb-20">
<div
className="absolute left-3 top-1 w-2.5 h-2.5 rounded-full bg-white border-2 z-10"
style={{
borderColor: 'var(--color-primary)',
}}
></div>
<div className="pl-12">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.6 }}
viewport={{ once: true }}
>
<div
className="rounded-2xl p-8 lg:p-12"
style={{
background: 'linear-gradient(135deg, var(--color-primary) 0%, #030359 100%)'
}}
>
<div className="flex flex-col lg:flex-row gap-8 lg:gap-12">
<div className="lg:w-1/3">
<h3 className="text-h3 text-white mb-6 lg:mb-0">{aboutUsData.philosophy.title || "Our Philosophy"}</h3>
</div>
<div className="lg:w-2/3">
<p className="text-body-white mb-8 opacity-90">
{aboutUsData.philosophy.description}
</p>
{aboutUsData.philosophy.points && aboutUsData.philosophy.points.length > 0 && (
<div className="flex flex-col gap-4">
{aboutUsData.philosophy.points.map((point, pointIndex) => (
<div key={pointIndex} className="flex items-start gap-4">
<div
className="bg-[#f8c301] content-stretch flex items-center justify-center rounded-full shrink-0 size-[32px]"
>
<svg className="block size-[16px]" fill="none" preserveAspectRatio="none" viewBox="0 0 16 16">
<g>
<path d="M3.33398 8H12.6673" stroke="#26231A" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.33333" />
<path d={svgPaths.p2c1c9a80} stroke="#26231A" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.33333" />
</g>
</svg>
</div>
<div className="flex flex-col gap-1">
<p className="font-['Inter',sans-serif] font-normal leading-[25.6px] text-[16px] text-white">
{point}
</p>
</div>
</div>
))}
</div>
)}
</div>
</div>
</div>
</motion.div>
</div>
</div>
)}
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 }}
viewport={{ once: true }}
className="flex justify-center items-center mt-16"
>
<PrimaryCTAButton
text="Design your leadership journey"
onClick={() => navigateTo('/contact')}
ariaLabel="Contact us to design your leadership journey"
className="cta-text-black"
/>
</motion.div>
</div>
</div>
</div>
</section>
)}
{/* Testimonials Section */}
<TestimonialsSection
customTestimonials={testimonialsData}
title="What Our Clients Say About Us"
subtitle="Hear from leaders who have transformed their approach through our comprehensive leadership development programs."
tagText="Client Success Stories"
/>
{/* CTA Banner Section - Dynamic from API */}
<CTABannerSection
ctaSection={aboutUsData?.cta_section}
isLoading={isLoading}
/>
</div>
);
}

View File

@@ -0,0 +1,767 @@
import {
BookOpen,
ChevronLeft,
ChevronRight,
Filter,
Grid,
List,
Search,
X
} from 'lucide-react';
import { useRef, useState, useEffect } from 'react';
import { BlogItem, useGetBlogsQuery } from '../redux/services/blogApi';
import { useGetFaqCategoriesQuery } from '../redux/services/faqApi';
import { ImageWithFallback } from './figma/ImageWithFallback';
import { PrimaryCTAButton } from './PrimaryCTAButton';
import { navigateTo } from './Router';
import { Badge } from './ui/badge';
import { Button } from './ui/button';
import { Card, CardContent } from './ui/card';
import { Input } from './ui/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
import { FullScreenLoader } from './FullScreenLoader';
import { getSlugWithId } from '../utils/urlHelpers';
// Define category type with ID and name
interface CategoryOption {
id: string;
name: string;
}
type DateRange =
| 'all_time'
| 'last_7_days'
| 'last_30_days'
| 'last_3_months'
| 'last_6_months';
type SortBy =
| 'most_recent'
| 'oldest_first'
| 'title_az';
export function Articles() {
const [searchTerm, setSearchTerm] = useState('');
const [selectedCategory, setSelectedCategory] = useState<CategoryOption>({ id: 'all', name: 'All Categories' });
const [selectedReadTime, setSelectedReadTime] = useState('All Read Times');
const [selectedDateRange, setSelectedDateRange] = useState<DateRange>('all_time');
const [selectedTopic, setSelectedTopic] = useState<{
id: string;
name: string;
}>({
id: 'all',
name: 'All Topics'
});
const [sortBy, setSortBy] = useState<SortBy>('most_recent');
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid');
const [currentPage, setCurrentPage] = useState(1);
const [debouncedSearch, setDebouncedSearch] = useState('');
const articlesPerPage = 6;
const containerRef = useRef<HTMLDivElement>(null);
const [allTags, setAllTags] = useState<{ id: string; name: string }[]>([]);
// Fetch categories for filter dropdown
const {
data: categoriesData,
isLoading: isLoadingCategories
} = useGetFaqCategoriesQuery({
limit: 100,
offset: 0
});
// Filter categories to only those for blog and create options with id and name
const categories: CategoryOption[] = [
{ id: 'all', name: 'All Categories' },
...(categoriesData?.data?.items
?.filter((category: any) => category.for_blog)
.map((category: any) => ({
id: category.id,
name: category.category_name
})) || [])
];
// Debounce search term
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedSearch(searchTerm);
}, 500);
return () => clearTimeout(timer);
}, [searchTerm]);
// Fetch blogs from API with all parameters
const {
data: blogsData,
isLoading: isLoadingBlogs,
isError: isBlogsError,
refetch: refetchBlogs
} = useGetBlogsQuery({
limit: articlesPerPage,
offset: (currentPage - 1) * articlesPerPage,
search: debouncedSearch || undefined,
content_status: 'publish',
content_type: 'BLOG',
date_range: selectedDateRange !== 'all_time' ? selectedDateRange : undefined,
sort_by: sortBy,
content_category_id: selectedCategory.id !== 'all' ? selectedCategory.id : undefined, // Send UUID
tag_id: selectedTopic.id !== 'all' ? selectedTopic.id : undefined,
});
const sortOptions = [
{ value: 'most_recent', label: 'Most Recent' },
{ value: 'oldest_first', label: 'Oldest First' },
{ value: 'title_az', label: 'Title A-Z' }
];
const readTimes = ['All Read Times', 'Under 5 min', '5-10 min', 'Over 10 min'];
const dateRanges = [
{ value: 'all_time', label: 'All Time' },
{ value: 'last_7_days', label: 'Last 7 days' },
{ value: 'last_30_days', label: 'Last 30 days' },
{ value: 'last_3_months', label: 'Last 3 months' }
];
// Format date function
const formatDate = (dateString: string) => {
return new Date(dateString).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
};
// Calculate read time based on content length (approx)
const calculateReadTime = (content: string): string => {
const wordsPerMinute = 200;
const wordCount = content.split(/\s+/).length;
const minutes = Math.ceil(wordCount / wordsPerMinute);
return `${minutes} min read`;
};
// Filter articles by read time (client-side only - API doesn't support this)
const getReadTimeFilteredArticles = () => {
if (selectedReadTime === 'All Read Times' || !blogsData?.data?.items) {
return blogsData?.data?.items || [];
}
return (blogsData?.data?.items || []).filter((blog: BlogItem) => {
const readTimeMinutes = parseInt(calculateReadTime(blog.content).replace(' min read', '')) || 0;
return (selectedReadTime === 'Under 5 min' && readTimeMinutes < 5) ||
(selectedReadTime === '5-10 min' && readTimeMinutes >= 5 && readTimeMinutes <= 10) ||
(selectedReadTime === 'Over 10 min' && readTimeMinutes > 10);
});
};
const finalFilteredArticles = getReadTimeFilteredArticles();
const totalPages = Math.ceil((blogsData?.data?.pagination?.total || 0) / articlesPerPage);
const clearAllFilters = () => {
setSearchTerm('');
setDebouncedSearch('');
setSelectedCategory({ id: 'all', name: 'All Categories' });
setSelectedReadTime('All Read Times');
setSelectedDateRange('all_time');
setSelectedTopic({ id: 'all', name: 'All Topics' });
setSortBy('most_recent');
setCurrentPage(1);
};
const hasActiveFilters = Boolean(
searchTerm ||
selectedCategory.id !== 'all' ||
selectedReadTime !== 'All Read Times' ||
selectedDateRange !== 'all_time' ||
selectedTopic.id !== 'all'
);
useEffect(() => {
if (!blogsData?.data?.items || allTags.length > 0) return;
const tags = Array.from(
new Map(
blogsData.data.items
.flatMap((blog: BlogItem) => blog.blog_tags || [])
.map(tag => [tag.id, { id: tag.id, name: tag.tag_name }])
).values()
);
setAllTags(tags);
}, [blogsData]);
// Handle loading state
if (isLoadingBlogs || isLoadingCategories) {
return (
<div className="min-h-screen flex items-center justify-center bg-white">
<FullScreenLoader text="Loading articles..." />
</div>
);
}
// Handle error state
if (isBlogsError) {
return (
<div className="min-h-screen flex items-center justify-center bg-white">
<div className="text-center">
<div className="w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mx-auto mb-4">
<X className="w-8 h-8 text-red-600" />
</div>
<h2 className="text-h3 mb-2">Failed to load articles</h2>
<p className="text-gray-600 mb-4">Please try again later</p>
<Button onClick={() => refetchBlogs()}>
Retry
</Button>
</div>
</div>
);
}
return (
<div style={{ backgroundColor: '#FFFFFF' }} ref={containerRef}>
{/* 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">{blogsData?.data?.pagination?.total || 0}+</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">{allTags.length}+</div>
<div className="text-small-white">Topics</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..."
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.id}
onValueChange={(value: string) => {
const category = categories.find(c => c.id === value);
if (category) {
setSelectedCategory(category);
}
}}
>
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors">
<SelectValue placeholder="All Categories">
{selectedCategory.name}
</SelectValue>
</SelectTrigger>
<SelectContent>
{categories.map((category) => (
<SelectItem key={category.id} value={category.id} className="text-small">
{category.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* Read Time Filter - Client-side only */}
<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">
<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">
<SelectValue placeholder="All Time" />
</SelectTrigger>
<SelectContent>
{dateRanges.map((range) => (
<SelectItem key={range.value} value={range.value}>
{range.label}
</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.id}
onValueChange={(value: string) => {
if (value === 'all') {
setSelectedTopic({ id: 'all', name: 'All Topics' });
return;
}
const topic = allTags.find(t => t.id === value);
if (topic) setSelectedTopic(topic);
}}
>
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors">
<SelectValue placeholder="All Topics" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">
All Topics
</SelectItem>
{allTags.map((tag) => (
<SelectItem key={tag.id} value={tag.id}>
{tag.name}
</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 {finalFilteredArticles.length} of {blogsData?.data?.pagination?.total || 0} articles
</div>
{/* Articles Results */}
{finalFilteredArticles.length === 0 ? (
<div className="text-center py-12">
<BookOpen className="w-16 h-16 text-gray-400 mx-auto mb-4" />
<p className="text-body-lg text-muted">
No articles found matching your criteria.
</p>
{hasActiveFilters && (
<Button
variant="outline"
onClick={clearAllFilters}
className="mt-4"
>
Clear Filters
</Button>
)}
</div>
) : (
<>
{/* Grid View */}
{viewMode === 'grid' && (
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{finalFilteredArticles.map((article: BlogItem) => (
<Card
key={article.id}
className="overflow-hidden hover:shadow-lg transition-all duration-300 cursor-pointer group"
onClick={() => {
// Use slug_name to create the URL with full UUID
const url = getSlugWithId(article.slug_name, article.id);
navigateTo(`/learning/articles/${url}`);
}}
>
<div className="aspect-video w-full bg-gray-100 overflow-hidden relative">
<ImageWithFallback
src={article.banner_img || '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={article.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 gap-2 mb-3">
<Badge variant="outline" className="text-small">
{article.content_category}
</Badge>
<span className="text-small text-muted">
{calculateReadTime(article.content)}
</span>
</div>
<h3 className="text-h4 mb-3 group-hover:text-[#04045B] transition-colors line-clamp-2">
{article.title}
</h3>
<p className="text-small text-muted mb-4 line-clamp-3">
{article.short_description || article.content.substring(0, 150) + '...'}
</p>
<div className="flex items-center justify-end gap-2">
<div className="text-small text-muted">
{formatDate(article.updated_at)}
</div>
</div>
{/* Display tags if available */}
{article.blog_tags && article.blog_tags.length > 0 && (
<div className="flex flex-wrap gap-1 mt-3 pt-3 border-t border-gray-100">
{article.blog_tags.slice(0, 3).map((tag, idx) => (
<Badge key={idx} variant="secondary" className="text-xs">
{tag.tag_name}
</Badge>
))}
{article.blog_tags.length > 3 && (
<span className="text-xs text-gray-500">+{article.blog_tags.length - 3}</span>
)}
</div>
)}
</CardContent>
</Card>
))}
</div>
)}
{/* List View */}
{viewMode === 'list' && (
<div className="space-y-6">
{finalFilteredArticles.map((article: BlogItem) => (
<Card
key={article.id}
className="overflow-hidden hover:shadow-lg transition-all duration-300 cursor-pointer group"
onClick={() => {
// Use slug_name to create the URL with full UUID
const url = getSlugWithId(article.slug_name, article.id);
navigateTo(`/learning/articles/${url}`);
}}
>
<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.banner_img || '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={article.title}
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
/>
</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.content_category}
</Badge>
<span className="text-small text-muted">
{calculateReadTime(article.content)}
</span>
</div>
<h3 className="text-h4 mb-2 group-hover:text-[#04045B] transition-colors">
{article.title}
</h3>
<p className="text-body text-muted mb-3">
{article.short_description || article.content.substring(0, 200) + '...'}
</p>
<div className="flex items-center justify-between">
<div className="text-small text-muted">
{formatDate(article.updated_at)}
</div>
{/* Display tags if available */}
{article.blog_tags && article.blog_tags.length > 0 && (
<div className="flex flex-wrap gap-1">
{article.blog_tags.slice(0, 2).map((tag, idx) => (
<Badge key={idx} variant="secondary" className="text-xs">
{tag.tag_name}
</Badge>
))}
{article.blog_tags.length > 2 && (
<span className="text-xs text-gray-500">+{article.blog_tags.length - 2}</span>
)}
</div>
)}
</div>
</div>
</div>
</div>
</div>
</Card>
))}
</div>
)}
{/* Pagination */}
{totalPages > 1 && (
<div className="flex items-center justify-center gap-2 mt-8">
<Button
variant="outline"
size="sm"
onClick={() => {
setCurrentPage(prev => Math.max(1, prev - 1));
containerRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' });
}}
disabled={currentPage === 1}
className="flex items-center gap-1 border-gray-300 disabled:opacity-50 disabled:cursor-not-allowed"
>
<ChevronLeft className="w-4 h-4" />
Previous
</Button>
<div className="flex items-center gap-1">
{Array.from({ length: totalPages }, (_, i) => {
const page = i + 1;
// Show limited pages for better UX
if (totalPages > 7) {
const showPage =
page === 1 ||
page === totalPages ||
(page >= currentPage - 1 && page <= currentPage + 1);
if (!showPage) {
if (page === currentPage - 2 || page === currentPage + 2) {
return <span key={page} className="px-2">...</span>;
}
return null;
}
}
return (
<Button
key={page}
variant={currentPage === page ? "default" : "outline"}
size="sm"
onClick={() => {
setCurrentPage(page);
containerRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' });
}}
className={`min-w-10 ${currentPage === page
? 'bg-[#04045B] text-white hover:bg-[#04045B]'
: 'border-gray-300 text-gray-700 hover:bg-gray-50'
}`}
>
{page}
</Button>
);
})}
</div>
<Button
variant="outline"
size="sm"
onClick={() => {
setCurrentPage(prev => Math.min(totalPages, prev + 1));
containerRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' });
}}
disabled={currentPage === totalPages}
className="flex items-center gap-1 border-gray-300 disabled:opacity-50 disabled:cursor-not-allowed"
>
Next
<ChevronRight className="w-4 h-4" />
</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>
);
}

View 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;
}

View File

@@ -0,0 +1,546 @@
import React, { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { Button } from './ui/button';
import { Card, CardContent } from './ui/card';
import { Badge } from './ui/badge';
import { Avatar, AvatarFallback, AvatarImage } from './ui/avatar';
import { ImageWithFallback } from './figma/ImageWithFallback';
import { CTABannerSection } from './CTABannerSection';
import { useCart } from './CartContext';
import {
Calendar,
Clock,
ChevronUp,
Bookmark,
Twitter,
Facebook,
Linkedin,
Link,
Heart,
Eye,
BookOpen,
ArrowLeft
} from 'lucide-react';
import { useGetBlogByIDQuery, useGetBlogsQuery } from '../redux/services/blogApi';
import { FullScreenLoader } from './FullScreenLoader';
import { extractIdFromSlug, extractSlugFromSlugAndId, getSlugWithId } from '../utils/urlHelpers';
interface BlogDetailProps {
params?: {
slugAndId?: string;
};
}
export function BlogDetail({ params }: BlogDetailProps) {
const { slugAndId } = useParams<{ slugAndId: string }>();
const navigate = useNavigate();
const [scrollProgress, setScrollProgress] = useState(0);
const [showBackToTop, setShowBackToTop] = useState(false);
const [isLiked, setIsLiked] = useState(false);
const [isBookmarked, setIsBookmarked] = useState(false);
const { addToCart } = useCart();
// Extract full ID from URL using the new function
const fullId = slugAndId ? extractIdFromSlug(slugAndId) : null;
const urlSlug = slugAndId ? extractSlugFromSlugAndId(slugAndId) : '';
// Fetch blog details by ID directly - no dependency on list API
const {
data: blogPost,
isLoading: isLoadingBlog,
isError: isBlogError,
refetch: refetchBlog
} = useGetBlogByIDQuery(fullId as string, {
skip: !fullId,
refetchOnMountOrArgChange: true,
});
// Fetch related blogs (excluding current blog)
const {
data: relatedBlogsData,
isLoading: isLoadingRelated
} = useGetBlogsQuery({
limit: 3,
offset: 0,
content_status: 'publish',
content_type: 'BLOG',
}, {
skip: !fullId,
});
// SEO: Check if URL slug matches the actual slug_name and redirect if needed
useEffect(() => {
if (blogPost && fullId) {
// Get the expected slug from the blog post
const expectedSlug = blogPost.slug_name;
// Create the expected URL with proper formatting
const expectedUrl = getSlugWithId(expectedSlug, fullId);
// Get the current URL slug
const currentSlugAndId = slugAndId || '';
// Compare (case-insensitive)
if (currentSlugAndId.toLowerCase() !== expectedUrl.toLowerCase()) {
// Redirect to the correct URL
navigate(`/learning/articles/${expectedUrl}`, { replace: true });
}
}
}, [blogPost, fullId, slugAndId, navigate]);
useEffect(() => {
if (blogPost?.title) {
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' });
};
// Calculate read time based on content length (approx)
const calculateReadTime = (content: string): string => {
const wordsPerMinute = 200;
const wordCount = content.split(/\s+/).length;
const minutes = Math.ceil(wordCount / wordsPerMinute);
return `${minutes} min read`;
};
// Filter related blogs (exclude current blog)
const relatedPosts = relatedBlogsData?.data?.items
?.filter((blog: any) => blog.id !== fullId)
.map((blog: any) => ({
id: blog.id,
title: blog.title,
slug: blog.slug_name,
excerpt: blog.short_description || blog.content.substring(0, 150) + '...',
author: blog.author_name || 'KLC Team',
publishedDate: blog.updated_at,
readTime: calculateReadTime(blog.content),
category: blog.content_category,
image: blog.banner_img || 'https://images.unsplash.com/photo-1552664730-d307ca884978?w=400&h=300&fit=crop'
})) || [];
// Handle related post click - use full UUID
const handleRelatedPostClick = (postSlug: string, postId: string) => {
const url = getSlugWithId(postSlug, postId);
navigate(`/learning/articles/${url}`);
};
// Handle loading state
if (isLoadingBlog) {
return (
<div className="min-h-screen flex items-center justify-center bg-white">
<FullScreenLoader text="Loading article..." />
</div>
);
}
// Handle error state
if (isBlogError || !blogPost) {
return (
<div className="min-h-screen flex items-center justify-center bg-white">
<div className="text-center max-w-md px-4">
<div className="w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mx-auto mb-4">
<BookOpen className="w-8 h-8 text-red-600" />
</div>
<h2 className="text-h3 mb-2">Article Not Found</h2>
<p className="text-gray-600 mb-6">
The article you're looking for could not be found. It may have been moved or removed.
</p>
<Button onClick={() => navigate('/learning/articles')}>
Back to Articles
</Button>
</div>
</div>
);
}
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={() => navigate('/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.content_category || 'Article'}</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.content_category || 'Article'}
</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.short_description || blogPost.content.substring(0, 200) + '...'}
</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="https://images.unsplash.com/photo-1494790108755-2616b612b47c?w=150&h=150&fit=crop" alt="Author" />
<AvatarFallback className="text-subhead font-medium">KLC</AvatarFallback>
</Avatar>
<div>
<div className="text-subhead font-medium mb-1" style={{ color: '#26231A' }}>
KLC Team
</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.updated_at || new Date().toISOString())}
</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" />
{calculateReadTime(blogPost.content)}
</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" />
0
</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' : ''}`} />
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.banner_img || 'https://images.unsplash.com/photo-1552664730-d307ca884978?w=1200&h=600&fit=crop'}
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%'
} as React.CSSProperties}
dangerouslySetInnerHTML={{ __html: blogPost.content }}
/>
</div>
</article>
{/* Enhanced Tag Pills with Hover States */}
{blogPost.blog_tags && blogPost.blog_tags.length > 0 && (
<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.blog_tags.map((tag: any) => (
<Badge
key={tag.tag_name}
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.tag_name}
</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="https://images.unsplash.com/photo-1494790108755-2616b612b47c?w=150&h=150&fit=crop" alt="Author" />
<AvatarFallback className="text-lg font-medium">KLC</AvatarFallback>
</Avatar>
<div className="flex-1">
<h4 className="text-h4 mb-3 font-semibold" style={{ color: '#26231A' }}>
About KLC Team
</h4>
<p className="text-body leading-relaxed mb-6" style={{ color: '#6F6F6F' }}>
The Kautilya Leadership Center team is dedicated to providing cutting-edge insights and research on leadership development, management strategies, and organizational excellence.
</p>
</div>
</div>
</CardContent>
</Card>
</div>
</div>
{/* Related Articles Section with Balanced Grid Layout */}
{relatedPosts.length > 0 && (
<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: any) => (
<Card
key={post.id}
className="overflow-hidden hover:shadow-xl transition-all duration-300 cursor-pointer group border-0"
onClick={() => {
// Use the same pattern as the main articles with full UUID
const url = getSlugWithId(post.slug, post.id);
navigate(`/learning/articles/${url}`);
}}
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>
);
}

View 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>
);
}

View 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="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="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="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="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>
);
}

View File

@@ -0,0 +1,201 @@
import React, { useState } from "react";
import { Calendar } from "lucide-react";
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 { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "./ui/dialog";
interface BookingFormData {
companyName: string;
contactName: string;
email: string;
phone: string;
role: string;
teamSize: string;
facilityZone: string;
additionalRequirements: string;
}
interface BookingModalProps {
isOpen: boolean;
onClose: () => void;
initialFacilityZone?: string;
}
export function BookingModal({ isOpen, onClose, initialFacilityZone = "" }: BookingModalProps) {
const [bookingForm, setBookingForm] = useState<BookingFormData>({
companyName: '',
contactName: '',
email: '',
phone: '',
role: '',
teamSize: '',
facilityZone: initialFacilityZone,
additionalRequirements: ''
});
const handleBookingSubmit = (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 within 24 hours.');
onClose();
};
const updateFormField = (field: keyof BookingFormData, value: string) => {
setBookingForm(prev => ({ ...prev, [field]: value }));
};
return (
<Dialog open={isOpen} onOpenChange={(open: boolean) => {
if (!open) {
onClose();
}
}}>
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle className="text-h3 mb-2">Book Our Learning Facility</DialogTitle>
<DialogDescription className="text-body text-muted">
Submit your booking request and we'll get back to you within 24 hours to confirm availability and discuss your requirements.
</DialogDescription>
</DialogHeader>
<form onSubmit={handleBookingSubmit} className="space-y-6 mt-6">
<div className="grid md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="companyName" className="text-body font-medium">Company/Organization Name *</Label>
<Input
id="companyName"
value={bookingForm.companyName}
onChange={(e) => updateFormField('companyName', e.target.value)}
placeholder="Enter company name"
required
className="text-body"
/>
</div>
<div className="space-y-2">
<Label htmlFor="contactName" className="text-body font-medium">Contact Person *</Label>
<Input
id="contactName"
value={bookingForm.contactName}
onChange={(e) => updateFormField('contactName', e.target.value)}
placeholder="Enter your full name"
required
className="text-body"
/>
</div>
</div>
<div className="grid md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="email" className="text-body font-medium">Email Address *</Label>
<Input
id="email"
type="email"
value={bookingForm.email}
onChange={(e) => updateFormField('email', e.target.value)}
placeholder="your.email@company.com"
required
className="text-body"
/>
</div>
<div className="space-y-2">
<Label htmlFor="phone" className="text-body font-medium">Phone Number</Label>
<Input
id="phone"
value={bookingForm.phone}
onChange={(e) => updateFormField('phone', e.target.value)}
placeholder="+1 (555) 123-4567"
className="text-body"
/>
</div>
</div>
<div className="grid md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="role" className="text-body font-medium">Your Role/Position</Label>
<Input
id="role"
value={bookingForm.role}
onChange={(e) => updateFormField('role', e.target.value)}
placeholder="e.g., Training Manager, HR Director"
className="text-body"
/>
</div>
<div className="space-y-2">
<Label htmlFor="teamSize" className="text-body font-medium">Expected Group Size *</Label>
<Select value={bookingForm.teamSize} onValueChange={(value:string) => updateFormField('teamSize', value)}>
<SelectTrigger className="text-body">
<SelectValue placeholder="Select group size" />
</SelectTrigger>
<SelectContent>
<SelectItem value="1-10">1-10 participants</SelectItem>
<SelectItem value="11-25">11-25 participants</SelectItem>
<SelectItem value="26-50">26-50 participants</SelectItem>
<SelectItem value="51-80">51-80 participants</SelectItem>
<SelectItem value="80+">80+ participants</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="facilityZone" className="text-body font-medium">Preferred Learning Zone</Label>
<Select value={bookingForm.facilityZone} onValueChange={(value:string) => updateFormField('facilityZone', value)}>
<SelectTrigger className="text-body">
<SelectValue placeholder="Select preferred zone (optional)" />
</SelectTrigger>
<SelectContent>
<SelectItem value="amphitheater">Training Amphitheater (80-120 people)</SelectItem>
<SelectItem value="collaboration">Collaboration Suites (4-8 people)</SelectItem>
<SelectItem value="outdoor">Outdoor Learning Pavilion (20-40 people)</SelectItem>
<SelectItem value="multiple">Multiple zones needed</SelectItem>
<SelectItem value="flexible">Flexible - advise best option</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="requirements" className="text-body font-medium">Additional Requirements</Label>
<Textarea
id="requirements"
value={bookingForm.additionalRequirements}
onChange={(e) => updateFormField('additionalRequirements', e.target.value)}
placeholder="Please share any specific requirements, preferred dates, catering needs, accommodation requirements, etc."
rows={4}
className="text-body resize-none"
/>
</div>
<div className="flex flex-col sm:flex-row gap-3 justify-end pt-4">
<Button
type="button"
variant="outline"
onClick={onClose}
className="text-body"
>
Cancel
</Button>
<Button
type="submit"
className="text-body px-8"
style={{
backgroundColor: 'var(--color-primary)',
color: 'white',
fontFamily: 'var(--font-family-base)'
}}
>
<Calendar className="w-4 h-4 mr-2" />
Submit Booking Request
</Button>
</div>
</form>
</DialogContent>
</Dialog>
);
}

View File

@@ -0,0 +1,101 @@
import { ArrowRight } from "lucide-react";
import { ImageWithFallback } from "./figma/ImageWithFallback";
import { BrandedTag } from "./about/BrandedTag";
import { PrimaryCTAButton } from "./PrimaryCTAButton";
import { navigateTo } from "./Router";
interface CTABannerSectionProps {
ctaSection?: {
id: string;
background_image_url: string;
text: string;
cta_text: string;
cta_destination: string;
description: string;
landing_page_type: string;
service_type: string | null;
};
isLoading?: boolean;
}
export function CTABannerSection({ ctaSection, isLoading }: CTABannerSectionProps) {
if (isLoading) {
return (
<section className="relative h-[700px] overflow-hidden bg-gray-100 animate-pulse">
<div className="absolute inset-0 bg-gray-200" />
<div className="relative h-full flex items-center justify-end section-margin-x">
<div className="bg-gray-300 rounded-lg p-16 max-w-2xl w-full h-96" />
</div>
</section>
);
}
// If no CTA section data is available, don't render anything
if (!ctaSection) {
return null;
}
return (
<section className="relative h-[700px] overflow-hidden">
{/* Background Image */}
<div className="absolute inset-0">
<ImageWithFallback
src={ctaSection.background_image_url || "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="Background image for call to action section"
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-4"
>
{ctaSection.text || "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>
{/* Description */}
{ctaSection.description && (
<p
className="text-body-white mb-6 opacity-90"
>
{ctaSection.description}
</p>
)}
{/* CTA Button */}
<PrimaryCTAButton
text={ctaSection.cta_text || "Schedule a Consultation"}
onClick={() => navigateTo(ctaSection.cta_destination || '/contact?topic=consulting')}
ariaLabel="Schedule a consultation with our leadership experts"
className="cta-banner-yellow"
/>
</div>
</div>
</section>
);
}

View 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>
);
}

View 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>
);
}

View 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;
}

View File

@@ -0,0 +1,575 @@
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;
type?: 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>
);
}

View 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>
)}
</>
);
}

View File

@@ -0,0 +1,492 @@
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';
import kautilyabg from '../assets/Kautilya.png';
import { useCreateLeadMutation, useGetLeadCategoriesQuery } from '../redux/services/contactUsApi';
interface ContactProps {
topic?: string;
}
export function Contact({ topic }: ContactProps) {
const [formData, setFormData] = useState({
name: '',
mobileNumber: '',
emailId: '',
interestedIn: '',
message: ''
});
const [isSubmitted, setIsSubmitted] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const { data: categoryData } = useGetLeadCategoriesQuery({
limit: 20,
offset: 0,
status: "active"
});
const categories = categoryData?.data?.items || [];
const [createLead] = useCreateLeadMutation();
useEffect(() => {
console.log('Contact component mounted with topic:', topic);
// Set default interested in based on topic parameter
if (topic) {
const interestedIn = getTopicSubject(topic);
console.log('Setting form interestedIn to:', interestedIn);
setFormData(prev => ({
...prev,
interestedIn: interestedIn
}));
}
}, [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);
try {
await createLead({
full_name: formData.name,
phone_number: formData.mobileNumber,
email_address: formData.emailId,
lead_category_xid: formData.interestedIn,
message: formData.message,
lead_status: "NEW"
}).unwrap();
setIsSubmitted(true);
} catch (error) {
console.error("Lead creation failed", error);
}
setIsSubmitting(false);
};
if (isSubmitted) {
return (
<div className="min-h-screen" style={{ backgroundColor: '#FFFFFF' }}>
<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>
<div className="flex justify-center">
<PrimaryCTAButton
text="Return to Home"
onClick={() => navigateTo('/')}
ariaLabel="Return to homepage"
/>
</div>
</div>
</div>
</div>
);
}
return (
<div className="min-h-screen" style={{ backgroundColor: '#FFFFFF' }}>
{/* Hero Section with Google Map Background */}
<section className="relative h-[600px] overflow-hidden">
{/* Google Map Background */}
<div className="absolute inset-0">
<ImageWithFallback
src={kautilyabg}
alt="Google Maps showing office location and navigation"
className="w-full h-full object-cover"
/>
{/* Dark overlay for better text readability */}
<div className="absolute inset-0 bg-black/60" />
</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="Contact Us" variant="white" />
{/* Main Headline */}
<h1 className="text-h1-white mb-6">
Reach us
</h1>
{/* Supporting Text */}
<p className="text-body-lg-white opacity-90 max-w-3xl mx-auto">
Get in touch with us to start your leadership 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">
{/* Corporate Office */}
<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">Corporate Office</h3>
<p className="text-body text-muted">
Leadership Centre Pvt. Ltd.<br />
No. 107, 1st Floor<br />
T.V. Industrial Estate, Worli<br />
Mumbai 400030
</p>
</div>
</div>
{/* Registered Office */}
<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">Registered Office</h3>
<p className="text-body text-muted">
Kautilya Leadership Services<br />
R.S. No. 5/6, Savaroli Kharpada Road<br />
Dhamani Taluk, Khalapur District<br />
Raigad 410202
</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">
connect@leadershipcentre.in<br />
connect@kautilyaservices.in
</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-22-2084 0097<br />
+91-8928738661
</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">Enquiry Form</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>
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
Name *
</label>
<Input
type="text"
required
value={formData.name}
onChange={(e) => handleInputChange('name', e.target.value)}
placeholder="Enter your full 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)' }}>
Mobile Number *
</label>
<Input
type="tel"
required
value={formData.mobileNumber}
onChange={(e) => handleInputChange('mobileNumber', 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>
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
Email Id *
</label>
<Input
type="email"
required
value={formData.emailId}
onChange={(e) => handleInputChange('emailId', e.target.value)}
placeholder="example@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)' }}>
Interested In *
</label>
<Select
value={formData.interestedIn}
onValueChange={(value: string) => handleInputChange('interestedIn', 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 your area of interest" />
</SelectTrigger>
<SelectContent>
<SelectContent>
{categories.map((cat: any) => (
<SelectItem key={cat.id} value={cat.id}>
{cat.category_type}
</SelectItem>
))}
</SelectContent>
</SelectContent>
</Select>
</div>
<div>
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
Enter Message *
</label>
<Textarea
required
value={formData.message}
onChange={(e) => handleInputChange('message', e.target.value)}
placeholder="Please enter your message or inquiry..."
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={() => { handleSubmit }}
ariaLabel="Send contact message"
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>
);
}

View 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('https://klc-hr.wdiprojects.com');
}, 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>
);
}

View 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>
);
}

View File

@@ -0,0 +1,268 @@
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;
reviews: 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>
{/* 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.reviews}
</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
variant="outline"
onClick={handleAddToCart}
className="flex-1 flex items-center justify-center gap-1.5 h-9 rounded-md transition-all duration-200 font-medium px-2"
style={{
borderColor: '#04045B',
color: '#04045B',
backgroundColor: 'transparent',
fontSize: '12px', // ⬅️ reduced
fontFamily: 'var(--font-family-base)',
fontWeight: '500',
borderWidth: '1px',
padding: '8px'
}}
onMouseEnter={(e: any) => {
e.currentTarget.style.backgroundColor = '#04045B';
e.currentTarget.style.color = 'white';
}}
onMouseLeave={(e: any) => {
e.currentTarget.style.backgroundColor = 'transparent';
e.currentTarget.style.color = '#04045B';
}}
>
<ShoppingCart className="w-3.5 h-3.5" /> {/* ⬅️ smaller icon */}
Add to Cart
</Button>
{/* Learn More */}
<Button
className="flex-1 flex items-center justify-center gap-1.5 h-9 rounded-md transition-all duration-200 font-medium px-2"
style={{
backgroundColor: '#04045B',
color: 'white',
fontSize: '12px', // ⬅️ reduced
fontFamily: 'var(--font-family-base)',
fontWeight: '500',
border: 'none',
padding: '8px'
}}
onMouseEnter={(e: any) => {
e.currentTarget.style.backgroundColor = '#030359';
}}
onMouseLeave={(e: any) => {
e.currentTarget.style.backgroundColor = '#04045B';
}}
>
Learn More
<ArrowRight className="w-3.5 h-3.5" /> {/* ⬅️ smaller icon */}
</Button>
</div>
</div>
</motion.div>
);
}

View 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>
);
}

View 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>
);
}

View File

@@ -0,0 +1,264 @@
import { ArrowUp, Facebook, X, Linkedin, Instagram } from "lucide-react";
import { navigateTo } from './Router';
import klcLogo from 'figma:asset/e98caa8afd8d11246bbff1dde75bbaae6f6a0894.png';
export function FooterNew() {
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-16">
{/* Logo and Quick Links Column */}
<div className="lg:flex-shrink-0 lg:max-w-md">
{/* Logo */}
<div className="flex items-center mb-8">
<img
src={klcLogo}
alt="Kautilya Leadership Centre"
className="h-16 w-auto object-contain"
style={{ filter: 'brightness(0) invert(1)' }}
/>
</div>
{/* Quick Links Section */}
<div className="mb-8">
<h3 className="text-subhead-white mb-6 text-[16px]">
Quicklinks
</h3>
{/* Navigation Links */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2 mb-8">
<button
onClick={() => navigateTo('/')}
className="block text-small-white transition-all duration-300 text-left"
style={{
color: 'white',
opacity: 0.5
}}
onMouseEnter={(e) => e.currentTarget.style.opacity = '1'}
onMouseLeave={(e) => e.currentTarget.style.opacity = '0.5'}
>
Home
</button>
<button
onClick={() => navigateTo('/learning/webcast')}
className="block text-small-white transition-all duration-300 text-left"
style={{
color: 'white',
opacity: 0.5
}}
onMouseEnter={(e) => e.currentTarget.style.opacity = '1'}
onMouseLeave={(e) => e.currentTarget.style.opacity = '0.5'}
>
Webcast
</button>
<button
onClick={() => navigateTo('/about-us')}
className="block text-small-white transition-all duration-300 text-left"
style={{
color: 'white',
opacity: 0.5
}}
onMouseEnter={(e) => e.currentTarget.style.opacity = '1'}
onMouseLeave={(e) => e.currentTarget.style.opacity = '0.5'}
>
About Us
</button>
<button
onClick={() => navigateTo('/learning/articles')}
className="block text-small-white transition-all duration-300 text-left"
style={{
color: 'white',
opacity: 0.5
}}
onMouseEnter={(e) => e.currentTarget.style.opacity = '1'}
onMouseLeave={(e) => e.currentTarget.style.opacity = '0.5'}
>
Blogs
</button>
<button
onClick={() => navigateTo('/services')}
className="block text-small-white transition-all duration-300 text-left"
style={{
color: 'white',
opacity: 0.5
}}
onMouseEnter={(e) => e.currentTarget.style.opacity = '1'}
onMouseLeave={(e) => e.currentTarget.style.opacity = '0.5'}
>
Services
</button>
<button
onClick={() => navigateTo('/faq')}
className="block text-small-white transition-all duration-300 text-left"
style={{
color: 'white',
opacity: 0.5
}}
onMouseEnter={(e) => e.currentTarget.style.opacity = '1'}
onMouseLeave={(e) => e.currentTarget.style.opacity = '0.5'}
>
FAQs
</button>
<button
onClick={() => navigateTo('/learning-facility')}
className="block text-small-white transition-all duration-300 text-left"
style={{
color: 'white',
opacity: 0.5
}}
onMouseEnter={(e) => e.currentTarget.style.opacity = '1'}
onMouseLeave={(e) => e.currentTarget.style.opacity = '0.5'}
>
Learning facility
</button>
<button
onClick={() => navigateTo('/learning-online')}
className="block text-small-white transition-all duration-300 text-left"
style={{
color: 'white',
opacity: 0.5
}}
onMouseEnter={(e) => e.currentTarget.style.opacity = '1'}
onMouseLeave={(e) => e.currentTarget.style.opacity = '0.5'}
>
Online courses
</button>
</div>
</div>
</div>
{/* DISCOVER LEADERSHIP Column */}
<div className="lg:flex-shrink-0 lg:max-w-3xl flex flex-col h-full">
{/* Discover Leadership Header */}
<h2 className="text-h3-white mb-6 text-[32px] text-[24px]">
DISCOVER LEADERSHIP
</h2>
{/* Company Description */}
<p
className="text-small-white leading-relaxed mb-6"
style={{ color: 'rgba(255, 255, 255, 0.85)' }}
>
Kautilya Leadership Centre is dedicated to building a world-class organisation, in thought and practice of leadership. We help people gain insights into their leadership orientations and enable them on a journey towards leadership development. This online platform allows a wide range of learning experiences, exposure to leadership thinking and concepts through online tools such as modules, webcasts of leaders, webinars, one-on-one consulting sessions.
</p>
<p
className="text-small-white leading-relaxed mb-6"
style={{ color: 'rgba(255, 255, 255, 0.85)' }}
>
Kautilya Leadership Centre also offers access to Kautilya Leadership Services, which is a learning space developed to discover, deliberate, reflect on one's capabilities and developments. This learning centre built over 2 acres is equipped with world-class facilities to help ease learning and development.
</p>
{/* Learning Journey Text */}
<div className="mb-8">
<p
className="text-small-white"
style={{ color: 'rgba(255, 255, 255, 0.75)' }}
>
Become Aware | Gain Insights | Discover Leadership
</p>
</div>
{/* Back To Top Button - Positioned to Bottom Right */}
<div className="flex-1 flex justify-end items-end">
<button
onClick={() => {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
}}
className="group flex items-center gap-3 px-6 py-3 rounded-lg transition-all duration-300"
style={{
backgroundColor: 'var(--color-brand-accent)',
color: 'var(--color-brand-black)'
}}
aria-label="Back to top"
>
<ArrowUp className="w-5 h-5 transition-transform duration-300 group-hover:-translate-y-1" />
<span className="font-semibold">BACK TO TOP</span>
</button>
</div>
</div>
</div>
{/* Bottom Footer */}
<div className="flex flex-col lg:flex-row justify-between items-start lg:items-center space-y-6 lg:space-y-0 mt-16 pt-8"
style={{ borderTop: '1px solid rgba(255, 255, 255, 0.2)' }}>
{/* Social Media Icons - Moved from left column */}
<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>
{/* Copyright - Centered */}
<div className="flex-1 flex justify-center lg:absolute lg:left-1/2 lg:transform lg:-translate-x-1/2">
<p
className="text-body-white text-center"
style={{ color: 'rgba(255, 255, 255, 0.85)' }}
>
Copyright 2025 Kautilya Leadership Centre
</p>
</div>
{/* Legal Links */}
<div className="flex flex-col lg:flex-row lg:items-center space-y-2 lg:space-y-0 lg:space-x-8">
<button
onClick={() => navigateTo('/privacy-policy')}
className="text-body-white transition-colors duration-300 hover:text-yellow-300 text-left cursor-pointer"
style={{ color: 'rgba(255, 255, 255, 0.85)' }}
>
Privacy and Cookie Policy
</button>
<button
onClick={() => navigateTo('/term-condition')}
className="text-body-white transition-colors duration-300 cursor-pointer text-left hover:text-yellow-300"
style={{ color: 'rgba(255, 255, 255, 0.85)' }}
>
Terms of Service
</button>
</div>
</div>
</div>
</footer>
);
}

View File

@@ -0,0 +1,22 @@
import React from "react";
import { Loader } from "./Loader";
interface FullScreenLoaderProps {
text?: string;
}
export const FullScreenLoader: React.FC<FullScreenLoaderProps> = ({
text = "Loading...",
}) => {
return (
<div className="fixed inset-0 flex flex-col items-center justify-center bg-white/100 z-[9999]">
<Loader />
{text && (
<p className="mt-6 text-lg text-gray-600">
{text}
</p>
)}
</div>
);
};

View File

@@ -0,0 +1,226 @@
import React, { useState, useEffect, useCallback } from "react";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { navigateTo } from "./Router";
import PrimaryCTAButton from "./PrimaryCTAButton";
interface HeroSectionItem {
id: string;
headline: string;
subtext: string;
background_image_url: string;
cta_text: string;
cta_destination: string;
}
interface SlideData {
id: string;
title: string;
backgroundImage: string;
shortTitle: string;
ctaText: string;
route: string;
}
interface HeroSectionProps {
heroSections: HeroSectionItem[];
isLoading: boolean;
}
export default function HeroSection({
heroSections,
isLoading,
}: HeroSectionProps) {
const slides: SlideData[] = heroSections.map((item) => ({
id: item.id,
title: item.headline,
backgroundImage: item.background_image_url,
shortTitle: item.subtext,
ctaText: item.cta_text,
route: item.cta_destination,
}));
const totalSlides = slides.length;
const [currentSlide, setCurrentSlide] = useState(0);
const [isAutoPlaying, setIsAutoPlaying] = useState(true);
const [progressValues, setProgressValues] = useState<number[]>([]);
const slideDuration = 5000;
/* Initialize progress array when slides load */
useEffect(() => {
if (totalSlides > 0) {
setProgressValues(new Array(totalSlides).fill(0));
}
}, [totalSlides]);
/* Auto slide */
useEffect(() => {
if (!isAutoPlaying || totalSlides === 0) return;
const interval = setInterval(() => {
setCurrentSlide((prev) => (prev + 1) % totalSlides);
}, slideDuration);
return () => clearInterval(interval);
}, [isAutoPlaying, totalSlides]);
/* Progress animation */
useEffect(() => {
if (!isAutoPlaying || totalSlides === 0) return;
const interval = setInterval(() => {
setProgressValues((prev) => {
const newProgress = [...prev];
newProgress[currentSlide] = Math.min(
newProgress[currentSlide] + 100 / (slideDuration / 100),
100
);
if (newProgress[currentSlide] >= 100) {
newProgress[currentSlide] = 0;
newProgress.forEach((_, index) => {
if (index !== currentSlide) newProgress[index] = 0;
});
}
return newProgress;
});
}, 100);
return () => clearInterval(interval);
}, [currentSlide, isAutoPlaying, totalSlides]);
/* Reset progress when slide changes */
useEffect(() => {
setProgressValues(new Array(totalSlides).fill(0));
}, [currentSlide, totalSlides]);
const goToSlide = useCallback(
(slideIndex: number) => {
if (slideIndex !== currentSlide) {
setCurrentSlide(slideIndex);
setIsAutoPlaying(false);
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]);
const handleMouseEnter = () => setIsAutoPlaying(false);
const handleMouseLeave = () => setIsAutoPlaying(true);
if (isLoading || slides.length === 0) return null;
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">
<h1 className="hero-title" style={{ whiteSpace: "pre-line" }}>
{slides[currentSlide].title}
</h1>
<PrimaryCTAButton
text={slides[currentSlide].ctaText}
onClick={() => navigateTo(slides[currentSlide].route)}
ariaLabel="Learn more about KLC"
/>
</div>
</div>
{/* Bottom Navigation */}
<div className="hero-navigation">
<div className="hero-progress-container">
{slides.map((slide, index) => (
<div
key={slide.id}
className="hero-progress-item"
onClick={() => goToSlide(index)}
>
<div
className={`hero-progress-segment ${
index === currentSlide ? "active" : ""
}`}
>
<div
className="hero-progress-fill"
style={{
width:
index === currentSlide
? `${progressValues[index] ?? 0}%`
: "0%",
}}
/>
</div>
<div
className={`hero-progress-number ${
index === currentSlide ? "active" : ""
}`}
>
{String(index + 1).padStart(2, "0")}
</div>
<div
className={`hero-progress-text ${
index === currentSlide ? "active" : ""
}`}
>
{slide.shortTitle}
</div>
</div>
))}
</div>
<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>
);
}

View File

@@ -0,0 +1,363 @@
import { ArrowUpRight } from "lucide-react";
import { ImageWithFallback } from "./figma/ImageWithFallback";
import { BrandedTag } from "./about/BrandedTag";
import { StandardCTAButton } from "./StandardCTAButton";
import { navigateTo } from "./Router";
import { useGetFeaturedBlogsQuery } from "../redux/services/homepageApi";
import { FullScreenLoader } from "./FullScreenLoader";
import { getSlugWithId } from "../utils/urlHelpers";
// Interface for featured blog items from API
interface FeaturedBlog {
id: string;
title: string;
short_description: string | null;
slug_name: string;
banner_img: string | null;
updated_at: string;
content_category: string;
content_type: string;
tags: string[];
featured: boolean;
featured_order: number;
}
// Insight Card Component Interface
interface InsightCardData {
id: string;
title: string;
description?: string;
date: string;
tags: string[];
image: string;
slug: string;
}
// 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
function ExploreAllButton() {
return (
<StandardCTAButton
text="Explore All"
onClick={() => navigateTo('/learning/articles')}
ariaLabel="Explore all leadership insights and ideas"
/>
);
}
// Large Insight Card Component
function LargeInsightCard({ card }: { card: InsightCardData }) {
const handleClick = () => {
if (card.slug && card.id) {
const url = getSlugWithId(card.slug, card.id);
navigateTo(`/learning/articles/${url}`);
}
};
const hasLink = !!(card.slug && card.id);
return (
<div
className={`relative h-[500px] rounded-xl overflow-hidden group ${hasLink ? 'cursor-pointer' : ''}`}
onClick={hasLink ? handleClick : undefined}
role={hasLink ? 'button' : undefined}
tabIndex={hasLink ? 0 : undefined}
onKeyDown={hasLink ? (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleClick();
}
} : undefined}
>
{/* 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 flex-wrap">
{card.tags.slice(0, 2).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: InsightCardData }) {
const handleClick = () => {
if (card.slug && card.id) {
const url = getSlugWithId(card.slug, card.id);
navigateTo(`/learning/articles/${url}`);
}
};
const hasLink = !!(card.slug && card.id);
return (
<div
className={`relative h-[235px] rounded-xl overflow-hidden group ${hasLink ? 'cursor-pointer' : ''}`}
onClick={hasLink ? handleClick : undefined}
role={hasLink ? 'button' : undefined}
tabIndex={hasLink ? 0 : undefined}
onKeyDown={hasLink ? (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleClick();
}
} : undefined}
>
{/* 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 flex-wrap">
{card.tags.slice(0, 2).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>
);
}
// Format date function
const formatDate = (dateString: string) => {
return new Date(dateString).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
};
export function InsightsSection() {
// Fetch featured blogs from API
const { data: featuredBlogs, isLoading, isError } = useGetFeaturedBlogsQuery({ limit: 3 });
// Transform API data to match InsightCardData format
const transformToInsightCard = (blog: FeaturedBlog): InsightCardData => ({
id: blog.id,
title: blog.title,
description: blog.short_description || undefined,
date: formatDate(blog.updated_at),
tags: blog.tags || [],
image: blog.banner_img || 'https://images.unsplash.com/photo-1481627834876-b7833e8f5570?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&w=1080',
slug: blog.slug_name,
});
// Handle loading state
if (isLoading) {
return (
<section
className="py-24"
style={{
backgroundColor: '#F7F7FD',
paddingTop: '8.75rem',
paddingBottom: '8.75rem'
}}
>
<div className="max-w-7xl mx-auto section-margin-x">
<div className="flex items-center justify-center min-h-[400px]">
<FullScreenLoader text="Loading insights..." />
</div>
</div>
</section>
);
}
// Handle error or no data state
if (isError || !featuredBlogs || featuredBlogs.length === 0) {
return (
<section
className="py-24"
style={{
backgroundColor: '#F7F7FD',
paddingTop: '8.75rem',
paddingBottom: '8.75rem'
}}
>
<div className="max-w-7xl mx-auto section-margin-x">
<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>
{/* Show message when no featured blogs available */}
<div className="text-center py-12">
<p className="text-gray-600">No featured insights available at the moment. Check back soon!</p>
</div>
</div>
</div>
</section>
);
}
// Ensure we have at least 3 blogs for the layout (use placeholders if needed)
const cards = featuredBlogs.map(transformToInsightCard);
// The layout expects: first card (large) + two small cards
const largeCard = cards[0];
const smallCards = cards.slice(1, 3);
// If we don't have enough cards, we can still render with what we have
const hasLargeCard = !!largeCard;
const hasSmallCards = smallCards.length > 0;
return (
<section
className="py-24"
style={{
backgroundColor: '#F7F7FD',
paddingTop: '8.75rem',
paddingBottom: '8.75rem'
}}
>
<div className="max-w-7xl mx-auto section-margin-x">
<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 */}
{hasLargeCard && (
<div className="lg:row-span-2">
<LargeInsightCard card={largeCard} />
</div>
)}
{/* Small Cards - Stack on right */}
{hasSmallCards && (
<div className="flex flex-col gap-4 md:gap-6 lg:gap-8">
{smallCards.map((card:any, index:any) => (
<SmallInsightCard key={card.id || index} card={card} />
))}
</div>
)}
</div>
</div>
</div>
</section>
);
}

View File

@@ -0,0 +1,919 @@
import React, { useState, useEffect } 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 { PrimaryCTAButton } from './PrimaryCTAButton';
import { StandardCTAButton } from './StandardCTAButton';
import {
ArrowRight,
CheckCircle,
Settings,
Calendar,
Download,
Network,
Users,
Target,
Brain,
Eye,
TrendingUp,
BarChart3,
Award,
Lightbulb,
Shield,
ChevronDown,
ChevronUp,
ArrowLeft,
Star,
Zap,
Globe,
Clock,
BookOpen,
MessageCircle,
Building,
Heart,
Compass,
User,
UserCheck,
Home,
Car,
Coffee,
Wifi
} from 'lucide-react';
import { TestimonialsSection } from './TestimonialsSection';
import { FullScreenLoader } from './FullScreenLoader';
import { useGetServiceListQuery } from '../redux/services/sercicesApi';
// Types based on API response (same as LeadershipDevelopment)
interface ServicePageData {
hero_section: {
id: string;
landing_page_type: string;
background_image_url: string;
background_image_alt_text: string;
headline: string;
subtext: string;
cta_text: string;
cta_destination: string;
};
overview: {
id: string;
title: string;
description: string;
highlight_text: string;
overview_cards: Array<{
id: string;
title: string;
description: string;
icon_url: string;
accessible_label: string;
}>;
};
audience_section: {
id: string;
title: string;
description: string;
audience_cards: Array<{
id: string;
title: string;
description: string;
icon_url: string;
accessible_label: string;
challenges: string[];
}>;
};
use_case_section: {
id: string;
title: string;
description: string;
use_case_cards: Array<{
id: string;
title: string;
description: string;
icon_url: string;
accessible_label: string;
highlight_text: string;
}>;
};
approach_section: {
id: string;
title: string;
description: string;
approach_cards: Array<{
id: string;
title: string;
description: string;
icon_url: string;
accessible_label: string;
bullets: string[];
}>;
outcomes: Array<{
id: string;
title: string;
description: string;
icon_url: string;
accessible_label: string;
bullets: string[];
}>;
};
stats_section: {
id: string;
title: string;
description: string;
stat_cards: Array<{
id: string;
value: string;
label: string;
icon_url: string;
accessible_label: string;
}>;
};
program_section: {
id: string;
title: string;
description: string;
program_phases: Array<{
phase: {
id: string;
phase_number: number;
title: string;
duration: string;
};
activities: Array<{
id: string;
phase_id: string;
text: string;
}>;
outcomes: Array<{
id: string;
phase_id: string;
text: string;
}>;
}>;
};
impact_section: {
id: string;
title: string;
description: string;
impact_stats: Array<{
id: string;
value: string;
description: string;
label: string;
icon_url: string;
accessible_label: string;
}>;
impact_benefits: Array<{
id: string;
title: string;
description: string;
icon_url: string;
accessible_label: string;
}>;
};
testimonial_section: Array<{
id: string;
profile_xid: string;
name: string;
designation: string;
content: string;
video_url: string | null;
display_order: number;
}>;
cta_section: {
id: string;
background_image_url: string;
text: string;
cta_text: string;
cta_destination: string;
description: string;
landing_page_type: string;
service_type: string;
};
}
// Map API icons to Lucide icons (same mapping as LeadershipDevelopment)
const getIconComponent = (iconUrl: string) => {
const iconMap: Record<string, any> = {
'/icons/building.svg': Building,
'/icons/home.svg': Home,
'/icons/hotel.svg': Building,
'/icons/coffee.svg': Coffee,
'/icons/wifi.svg': Wifi,
'/icons/settings.svg': Settings,
'/icons/user-check.svg': UserCheck,
'/icons/heart.svg': Heart,
'/icons/trending-up.svg': TrendingUp,
'/icons/users.svg': Users,
'/icons/target.svg': Target,
'/icons/star.svg': Star,
'/icons/compass.svg': Compass,
'/icons/book-open.svg': BookOpen,
'/icons/message-circle.svg': MessageCircle,
'/icons/clock.svg': Clock,
};
const IconComponent = iconMap[iconUrl] || Building;
return IconComponent;
};
export function KautilyaFacility() {
const [expandedUseCase, setExpandedUseCase] = useState<number | null>(null);
const [expandedFeature, setExpandedFeature] = useState<number | null>(0);
// API call with service_type = 'kautilya_facility'
const { data: apiResponse, isLoading, error } = useGetServiceListQuery({
service_type: 'kautilya_facility'
});
const apiData = apiResponse?.data as ServicePageData | undefined;
useEffect(() => {
window.scrollTo(0, 0);
}, []);
if (isLoading) {
return (
<div className="min-h-screen flex items-center justify-center bg-white">
<FullScreenLoader text="Loading Kautilya Facility..." />
</div>
);
}
if (error || !apiData) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="text-center">
<p className="text-red-600">Error loading content. Please try again later.</p>
<Button onClick={() => window.location.reload()} className="mt-4">Retry</Button>
</div>
</div>
);
}
// Transform data for UI (using API data with fallbacks to static data)
const targetAudience = apiData.audience_section?.audience_cards.map(card => ({
title: card.title,
description: card.description,
icon: getIconComponent(card.icon_url),
challenges: card.challenges || []
})) || [];
const useCases = apiData.use_case_section?.use_case_cards.map(card => ({
title: card.title,
description: card.description,
icon: getIconComponent(card.icon_url),
scenario: card.highlight_text
})) || [];
const facilityFeatures = apiData.program_section?.program_phases.map(phase => ({
phase: phase.phase.title,
duration: phase.phase.duration,
activities: phase.activities.map(activity => activity.text),
deliverables: phase.outcomes.map(outcome => outcome.text)
})) || [];
const expectedOutcomes = apiData.impact_section?.impact_benefits.map(benefit => ({
category: benefit.title,
description: benefit.description,
icon: getIconComponent(benefit.icon_url)
})) || [];
const testimonials = apiData.testimonial_section?.map(testimonial => {
const designationParts = testimonial.designation.split(',');
const role = designationParts[0]?.trim() || '';
const company = designationParts[1]?.trim() || '';
return {
id: parseInt(testimonial.id) || 0,
name: testimonial.name,
role: role,
company: company,
avatar: `https://ui-avatars.com/api/?name=${encodeURIComponent(testimonial.name)}&background=04045B&color=fff&size=128`,
quote: testimonial.content,
rating: 5,
isVideo: !!testimonial.video_url,
videoThumbnail: testimonial.video_url ? `/images/testimonials/thumbnails/${testimonial.id}.jpg` : undefined,
videoUrl: testimonial.video_url || undefined
};
}) || [];
return (
<div style={{ backgroundColor: '#FFFFFF', fontFamily: 'var(--font-family-base)' }}>
{/* Hero Section */}
<section className="relative min-h-[85vh] flex flex-col">
<div className="absolute inset-0 z-0">
<div
className="w-full h-full bg-cover bg-center bg-no-repeat"
style={{
backgroundImage: `url('${apiData.hero_section.background_image_url}')`
}}
/>
<div className="absolute inset-0 bg-gradient-to-r from-black/85 via-black/75 to-black/65"></div>
</div>
<div className="relative z-10 flex-1 flex items-center">
<div className="w-full section-margin-x">
<div className="max-w-4xl">
{/* Back Navigation */}
<div className="mb-8">
<Button
variant="ghost"
onClick={() => navigateTo('/services')}
className="text-white hover:text-white hover:bg-white/10 p-2 -ml-2"
>
<ArrowLeft className="w-4 h-4 mr-2" />
Back to Services
</Button>
</div>
<div className="mb-8">
<h1 className="text-h1-white">
{apiData.hero_section.headline}
</h1>
</div>
<p className="text-body-lg-white mb-8 max-w-3xl">
<strong>{apiData.hero_section.subtext}</strong>
</p>
<div className="flex justify-start">
<PrimaryCTAButton
text={apiData.hero_section.cta_text}
onClick={() => navigateTo(apiData.hero_section.cta_destination)}
ariaLabel={apiData.hero_section.cta_text}
className="primary-cta-button-blue cta-text-white"
/>
</div>
</div>
</div>
</div>
</section>
{/* 1. What Is This Service */}
<section className="py-24 lg:py-32" style={{ backgroundColor: '#FFFFFF' }}>
<div className="section-margin-x">
<div className="max-w-6xl mx-auto">
<div className="text-center mb-12">
<BrandedTag text="What Is This Service?" />
<h2 className="text-h2 mb-8 text-[#26231A]">{apiData.overview.title}</h2>
<div className="max-w-4xl mx-auto space-y-6">
<p className="text-body-lg text-[#6F6F6F] leading-relaxed">
{apiData.overview.description}
</p>
<div className="bg-[#04045B]/5 border-l-4 border-[#04045B] p-6 rounded-lg">
<p className="text-body-lg text-[#26231A] leading-relaxed">
<span className="font-semibold text-[#04045B]">The Business Problem It Solves:</span> {apiData.overview.highlight_text}
</p>
</div>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{apiData.overview.overview_cards.map((card, index) => {
const IconComponent = getIconComponent(card.icon_url);
return (
<div key={card.id} className="group bg-white border border-gray-200 rounded-xl p-8 hover:border-[#04045B]/20 hover:shadow-lg transition-all duration-300">
<div className="flex flex-col items-center text-center">
<div
className="w-16 h-16 rounded-xl flex items-center justify-center mb-6 group-hover:scale-105 transition-transform duration-300"
style={{ backgroundColor: '#04045B' }}
>
<IconComponent className="w-8 h-8 text-white" />
</div>
<h4 className="text-h4 mb-4 text-[#26231A] group-hover:text-[#04045B] transition-colors duration-300">
{card.title}
</h4>
<p className="text-body text-[#6F6F6F] leading-relaxed">
{card.description}
</p>
</div>
</div>
);
})}
</div>
</div>
</div>
</section>
{/* 2. Who Is It For */}
<section className="py-24 lg:py-32" style={{ backgroundColor: '#F9F9F9' }}>
<div className="section-margin-x">
<div className="max-w-6xl mx-auto">
<div className="text-center mb-16">
<BrandedTag text="Who Is It For?" />
<h2 className="text-h2 mb-8">{apiData.audience_section.title}</h2>
<p className="text-body-lg text-muted max-w-3xl mx-auto">
{apiData.audience_section.description}
</p>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{targetAudience.map((audience, index) => (
<Card key={index} className="h-full hover:shadow-lg transition-all duration-300">
<CardContent className="p-8">
<div className="flex items-center gap-4 mb-6">
<div
className="w-16 h-16 rounded-2xl flex items-center justify-center"
style={{ backgroundColor: 'var(--color-primary)' }}
>
<audience.icon className="w-8 h-8 text-white" />
</div>
</div>
<h3 className="text-h4 mb-4">{audience.title}</h3>
<p className="text-body text-muted mb-6">{audience.description}</p>
<div>
<h4 className="text-small font-semibold text-primary mb-3">Common Challenges:</h4>
<ul className="space-y-2">
{audience.challenges.map((challenge, challengeIndex) => (
<li key={challengeIndex} className="text-small text-muted flex items-start gap-2">
<div className="w-1 h-1 bg-primary rounded-full mt-2 flex-shrink-0"></div>
{challenge}
</li>
))}
</ul>
</div>
</CardContent>
</Card>
))}
</div>
</div>
</div>
</section>
{/* 3. When to Use It */}
<section className="py-24 lg:py-32" style={{ backgroundColor: '#FFFFFF' }}>
<div className="section-margin-x">
<div className="max-w-6xl mx-auto">
<div className="text-center mb-16">
<BrandedTag text="When to Use It?" />
<h2 className="text-h2 mb-8">{apiData.use_case_section.title}</h2>
<p className="text-body-lg text-muted max-w-2xl mx-auto">
{apiData.use_case_section.description}
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
{useCases.map((useCase, index) => (
<div
key={index}
className="bg-white rounded-xl border border-gray-200 p-6 hover:border-[#04045B] hover:shadow-lg transition-all duration-300"
>
<div className="flex items-start gap-4 mb-4">
<div
className="w-12 h-12 rounded-lg flex items-center justify-center flex-shrink-0"
style={{ backgroundColor: '#04045B' }}
>
<useCase.icon className="w-6 h-6 text-white" />
</div>
<div className="flex-1">
<h3 className="text-h4 mb-2 text-[#26231A]">
{useCase.title}
</h3>
<p className="text-body text-muted">
{useCase.description}
</p>
</div>
</div>
<div className="flex items-center gap-2 text-small text-[#04045B] bg-[#04045B]/5 px-3 py-2 rounded-lg">
<div className="w-2 h-2 rounded-full bg-[#F8C301]" />
<span className="font-medium">
{useCase.scenario}
</span>
</div>
</div>
))}
</div>
</div>
</div>
</section>
{/* 4. Our Approach */}
<section className="py-24 lg:py-32" style={{ backgroundColor: '#F9F9F9' }}>
<div className="section-margin-x">
<div className="w-full">
<div className="max-w-6xl mx-auto">
<div className="text-center mb-16">
<BrandedTag text="Our Approach" />
<h2 className="text-h2 mb-8 text-[#26231A]">{apiData.approach_section.title}</h2>
<p className="text-body-lg text-[#6F6F6F] max-w-3xl mx-auto">
{apiData.approach_section.description}
</p>
</div>
{/* Flowchart Container with Connecting Lines */}
<div className="relative mb-16 flex flex-col items-center">
{/* Desktop: Horizontal Flowchart */}
<div className="hidden lg:block w-full max-w-5xl">
<div className="relative">
{/* Row 1: First 3 approach cards from API */}
<div className="grid grid-cols-3 gap-8 mb-12 relative w-full">
{apiData.approach_section.approach_cards.slice(0, 3).map((card, idx) => {
const IconComponent = getIconComponent(card.icon_url);
return (
<div key={card.id} className={`bg-white border-2 ${idx === 1 ? 'border-[#F8C301]' : 'border-[#04045B]'} rounded-xl p-6 hover:shadow-lg transition-all duration-300 relative z-10`}>
<div className={`w-12 h-12 ${idx === 1 ? 'bg-[#F8C301]' : 'bg-[#04045B]'} rounded-lg flex items-center justify-center mb-4`}>
<IconComponent className="w-6 h-6 text-white" />
</div>
<h3 className="text-h4 text-[#26231A] mb-3">{card.title}</h3>
<p className="text-body text-[#6F6F6F] mb-4">{card.description}</p>
<div className="space-y-2">
{card.bullets.slice(0, 3).map((bullet, bulletIdx) => (
<div key={bulletIdx} className="text-small text-[#6F6F6F] bg-gray-50 px-3 py-2 rounded-lg">
{bullet}
</div>
))}
</div>
</div>
);
})}
{/* Arrows between first 3 cards */}
{apiData.approach_section.approach_cards.length >= 2 && (
<div className="absolute top-1/2 left-[calc(33.33%-2rem)] -translate-y-1/2 z-0 flex items-center">
<div className="w-16 h-0.5 bg-[#F8C301]"></div>
<ArrowRight className="w-6 h-6 text-[#F8C301] -ml-1" />
</div>
)}
{apiData.approach_section.approach_cards.length >= 3 && (
<div className="absolute top-1/2 left-[calc(66.66%-2rem)] -translate-y-1/2 z-0 flex items-center">
<div className="w-16 h-0.5 bg-[#04045B]"></div>
<ArrowRight className="w-6 h-6 text-[#04045B] -ml-1" />
</div>
)}
</div>
{/* Vertical Connector - Center Flow Down */}
<div className="flex justify-center mb-6">
<div className="flex flex-col items-center">
<div className="w-0.5 h-12 bg-[#F8C301]"></div>
<ArrowRight className="w-6 h-6 text-[#F8C301] rotate-90" />
</div>
</div>
{/* Row 2: Next 2 approach cards (if available) */}
{apiData.approach_section.approach_cards.length >= 4 && (
<div className="grid grid-cols-2 gap-8 w-full max-w-3xl mx-auto mb-12 relative">
{apiData.approach_section.approach_cards.slice(3, 5).map((card, idx) => {
const IconComponent = getIconComponent(card.icon_url);
const isFirstOfPair = idx === 0;
return (
<div key={card.id} className={`bg-white border-2 ${isFirstOfPair ? 'border-[#F8C301]' : 'border-[#04045B]'} rounded-xl p-6 hover:shadow-lg transition-all duration-300 relative z-10`}>
<div className={`w-12 h-12 ${isFirstOfPair ? 'bg-[#F8C301]' : 'bg-[#04045B]'} rounded-lg flex items-center justify-center mb-4`}>
<IconComponent className="w-6 h-6 text-white" />
</div>
<h3 className="text-h4 text-[#26231A] mb-3">{card.title}</h3>
<p className="text-body text-[#6F6F6F] mb-4">{card.description}</p>
<div className="space-y-2">
{card.bullets.slice(0, 3).map((bullet, bulletIdx) => (
<div key={bulletIdx} className="text-small text-[#6F6F6F] bg-gray-50 px-3 py-2 rounded-lg">
{bullet}
</div>
))}
</div>
</div>
);
})}
{/* Arrow between the two cards */}
{apiData.approach_section.approach_cards.length >= 5 && (
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-0 flex items-center">
<div className="w-16 h-0.5 bg-[#04045B]"></div>
<ArrowRight className="w-6 h-6 text-[#04045B] -ml-1" />
</div>
)}
</div>
)}
{/* Final Vertical Connector - Center Flow Down to Outcome */}
<div className="flex justify-center mb-6">
<div className="flex flex-col items-center">
<div className="w-0.5 h-12 bg-[#04045B]"></div>
<ArrowRight className="w-6 h-6 text-[#04045B] rotate-90" />
</div>
</div>
{/* Row 3: Expected Outcome - Use API outcomes data */}
<div className="flex justify-center w-full">
<div className="bg-[#04045B] text-white rounded-xl p-8 w-full max-w-2xl border-4 border-[#F8C301] shadow-xl">
<div className="flex items-center gap-3 mb-4">
{apiData.approach_section.outcomes && apiData.approach_section.outcomes[0] && (() => {
const OutcomeIcon = getIconComponent(apiData.approach_section.outcomes[0].icon_url);
return <OutcomeIcon className="w-10 h-10 text-[#F8C301]" />;
})() || <TrendingUp className="w-10 h-10 text-[#F8C301]" />}
<h3 className="text-h4 text-white">
{apiData.approach_section.outcomes?.[0]?.title || "Expected Outcome"}
</h3>
</div>
<p className="text-body text-white mb-4">
{apiData.approach_section.outcomes?.[0]?.description || "Transformational executive leaders with strategic capability, executive presence, and proven business impact."}
</p>
<div className="space-y-2">
{apiData.approach_section.outcomes?.[0]?.bullets?.slice(0, 2).map((bullet, idx) => (
<div key={idx} className="flex items-center gap-2 text-[#F8C301]">
<CheckCircle className="w-6 h-6" />
<span className="text-body text-white">{bullet}</span>
</div>
)) || (
<>
<div className="flex items-center gap-2 text-[#F8C301]">
<CheckCircle className="w-6 h-6" />
<span className="text-body text-white">Exceptional Learning Experience</span>
</div>
</>
)}
</div>
</div>
</div>
</div>
</div>
{/* Tablet & Mobile: Vertical Flowchart */}
<div className="lg:hidden space-y-8">
{/* Map all approach cards vertically */}
{apiData.approach_section.approach_cards.map((card, idx) => {
const IconComponent = getIconComponent(card.icon_url);
const isEven = idx % 2 === 0;
return (
<div key={card.id} className="relative">
<div className={`bg-white border-2 ${isEven ? 'border-[#04045B]' : 'border-[#F8C301]'} rounded-xl p-6 hover:shadow-lg transition-all duration-300`}>
<div className={`w-12 h-12 ${isEven ? 'bg-[#04045B]' : 'bg-[#F8C301]'} rounded-lg flex items-center justify-center mb-4`}>
<IconComponent className="w-6 h-6 text-white" />
</div>
<h3 className="text-h4 text-[#26231A] mb-3">{card.title}</h3>
<p className="text-body text-[#6F6F6F] mb-4">{card.description}</p>
<div className="space-y-2">
{card.bullets.map((bullet, bulletIdx) => (
<div key={bulletIdx} className="text-small text-[#6F6F6F] bg-gray-50 px-3 py-2 rounded-lg">
{bullet}
</div>
))}
</div>
</div>
{/* Connector Arrow */}
{idx < apiData.approach_section.approach_cards.length - 1 && (
<div className="flex justify-center my-4">
<ArrowRight className={`w-8 h-8 ${isEven ? 'text-[#F8C301]' : 'text-[#04045B]'} rotate-90`} />
</div>
)}
</div>
);
})}
{/* Expected Outcome - Use API outcomes data */}
<div className="bg-[#04045B] text-white rounded-xl p-8 border-4 border-[#F8C301] shadow-xl">
<div className="flex items-center gap-3 mb-4">
{apiData.approach_section.outcomes && apiData.approach_section.outcomes[0] && (() => {
const OutcomeIcon = getIconComponent(apiData.approach_section.outcomes[0].icon_url);
return <OutcomeIcon className="w-10 h-10 text-[#F8C301]" />;
})() || <TrendingUp className="w-10 h-10 text-[#F8C301]" />}
<h3 className="text-h4 text-white">
{apiData.approach_section.outcomes?.[0]?.title || "Expected Outcome"}
</h3>
</div>
<p className="text-body text-white mb-4">
{apiData.approach_section.outcomes?.[0]?.description || "Transformational executive leaders with strategic capability, executive presence, and proven business impact."}
</p>
<div className="space-y-2">
{apiData.approach_section.outcomes?.[0]?.bullets?.slice(0, 2).map((bullet, idx) => (
<div key={idx} className="flex items-center gap-2 text-[#F8C301]">
<CheckCircle className="w-6 h-6" />
<span className="text-body text-white">{bullet}</span>
</div>
)) || (
<>
<div className="flex items-center gap-2 text-[#F8C301]">
<CheckCircle className="w-6 h-6" />
<span className="text-body text-white">Exceptional Learning Experience</span>
</div>
</>
)}
</div>
</div>
</div>
</div>
{/* Framework Effectiveness - Stats Section */}
{apiData.stats_section && (
<div className="bg-gray-50 rounded-xl p-8">
<div className="text-center mb-8">
<h3 className="text-h3 text-[#26231A] mb-4">{apiData.stats_section.title}</h3>
<p className="text-body text-[#6F6F6F] max-w-2xl mx-auto">
{apiData.stats_section.description}
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{apiData.stats_section.stat_cards.map((stat) => {
const IconComponent = getIconComponent(stat.icon_url);
return (
<div key={stat.id} className="text-center bg-white rounded-lg p-6">
<div className="w-14 h-14 bg-[#04045B] rounded-lg flex items-center justify-center mx-auto mb-3">
<IconComponent className="w-7 h-7 text-white" />
</div>
<div className="text-h2 text-[#04045B] mb-2">{stat.value}</div>
<p className="text-body text-[#6F6F6F]">{stat.label}</p>
</div>
);
})}
</div>
</div>
)}
</div>
</div>
</div>
</section>
{/* 5. Sample Program Format */}
<section className="py-24 lg:py-32" style={{ backgroundColor: '#FFFFFF' }}>
<div className="section-margin-x">
<div className="max-w-6xl mx-auto">
<div className="text-center mb-16">
<BrandedTag text="Sample Program Format" />
<h2 className="text-h2 mb-8">{apiData.program_section.title}</h2>
<p className="text-body-lg text-muted max-w-3xl mx-auto">
{apiData.program_section.description}
</p>
</div>
<div className="space-y-8">
{facilityFeatures.map((feature, index) => (
<Card
key={index}
className="border border-gray-200 hover:border-primary/20 transition-all duration-300 hover:shadow-lg"
>
<CardContent className="p-0">
<div
className="flex items-center justify-between p-6 cursor-pointer"
onClick={() => setExpandedFeature(expandedFeature === index ? null : index)}
>
<div className="flex items-start gap-4 flex-1">
<div
className="w-12 h-12 rounded-xl flex items-center justify-center flex-shrink-0"
style={{ backgroundColor: 'var(--color-primary)' }}
>
<span className="text-white font-semibold">{index + 1}</span>
</div>
<div className="flex-1">
<h3 className="text-h4 mb-2">{feature.phase}</h3>
<div className="flex items-center gap-2 mb-3">
<Building className="w-4 h-4 text-muted" />
<span className="text-small text-muted">{feature.duration}</span>
</div>
</div>
</div>
<div className="ml-4">
{expandedFeature === index ? (
<ChevronUp className="w-5 h-5 text-muted" />
) : (
<ChevronDown className="w-5 h-5 text-muted" />
)}
</div>
</div>
{expandedFeature === index && (
<div className="px-6 pb-6 border-t border-gray-100">
<div className="pt-6 grid grid-cols-1 lg:grid-cols-2 gap-8">
<div>
<h4 className="text-h4 mb-4">Available Features</h4>
<ul className="space-y-3">
{feature.activities.map((activity, activityIndex) => (
<li key={activityIndex} className="flex items-start gap-3">
<CheckCircle className="w-4 h-4 text-primary flex-shrink-0 mt-1" />
<span className="text-body text-muted">{activity}</span>
</li>
))}
</ul>
</div>
<div>
<h4 className="text-h4 mb-4">Experience Benefits</h4>
<ul className="space-y-3">
{feature.deliverables.map((deliverable, deliverableIndex) => (
<li key={deliverableIndex} className="flex items-start gap-3">
<Star className="w-4 h-4 text-accent flex-shrink-0 mt-1" />
<span className="text-body text-muted">{deliverable}</span>
</li>
))}
</ul>
</div>
</div>
</div>
)}
</CardContent>
</Card>
))}
</div>
</div>
</div>
</section>
{/* 6. Impact You Can Expect */}
<section className="py-24 lg:py-32" style={{ backgroundColor: '#F9F9F9' }}>
<div className="section-margin-x">
<div className="max-w-6xl mx-auto">
<div className="text-center mb-16">
<BrandedTag text="Impact You Can Expect" />
<h2 className="text-h2 mb-8">{apiData.impact_section.title}</h2>
<p className="text-body-lg text-muted max-w-3xl mx-auto">
{apiData.impact_section.description}
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8 mb-12">
{apiData.impact_section.impact_stats.map((stat) => {
const IconComponent = getIconComponent(stat.icon_url);
return (
<Card key={stat.id} className="text-center bg-white hover:shadow-lg transition-all duration-300">
<CardContent className="p-8">
<div
className="w-16 h-16 rounded-2xl flex items-center justify-center mx-auto mb-6"
style={{ backgroundColor: 'var(--color-primary)' }}
>
<IconComponent className="w-8 h-8 text-white" />
</div>
<div className="text-5xl font-medium mb-4" style={{ color: 'var(--color-primary)' }}>
{stat.value}
</div>
<p className="text-body text-muted mb-2">{stat.description}</p>
<p className="text-small text-primary font-medium">{stat.label}</p>
</CardContent>
</Card>
);
})}
</div>
<div className="bg-white p-8 rounded-2xl shadow-lg">
<h3 className="text-h3 mb-6 text-center">Additional Facility Benefits</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{apiData.impact_section.impact_benefits.map((benefit) => {
const IconComponent = getIconComponent(benefit.icon_url);
return (
<div key={benefit.id} className="text-center">
<div className="w-12 h-12 bg-primary/10 rounded-lg flex items-center justify-center mx-auto mb-3">
<IconComponent className="w-6 h-6 text-primary" />
</div>
<h4 className="text-h4 mb-2">{benefit.title}</h4>
<p className="text-small text-muted">{benefit.description}</p>
</div>
);
})}
</div>
</div>
</div>
</div>
</section>
{/* 7. Client Examples / Testimonials */}
<TestimonialsSection
title="What Our Clients Say About Kautilya Facility"
subtitle="Hear from organizations that have experienced our world-class learning environment"
tagText="Client Success Stories"
customTestimonials={testimonials}
/>
{/* 8. CTA Section */}
{apiData.cta_section && (
<section className="relative h-[700px] overflow-hidden">
<div className="absolute inset-0">
<ImageWithFallback
src={apiData.cta_section.background_image_url}
alt={apiData.cta_section.text}
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)' }}
>
<BrandedTag text="Next Steps" variant="white" />
<h2 className="text-h2-white mb-8">
{apiData.cta_section.text}
<span className="italic" style={{ color: 'var(--color-brand-accent)' }}>
{" "}Get in touch{" "}
</span>
to schedule your facility tour.
</h2>
<StandardCTAButton
text={apiData.cta_section.cta_text}
onClick={() => navigateTo(apiData.cta_section.cta_destination)}
ariaLabel={apiData.cta_section.cta_text}
/>
{apiData.cta_section.description && (
<p className="text-body-white mt-6 opacity-90">
{apiData.cta_section.description}
</p>
)}
</div>
</div>
</section>
)}
</div>
);
}

View 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>
);
}

View File

@@ -0,0 +1,341 @@
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 LearningEnvionment() {
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">
{/* Desktop Layout - Grid with Sticky Sidebar */}
<div className="hidden lg: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="Our Services"
/>
<h2
id="recognition-section-heading"
className="text-h2 mb-6"
>
Shaping Leaders, Cultures, and Institutions
</h2>
<p className="text-body-lg text-muted mb-8">
No two institutions are alike and neither are their leadership needs. That's why every KLC service is rooted in research, tailored to context, and aligned with strategy. From shaping leaders and managers to shaping culture, developing talent frameworks, and offering practical high impact learning, we partner with you to create leadership solutions that deliver lasting value.
</p>
{/* CTA Button - Left aligned */}
<div className="primary-cta-container-left cta-left-locked">
<PrimaryCTAButton
text="Services Page"
onClick={() => navigateTo('/services')}
ariaLabel="Explore our services"
className="services-cta-override"
/>
</div>
</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>
{/* Mobile Layout - Stacked Header + Horizontal Scrollable Cards */}
<div className="lg:hidden">
{/* Mobile Header */}
<div className="text-center mb-8">
<BrandedTag
text="Our Services"
/>
<h2
id="recognition-section-heading-mobile"
className="text-h2 mb-6"
>
Shaping Leaders, Cultures, and Institutions
</h2>
<p className="text-body-lg text-muted mb-8">
No two institutions are alike — and neither are their leadership needs. That's why every KLC service is rooted in research, tailored to context, and aligned with strategy. From shaping leaders and managers to shaping culture, developing talent frameworks, and offering practical high impact learning, we partner with you to create leadership solutions that deliver lasting value.
</p>
{/* CTA Button - Left aligned for mobile */}
<div className="primary-cta-container-left cta-left-locked">
<PrimaryCTAButton
text="Services Page"
onClick={() => navigateTo('/services')}
ariaLabel="Explore our services"
className="services-cta-override"
/>
</div>
</div>
{/* Mobile Horizontal Scrollable Cards */}
<div className="relative">
<div
className="flex gap-6 overflow-x-auto scrollbar-hide pb-4"
style={{
scrollSnapType: 'x mandatory',
WebkitOverflowScrolling: 'touch'
}}
role="list"
aria-label="Leadership development services"
>
{recognitionItems.map((item, index) => (
<div
key={item.id}
className={`recognition-card-mobile group focus-ring flex-shrink-0 ${isVisible ? 'animate-in' : ''}`}
role="listitem"
aria-labelledby={`recognition-title-mobile-${item.id}`}
aria-describedby={`recognition-desc-mobile-${item.id}`}
tabIndex={0}
onKeyDown={(e) => handleKeyDown(e, index)}
style={{
scrollSnapAlign: 'start',
width: '320px',
transitionDelay: `${(index + 1) * 150}ms`,
opacity: isVisible ? 1 : 0
}}
>
<div
className="p-6 transition-all duration-300 hover:shadow-xl hover:-translate-y-1 border bg-white h-full"
style={{
borderColor: 'var(--color-border)',
borderRadius: '12px',
fontFamily: 'var(--font-family-brand)'
}}
>
<div className="flex items-start justify-between mb-6">
<div
className="w-12 h-12 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-2 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-mobile-${item.id}`}
className="text-h4 mb-4"
>
{item.title}
</h3>
<p
id={`recognition-desc-mobile-${item.id}`}
className="text-small text-muted leading-relaxed"
>
{item.description}
</p>
</div>
</div>
</div>
))}
</div>
</div>
</div>
</div>
</div>
</section>
);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,773 @@
import {
ChevronLeft,
ChevronRight,
Clock,
DollarSign,
Filter,
Grid,
List,
Search,
Star,
Users,
X
} from 'lucide-react';
import { motion } from 'motion/react';
import { useState, useEffect, useMemo, useCallback } from 'react';
import { useCart } from './CartContext';
import { CartItem, CartPopup } from './CartPopup';
import { CourseCard } from './CourseCard';
import { ImageWithFallback } from './figma/ImageWithFallback';
import { navigateTo } from './Router';
import { Badge } from './ui/badge';
import { Button } from './ui/button';
import { Card } from './ui/card';
import { Input } from './ui/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
import {
courseApi,
useGetCoursesQuery,
Course,
GetCoursesParams,
useGetCourseCategoriesQuery,
CourseCategory
} from '../redux/services/courseApi';
import { useDebounce } from '../redux/hooks/useDebounce';
// Helper function to parse rupee price from string (keep as is)
const parsePriceToNumber = (priceStr: string | number): number => {
if (typeof priceStr === 'number') return priceStr;
const numericStr = priceStr.toString().replace(/[^0-9.-]/g, '');
return parseFloat(numericStr) || 0;
};
// Format price with Rupee symbol (keep as is)
const formatPrice = (price: number): string => {
return `${price.toLocaleString('en-IN')}`;
};
export function LearningOnline() {
// UI state
const [searchTerm, setSearchTerm] = useState('');
const [selectedCategoryId, setSelectedCategoryId] = useState<string>('');
const [selectedCategoryName, setSelectedCategoryName] = useState('All Categories');
const [selectedPriceRange, setSelectedPriceRange] = useState('All Prices');
const [selectedDuration, setSelectedDuration] = useState('All Durations');
const [selectedRating, setSelectedRating] = useState('All Ratings');
const [sortBy, setSortBy] = useState(''); // ✅ Changed to match API value
// const [sortBy, setSortBy] = useState('most_popular'); // ✅ Changed to match API value
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid');
const [currentPage, setCurrentPage] = useState(1);
const coursesPerPage = 9;
const prefetchCourseById = courseApi.usePrefetch('getcoursebyid');
// Debounced search term to avoid too many API calls
const debouncedSearchTerm = useDebounce(searchTerm, 500);
// Fetch course categories
const { data: categoriesData, isLoading: categoriesLoading } = useGetCourseCategoriesQuery({
limit: 100,
offset: 0
});
// ✅ Updated sort options to match backend API values
const sortOptions = [
{ value: 'most_popular', label: 'Most Popular' },
{ value: 'newest', label: 'Newest First' },
{ value: 'title_asc', label: 'Title A-Z' },
{ value: 'price_low', label: 'Price: Low to High' },
{ value: 'price_high', label: 'Price: High to Low' },
{ value: 'highest_rated', label: 'Highest Rated' },
{ value: 'duration', label: 'Duration' }
];
const priceRanges = [
'All Prices',
'Under ₹20,000',
'₹20,000 - ₹35,000',
'₹35,000 - ₹50,000',
'Over ₹50,000'
];
const durations = [
'All Durations',
'Under 6 hours',
'6-10 hours',
'10-15 hours',
'Over 15 hours'
];
const ratings = [
'All Ratings',
'4.5+ Stars',
'4.0+ Stars',
'3.5+ Stars'
];
// Build categories list
const categories = useMemo(() => {
const cats = [{ id: '', name: 'All Categories' }];
if (categoriesData?.data?.items) {
categoriesData.data.items.forEach((cat: CourseCategory) => {
cats.push({ id: cat.id, name: cat.category_name });
});
}
return cats;
}, [categoriesData]);
// ✅ Helper function to convert UI price range to API format
const getPriceRangeForApi = useCallback((priceRange: string): string | undefined => {
switch (priceRange) {
case 'Under ₹20,000':
return 'under_20000';
case '₹20,000 - ₹35,000':
return '20000_35000';
case '₹35,000 - ₹50,000':
return '35000_50000';
case 'Over ₹50,000':
return 'above_50000';
default:
return undefined;
}
}, []);
// ✅ Helper function to convert UI duration to API format
const getDurationForApi = useCallback((duration: string): string | undefined => {
switch (duration) {
case 'Under 6 hours':
return 'under_6';
case '6-10 hours':
return '6_10';
case '10-15 hours':
return '10_15';
case 'Over 15 hours':
return 'above_15';
default:
return undefined;
}
}, []);
// Helper function to convert UI rating to API format
const getRatingForApi = useCallback((rating: string): number | undefined => {
switch (rating) {
case '4.5+ Stars':
return 4.5;
case '4.0+ Stars':
return 4.0;
case '3.5+ Stars':
return 3.5;
default:
return undefined;
}
}, []);
// ✅ Helper function to convert sort option to API format
const getSortByForApi = useCallback((sort: string): string | undefined => {
// sort is already in API format (most_popular, newest, title_asc, etc.)
// Just return it as is
return sort;
}, []);
// Build API filters based on current UI state
const apiFilters: GetCoursesParams = useMemo(() => {
const filters: GetCoursesParams = {
limit: 100,
offset: 0,
status: 'publish'
};
// Category filter
if (selectedCategoryId) {
filters.course_category = [selectedCategoryId];
}
// Search query
if (debouncedSearchTerm) {
filters.search_query = debouncedSearchTerm;
}
// Price range
const apiPriceRange = getPriceRangeForApi(selectedPriceRange);
if (apiPriceRange) {
filters.price_range = apiPriceRange;
}
// Duration range
const apiDurationRange = getDurationForApi(selectedDuration);
if (apiDurationRange) {
filters.duration_range = apiDurationRange;
}
// Rating
const apiRating = getRatingForApi(selectedRating);
if (apiRating !== undefined) {
filters.min_rating = apiRating;
}
// Sort by
const apiSortBy = getSortByForApi(sortBy);
if (apiSortBy && sortBy !== '') {
filters.sort_by = apiSortBy;
}
return filters;
}, [
selectedCategoryId,
debouncedSearchTerm,
selectedPriceRange,
selectedDuration,
selectedRating,
sortBy,
getPriceRangeForApi,
getDurationForApi,
getRatingForApi,
getSortByForApi
]);
// Fetch courses with API filters
const {
data: coursesData,
isLoading: coursesLoading,
isError,
isFetching // To show loading indicator while fetching
} = useGetCoursesQuery(apiFilters);
// Reset to page 1 when filters change
useEffect(() => {
setCurrentPage(1);
}, [
selectedCategoryId,
debouncedSearchTerm,
selectedPriceRange,
selectedDuration,
selectedRating,
sortBy
]);
// Transform API response to course format
const courses = useMemo(() => {
if (!coursesData?.data?.items) return [];
return coursesData.data.items.map((course: Course) => ({
id: course.id,
title: course.course_name,
thumbnail: course.thumbnail_img || 'https://images.unsplash.com/photo-1552664730-d307ca884978?w=400&h=250&fit=crop',
duration: `${course.total_duration || 0} hours`,
level: 'Intermediate',
format: course.retail_type === 'public' ? 'Cohort-based' : 'Self-paced',
rating: course.avg_rating || 4.5,
reviews: `${course.total_reviews || 0} review${(course.total_reviews || 0) === 1 ? '' : 's'}`,
category: course.course_category_name || 'General',
categoryId: course.course_category_xid || '',
description: course.course_desc || `Master ${course.course_name} with our comprehensive program.`,
price: formatPrice(course.best_value || 0),
originalPrice: formatPrice(course.price || 0),
course_status: course.course_status
}));
}, [coursesData]);
// Get total courses count from API response
const totalCoursesCount = coursesData?.data?.pagination_info?.total_count || 0;
// Paginate the courses (since API returns all courses based on filters, we paginate client-side)
const totalPages = Math.ceil(totalCoursesCount / coursesPerPage);
const startIndex = (currentPage - 1) * coursesPerPage;
const currentCourses = courses.slice(startIndex, startIndex + coursesPerPage);
// Handle category change
const handleCategoryChange = (value: string) => {
const selectedCat = categories.find(cat => cat.name === value);
if (selectedCat) {
setSelectedCategoryName(selectedCat.name);
setSelectedCategoryId(selectedCat.id);
} else {
setSelectedCategoryName('All Categories');
setSelectedCategoryId('');
}
};
const clearAllFilters = () => {
setSearchTerm('');
handleCategoryChange('All Categories');
setSelectedPriceRange('All Prices');
setSelectedDuration('All Durations');
setSelectedRating('All Ratings');
setSortBy('');
};
const hasActiveFilters = searchTerm ||
selectedCategoryName !== 'All Categories' ||
selectedPriceRange !== 'All Prices' ||
selectedDuration !== 'All Durations' ||
selectedRating !== 'All Ratings';
// Cart functionality
const { addToCart } = useCart();
const [isCartPopupOpen, setIsCartPopupOpen] = useState(false);
const [recentlyAddedItem, setRecentlyAddedItem] = useState<CartItem | null>(null);
const handleAddToCart = (item: CartItem) => {
addToCart(item);
setRecentlyAddedItem(item);
setIsCartPopupOpen(true);
};
const handleCloseCartPopup = () => {
setIsCartPopupOpen(false);
setRecentlyAddedItem(null);
};
const handleCourseClick = useCallback((courseId: string) => {
prefetchCourseById(courseId, { force: true });
navigateTo(`/course/${courseId}`);
}, [prefetchCourseById]);
// Show loading state
if (coursesLoading || categoriesLoading) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary mx-auto mb-4"></div>
<p className="text-gray-600">Loading courses...</p>
</div>
</div>
);
}
// Show error state
if (isError) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="text-center">
<h2 className="text-2xl font-bold text-red-600 mb-4">Error Loading Courses</h2>
<p className="text-gray-600 mb-4">Failed to load courses. Please try again later.</p>
<Button onClick={() => window.location.reload()}>Retry</Button>
</div>
</div>
);
}
return (
<div style={{ backgroundColor: '#FFFFFF' }}>
{/* Hero Banner */}
<section className="relative py-16 overflow-hidden">
<div
className="absolute inset-0"
style={{
backgroundImage: 'url(https://images.unsplash.com/photo-1522202176988-66273c2fd55f?w=1200&h=600&fit=crop)',
backgroundSize: 'cover',
backgroundPosition: 'center'
}}
>
<div className="absolute inset-0 bg-gradient-to-r from-black/80 via-black/60 to-black/40" />
</div>
<div className="relative section-margin-x">
<div className="max-w-6xl text-left">
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8 }}
>
<div className="branded-tag-system-white mb-6 justify-start">
<div className="dot"></div>
<span className="text">DIGITAL LEARNING PLATFORM</span>
</div>
<h1 className="text-h1-white mb-8" style={{ lineHeight: 'var(--line-height-h1)' }}>
Discover Your Leadership<br />Potential Online
</h1>
<div className="max-w-5xl mb-8">
<p className="text-body-lg-white" style={{ lineHeight: '1.7' }}>
Our Leadership Courses are structured packages which are targeted towards building your leadership abilities. Each course is a wholesome package which not only helps you gain awareness about your leadership style but also gives insights to build your leadership abilities. Every course contains curated content targeted towards a specific leadership ability. Each course consists of our proprietary profiling instruments Leadership Profilers, conceptual videos and experiences of leaders Leadership Webcasts, as well as additional content to supplement learning.
</p>
</div>
</motion.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">
<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 courses, topics, categories..."
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>
<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 || undefined} 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={selectedCategoryName} onValueChange={handleCategoryChange}>
<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.name} value={category.name} className="text-small">
{category.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* Price Range Filter */}
<div className="filter-section">
<label className="block text-small mb-2 font-medium text-gray-700">
Price Range
</label>
<Select value={selectedPriceRange} onValueChange={setSelectedPriceRange}>
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors">
<SelectValue placeholder="All Prices" />
</SelectTrigger>
<SelectContent>
{priceRanges.map((priceRange) => (
<SelectItem key={priceRange} value={priceRange} className="text-small">
{priceRange}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* Duration Filter */}
<div className="filter-section">
<label className="block text-small mb-2 font-medium text-gray-700">
Duration
</label>
<Select value={selectedDuration} onValueChange={setSelectedDuration}>
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors">
<SelectValue placeholder="All Durations" />
</SelectTrigger>
<SelectContent>
{durations.map((duration) => (
<SelectItem key={duration} value={duration} className="text-small">
{duration}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* Rating Filter */}
<div className="filter-section">
<label className="block text-small mb-2 font-medium text-gray-700">
Rating
</label>
<Select value={selectedRating} onValueChange={setSelectedRating}>
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors">
<SelectValue placeholder="All Ratings" />
</SelectTrigger>
<SelectContent>
{ratings.map((rating) => (
<SelectItem key={rating} value={rating} className="text-small">
{rating}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
</div>
</Card>
</div>
</div>
{/* Right Content Area */}
<div className="col-span-12 lg:col-span-9">
<div className="mb-4 text-small text-muted flex justify-between items-center">
<span>Showing {currentCourses.length} of {totalCoursesCount} courses</span>
{isFetching && (
<span className="text-xs text-blue-600 animate-pulse">Updating results...</span>
)}
</div>
{/* Courses Results */}
{currentCourses.length === 0 ? (
<div className="text-center py-12">
<p className="text-body-lg text-muted">
No courses found matching your criteria.
</p>
{hasActiveFilters && (
<Button
variant="outline"
onClick={clearAllFilters}
className="mt-4"
>
Clear Filters
</Button>
)}
</div>
) : (
<>
{/* Grid View */}
{viewMode === 'grid' && (
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
{currentCourses.map((course) => (
<div key={course.id} className="w-full">
<CourseCard
course={course}
className="h-[560px] flex flex-col w-full"
onClick={() => handleCourseClick(course.id)}
onAddToCart={handleAddToCart}
/>
</div>
))}
</div>
)}
{/* List View */}
{viewMode === 'list' && (
<div className="space-y-4">
{currentCourses.map((course) => (
<Card
key={course.id}
className="overflow-hidden hover:shadow-lg transition-all duration-300 cursor-pointer group"
onClick={() => handleCourseClick(course.id)}
>
<div className="flex flex-col md:flex-row">
<div className="md:w-80 flex-shrink-0">
<div className="aspect-video md:aspect-square w-full bg-gray-100 overflow-hidden relative">
<ImageWithFallback
src={course.thumbnail}
alt={course.title}
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
/>
{/* Duration and Level Badge */}
<div className="absolute top-2 left-2 flex gap-2">
<Badge variant="secondary" className="bg-black/60 text-white text-xs">
<Clock className="w-3 h-3 mr-1" />
{course.duration}
</Badge>
<Badge variant="secondary" className="bg-black/60 text-white text-xs">
{course.level}
</Badge>
</div>
{/* Format Badge */}
<div className="absolute top-2 right-2">
<Badge variant="outline" className="bg-white text-xs">
{course.format}
</Badge>
</div>
</div>
</div>
<div className="flex-1 p-6 flex flex-col">
<div className="flex-1">
{/* Category */}
<Badge variant="outline" className="mb-3 text-xs border-blue-200 text-blue-700">
{course.category}
</Badge>
{/* Title */}
<h3 className="text-h4 mb-3 group-hover:text-blue-600 transition-colors">
{course.title}
</h3>
{/* Description */}
<p className="text-body text-gray-600 mb-4 line-clamp-2">
{course.description}
</p>
{/* Stats */}
<div className="flex items-center gap-4 mb-4">
<div className="flex items-center gap-1">
<Star className="w-4 h-4 text-yellow-400 fill-current" />
<span className="text-small font-medium">{course.rating}</span>
</div>
<div className="flex items-center gap-1">
<Users className="w-4 h-4 text-gray-400" />
<span className="text-small text-gray-600">{course.reviews}</span>
</div>
</div>
</div>
{/* Bottom Row - Price and Button */}
<div className="flex items-center justify-between pt-4 border-t border-gray-100">
<div className="flex items-center gap-3">
<span className="text-h4 text-gray-900">{course.price}</span>
<span className="text-small text-gray-500 line-through">{course.originalPrice}</span>
</div>
<Button
variant="outline"
size="sm"
onClick={(e: React.MouseEvent) => {
e.stopPropagation();
handleAddToCart({
id: course.id,
title: course.title,
price: course.price,
originalPrice: course.originalPrice,
thumbnail: course.thumbnail,
category: course.category,
level: course.level,
type: 'course'
});
}}
className="flex items-center gap-2 hover:bg-blue-50 hover:border-blue-300"
>
<DollarSign className="w-4 h-4" />
Add to Cart
</Button>
</div>
</div>
</div>
</Card>
))}
</div>
)}
{/* Pagination */}
{totalPages > 1 && (
<div className="flex items-center justify-center gap-2 mt-8">
<Button
variant="outline"
size="sm"
onClick={() => setCurrentPage(prev => Math.max(prev - 1, 1))}
disabled={currentPage === 1}
className="flex items-center gap-2"
>
<ChevronLeft className="w-4 h-4" />
Previous
</Button>
<div className="flex items-center gap-2">
{Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => (
<Button
key={page}
variant={currentPage === page ? "default" : "outline"}
size="sm"
onClick={() => setCurrentPage(page)}
className="min-w-[40px]"
>
{page}
</Button>
))}
</div>
<Button
variant="outline"
size="sm"
onClick={() => setCurrentPage(prev => Math.min(prev + 1, totalPages))}
disabled={currentPage === totalPages}
className="flex items-center gap-2"
>
Next
<ChevronRight className="w-4 h-4" />
</Button>
</div>
)}
</>
)}
</div>
</div>
</div>
</section>
{/* Cart Popup */}
<CartPopup
isOpen={isCartPopupOpen}
onClose={handleCloseCartPopup}
recentlyAddedItem={recentlyAddedItem}
/>
</div>
);
}

View File

@@ -0,0 +1,175 @@
import React from "react";
interface LoaderProps {
className?: string;
}
export const Loader: React.FC<LoaderProps> = ({ className = "" }) => {
return (
<>
<style>{`
.pl {
width: 8em;
height: 8em;
}
.pl circle {
transform-box: fill-box;
transform-origin: 50% 50%;
}
.pl__ring2 {
animation: ring2_ 4s 0.04s ease-in-out infinite;
}
.pl__ring4 {
animation: ring4_ 4s 0.12s ease-in-out infinite;
}
.pl__ring6 {
animation: ring6_ 4s 0.2s ease-in-out infinite;
}
@keyframes ring2_ {
from {
stroke-dashoffset: -329.207488554;
transform: rotate(-0.25turn);
animation-timing-function: ease-in;
}
23% {
stroke-dashoffset: -82.46680575;
transform: rotate(1turn);
animation-timing-function: ease-out;
}
46%, 50% {
stroke-dashoffset: -329.207488554;
transform: rotate(2.25turn);
animation-timing-function: ease-in;
}
73% {
stroke-dashoffset: -82.46680575;
transform: rotate(3.5turn);
animation-timing-function: ease-out;
}
96%, to {
stroke-dashoffset: -329.207488554;
transform: rotate(4.75turn);
}
}
@keyframes ring4_ {
from {
stroke-dashoffset: -253.9600625988;
transform: rotate(-0.25turn);
animation-timing-function: ease-in;
}
23% {
stroke-dashoffset: -63.61725015;
transform: rotate(1turn);
animation-timing-function: ease-out;
}
46%, 50% {
stroke-dashoffset: -253.9600625988;
transform: rotate(2.25turn);
animation-timing-function: ease-in;
}
73% {
stroke-dashoffset: -63.61725015;
transform: rotate(3.5turn);
animation-timing-function: ease-out;
}
96%, to {
stroke-dashoffset: -253.9600625988;
transform: rotate(4.75turn);
}
}
@keyframes ring6_ {
from {
stroke-dashoffset: -203.795111962;
transform: rotate(-0.25turn);
animation-timing-function: ease-in;
}
23% {
stroke-dashoffset: -51.05087975;
transform: rotate(1turn);
animation-timing-function: ease-out;
}
46%, 50% {
stroke-dashoffset: -203.795111962;
transform: rotate(2.25turn);
animation-timing-function: ease-in;
}
73% {
stroke-dashoffset: -51.05087975;
transform: rotate(3.5turn);
animation-timing-function: ease-out;
}
96%, to {
stroke-dashoffset: -203.795111962;
transform: rotate(4.75turn);
}
}
.loader-center {
display: flex;
align-items: center;
justify-content: center;
}
`}</style>
<div className={`loader-center ${className}`}>
<svg
className="pl"
viewBox="0 0 128 128"
xmlns="http://www.w3.org/2000/svg"
>
{/* Ring 2 */}
<circle
className="pl__ring2"
cx="64"
cy="64"
r="52.5"
fill="none"
stroke="hsl(240,92%,19%)"
strokeWidth="12"
transform="rotate(-90,64,64)"
strokeLinecap="round"
strokeDasharray="329.9 329.9"
strokeDashoffset="-329.3"
/>
{/* Ring 4 */}
<circle
className="pl__ring4"
cx="64"
cy="64"
r="37.5"
fill="none"
stroke="hsl(13,90%,55%)"
strokeWidth="9"
transform="rotate(-90,64,64)"
strokeLinecap="round"
strokeDasharray="254.5 254.5"
strokeDashoffset="-254"
/>
{/* Ring 6 */}
<circle
className="pl__ring6"
cx="64"
cy="64"
r="22.5"
fill="none"
stroke="hsl(47,99%,49%)"
strokeWidth="9"
transform="rotate(-90,64,64)"
strokeLinecap="round"
strokeDasharray="204.2 204.2"
strokeDashoffset="-203.9"
/>
</svg>
</div>
</>
);
};

View 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;
}

View File

@@ -0,0 +1,128 @@
import React from 'react';
import accenture from "../assets/accenture.svg";
import adani from "../assets/adani-logo.svg";
import axis from "../assets/axis-bank.svg";
import ceat from "../assets/ceat-logo.svg";
import hsbc from "../assets/hsbc.svg";
import larsen from "../assets/larsen-toubro-logo.svg";
import levis from "../assets/levis.svg";
import tata from "../assets/tata-motors.svg";
import Frame1597884933 from "../imports/Frame1597884933-44-374";
// Logo data using Frame1597884944 logos with proper dimensions
const logoData = [
{ src: accenture, name: 'CANMOOR', width: 302, height: 54 }, // CANMOOR
{ src: ceat, name: 'BlackRock', width: 210, height: 54 }, // BlackRock
{ src: hsbc, name: 'Royal London', width: 145, height: 54 }, // Royal London
{ src: adani, name: 'Abstract', width: 172, height: 54 }, // Abstract
{ src: larsen, name: 'ARES', width: 163, height: 54 }, // ARES
{ src: axis, name: 'KADANS', width: 206, height: 54 }, // KADANS
{ src: levis, name: 'levis', width: 206, height: 54 }, // KADANS
{ src: tata, name: 'tata', width: 206, height: 54 },
];
// 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] 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>
</div>
</section>
);
}

Some files were not shown because too many files have changed in this diff Show More