Compare commits

30 Commits

Author SHA1 Message Date
priyanshuvish
53dfa83aa3 Merge branch 'main' of http://git.wdipl.com/KLC/KLC-Website-Frontend
All checks were successful
Build-Check / Build and Test PR (pull_request) Successful in 1m16s
Sonar Check / SonarQube Scan (pull_request) Successful in 1m25s
2026-04-22 13:14:26 +05:30
priyanshuvish
6d31c3b96f all changes 2026-04-22 13:14:23 +05:30
3fc97d24fc Upload files to ".gitea/workflows"
All checks were successful
Build-Check / Build and Test PR (pull_request) Successful in 1m36s
Sonar Check / SonarQube Scan (pull_request) Successful in 1m25s
2026-04-14 17:43:38 +00:00
priyanshuvish
ec9cbca147 learning online by default all as null 2026-03-27 19:41:03 +05:30
priyanshuvish
d69747addc Merge branch 'main' of http://git.wdipl.com/KLC/KLC-Website-Frontend 2026-03-27 15:37:20 +05:30
priyanshuvish
637ca3bc03 home page insight and corses filter implementation 2026-03-27 15:37:16 +05:30
a1c1d90491 delete packagelock json 2026-03-27 07:20:05 +00:00
priyanshuvish
b907162457 working left on homepage blog 2026-03-27 12:43:34 +05:30
priyanshuvish
f0b4719282 working on services and course 2026-03-25 16:00:15 +05:30
priyanshuvish
6f72f1c828 All implemaentation and 360deg tour 2026-03-20 19:43:27 +05:30
priyanshuvish
3e1f2ca425 article data added 2025-10-09 17:57:17 +05:30
priyanshuvish
dc072361f8 blog related changes 2025-10-09 17:47:44 +05:30
priyanshuvish
b5a8f0f9c3 images changes 2025-10-07 14:39:14 +05:30
priyanshuvish
06518400a7 changes aboutus and other 2025-10-03 19:55:00 +05:30
priyanshuvish
f540b267f8 add testimonial in kautilyafac 2025-10-01 15:30:06 +05:30
priyanshuvish
609edb06bf add kautilya facility new page 2025-10-01 15:05:54 +05:30
priyanshuvish
35de76d259 blog btn fix 2025-09-30 17:43:12 +05:30
priyanshuvish
5f2f319a33 fix 2025-09-30 17:35:32 +05:30
priyanshuvish
d2548f2408 add same modal in learning facility 2025-09-30 16:53:39 +05:30
priyanshuvish
d3e301cbbb contactus bg change 2025-09-30 16:13:19 +05:30
priyanshuvish
5a11fa4771 add login path on download 2025-09-30 16:07:29 +05:30
priyanshuvish
7b8fe79917 pagination added in artical and other changes 2025-09-30 15:56:17 +05:30
priyanshuvish
c67ace8edb onlone learning and programme deatail 2025-09-26 20:09:19 +05:30
priyanshuvish
9163e046d0 fix hero btn and navigate on click bar 2025-09-25 17:31:35 +05:30
priyanshuvish
38a7b6a260 other changes like hero sec align left 2025-09-25 16:45:18 +05:30
priyanshuvish
43b894604d changes done header update and service link 2025-09-25 16:03:46 +05:30
priyanshuvish
8020a17fdd about us changes done 2025-09-05 19:17:18 +05:30
priyanshuvish
819f7a6865 changes done 2025-09-05 17:59:33 +05:30
priyanshuvish
2a857ef729 comment from signup pages 2025-09-05 15:11:09 +05:30
priyanshuvish
cff8173987 changes and scroll top 2025-09-05 15:06:26 +05:30
120 changed files with 17888 additions and 14301 deletions

View File

@@ -0,0 +1,34 @@
name: Build-Check
on:
pull_request:
branches:
- main
- beta
- testing
- client
- staging
- production
jobs:
build-test:
name: Build and Test PR
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install Dependencies
run: npm install
- name: Build Check
run: npm run build
- name: Audit Dependencies
run: npm audit --audit-level=critical

View File

@@ -0,0 +1,67 @@
name: Enforce Image Standards
on:
pull_request:
branches:
- main
- beta
- testing
- client
- staging
- production
types: [opened, synchronize, reopened]
paths:
- '**/*.jpg'
- '**/*.jpeg'
- '**/*.png'
workflow_dispatch:
jobs:
optimize:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ gitea.head_ref }} # IMPORTANT
- name: Install Image Tools
run: |
sudo apt-get update
sudo apt-get install -y imagemagick jpegoptim pngquant
- name: Resize Oversized Images
run: |
find . -type f \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" \) \
-exec mogrify -resize 1920x1920\> {} \;
- name: Optimize JPEG
run: |
find . -type f \( -iname "*.jpg" -o -iname "*.jpeg" \) \
-exec jpegoptim --strip-all --max=85 {} \;
- name: Optimize PNG
run: |
find . -type f -iname "*.png" \
-exec pngquant --force --ext .png --quality=75-90 {} \;
# Commit changes if any
- name: Commit changes
run: |
git config --global user.name "CI Bot"
git config --global user.email "ci@local"
if [ -n "$(git status --porcelain)" ]; then
git add .
git commit -m "chore: optimize images via CI"
else
echo "No changes to commit"
fi
# Push back to PR branch
- name: Push changes
if: success()
run: |
git push origin HEAD:${{ gitea.head_ref }}

View File

@@ -0,0 +1,39 @@
name: Sonar Check
on:
pull_request:
branches:
- main
- beta
- testing
- client
- staging
- production
jobs:
sonarqube:
name: SonarQube Scan
runs-on: ubuntu-latest
container:
image: sonarsource/sonar-scanner-cli:12.0.0.3214_8.0.1
options: --user root
steps:
- name: Checkout Repository
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Run Sonar Scan
run: |
REPO_NAME=${{ gitea.event.repository.name }}
sonar-scanner \
-Dsonar.projectKey=$REPO_NAME \
-Dsonar.projectName=$REPO_NAME \
-Dsonar.sources=. \
-Dsonar.host.url=${{ secrets.SONARQUBE_HOST }} \
-Dsonar.token=${{ secrets.SONARQUBE_TOKEN }} \
-Dsonar.exclusions=node_modules/**,dist/**,coverage/** \
-Dsonar.qualitygate.wait=true

3821
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -53,6 +53,7 @@
"sonner": "^2.0.3",
"tailwind-merge": "*",
"tailwindcss": "^4.1.12",
"three": "^0.183.2",
"vaul": "^1.1.2"
},
"devDependencies": {
@@ -60,6 +61,7 @@
"@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"

View File

@@ -22,7 +22,6 @@ import { SelfLearnerSignUp } from "./components/SelfLearnerSignUp";
import { Consulting } from "./components/services/Consulting";
import { CultureCompetence } from "./components/services/CultureCompetence";
import { ExecutiveCoaching } from "./components/services/ExecutiveCoaching";
import { LeadershipDevelopment } from "./components/services/LeadershipDevelopment";
import { LearningFacility } from "./components/services/LearningFacility";
import { ManagementDevelopment } from "./components/services/ManagementDevelopment";
import { Terms } from "./components/Terms";
@@ -32,9 +31,15 @@ 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 HomePageNew from './pages/HomePageNew';
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 { LearningFacilityPage } from "./components/LearningFacilityPage";
import WebinarDetail from "./components/WebinarDetail";
// import EnrollPlaceholder from "./components/EnrollPlaceholder";
// import ForgotPasswordPlaceholder from "./components/ForgotPasswordPlaceholder";
// import DashboardPlaceholder from "./components/DashboardPlaceholder";
@@ -58,13 +63,15 @@ export default function App() {
<Route path="/leadership-journey" element={<LeadershipJourneyPage />} />
{/* Services Pages */}
{/* <Route path="/services/leadership-development" element={<LeadershipDevelopment />} />
<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/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 />} />
@@ -79,6 +86,7 @@ export default function App() {
{/* Webinars Pages */}
<Route path="/webinars" element={<WebinarsPage />} />
<Route path="/webinars/:webinar_id" element={<WebinarDetail />} />
<Route path="/webinars-legacy" element={<WebinarsListing />} />
{/* Learning Online */}
@@ -101,15 +109,19 @@ export default function App() {
<Route path="/contact" element={<Contact />} />
{/* Dynamic Routes */}
<Route path="/learning/articles/:slug" element={<BlogDetail />} />
<Route path="/learning/blogs/:slug" element={<BlogDetail />} />
<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 />} />
<Route path="/learning-facility" element={<LearningFacilityPage />} />
{/* 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 />} />

BIN
src/assets/Aparna-Nair.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
src/assets/Campus1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

BIN
src/assets/Campus2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

BIN
src/assets/Campus3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 MiB

BIN
src/assets/Campus4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 MiB

BIN
src/assets/Campus5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB

BIN
src/assets/Campus6.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

BIN
src/assets/Classroom1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

BIN
src/assets/Classroom2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

BIN
src/assets/Classroom3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

BIN
src/assets/Classroom4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

BIN
src/assets/Classroom5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

BIN
src/assets/Diju.jpeg Normal file

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

BIN
src/assets/K-Ramkumar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 KiB

BIN
src/assets/Kautilya.png Normal file

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

1
src/assets/accenture.svg Normal file
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

1
src/assets/axis-bank.svg Normal file
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

BIN
src/assets/boardroom.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 MiB

1
src/assets/ceat-logo.svg Normal file
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: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

1
src/assets/flipkart.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 21 KiB

1
src/assets/hsbc.svg Normal file
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

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

1
src/assets/levis.svg Normal file
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

BIN
src/assets/morning.jpg Normal file

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,219 +1,127 @@
import React, { useState, useRef, useEffect } from 'react';
import { Button } from './ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './ui/card';
import { Badge } from './ui/badge';
import { Input } from './ui/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
import { ImageWithFallback } from './figma/ImageWithFallback';
import { PrimaryCTAButton } from './PrimaryCTAButton';
import { navigateTo } from './Router';
import {
Search,
Calendar,
User,
ArrowRight,
BookOpen,
ChevronLeft,
ChevronRight,
Filter,
Grid,
List,
SortAsc,
Clock,
Eye,
Star,
ChevronLeft,
ChevronRight,
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';
// Mock articles data
const articles = [
{
id: '1',
slug: 'future-of-leadership-development',
title: 'The Future of Leadership Development: Trends and Innovations',
excerpt: 'Explore emerging trends in leadership development and how organizations are adapting to create more effective leaders for tomorrow\'s challenges.',
content: 'Leadership development is evolving rapidly in response to changing workplace dynamics, technological advances, and new generations entering the workforce...',
author: 'Sarah Johnson',
authorTitle: 'Senior Leadership Consultant',
authorAvatar: 'https://images.unsplash.com/photo-1494790108755-2616b612b47c?w=150&h=150&fit=crop&crop=face',
date: '2024-02-20',
readTime: '8 min read',
category: 'Leadership Development',
tags: ['Future of Work', 'Leadership Trends', 'Innovation', 'Strategy'],
thumbnail: 'https://images.unsplash.com/photo-1552664730-d307ca884978?w=600&h=400&fit=crop',
featured: true,
views: '2.4k',
likes: 45
},
{
id: '2',
slug: 'emotional-intelligence-in-leadership',
title: 'Emotional Intelligence: The Key to Effective Leadership',
excerpt: 'Discover how emotional intelligence impacts leadership effectiveness and learn practical strategies to develop your EQ.',
content: 'Emotional intelligence has become increasingly recognized as a critical factor in leadership success...',
author: 'Dr. Michael Chen',
authorTitle: 'Executive Coach',
authorAvatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=150&h=150&fit=crop&crop=face',
date: '2024-02-18',
readTime: '6 min read',
category: 'Personal Development',
tags: ['Emotional Intelligence', 'Leadership Skills', 'Communication', 'Self-Awareness'],
thumbnail: 'https://images.unsplash.com/photo-1559027615-cd4628902d4a?w=600&h=400&fit=crop',
featured: true,
views: '1.8k',
likes: 32
},
{
id: '3',
slug: 'building-high-performing-teams',
title: 'Building High-Performing Teams: A Leader\'s Guide',
excerpt: 'Learn the essential strategies for creating and maintaining high-performing teams that deliver exceptional results.',
content: 'High-performing teams don\'t happen by accident. They require intentional leadership, clear purpose, and the right environment...',
author: 'Lisa Rodriguez',
authorTitle: 'Team Development Specialist',
authorAvatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=150&h=150&fit=crop&crop=face',
date: '2024-02-15',
readTime: '10 min read',
category: 'Team Development',
tags: ['Team Building', 'Performance', 'Collaboration', 'Leadership'],
thumbnail: 'https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=600&h=400&fit=crop',
featured: false,
views: '3.1k',
likes: 58
},
{
id: '4',
slug: 'digital-transformation-leadership',
title: 'Leading Digital Transformation: A Strategic Approach',
excerpt: 'Navigate the complexities of digital transformation with proven leadership strategies and best practices.',
content: 'Digital transformation is reshaping industries and organizations worldwide. Successful transformation requires strong leadership...',
author: 'David Park',
authorTitle: 'Digital Strategy Consultant',
authorAvatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=150&h=150&fit=crop&crop=face',
date: '2024-02-12',
readTime: '12 min read',
category: 'Digital Transformation',
tags: ['Digital Strategy', 'Change Management', 'Technology', 'Innovation'],
thumbnail: 'https://images.unsplash.com/photo-1560472355-536de3962603?w=600&h=400&fit=crop',
featured: false,
views: '2.7k',
likes: 41
},
{
id: '5',
slug: 'crisis-leadership-strategies',
title: 'Crisis Leadership: Navigating Uncertainty with Confidence',
excerpt: 'Master the art of crisis leadership and learn how to guide your organization through challenging times.',
content: 'Crisis situations test the mettle of leaders and reveal the true strength of an organization...',
author: 'Jennifer Adams',
authorTitle: 'Crisis Management Expert',
authorAvatar: 'https://images.unsplash.com/photo-1580489944761-15a19d654956?w=150&h=150&fit=crop&crop=face',
date: '2024-02-10',
readTime: '9 min read',
category: 'Crisis Management',
tags: ['Crisis Leadership', 'Risk Management', 'Decision Making', 'Communication'],
thumbnail: 'https://images.unsplash.com/photo-1584697964358-3e14ca57658b?w=600&h=400&fit=crop',
featured: false,
views: '1.9k',
likes: 35
},
{
id: '6',
slug: 'sustainable-leadership-practices',
title: 'Sustainable Leadership: Building for the Long Term',
excerpt: 'Explore sustainable leadership practices that create lasting value for organizations and stakeholders.',
content: 'Sustainable leadership goes beyond short-term gains to create lasting value for all stakeholders...',
author: 'Robert Kim',
authorTitle: 'Sustainability Consultant',
authorAvatar: 'https://images.unsplash.com/photo-1560250097-0b93528c311a?w=150&h=150&fit=crop&crop=face',
date: '2024-02-08',
readTime: '7 min read',
category: 'Strategy',
tags: ['Sustainability', 'ESG', 'Long-term Thinking', 'Stakeholder Value'],
thumbnail: 'https://images.unsplash.com/photo-1542601906990-b4d3fb778b09?w=600&h=400&fit=crop',
featured: false,
views: '1.5k',
likes: 28
}
];
// 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('All Categories');
const [selectedAuthor, setSelectedAuthor] = useState('All Authors');
const [selectedCategory, setSelectedCategory] = useState<CategoryOption>({ id: 'all', name: 'All Categories' });
const [selectedReadTime, setSelectedReadTime] = useState('All Read Times');
const [selectedDateRange, setSelectedDateRange] = useState('All Time');
const [selectedTopic, setSelectedTopic] = useState('All Topics');
const [sortBy, setSortBy] = useState('Most Recent');
const [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 }[]>([]);
// Get unique values for filters
const categories = ['All Categories', ...Array.from(new Set(articles.map(article => article.category)))];
const authors = ['All Authors', ...Array.from(new Set(articles.map(article => article.author)))];
const readTimes = ['All Read Times', 'Under 5 min', '5-10 min', 'Over 10 min'];
const dateRanges = ['All Time', 'Last 7 days', 'Last 30 days', 'Last 3 months'];
const allTags = Array.from(new Set(articles.flatMap(article => article.tags)));
const sortOptions = [
{ value: 'Most Recent', label: 'Most Recent' },
{ value: 'oldest', label: 'Oldest First' },
{ value: 'title', label: 'Title A-Z' },
{ value: 'readTime', label: 'Read Time' },
{ value: 'popular', label: 'Most Popular' }
];
// Filter and sort articles
const filteredArticles = articles.filter(article => {
const matchesSearch = article.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
article.excerpt.toLowerCase().includes(searchTerm.toLowerCase()) ||
article.author.toLowerCase().includes(searchTerm.toLowerCase()) ||
article.tags.some(tag => tag.toLowerCase().includes(searchTerm.toLowerCase()));
const matchesCategory = selectedCategory === 'All Categories' || article.category === selectedCategory;
const matchesAuthor = selectedAuthor === 'All Authors' || article.author === selectedAuthor;
// Read time filter
const readTimeMinutes = parseInt(article.readTime);
const matchesReadTime = selectedReadTime === 'All Read Times' ||
(selectedReadTime === 'Under 5 min' && readTimeMinutes < 5) ||
(selectedReadTime === '5-10 min' && readTimeMinutes >= 5 && readTimeMinutes <= 10) ||
(selectedReadTime === 'Over 10 min' && readTimeMinutes > 10);
// Date range filter
const articleDate = new Date(article.date);
const now = new Date();
const matchesDateRange = selectedDateRange === 'All Time' ||
(selectedDateRange === 'Last 7 days' && (now.getTime() - articleDate.getTime()) <= 7 * 24 * 60 * 60 * 1000) ||
(selectedDateRange === 'Last 30 days' && (now.getTime() - articleDate.getTime()) <= 30 * 24 * 60 * 60 * 1000) ||
(selectedDateRange === 'Last 3 months' && (now.getTime() - articleDate.getTime()) <= 90 * 24 * 60 * 60 * 1000);
// Topic filter
const matchesTopic = selectedTopic === 'All Topics' ||
article.tags.includes(selectedTopic);
return matchesSearch && matchesCategory && matchesAuthor && matchesReadTime && matchesDateRange && matchesTopic;
}).sort((a, b) => {
switch (sortBy) {
case 'Most Recent':
return new Date(b.date).getTime() - new Date(a.date).getTime();
case 'oldest':
return new Date(a.date).getTime() - new Date(b.date).getTime();
case 'title':
return a.title.localeCompare(b.title);
case 'readTime':
return parseInt(a.readTime) - parseInt(b.readTime);
case 'popular':
return parseInt(b.views) - parseInt(a.views);
default:
return 0;
}
// Fetch categories for filter dropdown
const {
data: categoriesData,
isLoading: isLoadingCategories
} = useGetFaqCategoriesQuery({
limit: 100,
offset: 0
});
// Paginate results
const totalPages = Math.ceil(filteredArticles.length / articlesPerPage);
const startIndex = (currentPage - 1) * articlesPerPage;
const currentArticles = filteredArticles.slice(startIndex, startIndex + articlesPerPage);
// 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',
@@ -222,27 +130,94 @@ export function Articles() {
});
};
const clearAllFilters = () => {
setSearchTerm('');
setSelectedCategory('All Categories');
setSelectedAuthor('All Authors');
setSelectedReadTime('All Read Times');
setSelectedDateRange('All Time');
setSelectedTopic('All Topics');
setSortBy('Most Recent');
// 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 hasActiveFilters = searchTerm ||
selectedCategory !== 'All Categories' ||
selectedAuthor !== 'All Authors' ||
selectedReadTime !== 'All Read Times' ||
selectedDateRange !== 'All Time' ||
selectedTopic !== 'All Topics';
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' }}>
<div style={{ backgroundColor: '#FFFFFF' }} ref={containerRef}>
{/* Hero Section */}
<section className="relative py-28 overflow-hidden">
<div className="absolute inset-0">
@@ -260,11 +235,11 @@ export function Articles() {
<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>
@@ -277,7 +252,7 @@ export function Articles() {
<div className="section-margin-x">
<div className="grid grid-cols-3 gap-8 text-center">
<div>
<div className="text-h2-white mb-2">{articles.length}+</div>
<div className="text-h2-white mb-2">{blogsData?.data?.pagination?.total || 0}+</div>
<div className="text-small-white">Expert Articles</div>
</div>
<div>
@@ -285,8 +260,8 @@ export function Articles() {
<div className="text-small-white">Categories</div>
</div>
<div>
<div className="text-h2-white mb-2">25,400</div>
<div className="text-small-white">Total Reads</div>
<div className="text-h2-white mb-2">{allTags.length}+</div>
<div className="text-small-white">Topics</div>
</div>
</div>
</div>
@@ -303,7 +278,7 @@ export function Articles() {
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
<Input
type="text"
placeholder="Search articles, authors, topics..."
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"
@@ -320,11 +295,10 @@ export function Articles() {
<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'
}`}
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
}}
@@ -334,11 +308,10 @@ export function Articles() {
</button>
<button
onClick={() => setViewMode('list')}
className={`p-2 transition-colors ${
viewMode === 'list'
? 'text-white'
: 'bg-white text-gray-600 hover:bg-gray-50'
}`}
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
}}
@@ -406,46 +379,37 @@ export function Articles() {
<label className="block text-small mb-2 font-medium text-gray-700">
Category
</label>
<Select value={selectedCategory} onValueChange={setSelectedCategory}>
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors" style={{ '&:focus': { borderColor: 'var(--color-primary)' } }}>
<SelectValue placeholder="All Categories" />
<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} value={category} className="text-small">
{category}
<SelectItem key={category.id} value={category.id} className="text-small">
{category.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* Author Filter */}
<div className="filter-section">
<label className="block text-small mb-2 font-medium text-gray-700">
Author
</label>
<Select value={selectedAuthor} onValueChange={setSelectedAuthor}>
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors" style={{ '&:focus': { borderColor: 'var(--color-primary)' } }}>
<SelectValue placeholder="All Authors" />
</SelectTrigger>
<SelectContent>
{authors.map((author) => (
<SelectItem key={author} value={author} className="text-small">
{author}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* Read Time Filter */}
{/* 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" style={{ '&:focus': { borderColor: 'var(--color-primary)' } }}>
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors">
<SelectValue placeholder="All Read Times" />
</SelectTrigger>
<SelectContent>
@@ -464,13 +428,13 @@ export function Articles() {
Date Range
</label>
<Select value={selectedDateRange} onValueChange={setSelectedDateRange}>
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors" style={{ '&:focus': { borderColor: 'var(--color-primary)' } }}>
<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((dateRange) => (
<SelectItem key={dateRange} value={dateRange} className="text-small">
{dateRange}
{dateRanges.map((range) => (
<SelectItem key={range.value} value={range.value}>
{range.label}
</SelectItem>
))}
</SelectContent>
@@ -482,17 +446,28 @@ export function Articles() {
<label className="block text-small mb-2 font-medium text-gray-700">
Topic
</label>
<Select value={selectedTopic} onValueChange={setSelectedTopic}>
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors" style={{ '&:focus': { borderColor: 'var(--color-primary)' } }}>
<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 Topics" className="text-small">
<SelectItem value="all">
All Topics
</SelectItem>
{allTags.map((tag) => (
<SelectItem key={tag} value={tag} className="text-small">
{tag}
<SelectItem key={tag.id} value={tag.id}>
{tag.name}
</SelectItem>
))}
</SelectContent>
@@ -507,73 +482,85 @@ export function Articles() {
{/* Right Content Area - Scrollable Articles */}
<div className="col-span-12 lg:col-span-9">
<div className="mb-4 text-small text-muted">
Showing {currentArticles.length} of {filteredArticles.length} articles
Showing {finalFilteredArticles.length} of {blogsData?.data?.pagination?.total || 0} articles
</div>
{/* Articles Results */}
{currentArticles.length === 0 ? (
{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">
{currentArticles.map((article) => (
<Card
{finalFilteredArticles.map((article: BlogItem) => (
<Card
key={article.id}
className="overflow-hidden hover:shadow-lg transition-all duration-300 cursor-pointer group"
onClick={() => navigateTo(`/learning/articles/${article.slug}`)}
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.thumbnail}
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"
/>
{article.featured && (
<div className="absolute top-4 right-4">
<Badge
variant="secondary"
className="bg-yellow-100 text-yellow-800 border-yellow-200"
>
Featured
</Badge>
</div>
)}
</div>
<CardContent className="p-6">
<div className="flex items-center gap-2 mb-3">
<Badge variant="outline" className="text-small">
{article.category}
{article.content_category}
</Badge>
<span className="text-small text-muted">{article.readTime}</span>
<span className="text-small text-muted">
{calculateReadTime(article.content)}
</span>
</div>
<h3 className="text-h4 mb-3 group-hover:text-blue-600 transition-colors line-clamp-2">
<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.excerpt}
{article.short_description || article.content.substring(0, 150) + '...'}
</p>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<ImageWithFallback
src={article.authorAvatar}
alt={article.author}
className="w-6 h-6 rounded-full object-cover"
/>
<span className="text-small">{article.author}</span>
</div>
<div className="flex items-center justify-end gap-2">
<div className="text-small text-muted">
{formatDate(article.date)}
{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>
))}
@@ -583,29 +570,23 @@ export function Articles() {
{/* List View */}
{viewMode === 'list' && (
<div className="space-y-6">
{currentArticles.map((article) => (
<Card
{finalFilteredArticles.map((article: BlogItem) => (
<Card
key={article.id}
className="overflow-hidden hover:shadow-lg transition-all duration-300 cursor-pointer group"
onClick={() => navigateTo(`/learning/articles/${article.slug}`)}
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.thumbnail}
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"
/>
{article.featured && (
<div className="absolute top-2 right-2">
<Badge
variant="secondary"
className="bg-yellow-100 text-yellow-800 border-yellow-200 text-xs"
>
Featured
</Badge>
</div>
)}
</div>
<div className="flex-1 p-6">
@@ -613,38 +594,39 @@ export function Articles() {
<div className="flex-1">
<div className="flex items-center gap-2 mb-2">
<Badge variant="outline" className="text-small">
{article.category}
{article.content_category}
</Badge>
<span className="text-small text-muted">{article.readTime}</span>
<div className="flex items-center gap-1 text-small text-muted">
<Eye className="w-3 h-3" />
<span>{article.views}</span>
</div>
<span className="text-small text-muted">
{calculateReadTime(article.content)}
</span>
</div>
<h3 className="text-h4 mb-2 group-hover:text-blue-600 transition-colors">
<h3 className="text-h4 mb-2 group-hover:text-[#04045B] transition-colors">
{article.title}
</h3>
<p className="text-body text-muted mb-3">
{article.excerpt}
{article.short_description || article.content.substring(0, 200) + '...'}
</p>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<ImageWithFallback
src={article.authorAvatar}
alt={article.author}
className="w-6 h-6 rounded-full object-cover"
/>
<div>
<span className="text-small font-medium">{article.author}</span>
<span className="text-small text-muted ml-1"> {article.authorTitle}</span>
</div>
</div>
<div className="text-small text-muted">
{formatDate(article.date)}
{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>
@@ -657,29 +639,71 @@ export function Articles() {
{/* Pagination */}
{totalPages > 1 && (
<div className="flex items-center justify-center gap-4 mt-12">
<div className="flex items-center justify-center gap-2 mt-8">
<Button
variant="outline"
onClick={() => setCurrentPage(prev => Math.max(prev - 1, 1))}
size="sm"
onClick={() => {
setCurrentPage(prev => Math.max(1, prev - 1));
containerRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' });
}}
disabled={currentPage === 1}
className="text-body"
className="flex items-center gap-1 border-gray-300 disabled:opacity-50 disabled:cursor-not-allowed"
>
<ChevronLeft className="w-4 h-4 mr-2" />
<ChevronLeft className="w-4 h-4" />
Previous
</Button>
<span className="text-body">
Page {currentPage} of {totalPages}
</span>
<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"
onClick={() => setCurrentPage(prev => Math.min(prev + 1, totalPages))}
size="sm"
onClick={() => {
setCurrentPage(prev => Math.min(totalPages, prev + 1));
containerRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' });
}}
disabled={currentPage === totalPages}
className="text-body"
className="flex items-center gap-1 border-gray-300 disabled:opacity-50 disabled:cursor-not-allowed"
>
Next
<ChevronRight className="w-4 h-4 ml-2" />
<ChevronRight className="w-4 h-4" />
</Button>
</div>
)}
@@ -703,9 +727,9 @@ export function Articles() {
</div>
<div className="relative h-full flex items-center justify-end section-margin-x">
<div
<div
className="bg-opacity-95 backdrop-blur-sm rounded-lg p-16 max-w-2xl"
style={{
style={{
backgroundColor: 'var(--color-brand-primary)'
}}
>
@@ -715,8 +739,8 @@ export function Articles() {
</div>
<h2 className="text-h2-white mb-8">
Ready to explore more insights?
<span
Ready to explore more insights?
<span
className="italic"
style={{ color: 'var(--color-brand-accent)' }}
>
@@ -725,7 +749,7 @@ export function Articles() {
our complete library of leadership resources.
</h2>
<PrimaryCTAButton
<PrimaryCTAButton
text="Browse All Resources"
onClick={() => navigateTo('/learning/articles')}
ariaLabel="Browse all leadership articles and resources"

View File

@@ -1,18 +1,16 @@
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 { navigateTo } from './Router';
import { ImageWithFallback } from './figma/ImageWithFallback';
import { CTABannerSection } from './CTABannerSection';
import { useCart } from './CartContext';
import {
Calendar,
Clock,
ArrowRight,
ChevronUp,
Share2,
Bookmark,
Twitter,
Facebook,
@@ -21,424 +19,91 @@ import {
Heart,
Eye,
BookOpen,
ArrowLeft,
Star,
MessageCircle,
Users,
TrendingUp,
Award
ArrowLeft
} from 'lucide-react';
interface BlogPost {
id: string;
title: string;
slug: string;
excerpt: string;
content: string;
author: string;
authorBio: string;
authorImage: string;
publishedDate: string;
updatedDate?: string;
readTime: string;
views: number;
likes: number;
category: string;
tags: string[];
image: string;
featured: boolean;
}
interface RelatedPost {
id: string;
title: string;
slug: string;
excerpt: string;
author: string;
publishedDate: string;
readTime: string;
category: string;
image: string;
}
import { useGetBlogByIDQuery, useGetBlogsQuery } from '../redux/services/blogApi';
import { FullScreenLoader } from './FullScreenLoader';
import { extractIdFromSlug, extractSlugFromSlugAndId, getSlugWithId } from '../utils/urlHelpers';
interface BlogDetailProps {
params?: {
slug?: string;
slugAndId?: string;
};
}
// Articles data synced with Articles component for consistency
const articlesData = [
{
id: 1,
title: "The Future of Leadership Development: Trends and Innovations",
slug: "future-of-leadership-development",
excerpt: "Explore emerging trends in leadership development and how organizations are adapting to create more effective leaders for tomorrow's challenges.",
content: `
<p>Leadership development is evolving rapidly in response to changing workplace dynamics, technological advances, and new generations entering the workforce. Organizations worldwide are reimagining their approach to cultivating leadership talent.</p>
<h2>Emerging Trends in Leadership Development</h2>
<p>Several key trends are reshaping how we develop leaders:</p>
<ul>
<li><strong>Personalized Learning Paths:</strong> Customized development programs based on individual strengths, weaknesses, and career aspirations.</li>
<li><strong>Digital-First Delivery:</strong> Virtual reality simulations, AI-powered coaching, and mobile learning platforms.</li>
<li><strong>Continuous Feedback Culture:</strong> Real-time performance insights and ongoing mentorship relationships.</li>
<li><strong>Cross-Functional Exposure:</strong> Leaders developing broader business acumen through diverse role experiences.</li>
</ul>
<h2>Innovation in Leadership Development</h2>
<p>Organizations are experimenting with new methodologies and technologies to create more effective development experiences.</p>
<blockquote>
<p>"The future of leadership development lies in creating adaptive, resilient leaders who can thrive in uncertainty while driving innovation and human connection."</p>
<cite>— Sarah Johnson, Senior Leadership Consultant</cite>
</blockquote>
<h3>Technology-Enhanced Learning</h3>
<p>From virtual reality leadership scenarios to AI-powered coaching platforms, technology is creating immersive development experiences that accelerate learning and skill application.</p>
<p>The future belongs to leaders who embrace continuous learning, demonstrate emotional intelligence, and can navigate complexity with confidence and clarity.</p>
`,
author: "Sarah Johnson",
authorTitle: "Senior Leadership Consultant",
authorBio: "Sarah Johnson is a Senior Leadership Consultant with over 15 years of experience in organizational development and talent management. She specializes in designing innovative leadership programs for Fortune 500 companies.",
authorImage: "https://images.unsplash.com/photo-1494790108755-2616b612b47c?w=150&h=150&fit=crop",
publishedDate: "2024-02-20",
readTime: "8 min read",
category: "Leadership Development",
tags: ["Future of Work", "Leadership Trends", "Innovation", "Strategy"],
image: "https://images.unsplash.com/photo-1552664730-d307ca884978?w=1200&h=600&fit=crop",
featured: true,
views: 2400,
likes: 45
},
{
id: 2,
title: "Emotional Intelligence: The Key to Effective Leadership",
slug: "emotional-intelligence-in-leadership",
excerpt: "Discover how emotional intelligence impacts leadership effectiveness and learn practical strategies to develop your EQ.",
content: `
<p>Emotional intelligence has become increasingly recognized as a critical factor in leadership success. Research consistently shows that leaders with high emotional intelligence create more engaged teams, drive better business results, and build more resilient organizations.</p>
<h2>The Four Domains of Emotional Intelligence</h2>
<p>Daniel Goleman's framework identifies four key domains:</p>
<ul>
<li><strong>Self-Awareness:</strong> Understanding your emotions, strengths, and limitations.</li>
<li><strong>Self-Management:</strong> Effectively managing your emotions and behaviors.</li>
<li><strong>Social Awareness:</strong> Reading others' emotions and understanding group dynamics.</li>
<li><strong>Relationship Management:</strong> Influencing and managing relationships effectively.</li>
</ul>
<blockquote>
<p>"IQ gets you hired, but EQ gets you promoted. In leadership roles, emotional intelligence becomes the differentiator between good managers and great leaders."</p>
<cite>— Dr. Michael Chen, Executive Coach</cite>
</blockquote>
<h2>Developing Your Emotional Intelligence</h2>
<p>Unlike IQ, emotional intelligence can be developed throughout your career through deliberate practice, feedback, and reflection.</p>
<h3>Practical Strategies</h3>
<p>Start with mindfulness practices, seek feedback from trusted colleagues, and practice active listening in all your interactions. The investment in developing emotional intelligence pays dividends in leadership effectiveness.</p>
`,
author: "Dr. Michael Chen",
authorTitle: "Executive Coach",
authorBio: "Dr. Michael Chen is an Executive Coach and organizational psychologist with expertise in emotional intelligence and leadership development. He has coached hundreds of senior executives across various industries.",
authorImage: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=150&h=150&fit=crop",
publishedDate: "2024-02-18",
readTime: "6 min read",
category: "Personal Development",
tags: ["Emotional Intelligence", "Leadership Skills", "Communication", "Self-Awareness"],
image: "https://images.unsplash.com/photo-1559027615-cd4628902d4a?w=1200&h=600&fit=crop",
featured: true,
views: 1800,
likes: 32
},
{
id: 3,
title: "Building High-Performing Teams: A Leader's Guide",
slug: "building-high-performing-teams",
excerpt: "Learn the essential strategies for creating and maintaining high-performing teams that deliver exceptional results.",
content: `
<p>High-performing teams don't happen by accident. They require intentional leadership, clear purpose, and the right environment to thrive. Understanding the key elements that drive team performance is essential for any leader.</p>
<h2>Characteristics of High-Performing Teams</h2>
<p>Research identifies several common traits among exceptional teams:</p>
<ul>
<li><strong>Shared Purpose:</strong> Clear understanding of goals and how individual contributions matter.</li>
<li><strong>Psychological Safety:</strong> Environment where team members feel safe to take risks and share ideas.</li>
<li><strong>Diverse Perspectives:</strong> Variety of backgrounds, skills, and thinking styles.</li>
<li><strong>Effective Communication:</strong> Open, honest, and constructive dialogue.</li>
</ul>
<h2>Building Team Performance</h2>
<p>Creating high-performing teams requires deliberate effort in team formation, skill development, and culture building.</p>
<blockquote>
<p>"Great teams are not created by chance. They are the result of intentional leadership that focuses on both individual development and collective excellence."</p>
<cite>— Lisa Rodriguez, Team Development Specialist</cite>
</blockquote>
<p>The journey to high performance requires patience, consistency, and a commitment to continuous improvement at both individual and team levels.</p>
`,
author: "Lisa Rodriguez",
authorTitle: "Team Development Specialist",
authorBio: "Lisa Rodriguez is a Team Development Specialist with over 12 years of experience helping organizations build high-performing teams. She specializes in team dynamics, collaboration, and performance optimization.",
authorImage: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=150&h=150&fit=crop",
publishedDate: "2024-02-15",
readTime: "10 min read",
category: "Team Development",
tags: ["Team Building", "Performance", "Collaboration", "Leadership"],
image: "https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=1200&h=600&fit=crop",
featured: false,
views: 3100,
likes: 58
},
{
id: 4,
title: "Leading Digital Transformation: A Strategic Approach",
slug: "digital-transformation-leadership",
excerpt: "Navigate the complexities of digital transformation with proven leadership strategies and best practices.",
content: `
<p>Digital transformation is reshaping industries and organizations worldwide. Successful transformation requires strong leadership that can balance technological innovation with human-centered change management.</p>
<h2>The Leadership Challenge</h2>
<p>Digital transformation is as much about people and culture as it is about technology. Leaders must navigate resistance, build new capabilities, and maintain business performance during the transition.</p>
<h3>Key Success Factors</h3>
<ul>
<li><strong>Vision Clarity:</strong> Articulating a compelling future state that motivates change.</li>
<li><strong>Change Management:</strong> Supporting people through the transformation journey.</li>
<li><strong>Technology Integration:</strong> Selecting and implementing the right solutions.</li>
<li><strong>Cultural Evolution:</strong> Building a culture that embraces digital innovation.</li>
</ul>
<blockquote>
<p>"Digital transformation succeeds when leaders focus on transforming people and processes, not just implementing technology."</p>
<cite>— David Park, Digital Strategy Consultant</cite>
</blockquote>
<p>The most successful digital transformations are led by leaders who understand that technology enables transformation, but people make it happen.</p>
`,
author: "David Park",
authorTitle: "Digital Strategy Consultant",
authorBio: "David Park is a Digital Strategy Consultant with extensive experience in leading large-scale digital transformations across multiple industries. He specializes in technology strategy and organizational change.",
authorImage: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=150&h=150&fit=crop",
publishedDate: "2024-02-12",
readTime: "12 min read",
category: "Digital Transformation",
tags: ["Digital Strategy", "Change Management", "Technology", "Innovation"],
image: "https://images.unsplash.com/photo-1560472355-536de3962603?w=1200&h=600&fit=crop",
featured: false,
views: 2700,
likes: 41
},
{
id: 5,
title: "Crisis Leadership: Navigating Uncertainty with Confidence",
slug: "crisis-leadership-strategies",
excerpt: "Master the art of crisis leadership and learn how to guide your organization through challenging times.",
content: `
<p>Crisis situations test the mettle of leaders and reveal the true strength of an organization. Effective crisis leadership requires a unique combination of decisive action, clear communication, and emotional resilience.</p>
<h2>Principles of Crisis Leadership</h2>
<p>Successful crisis leaders follow key principles that help them navigate uncertainty:</p>
<ul>
<li><strong>Stay Calm Under Pressure:</strong> Maintaining composure to make clear-headed decisions.</li>
<li><strong>Communicate Transparently:</strong> Providing honest, frequent updates to all stakeholders.</li>
<li><strong>Act Decisively:</strong> Making tough decisions quickly with available information.</li>
<li><strong>Show Empathy:</strong> Understanding and addressing the human impact of the crisis.</li>
</ul>
<h2>Building Crisis Resilience</h2>
<p>The best time to prepare for a crisis is before it happens. Organizations need to build crisis response capabilities and leadership resilience proactively.</p>
<blockquote>
<p>"Crisis leadership is not about having all the answers—it's about maintaining trust and direction when everything seems uncertain."</p>
<cite>— Jennifer Adams, Crisis Management Expert</cite>
</blockquote>
<p>Leaders who excel in crisis situations combine strategic thinking with human compassion, creating stability in the midst of chaos.</p>
`,
author: "Jennifer Adams",
authorTitle: "Crisis Management Expert",
authorBio: "Jennifer Adams is a Crisis Management Expert with over 20 years of experience helping organizations navigate complex crises. She has advised government agencies and Fortune 500 companies on crisis preparedness and response.",
authorImage: "https://images.unsplash.com/photo-1580489944761-15a19d654956?w=150&h=150&fit=crop",
publishedDate: "2024-02-10",
readTime: "9 min read",
category: "Crisis Management",
tags: ["Crisis Leadership", "Risk Management", "Decision Making", "Communication"],
image: "https://images.unsplash.com/photo-1584697964358-3e14ca57658b?w=1200&h=600&fit=crop",
featured: false,
views: 1900,
likes: 35
},
{
id: 6,
title: "Sustainable Leadership: Building for the Long Term",
slug: "sustainable-leadership-practices",
excerpt: "Explore sustainable leadership practices that create lasting value for organizations and stakeholders.",
content: `
<p>Sustainable leadership goes beyond short-term gains to create lasting value for all stakeholders. It requires a long-term perspective that balances profit with purpose, growth with responsibility.</p>
<h2>The Sustainability Imperative</h2>
<p>Modern leaders must navigate increasing expectations for environmental and social responsibility while maintaining business performance.</p>
<h3>Key Elements of Sustainable Leadership</h3>
<ul>
<li><strong>Stakeholder Focus:</strong> Considering the needs of all stakeholders, not just shareholders.</li>
<li><strong>Environmental Stewardship:</strong> Making decisions that consider environmental impact.</li>
<li><strong>Social Responsibility:</strong> Contributing positively to communities and society.</li>
<li><strong>Economic Viability:</strong> Ensuring long-term business sustainability.</li>
</ul>
<blockquote>
<p>"Sustainable leadership is about creating value that endures beyond any individual leader's tenure, building organizations that thrive for generations."</p>
<cite>— Robert Kim, Sustainability Consultant</cite>
</blockquote>
<p>The future belongs to leaders who can balance multiple bottom lines—profit, people, and planet—while driving innovation and growth.</p>
`,
author: "Robert Kim",
authorTitle: "Sustainability Consultant",
authorBio: "Robert Kim is a Sustainability Consultant specializing in ESG strategy and sustainable business practices. He has helped numerous organizations integrate sustainability into their core business strategy.",
authorImage: "https://images.unsplash.com/photo-1560250097-0b93528c311a?w=150&h=150&fit=crop",
publishedDate: "2024-02-08",
readTime: "7 min read",
category: "Strategy",
tags: ["Sustainability", "ESG", "Long-term Thinking", "Stakeholder Value"],
image: "https://images.unsplash.com/photo-1542601906990-b4d3fb778b09?w=1200&h=600&fit=crop",
featured: false,
views: 1500,
likes: 28
}
];
// Function to get blog post by slug
const getBlogPostBySlug = (slug: string): BlogPost => {
const article = articlesData.find(article => article.slug === slug);
if (!article) {
return {
id: '1',
title: 'Article Not Found',
slug: 'not-found',
excerpt: `The requested article "${slug}" could not be found.`,
content: `
<p>We're sorry, but the article you're looking for could not be found. It may have been moved or removed.</p>
<p>Here are some available articles you can read:</p>
<ul>
<li><a href="/learning/articles/future-of-leadership-development">The Future of Leadership Development: Trends and Innovations</a></li>
<li><a href="/learning/articles/emotional-intelligence-in-leadership">Emotional Intelligence: The Key to Effective Leadership</a></li>
<li><a href="/learning/articles/building-high-performing-teams">Building High-Performing Teams: A Leader's Guide</a></li>
<li><a href="/learning/articles/digital-transformation-leadership">Leading Digital Transformation: A Strategic Approach</a></li>
<li><a href="/learning/articles/crisis-leadership-strategies">Crisis Leadership: Navigating Uncertainty with Confidence</a></li>
<li><a href="/learning/articles/sustainable-leadership-practices">Sustainable Leadership: Building for the Long Term</a></li>
</ul>
<p><a href="/learning/articles">Return to all articles</a></p>
`,
author: 'KLC Team',
authorBio: 'The Knowledge Leadership Centre team is dedicated to providing excellent leadership development resources.',
authorImage: 'https://images.unsplash.com/photo-1494790108755-2616b612b47c?w=150&h=150&fit=crop',
publishedDate: '2024-01-01',
updatedDate: '2024-01-01',
readTime: '1 min read',
views: 0,
likes: 0,
category: 'General',
tags: [],
image: 'https://images.unsplash.com/photo-1552664730-d307ca884978?w=1200&h=600&fit=crop',
featured: false
};
}
return {
id: article.id.toString(),
title: article.title,
slug: article.slug,
excerpt: article.excerpt,
content: article.content,
author: article.author,
authorBio: article.authorBio,
authorImage: article.authorImage,
publishedDate: article.publishedDate,
updatedDate: article.publishedDate,
readTime: article.readTime,
views: article.views,
likes: article.likes,
category: article.category,
tags: article.tags,
image: article.image,
featured: article.featured
};
};
const relatedPosts: RelatedPost[] = [
{
id: '2',
title: 'Emotional Intelligence: The Key to Effective Leadership',
slug: 'emotional-intelligence-in-leadership',
excerpt: 'Discover how emotional intelligence impacts leadership effectiveness and learn practical strategies to develop your EQ.',
author: 'Dr. Michael Chen',
publishedDate: '2024-02-18',
readTime: '6 min read',
category: 'Personal Development',
image: 'https://images.unsplash.com/photo-1559027615-cd4628902d4a?w=400&h=300&fit=crop'
},
{
id: '3',
title: 'Building High-Performing Teams: A Leader\'s Guide',
slug: 'building-high-performing-teams',
excerpt: 'Learn the essential strategies for creating and maintaining high-performing teams that deliver exceptional results.',
author: 'Lisa Rodriguez',
publishedDate: '2024-02-15',
readTime: '10 min read',
category: 'Team Development',
image: 'https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=400&h=300&fit=crop'
},
{
id: '4',
title: 'Leading Digital Transformation: A Strategic Approach',
slug: 'digital-transformation-leadership',
excerpt: 'Navigate the complexities of digital transformation with proven leadership strategies and best practices.',
author: 'David Park',
publishedDate: '2024-02-12',
readTime: '12 min read',
category: 'Digital Transformation',
image: 'https://images.unsplash.com/photo-1560472355-536de3962603?w=400&h=300&fit=crop'
}
];
export function BlogDetail({ params }: BlogDetailProps) {
const slug = params?.slug || '';
const { 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();
// Get blog post data based on slug
const blogPost = getBlogPostBySlug(slug);
// 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(() => {
document.title = `${blogPost.title} | KLC Blog`;
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]);
}, [blogPost?.title]);
const formatDate = (dateString: string) => {
return new Date(dateString).toLocaleDateString('en-US', {
@@ -450,8 +115,8 @@ export function BlogDetail({ params }: BlogDetailProps) {
const handleShare = (platform: string) => {
const url = window.location.href;
const title = blogPost.title;
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)}`,
@@ -471,13 +136,71 @@ export function BlogDetail({ params }: BlogDetailProps) {
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
<div
className="h-full transition-all duration-150"
style={{
style={{
width: `${scrollProgress}%`,
backgroundColor: '#04045B'
}}
@@ -506,7 +229,7 @@ export function BlogDetail({ params }: BlogDetailProps) {
<Button
variant="ghost"
size="sm"
onClick={() => navigateTo('/learning/articles')}
onClick={() => navigate('/learning/articles')}
className="p-0 h-auto font-medium hover:bg-transparent transition-colors"
style={{ color: '#6F6F6F' }}
>
@@ -514,7 +237,7 @@ export function BlogDetail({ params }: BlogDetailProps) {
Back to Articles
</Button>
<span className="text-[#E5E7EB]">•</span>
<span>{blogPost.category}</span>
<span>{blogPost.content_category || 'Article'}</span>
</div>
</div>
@@ -526,64 +249,64 @@ export function BlogDetail({ params }: BlogDetailProps) {
<header className="mb-16">
{/* Category Badge */}
<div className="mb-8">
<Badge
<Badge
className="mb-6 text-small px-4 py-2 font-medium border-none"
style={{
backgroundColor: 'rgba(4, 4, 91, 0.1)',
color: '#04045B'
}}
>
{blogPost.category}
{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.excerpt}
{blogPost.short_description || blogPost.content.substring(0, 200) + '...'}
</p>
</div>
</div>
{/* Enhanced Meta Bar with Cleaner Spacing */}
<div
<div
className="flex flex-col lg:flex-row items-start lg:items-center justify-between gap-6 p-6 rounded-xl border"
style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)', borderColor: 'rgba(0, 0, 0, 0.08)' }}
>
{/* Author Info with Improved Layout */}
<div className="flex items-center gap-4">
<Avatar className="w-14 h-14 ring-2 ring-white shadow-md">
<AvatarImage src={blogPost.authorImage} alt={blogPost.author} />
<AvatarFallback className="text-subhead font-medium">{blogPost.author.split(' ').map(n => n[0]).join('')}</AvatarFallback>
<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' }}>
{blogPost.author}
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.publishedDate)}
{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" />
{blogPost.readTime}
{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" />
{blogPost.views.toLocaleString()}
0
</span>
</div>
</div>
@@ -598,9 +321,9 @@ export function BlogDetail({ params }: BlogDetailProps) {
className={`transition-colors ${isLiked ? 'text-red-500' : 'text-[#6F6F6F]'}`}
>
<Heart className={`w-4 h-4 mr-2 ${isLiked ? 'fill-current' : ''}`} />
{blogPost.likes + (isLiked ? 1 : 0)}
0
</Button>
<Button
variant="ghost"
size="sm"
@@ -609,7 +332,7 @@ export function BlogDetail({ params }: BlogDetailProps) {
>
<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]">
@@ -628,7 +351,7 @@ export function BlogDetail({ params }: BlogDetailProps) {
{/* Featured Image with Better Aspect Ratio */}
<div className="aspect-[16/9] rounded-xl overflow-hidden mt-8 shadow-lg">
<ImageWithFallback
src={blogPost.image}
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"
/>
@@ -639,7 +362,7 @@ export function BlogDetail({ params }: BlogDetailProps) {
<article className="mb-20">
{/* Full Width Container - Uses complete available width */}
<div className="w-full">
<div
<div
className="prose prose-xl max-w-none blog-article-content w-full"
style={{
/* Enhanced Typography Hierarchy using Design System */
@@ -666,48 +389,50 @@ export function BlogDetail({ params }: BlogDetailProps) {
fontFamily: 'var(--font-family-base)',
color: '#26231A',
width: '100%'
}}
} as React.CSSProperties}
dangerouslySetInnerHTML={{ __html: blogPost.content }}
/>
</div>
</article>
{/* Enhanced Tag Pills with Hover States */}
<div className="mb-16">
<h3 className="text-subhead mb-6 font-medium" style={{ color: '#26231A' }}>
Topics covered in this article
</h3>
<div className="flex flex-wrap gap-3">
{blogPost.tags.map((tag) => (
<Badge
key={tag}
className="transition-all duration-200 text-body px-4 py-2 font-medium"
style={{
backgroundColor: 'rgba(4, 4, 91, 0.08)',
color: '#04045B',
border: '1px solid rgba(4, 4, 91, 0.15)'
}}
>
{tag}
</Badge>
))}
{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>
</div>
)}
{/* Enhanced Author Bio Card */}
<Card className="mb-16 shadow-md border-0" style={{ backgroundColor: '#FFFFFF' }}>
<CardContent className="p-8">
<div className="flex items-start gap-6">
<Avatar className="w-20 h-20 ring-4 ring-white shadow-lg">
<AvatarImage src={blogPost.authorImage} alt={blogPost.author} />
<AvatarFallback className="text-lg font-medium">{blogPost.author.split(' ').map(n => n[0]).join('')}</AvatarFallback>
<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 {blogPost.author}
About KLC Team
</h4>
<p className="text-body leading-relaxed mb-6" style={{ color: '#6F6F6F' }}>
{blogPost.authorBio}
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>
@@ -717,95 +442,101 @@ export function BlogDetail({ params }: BlogDetailProps) {
</div>
{/* Related Articles Section with Balanced Grid Layout */}
<section className="py-20" style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}>
<div className="section-margin-x">
<div className="max-w-6xl mx-auto">
<div className="text-center mb-16">
<div className="branded-tag-system mb-6">
<div className="dot"></div>
<span className="text">Continue Learning</span>
{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>
<h2 className="text-h2 mb-6 font-bold" style={{ color: '#26231A' }}>
Explore More Leadership Insights
</h2>
<p className="text-body-lg max-w-2xl mx-auto" style={{ color: '#6F6F6F' }}>
Discover additional strategies and frameworks to enhance your leadership effectiveness
</p>
</div>
{/* Balanced Card Grid with Equal Spacing */}
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
{relatedPosts.map((post) => (
<Card
key={post.id}
className="overflow-hidden hover:shadow-xl transition-all duration-300 cursor-pointer group border-0"
onClick={() => navigateTo(`/learning/articles/${post.slug}`)}
style={{ backgroundColor: '#FFFFFF' }}
>
<div className="aspect-[16/10] w-full bg-gray-100 overflow-hidden relative">
<ImageWithFallback
src={post.image}
alt={post.title}
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
/>
</div>
<CardContent className="p-6">
<div className="flex items-center justify-between mb-3">
<Badge
variant="outline"
className="text-small border-none"
{/* 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={{
backgroundColor: 'rgba(4, 4, 91, 0.1)',
color: '#04045B'
fontSize: 'var(--font-h4)',
fontWeight: 'var(--font-weight-h4)',
lineHeight: '1.3',
color: '#26231A',
fontFamily: 'var(--font-family-base)'
}}
>
{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>
{post.title}
</h3>
<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>
))}
<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>
</div>
</section>
</section>
)}
{/* CTA Section */}
<CTABannerSection />

View File

@@ -4,14 +4,44 @@ import { BrandedTag } from "./about/BrandedTag";
import { PrimaryCTAButton } from "./PrimaryCTAButton";
import { navigateTo } from "./Router";
export function CTABannerSection() {
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="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"
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"
/>
@@ -36,9 +66,9 @@ export function CTABannerSection() {
{/* Main Headline */}
<h2
className="text-h2-white mb-8"
className="text-h2-white mb-4"
>
Ready to transform your leadership?
{ctaSection.text || "Ready to transform your leadership?"}
<span
className="italic"
style={{ color: 'var(--color-brand-accent)' }}
@@ -48,20 +78,22 @@ export function CTABannerSection() {
to start your development journey now.
</h2>
{/* CTA Button - Updated to redirect to contact page */}
{/* Description */}
{ctaSection.description && (
<p
className="text-body-white mb-6 opacity-90"
>
{ctaSection.description}
</p>
)}
{/* CTA Button */}
<PrimaryCTAButton
text="Schedule a Consultation"
onClick={() => navigateTo('/contact?topic=consulting')}
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"
/>
{/* Supporting Text */}
<p
className="text-body-white mt-6 opacity-90"
>
Connect with our leadership experts to discuss your organization's specific development needs.
</p>
</div>
</div>
</section>

View File

@@ -14,21 +14,22 @@ export interface CartItem {
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
// 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
// cartItems: legacyCartItems, // Renamed to avoid confusion
// onRemoveItem: legacyOnRemoveItem, // Renamed to avoid confusion
recentlyAddedItem
}: CartPopupProps) {
const [showSuccess, setShowSuccess] = useState(false);

View File

@@ -8,6 +8,8 @@ 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;
@@ -15,29 +17,35 @@ interface ContactProps {
export function Contact({ topic }: ContactProps) {
const [formData, setFormData] = useState({
firstName: '',
lastName: '',
email: '',
phone: '',
company: '',
jobTitle: '',
subject: topic || '',
message: '',
source: ''
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 subject based on topic parameter
// Set default interested in based on topic parameter
if (topic) {
const subject = getTopicSubject(topic);
console.log('Setting form subject to:', subject);
const interestedIn = getTopicSubject(topic);
console.log('Setting form interestedIn to:', interestedIn);
setFormData(prev => ({
...prev,
subject: subject
interestedIn: interestedIn
}));
}
}, [topic]);
@@ -68,31 +76,48 @@ export function Contact({ topic }: ContactProps) {
e.preventDefault();
setIsSubmitting(true);
// Simulate form submission
await new Promise(resolve => setTimeout(resolve, 2000));
setIsSubmitted(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: '#F7F7FD' }}>
<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.
We've received your message and will get back to you within 24 hours.
Our leadership development experts are excited to help transform your organization.
</p>
<PrimaryCTAButton
text="Return to Home"
onClick={() => navigateTo('/')}
ariaLabel="Return to homepage"
/>
<div className="flex justify-center">
<PrimaryCTAButton
text="Return to Home"
onClick={() => navigateTo('/')}
ariaLabel="Return to homepage"
/>
</div>
</div>
</div>
</div>
@@ -100,19 +125,19 @@ export function Contact({ topic }: ContactProps) {
}
return (
<div className="min-h-screen" style={{ backgroundColor: '#F7F7FD' }}>
{/* Hero Section with CTA Banner Styling */}
<section className="relative h-[600px] overflow-hidden">
{/* Background Image */}
<div className="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="https://images.unsplash.com/photo-1600880292203-757bb62b4baf?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2940&q=80"
alt="Professional business meeting and consultation"
src={kautilyabg}
alt="Google Maps showing office location and navigation"
className="w-full h-full object-cover"
/>
{/* 40% black overlay for better text readability */}
<div className="absolute inset-0 bg-black/70" />
{/* Dark overlay for better text readability */}
<div className="absolute inset-0 bg-black/60" />
</div>
{/* Content Container */}
@@ -120,22 +145,16 @@ export function Contact({ topic }: ContactProps) {
{/* Hero Content Block */}
<div className="text-center max-w-4xl mx-auto">
{/* Branded Tag */}
<BrandedTag text="Get In Touch" variant="white" />
<BrandedTag text="Contact Us" variant="white" />
{/* Main Headline */}
<h1 className="text-h1-white mb-6">
Ready to transform your leadership?
<span
className="italic"
style={{ color: 'var(--color-brand-accent)' }}
>
{" "}Let's connect{" "}
</span>
Reach us
</h1>
{/* Supporting Text */}
<p className="text-body-lg-white opacity-90 max-w-3xl mx-auto">
To start your development journey.
Get in touch with us to start your leadership development journey.
</p>
</div>
</div>
@@ -149,7 +168,7 @@ export function Contact({ topic }: ContactProps) {
<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
<div
className="p-8 text-white"
style={{ backgroundColor: 'var(--color-brand-primary)' }}
>
@@ -161,44 +180,47 @@ export function Contact({ topic }: ContactProps) {
{/* Contact Details */}
<div className="p-8 space-y-8">
{/* Office Location */}
{/* Corporate Office */}
<div className="flex items-start gap-4">
<div
<div
className="w-12 h-12 rounded-lg flex items-center justify-center flex-shrink-0"
style={{ backgroundColor: 'rgba(248, 195, 1, 0.1)' }}
>
<MapPin className="w-6 h-6" style={{ color: 'var(--color-brand-primary)' }} />
</div>
<div>
<h3 className="text-h3 mb-2">Office Location</h3>
<h3 className="text-h3 mb-2">Corporate Office</h3>
<p className="text-body text-muted">
123 Leadership Avenue<br />
Business District, New Delhi<br />
110001, India
Leadership Centre Pvt. Ltd.<br />
No. 107, 1st Floor<br />
T.V. Industrial Estate, Worli<br />
Mumbai 400030
</p>
</div>
</div>
{/* Phone */}
{/* Registered Office */}
<div className="flex items-start gap-4">
<div
<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)' }} />
<MapPin className="w-6 h-6" style={{ color: 'var(--color-brand-primary)' }} />
</div>
<div>
<h3 className="text-h3 mb-2">Phone</h3>
<h3 className="text-h3 mb-2">Registered Office</h3>
<p className="text-body text-muted">
+91 11 4567 8900<br />
+91 98765 43210
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
<div
className="w-12 h-12 rounded-lg flex items-center justify-center flex-shrink-0"
style={{ backgroundColor: 'rgba(248, 195, 1, 0.1)' }}
>
@@ -207,33 +229,32 @@ export function Contact({ topic }: ContactProps) {
<div>
<h3 className="text-h3 mb-2">Email</h3>
<p className="text-body text-muted">
info@klc.edu<br />
leadership@klc.edu
connect@leadershipcentre.in<br />
connect@kautilyaservices.in
</p>
</div>
</div>
{/* Business Hours */}
{/* Phone */}
<div className="flex items-start gap-4">
<div
<div
className="w-12 h-12 rounded-lg flex items-center justify-center flex-shrink-0"
style={{ backgroundColor: 'rgba(248, 195, 1, 0.1)' }}
>
<Clock className="w-6 h-6" style={{ color: 'var(--color-brand-primary)' }} />
<Phone className="w-6 h-6" style={{ color: 'var(--color-brand-primary)' }} />
</div>
<div>
<h3 className="text-h3 mb-2">Business Hours</h3>
<h3 className="text-h3 mb-2">Phone</h3>
<p className="text-body text-muted">
Monday - Friday: 9:00 AM - 6:00 PM<br />
Saturday: 10:00 AM - 4:00 PM<br />
Sunday: Closed
+91-22-2084 0097<br />
+91-8928738661
</p>
</div>
</div>
</div>
{/* Quick Actions */}
<div
<div
className="p-8 border-t"
style={{ borderColor: 'var(--color-border)' }}
>
@@ -243,7 +264,7 @@ export function Contact({ topic }: ContactProps) {
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={{
style={{
borderColor: 'var(--color-brand-primary)',
color: 'var(--color-brand-primary)'
}}
@@ -255,7 +276,7 @@ export function Contact({ topic }: ContactProps) {
variant="outline"
className="w-full justify-start hover:bg-blue-50 hover:border-blue-200 transition-all duration-300"
onClick={() => navigateTo('/leadership-journey')}
style={{
style={{
borderColor: 'var(--color-brand-primary)',
color: 'var(--color-brand-primary)'
}}
@@ -274,203 +295,117 @@ export function Contact({ topic }: ContactProps) {
{/* Form Header */}
<div className="p-8 border-b" style={{ borderColor: 'var(--color-border)' }}>
<div className="flex items-center gap-3 mb-2">
<div
<div
className="w-10 h-10 rounded-lg flex items-center justify-center"
style={{ backgroundColor: 'rgba(248, 195, 1, 0.1)' }}
>
<Send className="w-5 h-5" style={{ color: 'var(--color-brand-primary)' }} />
</div>
<h2 className="text-h3">Send us a Message</h2>
<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 className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
First Name *
</label>
<Input
type="text"
required
value={formData.firstName}
onChange={(e) => handleInputChange('firstName', e.target.value)}
placeholder="John"
className="w-full h-12 border-gray-200 focus:border-blue-500 focus:ring-blue-500"
style={{
fontFamily: 'var(--font-family-base)',
fontSize: 'var(--font-body)'
}}
/>
</div>
<div>
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
Last Name *
</label>
<Input
type="text"
required
value={formData.lastName}
onChange={(e) => handleInputChange('lastName', e.target.value)}
placeholder="Doe"
className="w-full h-12 border-gray-200 focus:border-blue-500 focus:ring-blue-500"
style={{
fontFamily: 'var(--font-family-base)',
fontSize: 'var(--font-body)'
}}
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
Email Address *
</label>
<Input
type="email"
required
value={formData.email}
onChange={(e) => handleInputChange('email', e.target.value)}
placeholder="john.doe@company.com"
className="w-full h-12 border-gray-200 focus:border-blue-500 focus:ring-blue-500"
style={{
fontFamily: 'var(--font-family-base)',
fontSize: 'var(--font-body)'
}}
/>
</div>
<div>
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
Phone Number
</label>
<Input
type="tel"
value={formData.phone}
onChange={(e) => handleInputChange('phone', e.target.value)}
placeholder="+91 98765 43210"
className="w-full h-12 border-gray-200 focus:border-blue-500 focus:ring-blue-500"
style={{
fontFamily: 'var(--font-family-base)',
fontSize: 'var(--font-body)'
}}
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
Company/Organization
</label>
<Input
type="text"
value={formData.company}
onChange={(e) => handleInputChange('company', e.target.value)}
placeholder="Company Name"
className="w-full h-12 border-gray-200 focus:border-blue-500 focus:ring-blue-500"
style={{
fontFamily: 'var(--font-family-base)',
fontSize: 'var(--font-body)'
}}
/>
</div>
<div>
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
Job Title
</label>
<Input
type="text"
value={formData.jobTitle}
onChange={(e) => handleInputChange('jobTitle', e.target.value)}
placeholder="CEO, HR Director, etc."
className="w-full h-12 border-gray-200 focus:border-blue-500 focus:ring-blue-500"
style={{
fontFamily: 'var(--font-family-base)',
fontSize: 'var(--font-body)'
}}
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
Subject/Interest Area *
</label>
<Select
value={formData.subject}
onValueChange={(value) => handleInputChange('subject', value)}
>
<SelectTrigger
className="w-full h-12 border-gray-200 focus:border-blue-500 focus:ring-blue-500"
style={{
fontFamily: 'var(--font-family-base)',
fontSize: 'var(--font-body)'
}}
>
<SelectValue placeholder="Select a subject" />
</SelectTrigger>
<SelectContent>
<SelectItem value="Leadership Pipeline Development">Leadership Pipeline Development</SelectItem>
<SelectItem value="Executive Coaching">Executive Coaching</SelectItem>
<SelectItem value="Management Development">Management Development</SelectItem>
<SelectItem value="Culture Competence">Culture Competence</SelectItem>
<SelectItem value="Consulting Services">Consulting Services</SelectItem>
<SelectItem value="Learning Facility">Learning Facility Tour</SelectItem>
<SelectItem value="Online Courses">Online Learning Programs</SelectItem>
<SelectItem value="General Inquiry">General Inquiry</SelectItem>
</SelectContent>
</Select>
</div>
<div>
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
How did you hear about us?
</label>
<Select
value={formData.source}
onValueChange={(value) => handleInputChange('source', value)}
>
<SelectTrigger
className="w-full h-12 border-gray-200 focus:border-blue-500 focus:ring-blue-500"
style={{
fontFamily: 'var(--font-family-base)',
fontSize: 'var(--font-body)'
}}
>
<SelectValue placeholder="Select an option" />
</SelectTrigger>
<SelectContent>
<SelectItem value="Website">Website</SelectItem>
<SelectItem value="Google Search">Google Search</SelectItem>
<SelectItem value="Social Media">Social Media</SelectItem>
<SelectItem value="Referral">Referral</SelectItem>
<SelectItem value="Event/Conference">Event/Conference</SelectItem>
<SelectItem value="LinkedIn">LinkedIn</SelectItem>
<SelectItem value="Email Marketing">Email Marketing</SelectItem>
<SelectItem value="Other">Other</SelectItem>
</SelectContent>
</Select>
</div>
<div>
<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)' }}>
Message *
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="Tell us about your leadership development needs, goals, or any specific questions you have..."
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={{
style={{
fontFamily: 'var(--font-family-base)',
fontSize: 'var(--font-body)'
}}
@@ -484,9 +419,8 @@ export function Contact({ topic }: ContactProps) {
</div>
<PrimaryCTAButton
text={isSubmitting ? "Sending Message..." : "Send Message"}
onClick={() => {}}
onClick={() => { handleSubmit }}
ariaLabel="Send contact message"
disabled={isSubmitting}
className="w-full sm:w-auto"
/>
</div>
@@ -508,19 +442,19 @@ export function Contact({ topic }: ContactProps) {
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
<div
className="bg-opacity-95 backdrop-blur-sm rounded-lg p-16 max-w-2xl"
style={{
style={{
backgroundColor: 'var(--color-brand-primary)'
}}
>
@@ -529,8 +463,8 @@ export function Contact({ topic }: ContactProps) {
{/* Main Headline */}
<h2 className="text-h2-white mb-8">
Ready to transform your leadership?
<span
Ready to transform your leadership?
<span
className="italic"
style={{ color: 'var(--color-brand-accent)' }}
>
@@ -539,7 +473,7 @@ export function Contact({ topic }: ContactProps) {
to start your development journey now.
</h2>
<PrimaryCTAButton
<PrimaryCTAButton
text="Schedule a Consultation"
onClick={() => navigateTo('/contact?topic=consulting')}
ariaLabel="Schedule a consultation with our leadership experts"

View File

@@ -608,7 +608,7 @@ export function CorporateSignUp() {
</div>
{/* Need Help Section - Separate Section Below */}
<div
{/* <div
className="section-margin-x py-12"
style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}
>
@@ -649,7 +649,7 @@ export function CorporateSignUp() {
Contact Sales Team
</Button>
</div>
</div>
</div> */}
</div>
);
}

View File

@@ -1,9 +1,9 @@
import React from 'react';
import { Button } from './ui/button';
import { Badge } from './ui/badge';
import {
Users,
Clock,
import {
Users,
Clock,
Star,
ArrowRight,
ShoppingCart
@@ -22,7 +22,7 @@ export interface Course {
level: string;
format: string;
rating: number;
participants: string;
reviews: string;
category: string;
description: string;
price: string;
@@ -47,7 +47,7 @@ export function CourseCard({ course, onClick, className, onAddToCart }: CourseCa
const handleAddToCart = (e: React.MouseEvent) => {
e.stopPropagation(); // Prevent card click when clicking Add to Cart
if (onAddToCart) {
const cartItem: CartItem = {
id: course.id,
@@ -75,7 +75,7 @@ export function CourseCard({ course, onClick, className, onAddToCart }: CourseCa
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
/>
<div className="absolute top-4 left-4">
<Badge
<Badge
variant="secondary"
className="px-3 py-1 font-medium"
style={{
@@ -89,26 +89,12 @@ export function CourseCard({ course, onClick, className, onAddToCart }: CourseCa
{course.category}
</Badge>
</div>
<div className="absolute top-4 right-4">
<Badge
variant="outline"
className="px-3 py-1 font-medium bg-white/90 backdrop-blur-sm"
style={{
fontSize: 'var(--font-small)',
fontFamily: 'var(--font-family-base)',
borderColor: 'var(--color-primary)',
color: 'var(--color-primary)'
}}
>
{course.level}
</Badge>
</div>
</div>
{/* Card Content - Reduced horizontal padding */}
<div className="p-5 flex flex-col flex-1">
{/* Course Title */}
<h3
<h3
className="mb-3 group-hover:text-blue-600 transition-colors leading-snug"
style={{
fontSize: 'var(--font-h4)',
@@ -118,11 +104,14 @@ export function CourseCard({ course, onClick, className, onAddToCart }: CourseCa
fontFamily: 'var(--font-family-base)'
}}
>
{course.title}
{course.title
?.split(' ')
.slice(0, 3)
.join(' ') + (course.title.split(' ').length > 3 ? '...' : '')}
</h3>
{/* Course Description - Limited to 2 lines with ellipsis */}
<p
<p
className="mb-5 line-clamp-2 leading-relaxed"
style={{
fontSize: 'var(--font-body)',
@@ -138,14 +127,14 @@ export function CourseCard({ course, onClick, className, onAddToCart }: CourseCa
>
{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)',
<span style={{
fontSize: 'var(--font-small)',
fontFamily: 'var(--font-family-base)',
color: 'var(--color-gray-muted)',
fontWeight: '500'
@@ -155,20 +144,20 @@ export function CourseCard({ course, onClick, className, onAddToCart }: CourseCa
</div>
<div className="flex items-center gap-2">
<Users className="w-4 h-4 text-gray-400" />
<span style={{
fontSize: 'var(--font-small)',
<span style={{
fontSize: 'var(--font-small)',
fontFamily: 'var(--font-family-base)',
color: 'var(--color-gray-muted)',
fontWeight: '500'
}}>
{course.participants}
{course.reviews}
</span>
</div>
</div>
<div className="flex items-center gap-1">
<Star className="w-4 h-4 fill-current text-yellow-400" />
<span
<span
className="font-semibold"
style={{
fontSize: 'var(--font-small)',
@@ -185,7 +174,7 @@ export function CourseCard({ course, onClick, className, onAddToCart }: CourseCa
<div className="mb-5">
<div className="flex items-center justify-between">
<div className="flex items-baseline gap-3">
<span
<span
className="font-bold"
style={{
fontSize: '1.75rem',
@@ -196,7 +185,7 @@ export function CourseCard({ course, onClick, className, onAddToCart }: CourseCa
{course.price}
</span>
{course.originalPrice && (
<span
<span
className="line-through"
style={{
fontSize: 'var(--font-body)',
@@ -208,7 +197,7 @@ export function CourseCard({ course, onClick, className, onAddToCart }: CourseCa
</span>
)}
</div>
{course.originalPrice && (
{/* {course.originalPrice && (
<div className="text-right">
<span
className="text-green-600 font-semibold text-sm"
@@ -219,62 +208,64 @@ export function CourseCard({ course, onClick, className, onAddToCart }: CourseCa
Save {Math.round(((parseFloat(course.originalPrice.replace('$', '')) - parseFloat(course.price.replace('$', ''))) / parseFloat(course.originalPrice.replace('$', ''))) * 100)}%
</span>
</div>
)}
)} */}
</div>
</div>
{/* Action Buttons - Horizontal Layout with reduced gap */}
<div className="flex flex-row gap-2 mt-auto">
{/* Add to Cart Button - Outline Blue */}
{/* Add to Cart */}
<Button
variant="outline"
onClick={handleAddToCart}
className="flex-1 flex items-center justify-center gap-2 h-11 rounded-lg transition-all duration-200 font-medium"
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: 'var(--font-body)',
fontSize: '12px', // ⬅️ reduced
fontFamily: 'var(--font-family-base)',
fontWeight: '500',
borderWidth: '2px'
borderWidth: '1px',
padding: '8px'
}}
onMouseEnter={(e) => {
onMouseEnter={(e: any) => {
e.currentTarget.style.backgroundColor = '#04045B';
e.currentTarget.style.color = 'white';
}}
onMouseLeave={(e) => {
onMouseLeave={(e: any) => {
e.currentTarget.style.backgroundColor = 'transparent';
e.currentTarget.style.color = '#04045B';
}}
>
<ShoppingCart className="w-4 h-4" />
<ShoppingCart className="w-3.5 h-3.5" /> {/* ⬅️ smaller icon */}
Add to Cart
</Button>
{/* Learn More Button - Solid Blue */}
{/* Learn More */}
<Button
className="flex-1 flex items-center justify-center gap-2 h-11 rounded-lg transition-all duration-200 font-medium"
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: 'var(--font-body)',
fontSize: '12px', // ⬅️ reduced
fontFamily: 'var(--font-family-base)',
fontWeight: '500',
border: 'none'
border: 'none',
padding: '8px'
}}
onMouseEnter={(e) => {
onMouseEnter={(e: any) => {
e.currentTarget.style.backgroundColor = '#030359';
}}
onMouseLeave={(e) => {
onMouseLeave={(e: any) => {
e.currentTarget.style.backgroundColor = '#04045B';
}}
>
Learn More
<ArrowRight className="w-4 h-4" />
<ArrowRight className="w-3.5 h-3.5" /> {/* ⬅️ smaller icon */}
</Button>
</div>
</div>
</motion.div>
);
}
}

View File

@@ -60,7 +60,7 @@ export function FooterNew() {
Webcast
</button>
<button
onClick={() => navigateTo('/about')}
onClick={() => navigateTo('/about-us')}
className="block text-small-white transition-all duration-300 text-left"
style={{
color: 'white',
@@ -72,7 +72,7 @@ export function FooterNew() {
About Us
</button>
<button
onClick={() => navigateTo('/learning/blogs')}
onClick={() => navigateTo('/learning/articles')}
className="block text-small-white transition-all duration-300 text-left"
style={{
color: 'white',
@@ -108,7 +108,7 @@ export function FooterNew() {
FAQs
</button>
<button
onClick={() => navigateTo('/services/learning-facility')}
onClick={() => navigateTo('/learning-facility')}
className="block text-small-white transition-all duration-300 text-left"
style={{
color: 'white',
@@ -243,14 +243,14 @@ export function FooterNew() {
{/* 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')}
className="text-body-white transition-colors duration-300 hover:text-yellow-300 text-left"
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('/terms')}
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)' }}
>

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

@@ -1,65 +1,62 @@
import React, { useState, useEffect, useCallback } from 'react';
import { ChevronLeft, ChevronRight } from 'lucide-react';
import { navigateTo } from './Router';
import svgPaths from "../imports/svg-i1joeov37f";
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: number;
id: string;
title: string;
backgroundImage: string;
shortTitle: string;
ctaText: string;
route: string;
}
export default function HeroSection() {
const [currentSlide, setCurrentSlide] = useState(0);
const [isAutoPlaying, setIsAutoPlaying] = useState(true);
const [progressValues, setProgressValues] = useState([0, 0, 0, 0, 0]);
interface HeroSectionProps {
heroSections: HeroSectionItem[];
isLoading: boolean;
}
const slides: SlideData[] = [
{
id: 1,
title: "Build Leaders Who Drive Business Growth",
backgroundImage: "https://images.unsplash.com/photo-1705234384669-c6d31c61b789?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxleGVjdXRpdmUlMjBsZWFkZXJzaGlwJTIwZGV2ZWxvcG1lbnQlMjB0cmFpbmluZ3xlbnwxfHx8fDE3NTY4MDcyNjJ8MA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral",
shortTitle: "Leadership Development Programs",
ctaText: "Explore Leadership Journeys"
},
{
id: 2,
title: "Strengthen the Backbone: Your Managers",
backgroundImage: "https://images.unsplash.com/photo-1565688527174-775059ac429c?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxtYW5hZ2VtZW50JTIwdGVhbSUyMGRpc2N1c3Npb24lMjBvZmZpY2V8ZW58MXx8fHwxNzU2ODA2ODg1fDA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral",
shortTitle: "Management Development Programs",
ctaText: "Strengthen your Managerial Calibre"
},
{
id: 3,
title: "Shape Cultures That Accelerate Performance",
backgroundImage: "https://images.unsplash.com/photo-1531535807748-218331acbcb4?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxjb3Jwb3JhdGUlMjBjdWx0dXJlJTIwdGVhbSUyMGNvbGxhYm9yYXRpb258ZW58MXx8fHwxNzU2ODA2ODg5fDA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral",
shortTitle: "Culture & Competence Consulting",
ctaText: "Learn how we facilitate Change"
},
{
id: 4,
title: "Unlock Leadership Potential",
backgroundImage: "https://images.unsplash.com/photo-1714974528833-a10e19a8f951?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxleGVjdXRpdmUlMjBjb2FjaGluZyUyMG1lbnRvciUyMGNvbnZlcnNhdGlvbnxlbnwxfHx8fDE3NTY4MDY4OTR8MA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral",
shortTitle: "Coaching & Mentoring",
ctaText: "Start a Coaching Conversation"
},
{
id: 5,
title: "Know Your Leaders. Strengthen Your Pipeline.",
backgroundImage: "https://images.unsplash.com/photo-1697059361419-349e924ed363?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxidXNpbmVzcyUyMGxlYWRlcnMlMjBzdHJhdGVneSUyMG1lZXRpbmd8ZW58MXx8fHwxNzU2ODA3Mjc0fDA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral",
shortTitle: "Assessments & Leadership Pipeline",
ctaText: "Discover Our Assessment Solutions"
}
];
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 slideDuration = 5000; // 5 seconds per slide
// Auto-advance slides
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 (!isAutoPlaying) return;
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);
@@ -68,48 +65,50 @@ export default function HeroSection() {
return () => clearInterval(interval);
}, [isAutoPlaying, totalSlides]);
// Progress bar animation
/* Progress animation */
useEffect(() => {
if (!isAutoPlaying) return;
if (!isAutoPlaying || totalSlides === 0) return;
const interval = setInterval(() => {
setProgressValues(prev => {
setProgressValues((prev) => {
const newProgress = [...prev];
newProgress[currentSlide] = Math.min(newProgress[currentSlide] + (100 / (slideDuration / 100)), 100);
// Reset progress when slide changes
newProgress[currentSlide] = Math.min(
newProgress[currentSlide] + 100 / (slideDuration / 100),
100
);
if (newProgress[currentSlide] >= 100) {
newProgress[currentSlide] = 0;
// Reset other slides
newProgress.forEach((_, index) => {
if (index !== currentSlide) {
newProgress[index] = 0;
}
if (index !== currentSlide) newProgress[index] = 0;
});
}
return newProgress;
});
}, 100);
return () => clearInterval(interval);
}, [currentSlide, isAutoPlaying]);
}, [currentSlide, isAutoPlaying, totalSlides]);
// Reset progress when manually changing slides
/* Reset progress when slide changes */
useEffect(() => {
setProgressValues(prev => {
const newProgress = [0, 0, 0, 0, 0];
newProgress[currentSlide] = 0;
return newProgress;
});
}, [currentSlide]);
setProgressValues(new Array(totalSlides).fill(0));
}, [currentSlide, totalSlides]);
const goToSlide = useCallback((slideIndex: number) => {
if (slideIndex !== currentSlide) {
setCurrentSlide(slideIndex);
setIsAutoPlaying(false);
// Resume auto-play after manual interaction
setTimeout(() => setIsAutoPlaying(true), 3000);
}
}, [currentSlide]);
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;
@@ -121,10 +120,11 @@ export default function HeroSection() {
goToSlide(prev);
}, [currentSlide, totalSlides, goToSlide]);
// Pause auto-play on hover
const handleMouseEnter = () => setIsAutoPlaying(false);
const handleMouseLeave = () => setIsAutoPlaying(true);
if (isLoading || slides.length === 0) return null;
return (
<section
className="hero-section"
@@ -135,9 +135,9 @@ export default function HeroSection() {
{slides.map((slide, index) => (
<div
key={slide.id}
className={`hero-slide ${index === currentSlide ? 'active' : ''}`}
className={`hero-slide ${index === currentSlide ? "active" : ""}`}
style={{
backgroundImage: `url('${slide.backgroundImage}')`
backgroundImage: `url('${slide.backgroundImage}')`,
}}
>
<div className="hero-overlay" />
@@ -147,120 +147,20 @@ export default function HeroSection() {
{/* Hero Content */}
<div className="hero-content">
<div className="hero-text-section">
{/* Title */}
<h1 className="hero-title" style={{ whiteSpace: 'pre-line' }}>
<h1 className="hero-title" style={{ whiteSpace: "pre-line" }}>
{slides[currentSlide].title}
</h1>
{/* Dynamic CTA Button - Enhanced with Proper Navigation */}
<button
className="hero-slide-button group box-border content-stretch flex flex-row gap-2.5 items-center justify-start p-0 relative cursor-pointer overflow-hidden"
style={{
background: 'transparent',
border: 'none'
}}
onClick={() => {
console.log('Hero button clicked - navigating to contact page');
navigateTo('/contact?topic=leadership-pipeline');
}}
aria-label={slides[currentSlide].ctaText}
>
{/* Icon Container with Slide Animation */}
<div className="relative shrink-0 size-[50px] overflow-hidden">
{/* Background Rectangle - Consistent Blue Color */}
<div className="absolute inset-0 bg-[#04045B]" />
{/* Icon Layer - Sliding Animation */}
<div className="icon-layer absolute inset-0 w-full h-full">
{/* Primary Arrow - Slides out diagonally up-right */}
<div className="icon absolute inset-0 flex items-center justify-center transition-all duration-300 ease-in-out group-hover:translate-x-6 group-hover:-translate-y-6 group-hover:opacity-0">
<svg
className="block w-full h-full"
fill="none"
preserveAspectRatio="none"
viewBox="0 0 50 50"
>
<g clipPath="url(#clip0_95_1043_primary)">
<path d={svgPaths.p5b8d700} fill="white" />
<path d={svgPaths.p30b71a00} fill="white" />
</g>
<defs>
<clipPath id="clip0_95_1043_primary">
<rect fill="white" height="50" width="50" />
</clipPath>
</defs>
</svg>
</div>
{/* Secondary Arrow - Slides in from bottom-left */}
<div className="icon absolute inset-0 flex items-center justify-center opacity-0 -translate-x-6 translate-y-6 transition-all duration-300 ease-in-out group-hover:translate-x-0 group-hover:translate-y-0 group-hover:opacity-100">
<svg
className="block w-full h-full"
fill="none"
preserveAspectRatio="none"
viewBox="0 0 50 50"
>
<g clipPath="url(#clip0_95_1043_secondary)">
<path d={svgPaths.p5b8d700} fill="white" />
<path d={svgPaths.p30b71a00} fill="white" />
</g>
<defs>
<clipPath id="clip0_95_1043_secondary">
<rect fill="white" height="50" width="50" />
</clipPath>
</defs>
</svg>
</div>
</div>
</div>
{/* Text Section with Vertical Slide Animation */}
<div className="text-layer relative shrink-0 overflow-hidden flex items-center" style={{
height: '28px',
fontFamily: 'Inter, sans-serif',
fontSize: '20px',
fontWeight: '400',
lineHeight: '28px',
whiteSpace: 'nowrap',
color: '#ffffff'
}}>
{/* Primary Text - Slides up and out */}
<div
className="text-element absolute inset-0 flex items-center justify-start transition-all duration-300 ease-in-out group-hover:-translate-y-full group-hover:opacity-0"
style={{
color: '#ffffff !important',
textShadow: '0 1px 2px rgba(0, 0, 0, 0.4)',
fontFamily: 'Inter, sans-serif',
fontSize: '20px',
fontWeight: '400',
lineHeight: '28px'
}}
>
{slides[currentSlide].ctaText}
</div>
{/* Secondary Text - Slides in from bottom */}
<div
className="text-element absolute inset-0 flex items-center justify-start translate-y-full opacity-0 transition-all duration-300 ease-in-out group-hover:translate-y-0 group-hover:opacity-100"
style={{
color: '#ffffff !important',
textShadow: '0 1px 2px rgba(0, 0, 0, 0.4)',
fontFamily: 'Inter, sans-serif',
fontSize: '20px',
fontWeight: '400',
lineHeight: '28px'
}}
>
{slides[currentSlide].ctaText}
</div>
</div>
</button>
<PrimaryCTAButton
text={slides[currentSlide].ctaText}
onClick={() => navigateTo(slides[currentSlide].route)}
ariaLabel="Learn more about KLC"
/>
</div>
</div>
{/* Bottom Navigation */}
<div className="hero-navigation">
{/* Progress Section */}
<div className="hero-progress-container">
{slides.map((slide, index) => (
<div
@@ -268,30 +168,41 @@ export default function HeroSection() {
className="hero-progress-item"
onClick={() => goToSlide(index)}
>
{/* Progress Bar */}
<div
className={`hero-progress-segment ${index === currentSlide ? 'active' : ''}`}
className={`hero-progress-segment ${
index === currentSlide ? "active" : ""
}`}
>
<div
className="hero-progress-fill"
style={{ width: index === currentSlide ? `${progressValues[index]}%` : '0%' }}
style={{
width:
index === currentSlide
? `${progressValues[index] ?? 0}%`
: "0%",
}}
/>
</div>
{/* Progress Number */}
<div className={`hero-progress-number ${index === currentSlide ? 'active' : ''}`}>
{String(index + 1).padStart(2, '0')}
<div
className={`hero-progress-number ${
index === currentSlide ? "active" : ""
}`}
>
{String(index + 1).padStart(2, "0")}
</div>
{/* Progress Text */}
<div className={`hero-progress-text ${index === currentSlide ? 'active' : ''}`}>
<div
className={`hero-progress-text ${
index === currentSlide ? "active" : ""
}`}
>
{slide.shortTitle}
</div>
</div>
))}
</div>
{/* Navigation Arrows */}
<div className="hero-controls">
<button
className="hero-nav-button"
@@ -300,6 +211,7 @@ export default function HeroSection() {
>
<ChevronLeft className="w-5 h-5" />
</button>
<button
className="hero-nav-button"
onClick={nextSlide}

View File

@@ -1,43 +1,38 @@
import { ArrowUpRight } from "lucide-react";
import { ImageWithFallback } from "./figma/ImageWithFallback";
import { BrandedTag } from "./about/BrandedTag";
import { PrimaryCTAButton } from "./PrimaryCTAButton";
import { StandardCTAButton } from "./StandardCTAButton";
import { navigateTo } from "./Router";
import { useGetFeaturedBlogsQuery } from "../redux/services/homepageApi";
import { FullScreenLoader } from "./FullScreenLoader";
import { getSlugWithId } from "../utils/urlHelpers";
interface InsightCard {
id: number;
// 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;
}
const insightCards: InsightCard[] = [
{
id: 1,
title: "The Evolving DNA of a Modern Leader",
description: "Explore how leadership traits are shifting in the age of AI, remote work, and hyper-connectivity.",
date: "25-05-2025",
tags: ["Adaptability", "Strategic foresight"],
image: "https://images.unsplash.com/photo-1522202176988-66273c2fd55f?auto=format&fit=crop&w=800&q=80"
},
{
id: 2,
title: "Strategic Leadership in Uncertain Times",
date: "25-05-2025",
tags: ["Strategy", "Decision Making"],
image: "https://images.unsplash.com/photo-1553877522-43269d4ea984?auto=format&fit=crop&w=800&q=80"
},
{
id: 3,
title: "Building High-Performance Teams",
date: "25-05-2025",
tags: ["Team Building", "Performance"],
image: "https://images.unsplash.com/photo-1559136555-9303baea8ebd?auto=format&fit=crop&w=800&q=80"
}
];
// Insight Tag Component
function InsightTag({ text }: { text: string }) {
return (
@@ -72,22 +67,41 @@ function CalendarIcon() {
);
}
// Explore All Button Component - Updated to redirect to articles page
// Explore All Button Component
function ExploreAllButton() {
return (
<PrimaryCTAButton
<StandardCTAButton
text="Explore All"
onClick={() => navigateTo('/learning/articles')}
ariaLabel="Explore all leadership insights and ideas"
className="explore-all-cta-override"
/>
);
}
// Large Insight Card Component
function LargeInsightCard({ card }: { card: InsightCard }) {
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 cursor-pointer">
<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
@@ -101,8 +115,8 @@ function LargeInsightCard({ card }: { card: InsightCard }) {
<div className="insight-card-white-box absolute bottom-6 left-6 right-6 bg-white rounded-xl p-6 shadow-lg transition-all duration-300 group-hover:shadow-xl">
{/* Top section with tags and arrow */}
<div className="insight-card-header flex items-start justify-between mb-4">
<div className="insight-card-tags flex gap-2">
{card.tags.map((tag, index) => (
<div className="insight-card-tags flex gap-2 flex-wrap">
{card.tags.slice(0, 2).map((tag, index) => (
<InsightTag key={index} text={tag} />
))}
</div>
@@ -134,9 +148,29 @@ function LargeInsightCard({ card }: { card: InsightCard }) {
}
// Small Insight Card Component
function SmallInsightCard({ card }: { card: InsightCard }) {
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 cursor-pointer">
<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
@@ -150,8 +184,8 @@ function SmallInsightCard({ card }: { card: InsightCard }) {
<div className="insight-card-white-box absolute bottom-4 left-4 right-4 bg-white rounded-xl p-4 shadow-lg transition-all duration-300 group-hover:shadow-xl">
{/* Top section with tags and arrow */}
<div className="insight-card-header flex items-start justify-between mb-3">
<div className="insight-card-tags flex gap-2">
{card.tags.map((tag, index) => (
<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"
@@ -189,7 +223,97 @@ function SmallInsightCard({ card }: { card: InsightCard }) {
);
}
// 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"
@@ -200,7 +324,6 @@ export function InsightsSection() {
}}
>
<div className="max-w-7xl mx-auto section-margin-x">
{/* Branded Tag */}
<BrandedTag text="Leadership Insights" />
<div className="insights-container">
@@ -218,15 +341,20 @@ export function InsightsSection() {
{/* Main Grid Layout */}
<div className="insights-grid grid grid-cols-1 lg:grid-cols-2 gap-4 md:gap-6 lg:gap-8">
{/* Large Card - Takes full height on left */}
<div className="lg:row-span-2">
<LargeInsightCard card={insightCards[0]} />
</div>
{hasLargeCard && (
<div className="lg:row-span-2">
<LargeInsightCard card={largeCard} />
</div>
)}
{/* Small Cards - Stack on right */}
<div className="flex flex-col gap-4 md:gap-6 lg:gap-8">
<SmallInsightCard card={insightCards[1]} />
<SmallInsightCard card={insightCards[2]} />
</div>
{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>

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

175
src/components/Loader.tsx Normal file
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

@@ -1,27 +1,25 @@
import React from 'react';
import imgImage36 from "figma:asset/6bdf8056f51bbdc6dd9dab9044a6579a254bd02c.png";
import imgImage39 from "figma:asset/037c4659b7b0bf15b1dfdcd4868cb42e8257e838.png";
import imgImage43 from "figma:asset/c57ec1f4466f68e607139a3cd6d52f7e2f372408.png";
import imgImage45 from "figma:asset/4833274f0a593cd31fdefe553b70bb016de281af.png";
import imgImage38 from "figma:asset/d5bab6ea4f3d8cef3b0425c45cfee7faea19fdbc.png";
import imgImage47 from "figma:asset/e8fad960112d5eba554c3969d08891ebe4d4b9c7.png";
import 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: imgImage36, name: 'CANMOOR', width: 302, height: 54 }, // CANMOOR
{ src: imgImage45, name: 'BlackRock', width: 210, height: 54 }, // BlackRock
{ src: imgImage38, name: 'Royal London', width: 145, height: 54 }, // Royal London
{ src: imgImage39, name: 'Abstract', width: 172, height: 54 }, // Abstract
{ src: imgImage47, name: 'ARES', width: 163, height: 54 }, // ARES
{ src: imgImage43, name: 'KADANS', width: 206, height: 54 }, // KADANS
// Repeat logos for more variety in scrolling
{ src: imgImage36, name: 'CANMOOR', width: 302, height: 54 }, // CANMOOR (repeat)
{ src: imgImage45, name: 'BlackRock', width: 210, height: 54 }, // BlackRock (repeat)
{ src: imgImage38, name: 'Royal London', width: 145, height: 54 }, // Royal London (repeat)
{ src: imgImage39, name: 'Abstract', width: 172, height: 54 }, // Abstract (repeat)
{ src: imgImage47, name: 'ARES', width: 163, height: 54 }, // ARES (repeat)
{ src: imgImage43, name: 'KADANS', width: 206, height: 54 }, // KADANS (repeat)
{ 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
@@ -40,7 +38,7 @@ 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"
@@ -70,9 +68,9 @@ function LogoItem({ logo, index, duplicate = false }: LogoItemProps) {
export function LogosSection() {
return (
<section
<section
className="py-16 overflow-hidden"
style={{
style={{
backgroundColor: '#F7F7FD', // Same as app background
width: '100%',
}}
@@ -83,21 +81,21 @@ export function LogosSection() {
</div>
{/* Logo Ticker - Two Rows */}
<div
<div
className="w-full relative"
role="region"
aria-label="Client logos showcase"
>
{/* Top Row - Scrolling Left to Right */}
<div
className="relative h-[54px] mb-16 overflow-hidden"
<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={{
<div
className="scroll-left flex items-center h-full"
style={{
width: '400%',
gap: '80px',
paddingLeft: '40px',
@@ -106,54 +104,17 @@ export function LogosSection() {
>
{/* Create multiple sets for seamless infinite scroll */}
{[1, 2, 3, 4].map((setNumber) => (
<div
key={`top-set-${setNumber}`}
<div
key={`top-set-${setNumber}`}
className="flex items-center h-full"
style={{ gap: '80px' }}
>
{topRowLogos.map((logo, index) => (
<LogoItem
key={`top-${setNumber}-${index}`}
logo={logo}
index={index}
duplicate={setNumber > 1}
/>
))}
</div>
))}
</div>
</div>
</div>
{/* Bottom Row - Scrolling Right to Left */}
<div
className="relative h-[54px] overflow-hidden"
role="list"
aria-label="Client logos row 2"
>
<div className="flex items-center h-full will-change-transform">
<div
className="scroll-right flex items-center h-full"
style={{
width: '400%',
gap: '80px',
paddingLeft: '40px',
paddingRight: '40px'
}}
>
{/* Create multiple sets for seamless infinite scroll */}
{[1, 2, 3, 4].map((setNumber) => (
<div
key={`bottom-set-${setNumber}`}
className="flex items-center h-full"
style={{ gap: '80px' }}
>
{bottomRowLogos.map((logo, index) => (
<LogoItem
key={`bottom-${setNumber}-${index}`}
logo={logo}
index={index}
duplicate={setNumber > 1}
<LogoItem
key={`top-${setNumber}-${index}`}
logo={logo}
index={index}
duplicate={setNumber > 1}
/>
))}
</div>

View File

@@ -2,6 +2,8 @@ import klcLogo from '../assets/klc-logo-dark.png';
import {
ArrowRight,
BookMarked,
Building,
Building2,
ChevronDown,
ChevronRight,
Eye,
@@ -57,66 +59,83 @@ interface NavLink {
const navigationItems: NavLink[] = [
{
label: 'Services',
// items: [
// {
// label: 'Leadership Development',
// href: '/services/leadership-development',
// description: 'Executive and senior leadership programs',
// icon: Target
// },
// {
// label: 'Management Development',
// href: '/services/management-development',
// description: 'Middle management and team leader training',
// icon: Users
// },
// {
// label: 'Culture Competence',
// href: '/services/culture-competence',
// description: 'Building inclusive and high-performance cultures',
// icon: Heart
// },
// {
// label: 'Consulting',
// href: '/services/consulting',
// description: 'Strategic organizational transformation',
// icon: Lightbulb
// },
// {
// label: 'Executive Coaching',
// href: '/services/executive-coaching',
// description: 'Personalized leadership development',
// icon: GraduationCap
// }
// ]
// label: 'Online Courses',
href: '/services'
label: 'Our Services',
items: [
{
label: 'Our Services',
href: '/services',
description: 'Complete overview of our leadership development services',
icon: Building
},
{
label: 'Leadership Pipeline Development',
href: '/services/leadership-pipeline-development',
description: 'Build strong leadership pipeline across organizational levels',
icon: TrendingUp
},
{
label: 'Leadership Development',
href: '/services/leadership-development',
description: 'Executive and senior leadership programs',
icon: Target
},
{
label: 'Management Development',
href: '/services/management-development',
description: 'Middle management and team leader training',
icon: Users
},
{
label: 'Culture & Competence Consulting',
href: '/services/culture-competence',
description: 'Building inclusive and high-performance cultures',
icon: Heart
},
{
label: 'Coaching & Mentoring',
href: '/services/executive-coaching',
description: 'Personalized leadership development and mentoring',
icon: GraduationCap
},
{
label: 'Kautilya Facility',
href: '/services/kautilya-facility',
description: 'Premium learning campus and residential programs',
icon: Building2
}
]
},
// {
// label: 'About Us',
// items: [
// {
// label: 'About Us',
// href: '/about',
// description: 'Learn about our story, vision, and leadership approach',
// icon: Building
// },
// {
// label: 'Our Vision',
// href: '/about/our-vision',
// description: 'Our mission to transform leadership globally',
// icon: Eye
// },
// {
// label: 'Our Impact',
// href: '/about/our-impact',
// description: 'Real results and measurable outcomes',
// icon: TrendingUp
// },
// {
// label: 'Our Expertise',
// href: '/about/our-expertise',
// description: 'Industry-leading knowledge and experience',
// icon: Star
// }
// ]
// },
{
label: 'About Us',
// items: [
// {
// label: 'Our Vision',
// href: '/about/our-vision',
// description: 'Our mission to transform leadership globally',
// icon: Eye
// },
// {
// label: 'Our Impact',
// href: '/about/our-impact',
// description: 'Real results and measurable outcomes',
// icon: TrendingUp
// },
// {
// label: 'Our Expertise',
// href: '/about/our-expertise',
// description: 'Industry-leading knowledge and experience',
// icon: Star
// }
// ]
href: '/about-us'
},
{
@@ -173,17 +192,30 @@ function NavLink({ item, isMobile = false }: { item: NavLink; isMobile?: boolean
const location = useLocation();
const [isOpen, setIsOpen] = useState(false);
if (item.href) {
// Helper to check active state
const isActive = (href?: string, exact: boolean = false) => {
if (!href) return false;
if (exact) return location.pathname === href;
return location.pathname === href || location.pathname.startsWith(href + '/');
};
const currentPath = location.pathname;
const isAnySubItemActive = item.items?.some(subItem => isActive(subItem.href));
// ---------- Single link ----------
if (item.href && !item.items) {
const isActiveItem = isActive(item.href);
return (
<Button
variant="ghost"
onClick={() => navigate(item.href!)}
className={isMobile ? "w-full justify-start min-h-[44px]" : "min-h-[44px]"}
className={`min-h-[44px] transition-all duration-300 hover:-translate-y-1 ${isActiveItem ? 'bg-gray-100 text-[#04045b] font-medium' : ''
} ${isMobile ? 'w-full justify-start' : ''}`}
style={{
fontSize: '14px',
fontSize: '13px',
fontWeight: 'normal',
fontFamily: 'var(--font-family-base)',
color: 'var(--color-black)'
}}
>
{item.icon && <item.icon className="w-4 h-4 mr-2" />}
@@ -192,61 +224,69 @@ function NavLink({ item, isMobile = false }: { item: NavLink; isMobile?: boolean
);
}
// ---------- Mobile collapsible ----------
if (isMobile) {
return (
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
<CollapsibleTrigger asChild>
<Button
variant="ghost"
className="w-full justify-between min-h-[44px]"
className={`w-full justify-between min-h-[44px] ${isAnySubItemActive ? 'bg-gray-100 text-[#04045b] font-medium' : ''
}`}
style={{
fontSize: '14px',
fontWeight: 'normal',
fontFamily: 'var(--font-family-base)',
color: 'var(--color-black)'
}}
>
<span className="flex items-center">
{item.icon && <item.icon className="w-4 h-4 mr-2" />}
{item.label}
</span>
<ChevronRight className={`w-4 h-4 transition-transform ${isOpen ? 'rotate-90' : ''}`} />
<ChevronRight
className={`w-4 h-4 transition-transform ${isOpen ? 'rotate-90' : ''}`}
/>
</Button>
</CollapsibleTrigger>
<CollapsibleContent className="pl-4 space-y-1">
{item.items?.map((subItem) => (
<Button
key={subItem.href}
variant="ghost"
onClick={() => navigate(subItem.href)}
className="w-full justify-start min-h-[44px] pl-6"
style={{
fontSize: '14px',
fontWeight: 'normal',
fontFamily: 'var(--font-family-base)',
color: 'var(--color-black)'
}}
>
{subItem.icon && <subItem.icon className="w-4 h-4 mr-2" />}
{subItem.label}
</Button>
))}
{item.items?.map((subItem) => {
const isSubItemActive = isActive(subItem.href);
return (
<Button
key={subItem.href}
variant="ghost"
onClick={() => navigate(subItem.href)}
className={`w-full justify-start min-h-[44px] pl-6 ${isSubItemActive ? 'bg-gray-100 text-[#04045b] font-medium' : ''
}`}
style={{
fontSize: '14px',
fontWeight: 'normal',
fontFamily: 'var(--font-family-base)',
}}
>
{subItem.icon && <subItem.icon className="w-4 h-4 mr-2" />}
{subItem.label}
</Button>
);
})}
</CollapsibleContent>
</Collapsible>
);
}
// ---------- Desktop dropdown ----------
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
className="flex items-center gap-1 min-h-[44px] transition-all duration-300 hover:transform hover:-translate-y-1"
className={`flex items-center gap-1 min-h-[44px] transition-all duration-300 hover:-translate-y-1 ${isAnySubItemActive ? 'bg-gray-100 text-[#04045b] font-medium' : ''
}`}
style={{
fontSize: '14px',
fontWeight: 'normal',
fontFamily: 'var(--font-family-base)',
color: 'var(--color-black)'
}}
>
{item.icon && <item.icon className="w-4 h-4 mr-2" />}
@@ -254,6 +294,7 @@ function NavLink({ item, isMobile = false }: { item: NavLink; isMobile?: boolean
<ChevronDown className="w-4 h-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="start"
className="w-80"
@@ -261,63 +302,69 @@ function NavLink({ item, isMobile = false }: { item: NavLink; isMobile?: boolean
backgroundColor: 'var(--color-bg-white)',
border: `1px solid var(--color-border)`,
borderRadius: '12px',
boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
fontFamily: 'var(--font-family-base)'
boxShadow: '0 10px 15px -3px rgba(0,0,0,0.1),0 4px 6px -2px rgba(0,0,0,0.05)',
fontFamily: 'var(--font-family-base)',
}}
>
{item.items?.map((subItem) => (
<DropdownMenuItem
key={subItem.href}
onClick={() => navigate(subItem.href)}
className="flex items-start gap-3 p-4 cursor-pointer transition-all duration-300 hover:transform hover:-translate-y-1"
style={{
fontFamily: 'var(--font-family-base)'
}}
>
{subItem.icon && (
<div
className="w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0 mt-1"
style={{
backgroundColor: 'rgba(4, 4, 91, 0.1)',
color: 'var(--color-brand-primary)'
}}
>
<subItem.icon className="w-4 h-4" />
</div>
)}
<div className="flex-1">
<div
className="font-medium"
style={{
fontSize: '14px',
fontWeight: '400',
color: 'var(--color-black)',
fontFamily: 'var(--font-family-base)'
}}
>
{subItem.label}
</div>
{subItem.description && (
{item.items?.map((subItem) => {
const isSubItemActive = isActive(subItem.href);
return (
<DropdownMenuItem
key={subItem.href}
onClick={() => navigate(subItem.href)}
className={`flex items-start gap-3 p-4 cursor-pointer transition-all duration-300 hover:-translate-y-1 ${isSubItemActive ? 'bg-gray-100 text-[#04045b] font-medium' : ''
}`}
style={{
fontFamily: 'var(--font-family-base)',
}}
>
{subItem.icon && (
<div
className="mt-1"
className="w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0 mt-1"
style={{
fontSize: '12px',
color: 'var(--color-gray-muted)',
fontFamily: 'var(--font-family-base)',
lineHeight: 'var(--line-height-small)'
backgroundColor: 'rgba(4,4,91,0.1)',
color: 'var(--color-brand-primary)',
}}
>
{subItem.description}
<subItem.icon className="w-4 h-4" />
</div>
)}
</div>
</DropdownMenuItem>
))}
<div className="flex-1">
<div
className="font-medium"
style={{
fontSize: '14px',
fontWeight: isSubItemActive ? '500' : '400',
color: isSubItemActive ? '#04045b' : 'var(--color-black)',
fontFamily: 'var(--font-family-base)',
}}
>
{subItem.label}
</div>
{subItem.description && (
<div
className="mt-1"
style={{
fontSize: '12px',
color: 'var(--color-gray-muted)',
fontFamily: 'var(--font-family-base)',
lineHeight: 'var(--line-height-small)',
}}
>
{subItem.description}
</div>
)}
</div>
</DropdownMenuItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
);
}
function ProfileDropdown({ user }: { user: any }) {
const { signOut } = useAuth();
const navigate = useNavigate();
@@ -537,7 +584,7 @@ export function Navigation({ currentPage }: NavigationProps) {
) : (
<div className="hidden md:flex items-center">
<Button
onClick={() => navigate('/leadership-journey')}
onClick={() => navigate('/self-learner-signup')}
className="management-dev-glassmorphic-btn text-body px-8 py-4 min-h-[52px] border transition-all duration-300 group"
style={{
fontFamily: 'var(--font-family-base)',

View File

@@ -1,146 +1,101 @@
import React, { useEffect } from 'react';
import svgPaths from "../imports/svg-i1joeov37f";
import { navigateTo } from './Router';
import React, { useState } from 'react';
import { ArrowRight } from 'lucide-react';
interface PrimaryCTAButtonProps {
text: string;
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
className?: string;
onClick: () => void;
ariaLabel?: string;
debugId?: string; // Add debug identifier
className?: string;
disabled?: boolean;
}
export const PrimaryCTAButton: React.FC<PrimaryCTAButtonProps> = ({
text,
onClick,
export function PrimaryCTAButton({
text,
onClick,
ariaLabel,
className = '',
ariaLabel,
debugId = 'unknown'
}) => {
// Debug: Log when component mounts
useEffect(() => {
console.log(`PrimaryCTAButton ${debugId} mounted`);
}, [debugId]);
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
console.log(`Primary CTA Button ${debugId} clicked with text: "${text}"`); // Debug log
// Handle webinars navigation
if (text === "Join Our Webinars" || text === "Explore Webinars" || text === "View Webinars") {
console.log(`Navigating to webinars page for ${debugId}`);
navigateTo('/webinars');
} else if (text === "Register Free" || text === "Watch Replay" || text === "Launch in Zoom") {
// These are handled by individual webinar detail pages
console.log(`Webinar CTA handled by detail page for ${debugId}`);
} else if (onClick) {
console.log(`Custom onClick handler found for ${debugId}, executing it`); // Debug log
onClick(e);
} else {
console.log(`No custom onClick for ${debugId}, navigating to webcast page`); // Debug log
// Navigate to webcast page by default
navigateTo('/learning/webcast');
}
};
disabled = false
}: PrimaryCTAButtonProps) {
const [isHovered, setIsHovered] = useState(false);
return (
<button
className={`primary-cta-button group box-border content-stretch flex flex-row gap-2.5 items-center justify-start p-0 relative cursor-pointer overflow-hidden ${className}`}
style={{
background: 'transparent',
border: 'none',
width: 'fit-content' // Perfect width - no extra horizontal space
<button
onClick={onClick}
disabled={disabled}
aria-label={ariaLabel || text}
className={`primary-cta-button ${className}`}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '12px',
padding: '8px 20px',
borderRadius: '10px',
border: 'none',
cursor: disabled ? 'not-allowed' : 'pointer',
fontSize: 'var(--font-body-lg)',
fontWeight: 'var(--font-weight-h3)',
fontFamily: 'var(--font-family-base)',
backgroundColor: disabled ? '#9CA3AF' : 'var(--color-primary)',
color: '#FFFFFF',
boxShadow: disabled
? 'none'
: '0 4px 6px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.06)',
transition: 'all 300ms ease-in-out',
position: 'relative',
overflow: 'hidden',
opacity: disabled ? 0.6 : 1,
transform: isHovered && !disabled ? 'translateY(-2px)' : 'translateY(0)',
...(isHovered && !disabled && {
backgroundColor: '#030359', // Darker shade of primary brand color
boxShadow: '0 8px 25px rgba(4, 4, 91, 0.3), 0 4px 6px rgba(4, 4, 91, 0.1)'
})
}}
>
{/* Text */}
<span
style={{
fontSize: 'var(--font-body-lg)',
fontWeight: 'var(--font-weight-h3)',
fontFamily: 'var(--font-family-base)',
color: '#FFFFFF',
whiteSpace: 'nowrap'
}}
onClick={handleClick}
aria-label={ariaLabel || text}
>
{/* Icon Container with Slide Animation */}
<div className="relative shrink-0 size-[50px] overflow-hidden">
{/* Background Rectangle - Consistent Yellow Color */}
<div className="absolute inset-0 bg-[#F8C301]" />
{/* Icon Layer - Sliding Animation */}
<div className="icon-layer absolute inset-0 w-full h-full">
{/* Primary Arrow - Slides out diagonally up-right */}
<div className="icon absolute inset-0 flex items-center justify-center transition-all duration-300 ease-in-out group-hover:translate-x-6 group-hover:-translate-y-6 group-hover:opacity-0">
<svg
className="block w-full h-full"
fill="none"
preserveAspectRatio="none"
viewBox="0 0 50 50"
>
<g clipPath="url(#clip0_primary_cta_primary)">
<path d={svgPaths.p5b8d700} fill="white" />
<path d={svgPaths.p30b71a00} fill="white" />
</g>
<defs>
<clipPath id="clip0_primary_cta_primary">
<rect fill="white" height="50" width="50" />
</clipPath>
</defs>
</svg>
</div>
{/* Secondary Arrow - Slides in from bottom-left */}
<div className="icon absolute inset-0 flex items-center justify-center opacity-0 -translate-x-6 translate-y-6 transition-all duration-300 ease-in-out group-hover:translate-x-0 group-hover:translate-y-0 group-hover:opacity-100">
<svg
className="block w-full h-full"
fill="none"
preserveAspectRatio="none"
viewBox="0 0 50 50"
>
<g clipPath="url(#clip0_primary_cta_secondary)">
<path d={svgPaths.p5b8d700} fill="white" />
<path d={svgPaths.p30b71a00} fill="white" />
</g>
<defs>
<clipPath id="clip0_primary_cta_secondary">
<rect fill="white" height="50" width="50" />
</clipPath>
</defs>
</svg>
</div>
</div>
</div>
{/* Text Section with Vertical Slide Animation */}
<div className="text-layer relative shrink-0 overflow-hidden flex items-center" style={{
height: '28px',
fontFamily: 'Inter, sans-serif',
fontSize: '20px',
fontWeight: '400',
lineHeight: '28px',
whiteSpace: 'nowrap',
color: '#ffffff',
width: 'fit-content' // Perfect text width - no extra horizontal space
}}>
{/* Primary Text - Slides up and out */}
<div
className="text-element absolute inset-0 flex items-center justify-start transition-all duration-300 ease-in-out group-hover:-translate-y-full group-hover:opacity-0"
style={{
color: '#ffffff',
fontFamily: 'Inter, sans-serif',
fontSize: '20px',
fontWeight: '400',
lineHeight: '28px'
}}
>
{text}
</div>
{/* Secondary Text - Slides in from bottom */}
<div
className="text-element absolute inset-0 flex items-center justify-start translate-y-full opacity-0 transition-all duration-300 ease-in-out group-hover:translate-y-0 group-hover:opacity-100"
style={{
color: '#ffffff',
fontFamily: 'Inter, sans-serif',
fontSize: '20px',
fontWeight: '400',
lineHeight: '28px'
}}
>
{text}
</div>
</div>
</button>
{text}
</span>
{/* Arrow icon with enhanced animation */}
<span
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '20px',
height: '20px',
borderRadius: '50%',
backgroundColor: 'rgba(255, 255, 255, 0.2)',
transition: 'all 300ms ease-in-out',
transform: isHovered && !disabled ? 'translateX(4px) scale(1.1)' : 'translateX(0) scale(1)',
...(isHovered && !disabled && {
backgroundColor: 'rgba(255, 255, 255, 0.3)'
})
}}
>
<ArrowRight
size={14}
style={{
color: '#FFFFFF',
transition: 'all 300ms ease-in-out'
}}
/>
</span>
</button>
);
};
}
// Named exports for backward compatibility
export const PrimaryCTAButtonProps = PrimaryCTAButton;
export default PrimaryCTAButton;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
import { useEffect } from "react";
import { useLocation } from "react-router-dom";
const ScrollToTop = () => {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return null;
};
export default ScrollToTop;

View File

@@ -520,14 +520,14 @@ export function SelfLearnerSignIn() {
Looking for corporate access?{' '}
</span>
<button
onClick={() => navigateTo('/corporate-login')}
onClick={() => navigateTo('/contact')}
className="text-blue-600 hover:text-blue-700 transition-colors font-medium"
style={{
fontSize: 'var(--font-small)',
fontFamily: 'var(--font-family-base)'
}}
>
Corporate Sign In
Contact Us
</button>
</div>
</div>
@@ -539,7 +539,7 @@ export function SelfLearnerSignIn() {
</div>
{/* Need Help Section - Separate Section Below */}
<div
{/* <div
className="section-margin-x py-12"
style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}
>
@@ -580,7 +580,7 @@ export function SelfLearnerSignIn() {
Speak with Learning Advisor
</Button>
</div>
</div>
</div> */}
</div>
);
}

View File

@@ -394,11 +394,15 @@ export function SelfLearnerSignUp() {
}}
>
I agree to the{' '}
<span className="text-blue-600 hover:text-blue-700 cursor-pointer">
<span className="text-blue-600 hover:text-blue-700 cursor-pointer"
onClick={() => navigateTo('/term-condition')}
>
Terms of Service
</span>{' '}
and{' '}
<span className="text-blue-600 hover:text-blue-700 cursor-pointer">
<span className="text-blue-600 hover:text-blue-700 cursor-pointer"
onClick={() => navigateTo('/privacy-policy')}
>
Privacy Policy
</span>
</Label>
@@ -457,7 +461,7 @@ export function SelfLearnerSignUp() {
</div>
{/* Need Help Section - Separate Section Below */}
<div
{/* <div
className="section-margin-x py-12"
style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}
>
@@ -498,7 +502,7 @@ export function SelfLearnerSignUp() {
Speak with Learning Advisor
</Button>
</div>
</div>
</div> */}
</div>
);
}

View File

@@ -1,331 +1,399 @@
import { useState, useEffect } from 'react';
import { motion } from 'motion/react';
import {
Users,
Target,
TrendingUp,
BookOpen,
MessageCircle,
CheckCircle,
Download,
ArrowRight,
Award,
Lightbulb,
Building2,
Brain,
UserCheck,
ClipboardCheck,
Users2,
Briefcase
Users,
Target,
TrendingUp,
BookOpen,
MessageCircle,
CheckCircle,
Download,
ArrowRight,
Award,
Lightbulb,
Building2,
Brain,
UserCheck,
ClipboardCheck,
Users2,
Briefcase
} from 'lucide-react';
import { BrandedTag } from './about/BrandedTag';
import { PrimaryCTAButton } from './PrimaryCTAButton';
import { CTABannerSection } from './CTABannerSection';
import PrimaryCTAButton from './PrimaryCTAButton';
import { navigateTo } from './Router';
import { CTABannerSection } from './CTABannerSection';
export function Services() {
const [isVisible, setIsVisible] = useState(false);
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
setIsVisible(true);
}, []);
useEffect(() => {
setIsVisible(true);
}, []);
const services = [
{
id: 'leadership-development',
icon: Users,
title: 'Leadership Development Programs (LDP)',
description: 'At KLC, we design and deliver high-impact Leadership Development Programs that prepare leaders for tomorrow\'s business challenges. Our journeys go beyond classroom learning — combining business cases, reflective practice, and orientation-based frameworks to create leaders who not only adapt but also accelerate growth. Each program is customised to your strategy, ensuring leaders drive measurable business impact while building long-term succession readiness.',
ctaText: 'Design a Leadership Journey for Your Institution',
downloadTitle: 'Case Study: How a BFSI Major Built its Next-Gen Leadership Pipeline',
route: '/services/leadership-development'
},
{
id: 'assessments-pipeline',
icon: Target,
title: 'Assessments & Leadership Pipeline',
description: 'Knowing your leaders is the first step to shaping your future. KLC\'s assessment solutions — from 360-degree feedback to Development Centres — provide a clear view of leadership potential and performance. We don\'t just assess; we build actionable pathways for growth. Our pipeline development methods ensure succession is never left to chance, but actively cultivated to secure continuity and competitiveness.',
ctaText: 'Assess and Strengthen Your Leadership Bench',
downloadTitle: 'Case Study: Leadership Assessment & Succession Planning in a Top NBFC',
route: '/contact'
},
{
id: 'management-development',
icon: TrendingUp,
title: 'Management Development Programs (MDP)',
description: 'Managers form the backbone of every organisation. Our Management Development Programs strengthen this backbone by equipping managers with the capabilities to lead teams, handle complexity, and deliver consistent results. Rooted in research and practice, our programs blend coaching, peer learning, and simulation-based workshops to help managers grow into confident leaders.',
ctaText: 'Build Managerial Calibre in Your Teams',
downloadTitle: 'Case Study: Developing 400 Mid-Level Managers at a Global Manufacturing Firm',
route: '/services/management-development'
},
{
id: 'culture-competence',
icon: Building2,
title: 'Culture & Competence Consulting',
description: 'Institutions thrive when culture and competence are aligned to strategy. We partner with organisations to define, assess, and embed the cultural and competence frameworks that accelerate growth. From diagnosing culture to shaping values-in-action, our approach ensures leaders model the right behaviours and teams deliver with synergy.',
ctaText: 'Align Your Culture with Business Growth',
downloadTitle: 'Case Study: Embedding a Culture of Customer-Centricity in an ITES Giant',
route: '/services/culture-competence'
},
{
id: 'coaching-mentoring',
icon: MessageCircle,
title: 'Coaching & Mentoring',
description: 'Every leader needs a trusted space to reflect, reset, and grow. Our coaching and mentoring services create that space, helping leaders navigate transitions, build influence, and manage complex workplace relationships. With ICF-aligned coaching practices and decades of leadership experience, KLC coaches enable leaders to strengthen their impact on both business results and institutional culture.',
ctaText: 'Book a Leadership Coaching Conversation',
downloadTitle: 'Case Study: Coaching Senior Leaders Through Organisational Transition',
route: '/services/executive-coaching'
}
];
const services = [
{
id: 'leadership-development',
icon: Users,
title: 'Leadership Development Programs (LDP)',
description: 'At KLC, we design and deliver high-impact Leadership Development Programs that prepare leaders for tomorrow\'s business challenges. Our journeys go beyond classroom learning — combining business cases, reflective practice, and orientation-based frameworks to create leaders who not only adapt but also accelerate growth. Each program is customised to your strategy, ensuring leaders drive measurable business impact while building long-term succession readiness.',
ctaText: 'Learn More',
downloadTitle: 'Case Study: How a BFSI Major Built its Next-Gen Leadership Pipeline',
route: '/services/leadership-development'
},
{
id: 'assessments-pipeline',
icon: Target,
title: 'Assessments & Leadership Pipeline',
description: 'Knowing your leaders is the first step to shaping your future. KLC\'s assessment solutions — from 360-degree feedback to Development Centres — provide a clear view of leadership potential and performance. We don\'t just assess; we build actionable pathways for growth. Our pipeline development methods ensure succession is never left to chance, but actively cultivated to secure continuity and competitiveness.',
ctaText: 'Learn More',
downloadTitle: 'Case Study: Leadership Assessment & Succession Planning in a Top NBFC',
route: '/contact'
},
{
id: 'management-development',
icon: TrendingUp,
title: 'Management Development Programs (MDP)',
description: 'Managers form the backbone of every organisation. Our Management Development Programs strengthen this backbone by equipping managers with the capabilities to lead teams, handle complexity, and deliver consistent results. Rooted in research and practice, our programs blend coaching, peer learning, and simulation-based workshops to help managers grow into confident leaders.',
ctaText: 'Learn More',
downloadTitle: 'Case Study: Developing 400 Mid-Level Managers at a Global Manufacturing Firm',
route: '/services/management-development'
},
{
id: 'culture-competence',
icon: Building2,
title: 'Culture & Competence Consulting',
description: 'Institutions thrive when culture and competence are aligned to strategy. We partner with organisations to define, assess, and embed the cultural and competence frameworks that accelerate growth. From diagnosing culture to shaping values-in-action, our approach ensures leaders model the right behaviours and teams deliver with synergy.',
ctaText: 'Learn More',
downloadTitle: 'Case Study: Embedding a Culture of Customer-Centricity in an ITES Giant',
route: '/services/culture-competence'
},
{
id: 'coaching-mentoring',
icon: MessageCircle,
title: 'Coaching & Mentoring',
description: 'Every leader needs a trusted space to reflect, reset, and grow. Our coaching and mentoring services create that space, helping leaders navigate transitions, build influence, and manage complex workplace relationships. With ICF-aligned coaching practices and decades of leadership experience, KLC coaches enable leaders to strengthen their impact on both business results and institutional culture.',
ctaText: 'Learn More',
downloadTitle: 'Case Study: Coaching Senior Leaders Through Organisational Transition',
route: '/services/executive-coaching'
},
{
id: 'leadership-campus',
icon: BookOpen,
title: 'Leadership Campus (Facility)',
description: 'Experience learning in a world-class environment designed for transformation. Our state-of-the-art Kautilya Leadership Campus provides the perfect setting for immersive leadership development programs. Located in a serene and inspiring environment, our facility combines modern learning spaces with thoughtfully designed areas for reflection, collaboration, and networking. Create lasting memories and meaningful connections while developing leadership capabilities that drive business impact.',
ctaText: 'Learn More',
downloadTitle: 'Case Study: Transformative Leadership Retreats at Kautilya Campus',
route: '/services/learning-facility'
}
];
const caseStudies = [
{
title: 'Case Study on Mid-Sized Bank',
description: 'Comprehensive leadership development and culture transformation initiative',
format: 'PDF'
},
{
title: 'Case Study on Leading Insurance Company',
description: 'Strategic leadership pipeline development and succession planning',
format: 'PDF'
},
{
title: 'Case Study on Leading Private Sector Bank',
description: 'Management development and organizational capability building',
format: 'PDF'
}
];
const caseStudies = [
{
title: 'Case Study on Mid-Sized Bank',
description: 'Comprehensive leadership development and culture transformation initiative',
format: 'PDF'
},
{
title: 'Case Study on Leading Insurance Company',
description: 'Strategic leadership pipeline development and succession planning',
format: 'PDF'
},
{
title: 'Case Study on Leading Private Sector Bank',
description: 'Management development and organizational capability building',
format: 'PDF'
}
];
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('https://images.unsplash.com/photo-1600880292203-757bb62b4baf?w=1920&h=1080&fit=crop')`
}}
/>
<div className="absolute inset-0 bg-gradient-to-r from-black/80 via-black/70 to-black/60"></div>
</div>
<div className="relative z-10 flex-1 flex items-center">
<div className="container mx-auto section-margin-x">
<div className="text-center max-w-5xl mx-auto">
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: isVisible ? 1 : 0, y: isVisible ? 0 : 30 }}
transition={{ duration: 0.8 }}
>
<div className="branded-tag-system-white mb-8 justify-center">
<div className="dot"></div>
<span className="text">Our Services</span>
</div>
<h1 className="text-h1-white mb-6">
Building Leadership Capacity<br />
That Drives Results
</h1>
<p className="text-body-lg-white mb-8 max-w-3xl mx-auto">
At Kautilya Leadership Centre, we enable institutions and individuals to build leadership capacity,
align culture with strategy, and create enduring value. Our services span leadership development,
consulting, coaching, assessments, and digital learning each designed with research, context, and practice at the core.
</p>
<div className="flex justify-center">
<PrimaryCTAButton
text="Explore Our Approach"
onClick={() => navigateTo('/contact')}
ariaLabel="Contact us to explore our services approach"
className="cta-text-white"
/>
</div>
</motion.div>
</div>
</div>
</div>
</section>
{/* Services Overview Section */}
<section className="py-20 lg:py-28" style={{ backgroundColor: '#FFFFFF' }}>
<div className="section-margin-x">
<div className="max-w-7xl mx-auto">
{/* Services Grid */}
<div className="space-y-24">
{services.map((service, index) => (
<motion.div
key={service.id}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: index * 0.1 }}
viewport={{ once: true }}
className="max-w-7xl mx-auto"
>
<div className={`grid grid-cols-1 lg:grid-cols-12 gap-12 lg:gap-16 items-start ${index % 2 === 1 ? 'lg:direction-reverse' : ''
}`}>
{/* Content Section */}
<div className={`lg:col-span-7 ${index % 2 === 1 ? 'lg:col-start-6 lg:order-2' : 'lg:col-start-1 lg:order-1'
}`}>
{/* Icon and Title - Side by Side */}
<div className="flex items-start gap-4 mb-6">
<div
className="w-16 h-16 rounded-xl flex items-center justify-center flex-shrink-0"
style={{ backgroundColor: 'var(--color-primary)' }}
>
<service.icon className="w-8 h-8 text-white" />
</div>
<h2 className="text-h2 leading-tight">{service.title}</h2>
</div>
{/* Description */}
<p className="text-body-lg text-muted mb-8 leading-relaxed">
{service.description}
</p>
{/* CTA Button */}
<div className="mb-8 primary-cta-container-left cta-left-locked">
<PrimaryCTAButton
text={service.ctaText}
onClick={() => navigateTo('/contact')}
ariaLabel={`Contact us about ${service.title}`}
className="cta-text-black"
/>
</div>
{/* Download Resource */}
<button
className="w-full p-6 bg-gray-50 rounded-xl border border-gray-100 hover:shadow-lg hover:shadow-primary/10 hover:border-primary/20 transition-all duration-300 cursor-pointer group text-left"
onClick={() => {/* Handle download */ }}
>
<div className="flex items-start gap-4">
<div className="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center flex-shrink-0 transition-all duration-300 relative overflow-hidden">
<Download className="w-6 h-6 text-primary group-hover:animate-download-arrows" />
</div>
<div className="flex-1">
<h4 className="text-h4 mb-2">
Downloadable Resource
</h4>
<p className="text-small text-muted">
{service.downloadTitle}
</p>
</div>
</div>
</button>
</div>
{/* Image Section */}
<div className={`lg:col-span-5 ${index % 2 === 1 ? 'lg:col-start-1 lg:order-1' : 'lg:col-start-8 lg:order-2'
}`}>
<div className="relative">
<div
className="w-full aspect-[4/3] rounded-2xl bg-cover bg-center bg-no-repeat"
style={{
backgroundImage: `url('https://images.unsplash.com/photo-${index === 0 ? '1522071820-d3ca7b99e0dd' : // leadership team
index === 1 ? '1551836022-d5d88e9218df' : // assessment/analytics
index === 2 ? '1600880292203-757bb62b4baf' : // management meeting
index === 3 ? '1552664730-d307ca884978' : // culture/team
'1573496359142-b8d87734a5a2' // coaching
}?w=800&h=600&fit=crop')`
}}
>
<div className="absolute inset-0 bg-gradient-to-br from-primary/20 to-transparent rounded-2xl"></div>
</div>
</div>
</div>
</div>
</motion.div>
))}
</div>
</div>
</div>
</section>
{/* Case Studies Section */}
<section className="py-20" style={{ backgroundColor: '#F8F9FA' }}>
<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"
>
<div className="branded-tag-system mb-6 justify-center">
<div className="dot"></div>
<span className="text">Case Studies</span>
</div>
<h2 className="text-h2 mb-6">
Real Impact, Real Results
</h2>
<p className="text-body-lg text-muted max-w-2xl mx-auto">
Discover how we've helped organizations transform their leadership capabilities
and drive sustainable business growth.
</p>
</motion.div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{caseStudies.map((caseStudy, index) => (
<motion.div
key={index}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: index * 0.1 }}
viewport={{ once: true }}
className="bg-white rounded-2xl p-6 shadow-sm border border-gray-100 hover:shadow-lg transition-shadow cursor-pointer"
onClick={() => {/* Handle case study view */ }}
>
<div className="w-12 h-12 bg-primary/10 rounded-lg flex items-center justify-center mb-4">
<Award className="w-6 h-6 text-primary" />
</div>
<h3 className="text-h4 mb-3">{caseStudy.title}</h3>
<p className="text-small text-muted mb-4">
{caseStudy.description}
</p>
<button
className="w-full flex items-center justify-between p-3 bg-gray-50 hover:bg-primary/5 border border-gray-100 hover:border-primary/20 rounded-lg transition-all duration-300 cursor-pointer group"
onClick={(e) => {
e.stopPropagation();
/* Handle download */
}}
>
<div className="flex items-center gap-2">
<div className="w-8 h-8 bg-primary/10 group-hover:bg-primary/20 rounded-md flex items-center justify-center transition-colors duration-300">
<Download className="w-4 h-4 text-primary" />
</div>
<div className="text-left">
<div className="text-sm font-medium text-gray-900 group-hover:text-primary transition-colors duration-300">
Download {caseStudy.format}
</div>
<div className="text-xs text-gray-500">
Case Study Document
</div>
</div>
</div>
<ArrowRight className="w-4 h-4 text-gray-400 group-hover:text-primary transition-colors duration-300" />
</button>
</motion.div>
))}
</div>
</div>
</div>
</section>
{/* CTA Banner Section */}
<CTABannerSection />
return (
<div style={{ backgroundColor: '#FFFFFF', fontFamily: 'var(--font-family-base)' }}>
{/* Hero Section */}
<section className="relative flex flex-col py-24 min-h-[85vh]">
<div className="absolute inset-0 z-0">
<div
className="w-full h-full bg-cover bg-center bg-no-repeat"
style={{
backgroundImage: `url('https://images.unsplash.com/photo-1600880292203-757bb62b4baf?w=1920&h=1080&fit=crop')`
}}
/>
<div className="absolute inset-0 bg-gradient-to-r from-black/80 via-black/70 to-black/60"></div>
</div>
);
<div className="relative z-10 flex-1 flex items-left">
<div className="container mx-auto section-margin-x">
<div className="text-center max-w-6xl mx-auto">
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: isVisible ? 1 : 0, y: isVisible ? 0 : 30 }}
transition={{ duration: 0.8 }}
>
<div className="branded-tag-system-white mb-8 justify-center">
<div className="dot"></div>
<span className="text">Our Services</span>
</div>
<h1 className="text-h1-white mb-6 text-left">
Building Leadership Capacity<br />
That Drives Results
</h1>
<p className="text-body-lg-white mb-8 mx-auto text-left">
At Kautilya Leadership Centre, we enable institutions and individuals to build leadership capacity,
align culture with strategy, and create enduring value. Our services span leadership development,
consulting, coaching, assessments, and digital learning each designed with research, context, and practice at the core.
</p>
<div className="flex justify-left">
<PrimaryCTAButton
text="Explore Our Approach"
onClick={() => navigateTo('/contact')}
ariaLabel="Contact us to explore our services approach"
className="cta-text-white"
/>
</div>
</motion.div>
</div>
</div>
</div>
</section>
{/* Services Overview Section */}
<section className="py-20 lg:py-28" style={{ backgroundColor: '#FFFFFF' }}>
<div className="section-margin-x">
<div className="max-w-7xl mx-auto">
{/* Services Grid */}
<div className="space-y-40">
{services.map((service, index) => (
<motion.div
key={service.id}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: index * 0.1 }}
viewport={{ once: true }}
className="max-w-7xl mx-auto"
>
<div className="relative">
{/* Content Section */}
<div className="relative pr-0 lg:pr-[520px]">
{/* Icon and Title - Single Line (Figma Layout) */}
<div className="flex items-center gap-[15.5px] mb-8">
<div
className="w-16 h-16 rounded-[14px] flex items-center justify-center flex-shrink-0"
style={{ backgroundColor: 'var(--color-primary)' }}
>
<service.icon className="w-8 h-8 text-white" />
</div>
<h2 className="text-h2 leading-[57.6px] tracking-[-0.48px]" style={{ fontSize: '48px' }}>{service.title}</h2>
</div>
{/* Brief Overview */}
<p className="text-body-lg text-muted leading-relaxed mb-8">
{service.description.split('.')[0]}.
</p>
{/* Service Preview Cards - Figma Layout */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-8">
{/* Preview Card 1: What It Is */}
<div className="p-[21px] bg-white border border-gray-200 rounded-[14px] hover:shadow-lg hover:border-primary/30 transition-all duration-300">
<div className="flex items-center gap-3 mb-3">
<div className="w-8 h-8 rounded-[10px] bg-primary/10 flex items-center justify-center">
<Lightbulb className="w-4 h-4" style={{ color: 'var(--color-primary)' }} />
</div>
<h4 className="text-body-lg" style={{ fontWeight: 600, color: 'var(--color-black)' }}>What It Is</h4>
</div>
<p className="text-small text-muted leading-relaxed">
{index === 0 && 'Customized leadership journeys combining business cases, reflective practice, and strategic frameworks'}
{index === 1 && '360-degree feedback, Development Centres, and pipeline strategies for succession planning'}
{index === 2 && 'Manager capability building through coaching, peer learning, and simulation workshops'}
{index === 3 && 'Culture assessment, competence frameworks, and values-in-action embedding strategies'}
{index === 4 && 'ICF-aligned coaching and mentoring for leadership transitions and influence building'}
{index === 5 && 'State-of-the-art campus for immersive leadership development and transformation'}
</p>
</div>
{/* Preview Card 2: Who It's For */}
<div className="p-[21px] bg-white border border-gray-200 rounded-[14px] hover:shadow-lg hover:border-primary/30 transition-all duration-300">
<div className="flex items-center gap-3 mb-3">
<div className="w-8 h-8 rounded-[10px] bg-primary/10 flex items-center justify-center">
<Users2 className="w-4 h-4" style={{ color: 'var(--color-primary)' }} />
</div>
<h4 className="text-body-lg" style={{ fontWeight: 600, color: 'var(--color-black)' }}>Who It's For</h4>
</div>
<p className="text-small text-muted leading-relaxed">
{index === 0 && 'Senior leaders, high-potential managers, and succession pipeline candidates'}
{index === 1 && 'HR teams, talent managers, and succession planning committees'}
{index === 2 && 'Mid-level managers, team leaders, and aspiring first-time leaders'}
{index === 3 && 'Leadership teams, HR leaders, and change management sponsors'}
{index === 4 && 'C-suite executives, senior leaders navigating transitions and complex challenges'}
{index === 5 && 'Organizations seeking transformative leadership retreats and immersive learning'}
</p>
</div>
{/* Preview Card 3: Expected Impact - Full Width */}
<div className="p-[21px] bg-white border border-gray-200 rounded-[14px] hover:shadow-lg hover:border-primary/30 transition-all duration-300 sm:col-span-2">
<div className="flex items-center gap-3 mb-3">
<div className="w-8 h-8 rounded-[10px] bg-accent/20 flex items-center justify-center">
<CheckCircle className="w-4 h-4" style={{ color: 'var(--color-accent)' }} />
</div>
<h4 className="text-body-lg" style={{ fontWeight: 600, color: 'var(--color-black)' }}>Expected Impact</h4>
</div>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
<div className="flex items-start gap-2">
<ArrowRight className="w-4 h-4 mt-0.5 flex-shrink-0" style={{ color: 'var(--color-accent)' }} />
<p className="text-small text-muted">
{index === 0 && 'Leaders who drive measurable business impact'}
{index === 1 && 'Clear succession pathways and readiness'}
{index === 2 && 'Confident managers delivering consistent results'}
{index === 3 && 'Culture-strategy alignment and synergy'}
{index === 4 && 'Enhanced leadership influence and presence'}
{index === 5 && 'Lasting transformation and connections'}
</p>
</div>
<div className="flex items-start gap-2">
<ArrowRight className="w-4 h-4 mt-0.5 flex-shrink-0" style={{ color: 'var(--color-accent)' }} />
<p className="text-small text-muted">
{index === 0 && 'Long-term succession readiness'}
{index === 1 && 'Data-driven development plans'}
{index === 2 && 'Effective team leadership capability'}
{index === 3 && 'Values modeled by leadership'}
{index === 4 && 'Successful navigation of transitions'}
{index === 5 && 'Immersive learning environment'}
</p>
</div>
<div className="flex items-start gap-2">
<ArrowRight className="w-4 h-4 mt-0.5 flex-shrink-0" style={{ color: 'var(--color-accent)' }} />
<p className="text-small text-muted">
{index === 0 && 'Accelerated leadership growth'}
{index === 1 && 'Proactive talent cultivation'}
{index === 2 && 'Growth into confident leaders'}
{index === 3 && 'Sustained competitive advantage'}
{index === 4 && 'Improved decision-making confidence'}
{index === 5 && 'Meaningful networking opportunities'}
</p>
</div>
</div>
</div>
</div>
{/* CTA Button */}
<div className="primary-cta-container-left cta-left-locked">
<PrimaryCTAButton
text="Explore This Service"
onClick={() => navigateTo(service.route)}
ariaLabel={`Learn more about ${service.title}`}
className="cta-text-black"
/>
</div>
</div>
{/* Image Section - Positioned at Bottom Right (Figma Design) */}
<div className="absolute bottom-0 right-0 w-[496px] h-[479px] hidden lg:block overflow-hidden rounded-[16px]">
<div
className="absolute h-[479px] left-[-71px] top-0 w-[639px] rounded-[16px] bg-cover bg-center bg-no-repeat"
style={{
backgroundImage: `url('https://images.unsplash.com/photo-${index === 0 ? '1758270705696-ec9caffc73dd' : // leadership team
index === 1 ? '1551836022-d5d88e9218df' : // assessment/analytics
index === 2 ? '1600880292203-757bb62b4baf' : // management meeting
index === 3 ? '1552664730-d307ca884978' : // culture/team
index === 4 ? '1573496359142-b8d87734a5a2' : // coaching
'1582213782179-e0d53f98f2ca' // leadership campus/facility
}?w=800&h=600&fit=crop')`
}}
>
<div className="absolute inset-0 bg-gradient-to-br from-primary/20 to-transparent rounded-[16px]"></div>
</div>
</div>
</div>
</motion.div>
))}
</div>
</div>
</div>
</section>
{/* Case Studies Section */}
<section className="py-20" style={{ backgroundColor: '#F8F9FA' }}>
<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"
>
<div className="branded-tag-system mb-6 justify-center">
<div className="dot"></div>
<span className="text">Case Studies</span>
</div>
<h2 className="text-h2 mb-6">
Real Impact, Real Results
</h2>
<p className="text-body-lg text-muted max-w-2xl mx-auto">
Discover how we've helped organizations transform their leadership capabilities
and drive sustainable business growth.
</p>
</motion.div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{caseStudies.map((caseStudy, index) => (
<motion.div
key={index}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: index * 0.1 }}
viewport={{ once: true }}
className="bg-white rounded-2xl p-6 shadow-sm border border-gray-100 hover:shadow-lg transition-shadow cursor-pointer"
onClick={() => {/* Handle case study view */ }}
>
<div className="w-12 h-12 bg-primary/10 rounded-lg flex items-center justify-center mb-4">
<Award className="w-6 h-6 text-primary" />
</div>
<h3 className="text-h4 mb-3">{caseStudy.title}</h3>
<p className="text-small text-muted mb-4">
{caseStudy.description}
</p>
<button
className="w-full flex items-center justify-between p-3 bg-gray-50 hover:bg-primary/5 border border-gray-100 hover:border-primary/20 rounded-lg transition-all duration-300 cursor-pointer group"
onClick={(e) => {
e.stopPropagation();
/* Handle download */
}}
>
<div className="flex items-center gap-2">
<div className="w-8 h-8 bg-primary/10 group-hover:bg-primary/20 rounded-md flex items-center justify-center transition-colors duration-300">
<Download className="w-4 h-4 text-primary" />
</div>
<div className="text-left">
<div className="text-sm font-medium text-gray-900 group-hover:text-primary transition-colors duration-300">
Download {caseStudy.format}
</div>
<div className="text-xs text-gray-500">
Case Study Document
</div>
</div>
</div>
<ArrowRight className="w-4 h-4 text-gray-400 group-hover:text-primary transition-colors duration-300" />
</button>
</motion.div>
))}
</div>
</div>
</div>
</section>
{/* CTA Banner Section */}
<CTABannerSection />
</div>
);
}

View File

@@ -1,75 +1,49 @@
import { useState, useEffect, useRef } from "react";
import { motion } from "motion/react";
import {
Users,
Settings,
User,
Globe,
MessageSquare,
import {
Users,
Settings,
User,
Globe,
MessageSquare,
GraduationCap,
TrendingUp,
Building,
ArrowRight
} from "lucide-react";
import { BrandedTag } from "./about/BrandedTag";
import { PrimaryCTAButton } from "./PrimaryCTAButton";
import { StandardCTAButton } from "./StandardCTAButton";
import { navigateTo } from "./Router";
import { ImageWithFallback } from "./figma/ImageWithFallback";
// 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"
}
];
interface HighlightCard {
card_title: string;
icon_url: string;
accessible_label: string;
body_text: string;
display_order: number;
}
export function ServicesSection() {
interface ServicesSectionProps {
highlightCards?: HighlightCard[];
isLoading?: boolean;
}
export function ServicesSection({ highlightCards = [], isLoading = false }: ServicesSectionProps) {
const [isVisible, setIsVisible] = useState(false);
const cardRefs = useRef<(HTMLDivElement | null)[]>([]);
const sectionRef = useRef<HTMLDivElement>(null);
// Create service items from highlightCards data
const serviceItems = highlightCards.map((card, index) => ({
id: index + 1,
title: card.card_title,
description: card.body_text,
iconUrl: card.icon_url,
accessibleLabel: card.accessible_label,
route: '/services' // You might want to map to specific routes based on title
}));
// Add card refs helper
const addCardRef = (el: HTMLDivElement | null, index: number) => {
cardRefs.current[index] = el;
@@ -106,101 +80,129 @@ export function ServicesSection() {
}
};
// Show loading skeleton if isLoading is true
if (isLoading) {
return (
<section ref={sectionRef} className="py-16 lg:py-20" style={{ backgroundColor: '#fff' }}>
<div className="section-margin-x">
<div className="max-w-7xl mx-auto">
<div className="animate-pulse">
<div className="h-8 bg-gray-200 rounded w-1/4 mb-6"></div>
<div className="h-12 bg-gray-200 rounded w-2/3 mb-4"></div>
<div className="h-24 bg-gray-200 rounded w-full mb-8"></div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{[1, 2, 3, 4].map((i) => (
<div key={i} className="h-48 bg-gray-200 rounded"></div>
))}
</div>
</div>
</div>
</div>
</section>
);
}
return (
<section
<section
ref={sectionRef}
className="py-16 lg:py-20"
style={{
backgroundColor: '#F7F7FD',
style={{
backgroundColor: '#fff',
fontFamily: 'var(--font-family-brand)'
}}
aria-labelledby="recognition-section-heading"
>
<div className="section-margin-x">
<div className="max-w-7xl mx-auto">
<div className="grid grid-cols-12 gap-12 min-h-screen">
{/* 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="Leadership Development Programs"
<BrandedTag
text="Our Services"
/>
<h2
id="recognition-section-heading"
<h2
id="recognition-section-heading"
className="text-h2 mb-6"
>
Services That Shape Stronger Leaders
Shaping Leaders, Cultures, and Institutions
</h2>
<p className="text-body-lg text-muted">
Our comprehensive leadership development programs are designed to build future-ready leaders who thrive in complexity and drive measurable organizational impact.
<p 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">
<StandardCTAButton
text="Services Page"
onClick={() => navigateTo('/services')}
ariaLabel="Explore our services"
/>
</div>
</div>
</div>
{/* Right Side - Scrolling Cards */}
<div className="col-span-7">
<div
<div
className="recognition-cards space-y-6"
role="list"
aria-label="Leadership development services"
>
{recognitionItems.map((item, index) => (
{serviceItems.map((item, index) => (
<div
key={item.id}
ref={(el) => addCardRef(el, index)}
className={`recognition-card group scroll-animate-stagger focus-ring ${isVisible ? 'animate-in' : ''}`}
className={`recognition-card group scroll-animate-stagger cursor-pointer 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={{
style={{
transitionDelay: `${(index + 1) * 150}ms`,
opacity: isVisible ? 1 : 0
}}
onClick={() => navigateTo(item.route)}
>
<div
<div
className="p-8 transition-all duration-300 hover:shadow-xl hover:-translate-y-1 border bg-white"
style={{
style={{
borderColor: 'var(--color-border)',
borderRadius: '12px',
fontFamily: 'var(--font-family-brand)'
}}
>
<div className="flex items-start justify-between mb-6">
<div
<div className="flex items-start mb-6">
<div
className="w-14 h-14 flex items-center justify-center transition-transform duration-300 group-hover:scale-110"
style={{
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)'
{/* Image icon from icon_url */}
<img
src={item.iconUrl}
alt={item.accessibleLabel || item.title}
className="w-8 h-8 object-contain filter brightness-0 invert" // Makes white icon on colored background
onError={(e) => {
// Fallback if image fails to load
e.currentTarget.style.display = 'none';
// You could add a fallback icon here if needed
}}
>
{item.badge}
</div>
)}
/>
</div>
</div>
<div className="recognition-card-content">
<h3
<h3
id={`recognition-title-${item.id}`}
className="text-h4 mb-4"
>
{item.title}
</h3>
<p
<p
id={`recognition-desc-${item.id}`}
className="text-small text-muted leading-relaxed"
>
@@ -214,21 +216,102 @@ export function ServicesSection() {
</div>
</div>
{/* CTA Button - Updated to redirect to leadership journey */}
<div className="flex justify-center mt-16">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.6 }}
viewport={{ once: true }}
>
<PrimaryCTAButton
text="Get Started Today"
onClick={() => navigateTo('/leadership-journey')}
ariaLabel="Get started with leadership development programs"
className="get-started-cta-override"
{/* Mobile Layout - Stacked Header + Horizontal Scrollable Cards */}
<div className="lg:hidden">
{/* Mobile Header */}
<div className="text-center mb-8">
<BrandedTag
text="Our Services"
/>
</motion.div>
<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">
<StandardCTAButton
text="Services Page"
onClick={() => navigateTo('/services')}
ariaLabel="Explore our services"
/>
</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"
>
{serviceItems.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 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',
}}
>
<ImageWithFallback
src={item.iconUrl}
alt={item.accessibleLabel || item.title}
className="w-full h-full object-cover"
/>
</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>

View File

@@ -1,9 +1,19 @@
import { Button } from "./ui/button";
import { useAnimatedCounter } from "./hooks/useAnimatedCounter";
import { ArrowUpRight } from "lucide-react";
import { useState, useEffect } from "react";
import { BrandedTag } from "./about/BrandedTag";
import { PrimaryCTAButton } from "./PrimaryCTAButton";
import { useAnimatedCounter } from "../redux/hooks/useAnimatedCounter";
interface Stat {
id: string;
number: number;
suffix: string;
label: string;
display_order: number;
}
interface StatsSectionProps {
stats: Stat[];
isLoading?: boolean;
}
interface StatItemProps {
end: number;
@@ -17,124 +27,104 @@ function StatItem({ end, suffix, label, duration = 2000 }: StatItemProps) {
return (
<div className="mb-0">
{/* Top line */}
<div
<div
className="w-full h-[1px] mb-4"
style={{ backgroundColor: 'var(--color-brand-gray-muted)' }}
style={{ backgroundColor: "var(--color-brand-gray-muted)" }}
/>
{/* Large number */}
<div
<span
ref={ref}
className="stats-number mb-3 leading-none"
style={{
color: 'var(--color-brand-primary)',
fontFamily: 'var(--font-family-base)'
style={{
color: "var(--color-brand-primary)",
fontFamily: "var(--font-family-base)",
}}
>
{count}
</div>
{/* Yellow square and label */}
</span>
<div className="flex items-center mb-4">
<div
<div
className="w-2 h-2 mr-3 flex-shrink-0"
style={{ backgroundColor: 'var(--color-brand-accent)' }}
style={{ backgroundColor: "var(--color-brand-accent)" }}
/>
<span
<span
className="text-eyebrow"
style={{ color: 'var(--color-brand-black)' }}
style={{ color: "var(--color-brand-black)" }}
>
{label}
</span>
</div>
{/* Bottom line */}
<div
<div
className="w-full h-[1px]"
style={{ backgroundColor: 'var(--color-brand-gray-muted)' }}
style={{ backgroundColor: "var(--color-brand-gray-muted)" }}
/>
</div>
);
}
export function StatsSection() {
export function StatsSection({ stats = [], isLoading }: StatsSectionProps) {
const sortedStats = [...stats].sort(
(a, b) => a.display_order - b.display_order
);
if (isLoading) return null;
return (
<section
<section
className="py-20"
style={{ backgroundColor: 'var(--color-brand-bg-light)' }}
style={{ backgroundColor: "var(--color-brand-bg-light)" }}
>
<div className="section-margin-x">
<div className="max-w-7xl mx-auto">
<div className="mb-12 lg:mb-16 md:mb-12 sm:mb-8">
{/* Branded Tag */}
<BrandedTag
text="Serving Leaders Across Industries"
/>
{/* Main Heading */}
<BrandedTag text="Serving Leaders Across Industries" />
<div className="grid grid-cols-1 lg:grid-cols-12 gap-8 lg:gap-12 items-start">
<div className="lg:col-span-6 md:col-span-8 sm:col-span-12">
<h2 className="text-h2 mb-8">
Driving impact by building future-ready leaders who thrive in
complexity and lead with confidence.
Your Partner in Leadership, Culture, and Capability Building
</h2>
{/* CTA Button */}
<PrimaryCTAButton
<PrimaryCTAButton
text="About Us"
onClick={() => console.log('About us clicked')}
onClick={() => console.log("About us clicked")}
ariaLabel="Learn more about KLC"
/>
</div>
{/* Desktop Statistics */}
{/* Desktop */}
<div className="hidden lg:block lg:col-start-9 lg:col-end-13">
<div className="space-y-6">
<StatItem
end={27000}
suffix="+"
label="LEADERS DEVELOPED"
duration={2500}
/>
<StatItem
end={150}
suffix="+"
label="CORPORATE CLIENTS"
duration={2000}
/>
<StatItem
end={20}
suffix="+"
label="COUNTRIES SERVED"
duration={1800}
/>
{sortedStats.map((stat) => (
<StatItem
key={stat.id}
end={stat.number}
suffix={stat.suffix}
label={stat.label}
/>
))}
</div>
</div>
</div>
{/* Mobile Statistics - Show below content on mobile/tablet */}
{/* Mobile */}
<div className="block lg:hidden mt-12">
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 sm:gap-8">
<StatItem
end={27000}
suffix="+"
label="LEADERS DEVELOPED"
duration={2500}
/>
<StatItem
end={150}
suffix="+"
label="CORPORATE CLIENTS"
duration={2000}
/>
<StatItem
end={20}
suffix="+"
label="COUNTRIES SERVED"
duration={1800}
/>
{sortedStats.map((stat) => (
<StatItem
key={stat.id}
end={stat.number}
suffix={stat.suffix}
label={stat.label}
/>
))}
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,344 @@
import React, { useEffect } from 'react';
import { motion, AnimatePresence } from 'motion/react';
import { X } from 'lucide-react';
interface TeamMember {
name: string;
role: string;
image: string;
fullBio?: string;
experience?: string;
expertise?: string[];
education?: string;
achievements?: string[];
clientWork?: string;
boardRoles?: string;
}
interface TeamMemberModalProps {
member: TeamMember | null;
isOpen: boolean;
onClose: () => void;
}
export function TeamMemberModal({ member, isOpen, onClose }: TeamMemberModalProps) {
// Prevent background scroll when modal is open
useEffect(() => {
if (isOpen) {
// Disable body scroll
document.body.style.overflow = 'hidden';
document.body.style.height = '100%';
} else {
// Re-enable body scroll
document.body.style.overflow = '';
document.body.style.height = '';
}
// Cleanup function to restore scroll on unmount
return () => {
document.body.style.overflow = '';
document.body.style.height = '';
};
}, [isOpen]);
// Handle escape key press
useEffect(() => {
const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape' && isOpen) {
onClose();
}
};
if (isOpen) {
document.addEventListener('keydown', handleEscape);
}
return () => {
document.removeEventListener('keydown', handleEscape);
};
}, [isOpen, onClose]);
if (!member) return null;
return (
<AnimatePresence>
{isOpen && (
<>
{/* Overlay */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 team-member-modal-overlay z-[9998]"
onClick={onClose}
style={{ zIndex: 9998 }}
/>
{/* Modal */}
<div
className="fixed inset-0 flex items-center justify-center p-4 z-[9999]"
style={{ zIndex: 9999 }}
>
<motion.div
initial={{ opacity: 0, scale: 0.95, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.95, y: 20 }}
transition={{ duration: 0.3, ease: [0.4, 0, 0.2, 1] }}
className="relative w-full max-w-6xl xl:max-w-5xl lg:max-w-4xl md:max-w-3xl sm:max-w-full max-h-[70vh] lg:max-h-[75vh] md:max-h-[80vh] sm:max-h-[85vh] overflow-hidden team-member-modal-container rounded-2xl"
style={{ backgroundColor: '#FFFFFF' }}
onClick={(e) => e.stopPropagation()}
>
{/* Modal Content */}
<div
className="rounded-2xl overflow-hidden"
style={{
backgroundColor: '#FFFFFF'
}}
>
{/* Close Button */}
<button
onClick={onClose}
className="absolute top-4 right-4 md:top-6 md:right-6 z-10 w-10 h-10 md:w-12 md:h-12 bg-white/90 hover:bg-white rounded-full flex items-center justify-center transition-all duration-200 shadow-lg hover:shadow-xl"
style={{ color: '#26231A' }}
aria-label="Close modal"
>
<X size={20} className="md:w-6 md:h-6" />
</button>
<div className="overflow-y-auto max-h-[70vh] lg:max-h-[75vh] md:max-h-[80vh] sm:max-h-[85vh] team-member-modal-scroll">
{/* Header Section */}
<div className="relative p-6 md:p-8 lg:p-12 rounded-t-2xl" style={{ backgroundColor: '#FFFFFF' }}>
<div className="flex flex-col lg:flex-row gap-8 lg:gap-12">
{/* Profile Image */}
<div className="flex-shrink-0">
<div className="w-32 h-32 lg:w-40 lg:h-40 rounded-2xl overflow-hidden shadow-lg">
<img
src={member.image}
alt={member.name}
className="w-full h-full object-cover"
/>
</div>
</div>
{/* Basic Info */}
<div className="flex-1">
<h2
className="text-h2 mb-3"
style={{
fontFamily: 'var(--font-family-base)',
color: '#26231A'
}}
>
{member.name}
</h2>
<p
className="text-h4 mb-6"
style={{
fontFamily: 'var(--font-family-base)',
color: '#04045B'
}}
>
{member.role}
</p>
{member.experience && (
<div className="mb-6">
<h4
className="text-body-lg mb-2"
style={{
fontFamily: 'var(--font-family-base)',
color: '#26231A',
fontWeight: '600'
}}
>
Experience Overview
</h4>
<p
className="text-body leading-relaxed"
style={{
fontFamily: 'var(--font-family-base)',
color: '#6F6F6F'
}}
>
{member.experience}
</p>
</div>
)}
</div>
</div>
</div>
{/* Detailed Content */}
<div className="px-6 md:px-8 lg:px-12 pb-6 md:pb-8 lg:pb-12 rounded-b-2xl">
{/* Full Bio */}
{member.fullBio && (
<div className="mb-8">
<h3
className="text-h4 mb-4"
style={{
fontFamily: 'var(--font-family-base)',
color: '#26231A'
}}
>
Professional Background
</h3>
<div
className="text-body leading-relaxed space-y-4"
style={{
fontFamily: 'var(--font-family-base)',
color: '#26231A'
}}
>
{member.fullBio.split('\n\n').map((paragraph, index) => (
<p key={index}>{paragraph}</p>
))}
</div>
</div>
)}
{/* Expertise Areas */}
{member.expertise && member.expertise.length > 0 && (
<div className="mb-8">
<h3
className="text-h4 mb-4"
style={{
fontFamily: 'var(--font-family-base)',
color: '#26231A'
}}
>
Areas of Expertise
</h3>
<div className="flex flex-wrap gap-3">
{member.expertise.map((skill, index) => (
<span
key={index}
className="px-4 py-2 rounded-lg text-small"
style={{
backgroundColor: 'rgba(4, 4, 91, 0.1)',
color: '#04045B',
fontFamily: 'var(--font-family-base)',
fontWeight: '500'
}}
>
{skill}
</span>
))}
</div>
</div>
)}
{/* Achievements */}
{member.achievements && member.achievements.length > 0 && (
<div className="mb-8">
<h3
className="text-h4 mb-4"
style={{
fontFamily: 'var(--font-family-base)',
color: '#26231A'
}}
>
Key Achievements
</h3>
<div className="space-y-3">
{member.achievements.map((achievement, index) => (
<div key={index} className="flex items-start gap-3">
<div
className="w-2 h-2 rounded-full mt-2 flex-shrink-0"
style={{ backgroundColor: '#F8C301' }}
/>
<p
className="text-body"
style={{
fontFamily: 'var(--font-family-base)',
color: '#26231A'
}}
>
{achievement}
</p>
</div>
))}
</div>
</div>
)}
{/* Client Work */}
{member.clientWork && (
<div className="mb-8">
<h3
className="text-h4 mb-4"
style={{
fontFamily: 'var(--font-family-base)',
color: '#26231A'
}}
>
Client Portfolio
</h3>
<p
className="text-body leading-relaxed"
style={{
fontFamily: 'var(--font-family-base)',
color: '#26231A'
}}
>
{member.clientWork}
</p>
</div>
)}
{/* Board Roles */}
{member.boardRoles && (
<div className="mb-8">
<h3
className="text-h4 mb-4"
style={{
fontFamily: 'var(--font-family-base)',
color: '#26231A'
}}
>
Board Positions & Leadership Roles
</h3>
<p
className="text-body leading-relaxed"
style={{
fontFamily: 'var(--font-family-base)',
color: '#26231A'
}}
>
{member.boardRoles}
</p>
</div>
)}
{/* Education */}
{member.education && (
<div className="mb-8">
<h3
className="text-h4 mb-4"
style={{
fontFamily: 'var(--font-family-base)',
color: '#26231A'
}}
>
Education & Qualifications
</h3>
<p
className="text-body leading-relaxed"
style={{
fontFamily: 'var(--font-family-base)',
color: '#26231A'
}}
>
{member.education}
</p>
</div>
)}
</div>
</div>
</div>
</motion.div>
</div>
</>
)}
</AnimatePresence>
);
}

View File

@@ -5,7 +5,7 @@ import { useState, useRef, useEffect } from "react";
import { BrandedTag } from "./about/BrandedTag";
interface Testimonial {
id?: number;
id?: number | string;
name: string;
role: string;
company?: string;
@@ -16,8 +16,13 @@ interface Testimonial {
isVideo?: boolean;
videoThumbnail?: string;
videoUrl?: string;
designation?: string;
content?: string;
video_url?: string;
profile_xid?: string;
}
// Default testimonials as fallback
const defaultTestimonialsData: Testimonial[] = [
{
id: 1,
@@ -52,38 +57,6 @@ const defaultTestimonialsData: Testimonial[] = [
isVideo: true,
videoThumbnail: "https://images.unsplash.com/photo-1560472355-109703aa3edc?w=600&h=300&fit=crop",
videoUrl: "https://example.com/testimonial-video-2.mp4"
},
{
id: 4,
name: "David Thompson",
role: "Senior Manager",
company: "Enterprise Solutions",
avatar: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=400&h=400&fit=crop&crop=face",
quote: "The personalized coaching and development programs have been game-changing for our organization's leadership pipeline and succession planning initiatives.",
rating: 5,
isVideo: false
},
{
id: 5,
name: "Lisa Wang",
role: "Product Manager",
company: "Digital Ventures",
avatar: "https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?w=400&h=400&fit=crop&crop=face",
quote: "KLC has transformed how we think about leadership in the digital age. The insights and strategies have been invaluable for our team's growth and innovation culture.",
rating: 5,
isVideo: true,
videoThumbnail: "https://images.unsplash.com/photo-1559136555-9303baea8ebd?w=600&h=300&fit=crop",
videoUrl: "https://example.com/testimonial-video-3.mp4"
},
{
id: 6,
name: "Robert Kim",
role: "Regional Director",
company: "Global Corp",
avatar: "https://images.unsplash.com/photo-1519244703995-f4e0f30006d5?w=400&h=400&fit=crop&crop=face",
quote: "The leadership development framework provided by KLC has been instrumental in building a more cohesive and effective leadership team across our regions.",
rating: 4,
isVideo: false
}
];
@@ -138,12 +111,18 @@ function VideoModal({ isOpen, onClose, videoUrl }: {
);
}
// Individual Testimonial Card - Updated with Landing Page Design Standards
// Individual Testimonial Card
function TestimonialCard({ testimonial, onPlayVideo }: {
testimonial: Testimonial;
onPlayVideo: (videoUrl: string) => void;
}) {
const avatarSrc = testimonial.avatar || testimonial.image;
const isVideo = testimonial.isVideo || !!testimonial.video_url;
const videoUrl = testimonial.videoUrl || testimonial.video_url || "";
const role = testimonial.role || testimonial.designation || "";
const quote = testimonial.quote || testimonial.content || "";
const name = testimonial.name || "";
const rating = testimonial.rating || 5;
return (
<motion.div
@@ -162,14 +141,14 @@ function TestimonialCard({ testimonial, onPlayVideo }: {
}}
>
{/* Video Testimonials */}
{testimonial.isVideo ? (
{isVideo ? (
<div
className="relative h-full cursor-pointer overflow-hidden group rounded-xl"
onClick={() => onPlayVideo(testimonial.videoUrl || "")}
onClick={() => onPlayVideo(videoUrl)}
>
<ImageWithFallback
src={testimonial.videoThumbnail || avatarSrc || ""}
alt={`${testimonial.name} video testimonial`}
src={testimonial.videoThumbnail || avatarSrc || "https://images.unsplash.com/photo-1552664730-d307ca884978?w=600&h=300&fit=crop"}
alt={`${name} video testimonial`}
className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
/>
@@ -203,16 +182,16 @@ function TestimonialCard({ testimonial, onPlayVideo }: {
<div className="w-10 h-10 rounded-full overflow-hidden bg-white shadow-lg flex-shrink-0">
<ImageWithFallback
src={avatarSrc || ""}
alt={testimonial.name}
alt={name}
className="w-full h-full object-cover"
/>
</div>
<div className="min-w-0 flex-1">
<h4 className="font-semibold text-white mb-1 text-sm">
{testimonial.name}
{name}
</h4>
<p className="text-xs text-white/80 truncate">
{testimonial.role}
{role}
{testimonial.company && `${testimonial.company}`}
</p>
</div>
@@ -223,7 +202,7 @@ function TestimonialCard({ testimonial, onPlayVideo }: {
<Star
key={star}
size={14}
className={star <= testimonial.rating ? 'fill-current text-yellow-400' : 'text-white/40'}
className={star <= rating ? 'fill-current text-yellow-400' : 'text-white/40'}
/>
))}
</div>
@@ -239,16 +218,16 @@ function TestimonialCard({ testimonial, onPlayVideo }: {
<div className="w-12 h-12 rounded-full overflow-hidden bg-gray-100 flex-shrink-0">
<ImageWithFallback
src={avatarSrc || ""}
alt={testimonial.name}
alt={name}
className="w-full h-full object-cover"
/>
</div>
<div className="min-w-0">
<h4 className="font-semibold text-black mb-1 text-sm">
{testimonial.name}
{name}
</h4>
<p className="text-xs text-gray-600">
{testimonial.role}
{role}
</p>
{testimonial.company && (
<p className="text-xs text-gray-500 font-medium">
@@ -264,7 +243,7 @@ function TestimonialCard({ testimonial, onPlayVideo }: {
<Star
key={star}
size={14}
className={star <= testimonial.rating ? 'fill-current text-yellow-400' : 'text-gray-300'}
className={star <= rating ? 'fill-current text-yellow-400' : 'text-gray-300'}
/>
))}
</div>
@@ -277,7 +256,7 @@ function TestimonialCard({ testimonial, onPlayVideo }: {
"
</span>
<span className="relative z-10">
{testimonial.quote}
{quote}
</span>
</div>
</blockquote>

View File

@@ -1,9 +1,9 @@
import React, { useState } from "react";
import { motion } from "motion/react";
import {
Building,
Users,
Presentation,
import {
Building,
Users,
Presentation,
Coffee,
Play,
Calendar,
@@ -22,7 +22,7 @@ import { Label } from "./ui/label";
import { Textarea } from "./ui/textarea";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select";
import { navigateTo } from "./Router";
import kautilya from "../assets/Kautilya.png";
// Calendar helper functions
const getDaysInMonth = (date: Date) => {
return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
@@ -41,7 +41,7 @@ const isDateAvailable = (date: Date) => {
today.setHours(0, 0, 0, 0);
const selectedDate = new Date(date);
selectedDate.setHours(0, 0, 0, 0);
// Available if it's today or in the future, and not a Sunday
return selectedDate >= today && selectedDate.getDay() !== 0;
};
@@ -104,10 +104,9 @@ interface BookingFormData {
interface FacilityCardProps {
facility: typeof facilities[0];
index: number;
onBookNow: (facility: typeof facilities[0]) => void;
}
function FacilityCard({ facility, index, onBookNow }: FacilityCardProps) {
function FacilityCard({ facility, index }: FacilityCardProps) {
const IconComponent = facility.icon;
return (
@@ -118,7 +117,7 @@ function FacilityCard({ facility, index, onBookNow }: FacilityCardProps) {
transition={{ duration: 0.7, delay: index * 0.15 }}
viewport={{ once: true, margin: "-50px" }}
>
{/* Background Image - Full Height */}
<div className="absolute inset-0">
<ImageWithFallback
@@ -134,10 +133,10 @@ function FacilityCard({ facility, index, onBookNow }: FacilityCardProps) {
<div className="relative z-10 h-full flex flex-col justify-end p-8 max-lg:p-6">
{/* Icon */}
<div className="flex justify-center mb-4">
<div
<div
className="w-16 h-16 rounded-2xl flex items-center justify-center bg-white/20 backdrop-blur-sm border border-white/30"
>
<IconComponent
<IconComponent
className="w-8 h-8 text-white"
/>
</div>
@@ -153,14 +152,10 @@ function FacilityCard({ facility, index, onBookNow }: FacilityCardProps) {
{facility.description}
</p>
{/* Book Now Button */}
{/* Capacity Info */}
<div className="flex justify-center">
<div className="hero-slide-button">
<PrimaryCTAButton
text="Book Now"
onClick={() => onBookNow(facility)}
ariaLabel={`Book ${facility.name} now`}
/>
<div className="bg-white/20 backdrop-blur-sm rounded-lg px-4 py-2 border border-white/30">
<p className="text-white text-sm font-medium">Capacity: {facility.capacity} people</p>
</div>
</div>
</div>
@@ -169,14 +164,14 @@ function FacilityCard({ facility, index, onBookNow }: FacilityCardProps) {
}
// Modal Component for Virtual Tour and Booking
function BookingModal({
facility,
isOpen,
onClose
}: {
facility: typeof facilities[0] | null;
isOpen: boolean;
onClose: () => void;
function BookingModal({
facility,
isOpen,
onClose
}: {
facility: typeof facilities[0] | null;
isOpen: boolean;
onClose: () => void;
}) {
const [bookingForm, setBookingForm] = useState<BookingFormData>({
companyName: '',
@@ -262,7 +257,7 @@ function BookingModal({
for (let day = 1; day <= daysInMonth; day++) {
const date = new Date(currentMonth.getFullYear(), currentMonth.getMonth(), day);
const isAvailable = isDateAvailable(date);
const isSelected = selectedDate &&
const isSelected = selectedDate &&
date.getFullYear() === selectedDate.getFullYear() &&
date.getMonth() === selectedDate.getMonth() &&
date.getDate() === selectedDate.getDate();
@@ -330,7 +325,7 @@ function BookingModal({
</div>
{/* Calendar Footer - Compact */}
<div className="pt-2 border-t border-gray-200">
{/* <div className="pt-2 border-t border-gray-200">
<div className="flex items-center justify-center gap-3 text-xs text-gray-500">
<div className="flex items-center gap-1">
<div className="w-2 h-2 rounded-full bg-primary"></div>
@@ -341,7 +336,7 @@ function BookingModal({
<span>Available</span>
</div>
</div>
</div>
</div> */}
</div>
);
};
@@ -349,21 +344,21 @@ function BookingModal({
if (!isOpen || !facility) return null;
return (
<div
<div
className="fixed inset-0 bg-black/60 flex items-center justify-center p-2 lg:p-4 z-popup-modal virtual-space-modal-overlay"
onClick={onClose}
>
<div
<div
className="bg-white rounded-xl lg:rounded-2xl max-w-5xl w-full h-[85vh] overflow-hidden virtual-space-modal-container flex flex-col"
onClick={(e) => e.stopPropagation()}
>
{/* Modal Header - Compact */}
<div
<div
className="flex items-center justify-between p-4 lg:p-6 border-b flex-shrink-0"
style={{ backgroundColor: 'rgba(4, 4, 91, 0.02)' }}
>
<div className="flex items-center gap-3 lg:gap-4">
<div
<div
className="w-10 h-10 lg:w-12 lg:h-12 rounded-lg flex items-center justify-center"
style={{ backgroundColor: 'var(--color-primary)' }}
>
@@ -371,7 +366,7 @@ function BookingModal({
</div>
<div>
<h2 className="text-body lg:text-subhead mb-1">
{facility.name} Virtual Tour & Booking
Kautilya Leadership Centre
</h2>
<p className="text-small text-muted">
Capacity: {facility.capacity} people
@@ -391,7 +386,7 @@ function BookingModal({
{/* Modal Content - Side by Side Layout No Scroll */}
<div className="grid grid-cols-1 lg:grid-cols-2 flex-1 min-h-0">
{/* Left Side - Virtual Tour */}
<div className="p-3 lg:p-6 border-r border-gray-100 flex flex-col min-h-0">
<div className="p-3 lg:p-6 border-r border-gray-100 flex flex-col min-h-0 overflow-y-auto">
<div className="flex flex-col h-full space-y-2 lg:space-y-3">
{/* Video Section - Compact */}
<div className="flex-shrink-0">
@@ -399,7 +394,7 @@ function BookingModal({
<Play className="w-4 h-4 text-primary" />
<h3 className="text-small lg:text-body font-semibold">Virtual Tour</h3>
</div>
{/* Virtual Tour Container - Developer-Ready for 360 Viewer or Video */}
<div className="aspect-video rounded-lg overflow-hidden bg-gray-100 shadow-md relative">
{/*
@@ -414,7 +409,7 @@ function BookingModal({
Current implementation: Fallback video iframe
Replace the entire div below with your 360 viewer component
*/}
<div
<div
id={`virtual-tour-container-${facility.id}`}
className="w-full h-full relative"
data-facility-id={facility.id}
@@ -422,35 +417,22 @@ function BookingModal({
data-tour-type="360-viewer" // Change to "video" for video fallback
>
{/* Fallback: Video iframe - Replace this entire section with 360 viewer */}
<iframe
{/* <iframe
src={facility.videoUrl}
title={`${facility.name} Virtual Tour`}
className="w-full h-full"
allowFullScreen
frameBorder="0"
/> */}
<ImageWithFallback
src={kautilya}
alt={`${facility.name} Virtual Tour`}
className="w-full h-full object-cover"
/>
{/* 360 Viewer Placeholder - Remove iframe above and uncomment/implement below */}
{/*
Example A-Frame 360 implementation:
<a-scene
embedded
style={{width: '100%', height: '100%'}}
vr-mode-ui="enabled: false"
>
<a-sky src={facility.panoramaUrl || facility.image} />
<a-camera wasd-controls-enabled="false" look-controls="enabled: true" />
</a-scene>
*/}
{/* Alternative: Custom 360 Photo Viewer Container */}
{/*
<div className="w-full h-full" id={`pannellum-${facility.id}`}>
// Pannellum or other 360 viewer initialization
</div>
*/}
</div>
{/* Interactive Controls Overlay (optional) */}
<div className="absolute bottom-2 left-2 right-2 flex justify-between items-center pointer-events-none">
<div className="bg-black/20 backdrop-blur-sm rounded px-2 py-1">
@@ -462,7 +444,7 @@ function BookingModal({
</div>
</div>
</div>
{/* Compact Info Section */}
<div className="flex-1 min-h-0 space-y-2 lg:space-y-3">
{/* About - Compact */}
@@ -472,14 +454,14 @@ function BookingModal({
{facility.description}
</p>
</div>
{/* Features - Compact */}
<div className="bg-gray-50 rounded-lg p-2 lg:p-3">
<h4 className="text-small font-semibold mb-2">Key Features</h4>
<div className="space-y-1">
{facility.features.slice(0, 3).map((feature, index) => (
<div key={index} className="flex items-center gap-2">
<div
<div
className="w-1.5 h-1.5 rounded-full flex-shrink-0"
style={{ backgroundColor: 'var(--color-primary)' }}
/>
@@ -504,7 +486,7 @@ function BookingModal({
{/* Action Button - Compact */}
<Button
variant="outline"
onClick={() => navigateTo('/services/learning-facility')}
onClick={() => navigateTo('/learning-facility')}
className="w-full h-8 lg:h-10 text-xs lg:text-small"
>
<Building className="w-3 h-3 lg:w-4 lg:h-4 mr-1" />
@@ -531,7 +513,7 @@ function BookingModal({
<h4 className="text-small font-medium text-primary">Company Information</h4>
<div className="w-10 h-0.5 bg-primary"></div>
</div>
<div className="space-y-2">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-2">
<div>
@@ -561,7 +543,7 @@ function BookingModal({
/>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-2">
<div>
<Label htmlFor="email" className="text-xs font-normal text-black mb-1 block">
@@ -591,13 +573,13 @@ function BookingModal({
/>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-2">
<div>
<Label htmlFor="role" className="text-xs font-normal text-black mb-1 block">
Your Role *
</Label>
<Select value={bookingForm.role} onValueChange={(value:string) => updateFormField('role', value)}>
<Select value={bookingForm.role} onValueChange={(value: string) => updateFormField('role', value)}>
<SelectTrigger className="h-8 text-sm border border-gray-300 rounded focus:border-primary focus:ring-1 focus:ring-primary/30 transition-all duration-200">
<SelectValue placeholder="Role" className="text-gray-400 opacity-50" />
</SelectTrigger>
@@ -615,7 +597,7 @@ function BookingModal({
<Label htmlFor="teamSize" className="text-xs font-normal text-black mb-1 block">
Expected Team Size *
</Label>
<Select value={bookingForm.teamSize} onValueChange={(value:string) => updateFormField('teamSize', value)}>
<Select value={bookingForm.teamSize} onValueChange={(value: string) => updateFormField('teamSize', value)}>
<SelectTrigger className="h-8 text-sm border border-gray-300 rounded focus:border-primary focus:ring-1 focus:ring-primary/30 transition-all duration-200">
<SelectValue placeholder="Size" className="text-gray-400 opacity-50" />
</SelectTrigger>
@@ -638,10 +620,10 @@ function BookingModal({
<h4 className="text-small font-medium text-primary">Select Your Date</h4>
<div className="w-10 h-0.5 bg-primary"></div>
</div>
<div className="space-y-3">
{/* Selected Facility Info */}
<div className="bg-blue-50 rounded p-2 border border-blue-100">
{/* <div className="bg-blue-50 rounded p-2 border border-blue-100">
<div className="flex items-center gap-2">
<div
className="w-6 h-6 rounded flex items-center justify-center"
@@ -654,7 +636,7 @@ function BookingModal({
<p className="text-xs text-gray-600">Capacity: {facility.capacity} people</p>
</div>
</div>
</div>
</div> */}
{/* Calendar Date Selection */}
<div className="space-y-2">
@@ -667,8 +649,8 @@ function BookingModal({
<div className="flex items-center gap-2">
<Calendar className="w-3 h-3 text-green-600" />
<span className="text-xs font-medium text-green-800">
Selected: {selectedDate.toLocaleDateString('en-US', {
month: 'short',
Selected: {selectedDate.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric'
})}
@@ -677,7 +659,7 @@ function BookingModal({
</div>
)}
</div>
{/* Additional Requirements */}
<div>
<Label htmlFor="additionalRequirements" className="text-xs font-normal text-black mb-1 block">
@@ -731,8 +713,9 @@ export function VirtualSpaceSection() {
const [selectedFacility, setSelectedFacility] = useState<typeof facilities[0] | null>(null);
const [isModalOpen, setIsModalOpen] = useState(false);
const handleBookNow = (facility: typeof facilities[0]) => {
setSelectedFacility(facility);
const handleBookNow = () => {
// Set the first facility as default for booking
setSelectedFacility(facilities[0]);
setIsModalOpen(true);
};
@@ -752,7 +735,6 @@ export function VirtualSpaceSection() {
key={facility.id}
facility={facility}
index={index}
onBookNow={handleBookNow}
/>
))}
</div>
@@ -761,21 +743,21 @@ export function VirtualSpaceSection() {
<div className="absolute top-0 left-0 right-0 z-20 py-16 max-md:py-12 section-margin-x ">
<div className="max-w-4xl mx-auto text-center exp-our-head-desktop-sec">
{/* Branded Tag */}
<motion.div
<motion.div
initial={{ opacity: 0, y: -20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
viewport={{ once: true }}
>
<BrandedTag
text="Virtual Learning Environment"
<BrandedTag
text="Virtual Learning Environment"
className="justify-center"
variant="white"
/>
</motion.div>
{/* Main Heading */}
<motion.h2
<motion.h2
className="text-5xl font-bold leading-tight mb-4 max-lg:text-4xl max-md:text-3xl text-white"
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
@@ -786,7 +768,7 @@ export function VirtualSpaceSection() {
</motion.h2>
{/* Subheading */}
<motion.p
<motion.p
className="text-lg leading-relaxed max-w-2xl mx-auto max-lg:text-base mb-6 text-white/90"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
@@ -796,7 +778,7 @@ export function VirtualSpaceSection() {
Take a virtual walk through our state-of-the-art facility designed to inspire leadership excellence and foster collaborative learning.
</motion.p>
{/* Main CTA Button - Explore Our Space */}
{/* Main CTA Button - Book Now */}
<motion.div
className="flex justify-center"
initial={{ opacity: 0, y: 20 }}
@@ -805,10 +787,10 @@ export function VirtualSpaceSection() {
viewport={{ once: true }}
>
<div className="hero-slide-button">
<PrimaryCTAButton
text="Explore Our Space"
onClick={() => navigateTo('/services/learning-facility')}
ariaLabel="Explore our virtual learning space and facilities"
<PrimaryCTAButton
text="Book Now"
onClick={handleBookNow}
ariaLabel="Book our virtual learning space and facilities"
/>
</div>
</motion.div>
@@ -820,7 +802,7 @@ export function VirtualSpaceSection() {
</div>
{/* Booking Modal */}
<BookingModal
<BookingModal
facility={selectedFacility}
isOpen={isModalOpen}
onClose={handleCloseModal}

View File

@@ -0,0 +1,355 @@
import React, { useEffect, useRef, useState } from 'react';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import panormaImage from '../assets/panoramas/cedar_bridge_sunset.jpg';
import panormaImage2 from '../assets/panoramas/cayley_interior.jpg';
interface VirtualTour360Props {
isOpen: boolean;
onClose: () => void;
onBookNow?: () => void;
}
// Your 360° panorama images
const tourScenes = [
{
id: 0,
title: "Cedar Bridge Sunset",
capacity: "Outdoor Learning Space",
description: "A serene outdoor setting with stunning sunset views, perfect for meditation and reflection sessions at Kautilya Leadership Centre.",
features: ["Natural environment", "Sunset views", "Peaceful atmosphere", "Meditation area", "Reflection space"],
panoramaUrl: panormaImage
},
{
id: 1,
title: "Training Amphitheater",
capacity: "80-120 attendees",
description: "State-of-the-art amphitheater with tiered seating, advanced acoustics, and immersive presentation technology.",
features: ["Tiered seating", "4K projection", "Live streaming", "Acoustic panels"],
panoramaUrl: panormaImage2
},
];
export function VirtualTour360({ isOpen, onClose, onBookNow }: VirtualTour360Props) {
const containerRef = useRef<HTMLDivElement>(null);
const [currentSceneIndex, setCurrentSceneIndex] = useState(0);
const [isLoading, setIsLoading] = useState(true);
// Three.js refs
const sceneRef = useRef<THREE.Scene | null>(null);
const cameraRef = useRef<THREE.PerspectiveCamera | null>(null);
const rendererRef = useRef<THREE.WebGLRenderer | null>(null);
const controlsRef = useRef<OrbitControls | null>(null);
const currentMeshRef = useRef<THREE.Mesh | null>(null);
const currentTextureRef = useRef<THREE.Texture | null>(null);
const animationRef = useRef<number | null>(null);
// Initialize Three.js scene
useEffect(() => {
if (!isOpen || !containerRef.current) return;
const container = containerRef.current;
// Setup scene
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x050510);
sceneRef.current = scene;
// Setup camera
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 0, 0.1);
cameraRef.current = camera;
// Setup renderer
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.domElement.style.touchAction = "none"; // Important for touch events
container.appendChild(renderer.domElement);
rendererRef.current = renderer;
// Setup controls - ONLY ROTATION (no built-in zoom)
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableZoom = false; // Disable built-in zoom, we'll handle custom zoom
controls.enablePan = false;
controls.enableRotate = true;
controls.rotateSpeed = 1.2;
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.target.set(0, 0, 0);
controlsRef.current = controls;
// 🔥 CUSTOM ZOOM - Mouse wheel (desktop)
const handleWheel = (event: WheelEvent) => {
if (!cameraRef.current) return;
event.preventDefault();
const zoomSpeed = 0.05;
cameraRef.current.fov += event.deltaY * zoomSpeed;
// Limit zoom range
cameraRef.current.fov = Math.max(40, Math.min(90, cameraRef.current.fov));
cameraRef.current.updateProjectionMatrix();
};
renderer.domElement.addEventListener("wheel", handleWheel, { passive: false });
// 🔥 CUSTOM ZOOM - Mobile pinch gesture
let lastDistance = 0;
const getDistance = (touches: TouchList) => {
if (touches.length < 2) return 0;
const dx = touches[0].clientX - touches[1].clientX;
const dy = touches[0].clientY - touches[1].clientY;
return Math.sqrt(dx * dx + dy * dy);
};
const handleTouchMove = (e: TouchEvent) => {
if (!cameraRef.current) return;
if (e.touches.length === 2) {
e.preventDefault();
const distance = getDistance(e.touches);
if (lastDistance > 0) {
const delta = distance - lastDistance;
cameraRef.current.fov -= delta * 0.05;
cameraRef.current.fov = Math.max(40, Math.min(90, cameraRef.current.fov));
cameraRef.current.updateProjectionMatrix();
}
lastDistance = distance;
}
};
const handleTouchEnd = () => {
lastDistance = 0;
};
renderer.domElement.addEventListener("touchmove", handleTouchMove, { passive: false });
renderer.domElement.addEventListener("touchend", handleTouchEnd);
// Animation loop
const animate = () => {
animationRef.current = requestAnimationFrame(animate);
if (controlsRef.current) controlsRef.current.update();
if (rendererRef.current && sceneRef.current && cameraRef.current) {
rendererRef.current.render(sceneRef.current, cameraRef.current);
}
};
animate();
// Load first panorama
loadPanorama(tourScenes[0].panoramaUrl);
// Handle window resize
const handleResize = () => {
if (cameraRef.current && rendererRef.current) {
cameraRef.current.aspect = window.innerWidth / window.innerHeight;
cameraRef.current.updateProjectionMatrix();
rendererRef.current.setSize(window.innerWidth, window.innerHeight);
}
};
window.addEventListener('resize', handleResize);
// Cleanup
return () => {
if (animationRef.current) cancelAnimationFrame(animationRef.current);
window.removeEventListener('resize', handleResize);
renderer.domElement.removeEventListener("wheel", handleWheel);
renderer.domElement.removeEventListener("touchmove", handleTouchMove);
renderer.domElement.removeEventListener("touchend", handleTouchEnd);
if (controlsRef.current) controlsRef.current.dispose();
if (rendererRef.current) rendererRef.current.dispose();
if (currentTextureRef.current) currentTextureRef.current.dispose();
if (container && renderer.domElement && container.contains(renderer.domElement)) {
container.removeChild(renderer.domElement);
}
};
}, [isOpen]);
// Load panorama image
const loadPanorama = (imageUrl: string) => {
if (!sceneRef.current) return;
setIsLoading(true);
const textureLoader = new THREE.TextureLoader();
textureLoader.load(
imageUrl,
(texture) => {
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.ClampToEdgeWrapping;
texture.colorSpace = THREE.SRGBColorSpace;
// Remove old mesh
if (currentMeshRef.current) sceneRef.current?.remove(currentMeshRef.current);
if (currentTextureRef.current) currentTextureRef.current.dispose();
// Create new sphere with texture
const geometry = new THREE.SphereGeometry(500, 64, 64);
const material = new THREE.MeshBasicMaterial({ map: texture, side: THREE.BackSide });
const sphere = new THREE.Mesh(geometry, material);
sceneRef.current?.add(sphere);
currentMeshRef.current = sphere;
currentTextureRef.current = texture;
// Reset camera FOV when loading new scene
if (cameraRef.current) {
cameraRef.current.fov = 75;
cameraRef.current.updateProjectionMatrix();
}
setIsLoading(false);
},
undefined,
(error) => {
console.error('Error loading panorama:', error);
setIsLoading(false);
}
);
};
// Navigate to next scene
const nextScene = () => {
const nextIndex = (currentSceneIndex + 1) % tourScenes.length;
setCurrentSceneIndex(nextIndex);
loadPanorama(tourScenes[nextIndex].panoramaUrl);
};
// Navigate to previous scene
const prevScene = () => {
const prevIndex = (currentSceneIndex - 1 + tourScenes.length) % tourScenes.length;
setCurrentSceneIndex(prevIndex);
loadPanorama(tourScenes[prevIndex].panoramaUrl);
};
// Jump to specific scene
const goToScene = (index: number) => {
setCurrentSceneIndex(index);
loadPanorama(tourScenes[index].panoramaUrl);
};
const currentScene = tourScenes[currentSceneIndex];
if (!isOpen) return null;
return (
<div className="fixed inset-0 z-[100] bg-black">
{/* Canvas container for Three.js */}
<div ref={containerRef} className="absolute inset-0" />
{/* UI Overlay */}
<div className="absolute inset-0 pointer-events-none">
{/* Top Bar */}
<div className="absolute top-0 left-0 right-0 bg-gradient-to-r from-[#04045B]/95 to-[#04045B]/80 backdrop-blur-md border-b border-yellow-400/30 pointer-events-auto z-30">
<div className="flex justify-between items-center px-4 md:px-6 py-3 md:py-4">
<div className="flex items-center gap-2 md:gap-3">
<div className="w-8 h-8 md:w-10 md:h-10 bg-yellow-400 rounded-xl flex items-center justify-center font-bold text-[#04045B] text-base md:text-xl">
KL
</div>
<div className="hidden sm:block">
<h1 className="text-white font-semibold text-sm md:text-lg">Kautilya Leadership Centre</h1>
<p className="text-white/70 text-xs">360° Immersive Experience</p>
</div>
</div>
<div className="flex items-center gap-2 md:gap-3">
<button
onClick={prevScene}
className="w-8 h-8 md:w-9 md:h-9 rounded-full bg-white/20 hover:bg-yellow-400 hover:text-[#04045B] transition-all flex items-center justify-center text-white text-sm md:text-base touch-manipulation"
aria-label="Previous scene"
>
</button>
<div className="bg-black/50 backdrop-blur-sm px-3 py-1.5 md:px-4 rounded-full text-xs md:text-sm font-medium">
{currentScene.title}
</div>
<button
onClick={nextScene}
className="w-8 h-8 md:w-9 md:h-9 rounded-full bg-white/20 hover:bg-yellow-400 hover:text-[#04045B] transition-all flex items-center justify-center text-white text-sm md:text-base touch-manipulation"
aria-label="Next scene"
>
</button>
<button
onClick={onClose}
className="w-8 h-8 md:w-9 md:h-9 rounded-full bg-white/20 hover:bg-red-500 transition-all flex items-center justify-center text-white text-sm md:text-base touch-manipulation"
aria-label="Close"
>
</button>
</div>
</div>
</div>
{/* Info Panel - Bottom Left */}
<div className="absolute bottom-4 md:bottom-6 left-4 md:left-6 max-w-[280px] md:max-w-[320px] bg-[#04045B]/90 backdrop-blur-md rounded-xl md:rounded-2xl p-4 md:p-5 border border-yellow-400/40 pointer-events-auto z-25">
<h2 className="text-yellow-400 text-lg md:text-2xl font-bold mb-1">{currentScene.title}</h2>
<div className="text-white/80 text-xs md:text-sm mb-2 md:mb-3 flex items-center gap-2">
🎯 Capacity: {currentScene.capacity}
</div>
<p className="text-white/90 text-xs md:text-sm mb-3 md:mb-4 leading-relaxed">
{currentScene.description}
</p>
<div className="flex flex-wrap gap-1.5 md:gap-2 mb-3 md:mb-4">
{currentScene.features.map((feature, idx) => (
<span key={idx} className="bg-yellow-400/20 text-yellow-400 text-[10px] md:text-xs px-2 py-1 md:px-3 rounded-full">
{feature}
</span>
))}
</div>
<button
onClick={() => {
onBookNow?.();
onClose();
}}
className="w-full bg-yellow-400 hover:bg-yellow-500 text-[#04045B] font-bold py-2 md:py-2.5 rounded-full transition-all flex items-center justify-center gap-2 text-sm md:text-base touch-manipulation"
>
📅 Book This Space
</button>
</div>
{/* Scene Thumbnails - Bottom Right */}
<div className="absolute bottom-4 md:bottom-6 right-4 md:right-6 flex flex-col gap-2 pointer-events-auto z-25">
{tourScenes.map((scene, idx) => (
<button
key={scene.id}
onClick={() => goToScene(idx)}
className={`bg-black/70 backdrop-blur-sm rounded-full px-3 py-1.5 md:px-4 md:py-2 flex items-center gap-2 md:gap-3 hover:bg-[#04045B]/90 transition-all touch-manipulation ${
idx === currentSceneIndex ? 'border-l-4 border-yellow-400' : 'border-l-4 border-yellow-400/40'
}`}
>
<div className="w-1.5 h-1.5 md:w-2 md:h-2 bg-yellow-400 rounded-full" />
<span className="text-white text-[10px] md:text-xs font-medium truncate max-w-[120px] md:max-w-[150px]">
{scene.title}
</span>
</button>
))}
</div>
{/* Loading Overlay */}
{isLoading && (
<div className="absolute inset-0 bg-[#04045B] flex items-center justify-center z-50">
<div className="text-center">
<div className="w-12 h-12 border-4 border-yellow-400/30 border-t-yellow-400 rounded-full animate-spin mx-auto mb-4"></div>
<p className="text-white text-sm">Loading 360° Experience...</p>
</div>
</div>
)}
{/* Instruction Hint */}
<div className="absolute bottom-4 md:bottom-6 left-1/2 transform -translate-x-1/2 bg-black/60 backdrop-blur-sm rounded-full px-3 py-1.5 md:px-4 md:py-2 text-white text-[10px] md:text-xs pointer-events-auto whitespace-nowrap">
🖱 Drag to look around 👆 Pinch/Scroll to zoom in/out
</div>
</div>
</div>
);
}

View File

@@ -41,19 +41,53 @@ import {
} from 'lucide-react';
import { motion, AnimatePresence } from 'motion/react';
import { navigateTo } from './Router';
import { useParams } from 'react-router-dom';
import { ImageWithFallback } from './figma/ImageWithFallback';
import { BrandedTag } from './about/BrandedTag';
import { PrimaryCTAButton } from './PrimaryCTAButton';
import { toast } from 'sonner@2.0.3';
import { getWebinarBySlug, sharedWebinarsData, type WebinarData } from '../data/webinarsData';
import { toast } from 'sonner';
import { useGetWebinarByIdQuery } from '../redux/services/webinarApi';
interface WebinarDetailProps {
params: { slug: string };
params?: { webinar_id: string };
}
export default function WebinarDetail({ params }: WebinarDetailProps) {
// Get webinar data from shared data source
const [webinar, setWebinar] = useState<WebinarData | null>(null);
// Helper function to format date
const formatDate = (dateTimeString: string) => {
const date = new Date(dateTimeString);
return {
date: date.toLocaleDateString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
}),
time: date.toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit',
hour12: true
}),
full: date
};
};
// Helper to get webinar status based on date and API status
const getWebinarStatus = (sessionDatetime: string, webinarStatus: string): 'upcoming' | 'live' | 'recorded' => {
const now = new Date();
const sessionDate = new Date(sessionDatetime);
if (webinarStatus === 'cancelled') return 'recorded';
if (webinarStatus === 'live') return 'live';
if (sessionDate > now) return 'upcoming';
return 'recorded';
};
export default function WebinarDetail({ params }: WebinarDetailProps = {}) {
const routeParams = useParams<{ webinar_id: string }>();
const webinarId = routeParams.webinar_id || params?.webinar_id;
const { data: webinarData, isLoading, error } = useGetWebinarByIdQuery(webinarId as string, {
skip: !webinarId
});
const [timeLeft, setTimeLeft] = useState({ days: 0, hours: 0, minutes: 0, seconds: 0 });
const [isRegistered, setIsRegistered] = useState(false);
const [showRegistrationForm, setShowRegistrationForm] = useState(false);
@@ -75,22 +109,76 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
const videoRef = useRef<HTMLVideoElement>(null);
const shareMenuRef = useRef<HTMLDivElement>(null);
// Load webinar data on component mount
useEffect(() => {
const webinarData = getWebinarBySlug(params.slug);
if (webinarData) {
setWebinar(webinarData);
} else {
// Fallback: redirect to webinars page if webinar not found
navigateTo('/webinars');
}
}, [params.slug]);
// Transform API data to match your component's expected format
const webinar = webinarData?.data ? {
id: webinarData.data.id,
title: webinarData.data.session_title,
description: webinarData.data.description || '',
thumbnail: webinarData.data.media?.file_name
? `/api/media/${webinarData.data.media.id}`
: '/images/default-webinar.jpg',
theme: webinarData.data.webinar_category_id || 'Webinar',
date: formatDate(webinarData.data.session_datetime).date,
time: formatDate(webinarData.data.session_datetime).time,
timezone: 'IST', // You might need to fetch timezone name from timezone_xid
duration: `${webinarData.data.duration_minutes} minutes`,
status: getWebinarStatus(webinarData.data.session_datetime, webinarData.data.webinar_status),
attendees: `${webinarData.data.max_attendee}`, // Max capacity or registered count
maxAttendees: webinarData.data.max_attendee,
registrationOpen: webinarData.data.require_registration && webinarData.data.webinar_status === 'scheduled',
recordingReady: webinarData.data.webinar_status === 'ended',
zoomUrl: '#', // Not provided in API, might need additional endpoint
recordingUrl: '#', // Not provided in API
price: 'Free', // Not in API, adjust as needed
format: 'Virtual Event',
host: {
name: webinarData.data.speakers?.[0]?.name || 'Host Name',
title: webinarData.data.speakers?.[0]?.designation || 'Host',
company: webinarData.data.speakers?.[0]?.company || '',
bio: webinarData.data.speakers?.[0]?.bio || '',
avatar: webinarData.data.speakers?.[0]?.image_url || '/images/default-avatar.jpg',
linkedin: '#'
},
panelists: webinarData.data.speakers?.filter(s => !s.is_host).map(speaker => ({
id: speaker.id,
name: speaker.name,
title: speaker.designation,
company: speaker.company,
bio: speaker.bio,
avatar: speaker.image_url || '/images/default-avatar.jpg',
linkedin: '#'
})) || [],
abstract: webinarData.data.about_this_session?.description || webinarData.data.description || '',
keyTakeaways: webinarData.data.about_this_session?.points?.map(p => p.point) || [],
agenda: webinarData.data.agenda_items?.map(item => ({
time: formatDate(item.start_time).time,
title: item.title,
description: item.description
})) || [],
faqs: [] as Array<{ question: string; answer: string }>, // Not in API response
tags: [] as string[], // Not in API response, derive from category or other fields
relatedProgrammes: webinarData.data.course_links?.map(courseLink => ({
id: courseLink.course.id,
slug: courseLink.course.course_name.toLowerCase().replace(/\s+/g, '-'),
title: courseLink.course.course_name,
description: courseLink.course.course_desc,
category: courseLink.course.course_category_name,
level: courseLink.course.retail_type === 'private' ? 'Private' : 'Public',
duration: courseLink.course.total_duration ? `${courseLink.course.total_duration} mins` : 'Self-paced',
participants: courseLink.course.total_reviews || 0,
rating: courseLink.course.avg_rating || 0,
price: courseLink.course.price ? `${courseLink.course.price}` : 'Free',
originalPrice: courseLink.course.best_value ? `${courseLink.course.best_value}` : `${courseLink.course.price}`,
image: courseLink.course.thumbnail_img || '/images/default-course.jpg',
bestValue: courseLink.course.best_value
})) || []
} : null;
// Countdown timer
useEffect(() => {
if (!webinar || webinar.status !== 'upcoming') return;
if (!webinar || webinar.status !== 'upcoming' || !webinarData?.data.session_datetime) return;
const targetDate = new Date(`${webinar.date}T${webinar.time}:00`);
const targetDate = new Date(webinarData.data.session_datetime);
const updateCountdown = () => {
const now = new Date().getTime();
@@ -110,7 +198,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
const interval = setInterval(updateCountdown, 1000);
return () => clearInterval(interval);
}, [webinar?.date, webinar?.time, webinar?.status]);
}, [webinar, webinarData?.data.session_datetime]);
// Close share menu when clicking outside
useEffect(() => {
@@ -129,7 +217,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
e.preventDefault();
setIsSubmittingRegistration(true);
// Simulate API call
// TODO: Integrate with your registration API endpoint
await new Promise(resolve => setTimeout(resolve, 2000));
setIsSubmittingRegistration(false);
@@ -163,7 +251,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
if (!webinar) return { text: 'Loading...', onClick: () => {}, disabled: true };
const now = new Date();
const webinarDate = new Date(`${webinar.date}T${webinar.time}:00`);
const webinarDate = webinarData?.data.session_datetime ? new Date(webinarData.data.session_datetime) : new Date();
const tenMinutesBefore = new Date(webinarDate.getTime() - 10 * 60 * 1000);
switch (webinar.status) {
@@ -177,7 +265,10 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
if (now >= tenMinutesBefore) {
return {
text: 'Launch in Zoom',
onClick: () => window.open(webinar.zoomUrl, '_blank'),
onClick: () => {
// You'll need to get the Zoom URL from a separate endpoint
toast.info('Zoom link would open here');
},
disabled: false
};
} else {
@@ -256,7 +347,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
};
// Show loading state while webinar data is loading
if (!webinar) {
if (isLoading) {
return (
<div className="min-h-screen flex items-center justify-center" style={{ backgroundColor: '#FFFFFF' }}>
<div className="text-center">
@@ -267,7 +358,26 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
);
}
// Show error state
if (error || !webinar) {
return (
<div className="min-h-screen flex items-center justify-center" style={{ backgroundColor: '#FFFFFF' }}>
<div className="text-center">
<AlertCircle className="w-12 h-12 text-red-500 mx-auto mb-4" />
<h2 className="text-h2 mb-2">Webinar Not Found</h2>
<p className="text-body text-gray-600 mb-6">The webinar you're looking for doesn't exist or has been removed.</p>
<PrimaryCTAButton
text="Browse Webinars"
onClick={() => navigateTo('/webinars')}
className="cta-text-black"
/>
</div>
</div>
);
}
const ctaProps = getCTAProps();
const sessionDateTime = webinarData?.data.session_datetime ? new Date(webinarData.data.session_datetime) : null;
return (
<div className="min-h-screen" style={{ backgroundColor: '#FFFFFF' }}>
@@ -298,7 +408,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
</p>
{/* Countdown Timer for Upcoming Webinars */}
{webinar.status === 'upcoming' && (
{webinar.status === 'upcoming' && sessionDateTime && (
<div className="mb-8">
<div className="grid grid-cols-4 gap-4 max-w-md mb-4">
{[
@@ -318,12 +428,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
))}
</div>
<div className="text-small-white opacity-80">
Starts {new Date(`${webinar.date}T${webinar.time}:00`).toLocaleDateString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
})} at {webinar.time} {webinar.timezone}
Starts {webinar.date} at {webinar.time}
</div>
</div>
)}
@@ -337,11 +442,11 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
<span className="text-small font-medium">LIVE NOW</span>
</div>
<div className="text-small-white">
{webinar.attendees} people watching
Max capacity: {webinar.maxAttendees} attendees
</div>
</div>
<div className="text-body-lg-white">
Session in progress Ends at {webinar.endTime} {webinar.timezone}
Session in progress
</div>
</div>
)}
@@ -356,7 +461,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
)}
</div>
<div className="text-body-lg-white">
Session held on {new Date(`${webinar.date}T${webinar.time}:00`).toLocaleDateString('en-US', {
Session held on {sessionDateTime?.toLocaleDateString('en-US', {
month: 'long',
day: 'numeric',
year: 'numeric'
@@ -425,7 +530,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
<div className="flex items-center gap-2 text-muted">
<Users className="w-4 h-4" />
<span className="text-small">
{webinar.attendees} {webinar.status === 'live' ? 'watching' : 'registered'}
{webinar.attendees} {webinar.status === 'live' ? 'watching' : 'capacity'}
</span>
</div>
</div>
@@ -445,45 +550,49 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
{webinar.abstract}
</p>
<div>
<h3 className="text-h3 mb-4">What You'll Learn</h3>
<ul className="space-y-3">
{webinar.keyTakeaways.map((takeaway, index) => (
<li key={index} className="flex items-start gap-3">
<CheckCircle className="w-5 h-5 text-[#04045B] mt-0.5 flex-shrink-0" />
<span className="text-body">{takeaway}</span>
</li>
))}
</ul>
</div>
{webinar.keyTakeaways.length > 0 && (
<div>
<h3 className="text-h3 mb-4">What You'll Learn</h3>
<ul className="space-y-3">
{webinar.keyTakeaways.map((takeaway, index) => (
<li key={index} className="flex items-start gap-3">
<CheckCircle className="w-5 h-5 text-[#04045B] mt-0.5 flex-shrink-0" />
<span className="text-body">{takeaway}</span>
</li>
))}
</ul>
</div>
)}
</div>
</section>
{/* Agenda Timeline */}
<section>
<h2 className="text-h2 mb-6">Session Agenda</h2>
<div className="space-y-6">
{webinar.agenda.map((item, index) => (
<div key={index} className="flex gap-6">
<div className="flex flex-col items-center">
<div className="w-4 h-4 bg-[#04045B] rounded-full" />
{index < webinar.agenda.length - 1 && (
<div className="w-0.5 h-16 bg-gray-200 mt-2" />
)}
</div>
<div className="flex-1 pb-8">
<div className="flex flex-col sm:flex-row sm:items-center gap-2 mb-2">
<Badge variant="outline" className="w-fit text-[#04045B] border-[#04045B]">
{item.time}
</Badge>
<h3 className="text-h4">{item.title}</h3>
{webinar.agenda.length > 0 && (
<section>
<h2 className="text-h2 mb-6">Session Agenda</h2>
<div className="space-y-6">
{webinar.agenda.map((item, index) => (
<div key={index} className="flex gap-6">
<div className="flex flex-col items-center">
<div className="w-4 h-4 bg-[#04045B] rounded-full" />
{index < webinar.agenda.length - 1 && (
<div className="w-0.5 h-16 bg-gray-200 mt-2" />
)}
</div>
<div className="flex-1 pb-8">
<div className="flex flex-col sm:flex-row sm:items-center gap-2 mb-2">
<Badge variant="outline" className="w-fit text-[#04045B] border-[#04045B]">
{item.time}
</Badge>
<h3 className="text-h4">{item.title}</h3>
</div>
<p className="text-body text-muted">{item.description}</p>
</div>
<p className="text-body text-muted">{item.description}</p>
</div>
</div>
))}
</div>
</section>
))}
</div>
</section>
)}
{/* Host & Panelists */}
<section>
@@ -503,14 +612,16 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
<h3 className="text-h4 mb-1">{webinar.host.name}</h3>
<p className="text-small text-[#04045B] font-medium">HOST</p>
</div>
<Button
variant="ghost"
size="sm"
onClick={() => window.open(webinar.host.linkedin, '_blank')}
className="text-[#04045B] hover:bg-[#04045B] hover:text-white"
>
<ExternalLink className="w-4 h-4" />
</Button>
{webinar.host.linkedin !== '#' && (
<Button
variant="ghost"
size="sm"
onClick={() => window.open(webinar.host.linkedin, '_blank')}
className="text-[#04045B] hover:bg-[#04045B] hover:text-white"
>
<ExternalLink className="w-4 h-4" />
</Button>
)}
</div>
<p className="text-small text-muted mb-1">{webinar.host.title}</p>
<p className="text-small text-muted">{webinar.host.company}</p>
@@ -535,14 +646,16 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
<h3 className="text-h4 mb-1">{panelist.name}</h3>
<p className="text-small text-[#F8C301] font-medium">SPEAKER</p>
</div>
<Button
variant="ghost"
size="sm"
onClick={() => window.open(panelist.linkedin, '_blank')}
className="text-[#04045B] hover:bg-[#04045B] hover:text-white"
>
<ExternalLink className="w-4 h-4" />
</Button>
{panelist.linkedin !== '#' && (
<Button
variant="ghost"
size="sm"
onClick={() => window.open(panelist.linkedin, '_blank')}
className="text-[#04045B] hover:bg-[#04045B] hover:text-white"
>
<ExternalLink className="w-4 h-4" />
</Button>
)}
</div>
<p className="text-small text-muted mb-1">{panelist.title}</p>
<p className="text-small text-muted">{panelist.company}</p>
@@ -604,7 +717,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
<div className="text-center mb-6">
<div className="text-h3 mb-2">{webinar.price}</div>
<div className="text-small text-muted">
{webinar.maxAttendees - parseInt(webinar.attendees.replace(/\D/g, '')) || 0} spots remaining
{webinar.maxAttendees} spots available
</div>
</div>
@@ -612,15 +725,8 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
<div className="flex items-center gap-3">
<Calendar className="w-5 h-5 text-[#04045B]" />
<div>
<div className="text-body font-medium">
{new Date(`${webinar.date}T${webinar.time}:00`).toLocaleDateString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
})}
</div>
<div className="text-small text-muted">{webinar.time} {webinar.timezone}</div>
<div className="text-body font-medium">{webinar.date}</div>
<div className="text-small text-muted">{webinar.time}</div>
</div>
</div>
@@ -636,7 +742,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
<div className="flex items-center gap-3">
<Users className="w-5 h-5 text-[#04045B]" />
<div className="text-body">{webinar.attendees} registered</div>
<div className="text-body">Capacity: {webinar.attendees} attendees</div>
</div>
</div>
@@ -863,9 +969,9 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
{/* Add to Cart Button - Outline Blue */}
<Button
variant="outline"
onClick={(e) => {
onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
// Add to cart functionality would go here
toast.info('Add to cart functionality would go here');
}}
className="flex-1 flex items-center justify-center gap-2 h-11 rounded-lg transition-all duration-200 font-medium"
style={{
@@ -877,11 +983,11 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
fontWeight: '500',
borderWidth: '2px'
}}
onMouseEnter={(e) => {
onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {
e.currentTarget.style.backgroundColor = '#04045B';
e.currentTarget.style.color = 'white';
}}
onMouseLeave={(e) => {
onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {
e.currentTarget.style.backgroundColor = 'transparent';
e.currentTarget.style.color = '#04045B';
}}
@@ -901,10 +1007,10 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
fontWeight: '500',
border: 'none'
}}
onMouseEnter={(e) => {
onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {
e.currentTarget.style.backgroundColor = '#030359';
}}
onMouseLeave={(e) => {
onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {
e.currentTarget.style.backgroundColor = '#04045B';
}}
>

View File

@@ -1,145 +1,100 @@
import React, { useState, useRef, useEffect } from 'react';
import { Button } from './ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './ui/card';
import { Badge } from './ui/badge';
import { Input } from './ui/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
import { Slider } from './ui/slider';
import { ImageWithFallback } from './figma/ImageWithFallback';
import { PrimaryCTAButton } from './PrimaryCTAButton';
import { navigateTo } from './Router';
import { sharedWebinarsData, type WebinarData } from '../data/webinarsData';
import { WebcastCTABanner } from './WebcastCTABanner';
import {
Search,
Calendar,
Clock,
Users,
Play,
ArrowRight,
ChevronLeft,
ChevronRight,
Clock,
Eye,
Filter,
Grid,
List,
SortAsc,
Eye,
Play,
Search,
Star,
ChevronLeft,
ChevronRight,
Users,
X
} from 'lucide-react';
import { useEffect, useRef, useState } from 'react';
import { useWebinarListQuery, type WebinarItem } from '../redux/services/webinarApi';
import { ImageWithFallback } from './figma/ImageWithFallback';
import { FullScreenLoader } from './FullScreenLoader';
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 { Slider } from './ui/slider';
import { WebcastCTABanner } from './WebcastCTABanner';
// Status options with proper mapping to API values
const statusOptions = [
{ value: 'scheduled', label: '📅 Scheduled', color: 'bg-blue-100 text-blue-800 border-blue-200' },
{ value: 'live', label: '🔴 Live', color: 'bg-red-100 text-red-800 border-red-200' },
{ value: 'ended', label: '✅ Ended', color: 'bg-gray-100 text-gray-800 border-gray-200' },
{ value: 'cancelled', label: '❌ Cancelled', color: 'bg-red-50 text-red-600 border-red-200' }
];
const sortOptions = [
{ value: 'most_popular', label: 'Most Popular' },
{ value: 'newest', label: 'Newest First' },
{ value: 'oldest', label: 'Oldest First' },
{ value: 'title', label: 'Title A-Z' },
{ value: 'duration', label: 'Duration' }
];
// Static tags for all webinars
const staticTags = ['Leadership', 'Executive Development', 'Strategy', 'Innovation', 'Change Management', 'Business Growth', 'Team Building', 'Digital Transformation'];
export function Webinars() {
const [searchTerm, setSearchTerm] = useState('');
const [selectedCategory, setSelectedCategory] = useState('All Categories');
const [selectedFormat, setSelectedFormat] = useState('All Formats');
const [selectedLevel, setSelectedLevel] = useState('All Levels');
// Updated state for multi-select status pills
const [selectedStatuses, setSelectedStatuses] = useState<string[]>([]);
// Updated state for duration slider (min, max in minutes)
const [durationRange, setDurationRange] = useState([0, 120]);
// Attendee range slider state
const [attendeeRange, setAttendeeRange] = useState([0, 5000]);
const [sortBy, setSortBy] = useState('Most Popular');
const [sortBy, setSortBy] = useState('most_popular');
const [viewType, setViewType] = useState<'grid' | 'list'>('grid');
const [currentPage, setCurrentPage] = useState(1);
const webinarsPerPage = 6;
const containerRef = useRef<HTMLDivElement>(null);
// Use shared webinars data instead of local mock data
const webinars = sharedWebinarsData;
// Get unique values for filters from shared data
const categories = ['All Categories', ...Array.from(new Set(webinars.map(webinar => webinar.category)))];
const formats = ['All Formats', ...Array.from(new Set(webinars.map(webinar => webinar.format)))];
const levels = ['All Levels', ...Array.from(new Set(webinars.map(webinar => webinar.level)))];
// Status options for pills - updated to match shared data structure
const statusOptions = [
{ value: 'upcoming', label: '📅 Upcoming', color: 'bg-blue-100 text-blue-800 border-blue-200' },
{ value: 'live', label: '🔴 Live', color: 'bg-red-100 text-red-800 border-red-200' },
{ value: 'recorded', label: '▶️ Recorded', color: 'bg-green-100 text-green-800 border-green-200' },
{ value: 'featured', label: '⭐ Featured', color: 'bg-yellow-100 text-yellow-800 border-yellow-200' }
];
const sortOptions = [
{ value: 'Most Popular', label: 'Most Popular' },
{ value: 'newest', label: 'Newest First' },
{ value: 'oldest', label: 'Oldest First' },
{ value: 'title', label: 'Title A-Z' },
{ value: 'duration', label: 'Duration' }
];
// Helper function to convert attendees string to number
const parseAttendees = (attendeesStr: string): number => {
const numStr = attendeesStr.replace(/[^\d]/g, '');
return parseInt(numStr) || 0;
};
// Helper function to convert duration string to minutes
const parseDuration = (durationStr: string): number => {
const numStr = durationStr.replace(/[^\d]/g, '');
return parseInt(numStr) || 0;
};
// Filter and sort webinars
const filteredWebinars = webinars.filter(webinar => {
const matchesSearch = webinar.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
webinar.description.toLowerCase().includes(searchTerm.toLowerCase()) ||
webinar.presenter.toLowerCase().includes(searchTerm.toLowerCase()) ||
webinar.tags.some(tag => tag.toLowerCase().includes(searchTerm.toLowerCase()));
const matchesCategory = selectedCategory === 'All Categories' || webinar.category === selectedCategory;
const matchesFormat = selectedFormat === 'All Formats' || webinar.format === selectedFormat;
const matchesLevel = selectedLevel === 'All Levels' || webinar.level === selectedLevel;
// Updated status filter for multi-select with shared data structure
const matchesStatus = selectedStatuses.length === 0 ||
selectedStatuses.some(status => {
if (status === 'featured') return webinar.featured;
return webinar.status === status;
});
// Duration filter using range
const durationMinutes = parseDuration(webinar.duration);
const matchesDuration = durationMinutes >= durationRange[0] && durationMinutes <= durationRange[1];
// Attendee filter using range
const attendeeCount = parseAttendees(webinar.attendees);
const matchesAttendees = attendeeCount >= attendeeRange[0] && attendeeCount <= attendeeRange[1];
return matchesSearch && matchesCategory && matchesFormat && matchesLevel && matchesStatus && matchesDuration && matchesAttendees;
}).sort((a, b) => {
switch (sortBy) {
case 'newest':
return new Date(b.date).getTime() - new Date(a.date).getTime();
case 'oldest':
return new Date(a.date).getTime() - new Date(b.date).getTime();
case 'title':
return a.title.localeCompare(b.title);
case 'duration':
return parseDuration(b.duration) - parseDuration(a.duration);
default:
return 0;
}
// Fetch webinars from API
const {
data: webinarResponse,
isLoading,
isError,
} = useWebinarListQuery({
limit: 100,
offset: 0,
search: searchTerm || undefined,
status: selectedStatuses.length > 0 ? selectedStatuses : undefined,
minDuration: durationRange[0] > 0 ? durationRange[0] : undefined,
maxDuration: durationRange[1] < 120 ? durationRange[1] : undefined,
minAttendees: attendeeRange[0] > 0 ? attendeeRange[0] : undefined,
maxAttendees: attendeeRange[1] < 5000 ? attendeeRange[1] : undefined,
sortBy: sortBy as any,
});
// Statistics
const stats = {
total: webinars.length,
upcoming: webinars.filter(w => w.status === 'upcoming').length,
live: webinars.filter(w => w.status === 'live').length,
recorded: webinars.filter(w => w.status === 'recorded').length,
featured: webinars.filter(w => w.featured).length,
categories: new Set(webinars.map(w => w.category)).size
const webinars = webinarResponse?.data?.items || [];
// Get random tags for each webinar (3 random tags from staticTags)
const getRandomTags = (seed: string) => {
// Use the webinar ID as seed to get consistent tags for each webinar
const hash = seed.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0);
const shuffled = [...staticTags];
for (let i = shuffled.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
}
return shuffled.slice(0, 3);
};
// Paginate results
const totalPages = Math.ceil(filteredWebinars.length / webinarsPerPage);
const currentWebinars = filteredWebinars.slice((currentPage - 1) * webinarsPerPage, currentPage * webinarsPerPage);
// Get unique categories from API data
const categories = [
'All Categories',
...Array.from(new Set(webinars.map(webinar => webinar.session_title?.split(' ')[0] || 'General')))
];
// Helper functions
const formatDate = (dateString: string) => {
return new Date(dateString).toLocaleDateString('en-US', {
year: 'numeric',
@@ -148,118 +103,161 @@ export function Webinars() {
});
};
const formatDuration = (minutes: number) => {
if (minutes >= 60) {
const hours = Math.floor(minutes / 60);
const remainingMinutes = minutes % 60;
return remainingMinutes > 0 ? `${hours}h ${remainingMinutes}m` : `${hours}h`;
}
return `${minutes}min`;
};
const getStatusBadge = (status: string) => {
switch (status) {
case 'live':
return <Badge className="bg-red-600 text-white animate-pulse">LIVE NOW</Badge>;
case 'scheduled':
return <Badge className="bg-blue-600 text-white">SCHEDULED</Badge>;
case 'ended':
return <Badge className="bg-gray-600 text-white">ENDED</Badge>;
case 'cancelled':
return <Badge className="bg-red-400 text-white">CANCELLED</Badge>;
default:
return null;
}
};
const getActionText = (status: string) => {
switch (status) {
case 'live':
return 'Join Now';
case 'scheduled':
return 'Register';
case 'ended':
return 'Watch Recording';
case 'cancelled':
return 'Cancelled';
default:
return 'Learn More';
}
};
// Statistics
const stats = {
total: webinars.length,
scheduled: webinars.filter(w => w.webinar_status === 'scheduled').length,
live: webinars.filter(w => w.webinar_status === 'live').length,
ended: webinars.filter(w => w.webinar_status === 'ended').length,
cancelled: webinars.filter(w => w.webinar_status === 'cancelled').length,
categories: categories.length - 1
};
// Filter webinars
const filteredWebinars = webinars.filter(webinar => {
const matchesCategory = selectedCategory === 'All Categories' ||
(webinar.session_title && webinar.session_title.toLowerCase().includes(selectedCategory.toLowerCase()));
return matchesCategory;
});
// Paginate results
const totalPages = Math.ceil(filteredWebinars.length / webinarsPerPage);
const currentWebinars = filteredWebinars.slice((currentPage - 1) * webinarsPerPage, currentPage * webinarsPerPage);
const clearAllFilters = () => {
setSearchTerm('');
setSelectedCategory('All Categories');
setSelectedFormat('All Formats');
setSelectedLevel('All Levels');
setSelectedStatuses([]);
setDurationRange([0, 120]);
setAttendeeRange([0, 5000]);
setSortBy('Most Popular');
setSortBy('most_popular');
};
const hasActiveFilters = searchTerm ||
selectedCategory !== 'All Categories' ||
selectedFormat !== 'All Formats' ||
selectedLevel !== 'All Levels' ||
selectedStatuses.length > 0 ||
durationRange[0] !== 0 || durationRange[1] !== 120 ||
attendeeRange[0] !== 0 || attendeeRange[1] !== 5000;
const hasActiveFilters = searchTerm ||
selectedCategory !== 'All Categories' ||
selectedStatuses.length > 0 ||
durationRange[0] !== 0 || durationRange[1] !== 120 ||
attendeeRange[0] !== 0 || attendeeRange[1] !== 5000;
// Status pill toggle function
const toggleStatus = (status: string) => {
setSelectedStatuses(prev =>
prev.includes(status)
setSelectedStatuses(prev =>
prev.includes(status)
? prev.filter(s => s !== status)
: [...prev, status]
);
};
// Updated WebinarCard component that navigates to consistent route
const WebinarCard = ({ webinar }: { webinar: WebinarData }) => {
useEffect(() => {
setCurrentPage(1);
}, [searchTerm, selectedCategory, selectedStatuses, durationRange, attendeeRange, sortBy]);
const WebinarCard = ({ webinar }: { webinar: WebinarItem }) => {
const handleCardClick = () => {
// Navigate to consistent webinar detail route
navigateTo(`/webinar/${webinar.slug}`);
};
const getStatusBadge = () => {
switch (webinar.status) {
case 'live':
return <Badge className="bg-red-600 text-white animate-pulse">LIVE</Badge>;
case 'upcoming':
return <Badge className="bg-blue-600 text-white">UPCOMING</Badge>;
case 'recorded':
return <Badge className="bg-green-600 text-white">RECORDED</Badge>;
default:
return null;
if (webinar.webinar_status !== 'cancelled') {
navigateTo(`/webinars/${webinar.id}`);
}
};
const getActionText = () => {
switch (webinar.status) {
case 'live':
return 'Join Now';
case 'upcoming':
return 'Register';
case 'recorded':
return 'Watch Recording';
default:
return 'Learn More';
}
};
const isCancelled = webinar.webinar_status === 'cancelled';
const webinarTags = getRandomTags(webinar.id);
if (viewType === 'list') {
return (
<Card
className="mb-4 cursor-pointer transition-all duration-300 hover:shadow-lg hover:transform hover:-translate-y-1"
<Card
className={`mb-4 cursor-pointer transition-all duration-300 hover:shadow-lg hover:transform hover:-translate-y-1 ${isCancelled ? 'opacity-75' : ''}`}
onClick={handleCardClick}
style={isCancelled ? { cursor: 'not-allowed' } : {}}
>
<CardContent className="p-6">
<div className="flex gap-6">
{/* Thumbnail */}
<div className="flex-shrink-0 w-32 h-24 rounded-lg overflow-hidden">
<ImageWithFallback
src={webinar.thumbnail}
alt={webinar.title}
className="w-full h-full object-cover"
/>
<div className="flex-shrink-0 w-32 h-24 rounded-lg overflow-hidden bg-gradient-to-br from-gray-100 to-gray-200 flex items-center justify-center">
<Play className="w-8 h-8 text-gray-400" />
</div>
{/* Content */}
<div className="flex-1">
<div className="flex justify-between items-start mb-2">
<div className="flex items-center gap-2">
{getStatusBadge()}
{webinar.featured && (
<Badge className="bg-yellow-100 text-yellow-800">Featured</Badge>
)}
{getStatusBadge(webinar.webinar_status)}
</div>
<span className="text-small text-gray-500">{formatDate(webinar.date)}</span>
<span className="text-small text-gray-500">{formatDate(webinar.session_datetime)}</span>
</div>
<h3 className="text-h4 mb-2 line-clamp-2">{webinar.title}</h3>
<p className="text-body text-gray-600 mb-3 line-clamp-2">{webinar.description}</p>
<h3 className="text-h4 mb-2 line-clamp-2">{webinar.session_title}</h3>
<p className="text-body text-gray-600 mb-3 line-clamp-2">
{webinar.description || 'No description available'}
</p>
{/* Tags */}
<div className="flex flex-wrap gap-2 mb-3">
{webinarTags.map((tag, idx) => (
<Badge key={idx} variant="outline" className="text-xs bg-gray-50">
{tag}
</Badge>
))}
</div>
<div className="flex items-center justify-between">
<div className="flex items-center gap-4 text-small text-gray-500">
<span className="flex items-center gap-1">
<Users className="w-4 h-4" />
{webinar.presenter}
{webinar.owner || 'Kautilya Leadership'}
</span>
<span className="flex items-center gap-1">
<Clock className="w-4 h-4" />
{webinar.duration}
{formatDuration(webinar.duration_minutes)}
</span>
<span className="flex items-center gap-1">
<Eye className="w-4 h-4" />
{webinar.attendees}
Max {webinar.max_attendee.toLocaleString()}
</span>
</div>
<div className="flex items-center gap-2 text-primary font-medium">
<span className="text-small">{getActionText()}</span>
<ArrowRight className="w-4 h-4" />
</div>
{!isCancelled && (
<div className="flex items-center gap-2 font-medium" style={{ color: '#04045b' }}>
<span className="text-small">{getActionText(webinar.webinar_status)}</span>
<ArrowRight className="w-4 h-4" />
</div>
)}
</div>
</div>
</div>
@@ -268,100 +266,122 @@ export function Webinars() {
);
}
// Grid View
return (
<Card
className="cursor-pointer transition-all duration-300 hover:shadow-lg hover:transform hover:-translate-y-2 group overflow-hidden"
<Card
className={`cursor-pointer transition-all duration-300 hover:shadow-lg hover:transform hover:-translate-y-2 group overflow-hidden ${isCancelled ? 'opacity-75' : ''}`}
onClick={handleCardClick}
style={isCancelled ? { cursor: 'not-allowed' } : {}}
>
{/* Image */}
<div className="aspect-video relative overflow-hidden">
<ImageWithFallback
src={webinar.thumbnail}
alt={webinar.title}
className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
/>
<div className="aspect-video relative overflow-hidden bg-gradient-to-br from-gray-100 to-gray-200">
<div className="w-full h-full flex items-center justify-center">
<Play className="w-12 h-12 text-gray-400" />
</div>
{/* Status Badge */}
<div className="absolute top-4 left-4">
{getStatusBadge()}
{getStatusBadge(webinar.webinar_status)}
</div>
{/* Featured Badge */}
{webinar.featured && (
<div className="absolute top-4 right-4">
<Badge className="bg-yellow-100 text-yellow-800 border border-yellow-200">
<Star className="w-3 h-3 mr-1" />
Featured
</Badge>
{/* Play Icon Overlay */}
{!isCancelled && (
<div className="absolute inset-0 bg-black bg-opacity-40 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex items-center justify-center">
<div className="bg-white bg-opacity-90 rounded-full p-3">
<Play className="w-6 h-6 text-gray-800" />
</div>
</div>
)}
{/* Play Icon Overlay */}
<div className="absolute inset-0 bg-black bg-opacity-40 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex items-center justify-center">
<div className="bg-white bg-opacity-90 rounded-full p-3">
<Play className="w-6 h-6 text-gray-800" />
</div>
</div>
</div>
{/* Content */}
<CardContent className="p-6">
<div className="flex items-center justify-between mb-2">
<Badge variant="secondary" className="text-xs">
{webinar.category}
{webinar.recurring_webinar ? 'Recurring' : 'One-time'}
</Badge>
<span className="text-small text-gray-500">{formatDate(webinar.date)}</span>
<span className="text-small text-gray-500">{formatDate(webinar.session_datetime)}</span>
</div>
<h3 className="text-h4 mb-3 line-clamp-2 group-hover:text-primary transition-colors">
{webinar.title}
{webinar.session_title}
</h3>
<p className="text-body text-gray-600 mb-4 line-clamp-2">
{webinar.description}
{webinar.description || 'No description available'}
</p>
{/* Tags */}
<div className="flex flex-wrap gap-2 mb-4">
{webinarTags.slice(0, 2).map((tag, idx) => (
<Badge key={idx} variant="outline" className="text-xs bg-gray-50">
{tag}
</Badge>
))}
</div>
<div className="space-y-3">
<div className="flex items-center gap-2 text-small text-gray-500">
<Users className="w-4 h-4" />
<span>{webinar.presenter}</span>
<span>{webinar.owner || 'Kautilya Leadership'}</span>
</div>
<div className="flex items-center justify-between text-small text-gray-500">
<div className="flex items-center gap-1">
<Clock className="w-4 h-4" />
<span>{webinar.duration}</span>
<span>{formatDuration(webinar.duration_minutes)}</span>
</div>
<div className="flex items-center gap-1">
<Eye className="w-4 h-4" />
<span>{webinar.attendees}</span>
<span>Max {webinar.max_attendee.toLocaleString()}</span>
</div>
</div>
</div>
<div className="flex items-center justify-between mt-4 pt-4 border-t">
<div className="flex items-center gap-1">
{webinar.tags.slice(0, 2).map((tag, index) => (
<Badge key={index} variant="outline" className="text-xs">
{tag}
</Badge>
))}
{!isCancelled && (
<div className="flex items-center justify-between mt-4 pt-4 border-t">
<div className="flex items-center gap-1 text-xs text-gray-500">
{webinar.require_registration && (
<>
<Star className="w-3 h-3 text-yellow-500" />
<span>Registration Required</span>
</>
)}
</div>
<div className="flex items-center gap-2 font-medium group-hover:translate-x-1 transition-transform" style={{ color: '#04045b' }}>
<span className="text-small">{getActionText(webinar.webinar_status)}</span>
<ArrowRight className="w-4 h-4" />
</div>
</div>
<div className="flex items-center gap-2 text-primary font-medium group-hover:translate-x-1 transition-transform">
<span className="text-small">{getActionText()}</span>
<ArrowRight className="w-4 h-4" />
</div>
</div>
)}
</CardContent>
</Card>
);
};
if (isLoading) {
return (
<div className="min-h-screen flex items-center justify-center bg-white">
<FullScreenLoader text="Loading webinars..." />
</div>
);
}
if (isError) {
return (
<div className="min-h-screen flex items-center justify-center bg-white">
<div className="text-center">
<p className="text-red-600 mb-4">Error loading webinars. Please try again later.</p>
<Button onClick={() => window.location.reload()}>Retry</Button>
</div>
</div>
);
}
return (
<div style={{ backgroundColor: '#FFFFFF' }}>
{/* Hero Section with Background Image */}
{/* Hero Section */}
<section className="relative h-[400px] overflow-hidden">
{/* Background Image */}
<div className="absolute inset-0">
<ImageWithFallback
src="https://images.unsplash.com/photo-1652265540589-46f91535337b?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxidXNpbmVzcyUyMHByZXNlbnRhdGlvbiUyMHdlYmluYXIlMjBjb25mZXJlbmNlfGVufDF8fHx8MTc1NTg1NDI3MHww&ixlib=rb-4.1.0&q=80&w=1080"
@@ -371,23 +391,20 @@ export function Webinars() {
<div className="absolute inset-0 bg-black/60" />
</div>
{/* Hero Content */}
<div className="relative h-full flex flex-col justify-center section-margin-x">
<div className="text-center">
<h1 className="text-h1-white mb-6">
Leadership Webcasts &<br />
Expert Insights
</h1>
<p className="text-body-lg-white max-w-3xl mx-auto">
Explore our comprehensive collection of expert insights, research, and practical guidance
Explore our comprehensive collection of expert insights, research, and practical guidance
to elevate your leadership journey and drive organizational excellence.
</p>
</div>
</div>
{/* Statistics Strip at Bottom */}
<div className="absolute bottom-0 left-0 right-0">
<div className="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">
@@ -413,12 +430,11 @@ export function Webinars() {
<section className="py-8" style={{ backgroundColor: '#FFFFFF' }}>
<div className="section-margin-x">
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 mb-6">
{/* Search Bar */}
<div className="relative max-w-md flex-1">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
<Input
type="text"
placeholder="Search webcasts..."
placeholder="Search webinars..."
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"
@@ -430,18 +446,16 @@ export function Webinars() {
/>
</div>
{/* View Toggle and Sort */}
<div className="flex items-center gap-4">
<div className="flex items-center border border-gray-300 rounded-lg overflow-hidden">
<button
onClick={() => setViewType('grid')}
className={`p-2 transition-colors ${
viewType === 'grid'
? 'text-white'
className={`p-2 transition-colors ${viewType === 'grid'
? 'text-white'
: 'bg-white text-gray-600 hover:bg-gray-50'
}`}
}`}
style={{
backgroundColor: viewType === 'grid' ? 'var(--color-primary)' : undefined
backgroundColor: viewType === 'grid' ? '#04045b' : undefined
}}
aria-label="Grid view"
>
@@ -449,13 +463,12 @@ export function Webinars() {
</button>
<button
onClick={() => setViewType('list')}
className={`p-2 transition-colors ${
viewType === 'list'
? 'text-white'
className={`p-2 transition-colors ${viewType === 'list'
? 'text-white'
: 'bg-white text-gray-600 hover:bg-gray-50'
}`}
}`}
style={{
backgroundColor: viewType === 'list' ? 'var(--color-primary)' : undefined
backgroundColor: viewType === 'list' ? '#04045b' : undefined
}}
aria-label="List view"
>
@@ -480,31 +493,28 @@ export function Webinars() {
</div>
</section>
{/* Main Content Section with Sidebar */}
{/* Main Content Section */}
<section className="pb-16" style={{ backgroundColor: '#FFFFFF' }}>
<div className="section-margin-x">
<div className="grid grid-cols-12 gap-8">
{/* Left Sidebar - Sticky Filters */}
{/* Left Sidebar 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)' }} />
<Filter className="w-3.5 h-3.5" style={{ color: '#04045b' }} />
</div>
<h3 className="text-body font-semibold text-gray-800">
Filters
</h3>
<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"
className="text-xs px-2 py-1 rounded-md transition-colors"
>
<X className="w-3 h-3 mr-1" />
Clear
@@ -513,67 +523,9 @@ export function Webinars() {
</div>
</div>
{/* Filter Content */}
<div className="p-4">
<div className="space-y-6">
{/* Category Filter */}
<div className="filter-section">
<label className="block text-small mb-2 font-medium text-gray-700">
Category
</label>
<Select value={selectedCategory} onValueChange={setSelectedCategory}>
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors">
<SelectValue placeholder="All Categories" />
</SelectTrigger>
<SelectContent>
{categories.map((category) => (
<SelectItem key={category} value={category} className="text-small">
{category}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* Format Filter */}
<div className="filter-section">
<label className="block text-small mb-2 font-medium text-gray-700">
Format
</label>
<Select value={selectedFormat} onValueChange={setSelectedFormat}>
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors">
<SelectValue placeholder="All Formats" />
</SelectTrigger>
<SelectContent>
{formats.map((format) => (
<SelectItem key={format} value={format} className="text-small">
{format}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* Level Filter */}
<div className="filter-section">
<label className="block text-small mb-2 font-medium text-gray-700">
Level
</label>
<Select value={selectedLevel} onValueChange={setSelectedLevel}>
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors">
<SelectValue placeholder="All Levels" />
</SelectTrigger>
<SelectContent>
{levels.map((level) => (
<SelectItem key={level} value={level} className="text-small">
{level}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* Status Filter - Multi-select Pills */}
{/* Status Filter */}
<div className="filter-section">
<label className="block text-small mb-3 font-medium text-gray-700">
Status
@@ -585,8 +537,8 @@ export function Webinars() {
onClick={() => toggleStatus(status.value)}
className={`
px-3 py-1.5 rounded-full text-xs font-medium border transition-all duration-200
${selectedStatuses.includes(status.value)
? `${status.color} ring-2 ring-blue-200 shadow-sm`
${selectedStatuses.includes(status.value)
? `${status.color} ring-2 ring-blue-200 shadow-sm`
: 'bg-gray-50 text-gray-600 border-gray-200 hover:bg-gray-100 hover:border-gray-300'
}
`}
@@ -602,10 +554,10 @@ export function Webinars() {
)}
</div>
{/* Duration Filter - Slider */}
{/* Duration Filter */}
<div className="filter-section">
<label className="block text-small mb-3 font-medium text-gray-700">
Duration
Duration (minutes)
</label>
<div className="px-2">
<Slider
@@ -620,19 +572,13 @@ export function Webinars() {
<span>{durationRange[0]} min</span>
<span>{durationRange[1]} min</span>
</div>
<div className="mt-1 text-center text-xs text-gray-400">
{durationRange[0] === 0 && durationRange[1] === 120
? 'All durations'
: `${durationRange[0]}-${durationRange[1]} minutes`
}
</div>
</div>
</div>
{/* Attendee Count Filter - Slider */}
{/* Attendee Filter */}
<div className="filter-section">
<label className="block text-small mb-3 font-medium text-gray-700">
Attendees
Max Attendees
</label>
<div className="px-2">
<Slider
@@ -647,12 +593,6 @@ export function Webinars() {
<span>{attendeeRange[0].toLocaleString()}</span>
<span>{attendeeRange[1].toLocaleString()}+</span>
</div>
<div className="mt-1 text-center text-xs text-gray-400">
{attendeeRange[0] === 0 && attendeeRange[1] === 5000
? 'Any size'
: `${attendeeRange[0].toLocaleString()}-${attendeeRange[1].toLocaleString()}+`
}
</div>
</div>
</div>
</div>
@@ -663,24 +603,22 @@ export function Webinars() {
{/* Right Main Content */}
<div className="col-span-12 lg:col-span-9">
{/* Results Header */}
<div className="flex items-center justify-between mb-6">
<div className="text-body text-gray-600">
Showing {currentWebinars.length} of {filteredWebinars.length} webcasts
Showing {currentWebinars.length} of {filteredWebinars.length} webinars
</div>
<div className="text-small text-gray-500">
Page {currentPage} of {totalPages}
</div>
</div>
{/* Content Area */}
<div ref={containerRef}>
{currentWebinars.length === 0 ? (
<div className="text-center py-12">
<div className="text-gray-400 mb-4">
<Search className="w-12 h-12 mx-auto mb-4" />
</div>
<h3 className="text-h4 mb-2">No webcasts found</h3>
<h3 className="text-h4 mb-2">No webinars found</h3>
<p className="text-body text-gray-600 mb-4">
Try adjusting your filters or search terms
</p>
@@ -692,7 +630,6 @@ export function Webinars() {
</div>
) : (
<>
{/* Grid View */}
{viewType === 'grid' ? (
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6 mb-8">
{currentWebinars.map((webinar) => (
@@ -700,7 +637,6 @@ export function Webinars() {
))}
</div>
) : (
/* List View */
<div className="space-y-4 mb-8">
{currentWebinars.map((webinar) => (
<WebinarCard key={webinar.id} webinar={webinar} />
@@ -717,27 +653,38 @@ export function Webinars() {
onClick={() => setCurrentPage(prev => Math.max(1, prev - 1))}
disabled={currentPage === 1}
>
<ChevronLeft className="w-4 h-4 mr-1" />
<ChevronLeft className="w-4 h-4" />
Previous
</Button>
<div className="flex items-center gap-1">
{Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
const page = Math.max(1, Math.min(totalPages - 4, currentPage - 2)) + i;
{Array.from({ length: Math.min(totalPages, 5) }, (_, i) => {
const page = i + 1;
return (
<Button
key={page}
variant={currentPage === page ? "default" : "outline"}
size="sm"
onClick={() => setCurrentPage(page)}
className="w-10"
className="min-w-10"
style={currentPage === page ? { backgroundColor: '#04045b' } : {}}
>
{page}
</Button>
);
})}
{totalPages > 5 && <span className="px-2">...</span>}
{totalPages > 5 && (
<Button
variant={currentPage === totalPages ? "default" : "outline"}
size="sm"
onClick={() => setCurrentPage(totalPages)}
className="min-w-10"
style={currentPage === totalPages ? { backgroundColor: '#04045b' } : {}}
>
{totalPages}
</Button>
)}
</div>
<Button
variant="outline"
size="sm"
@@ -745,7 +692,7 @@ export function Webinars() {
disabled={currentPage === totalPages}
>
Next
<ChevronRight className="w-4 h-4 ml-1" />
<ChevronRight className="w-4 h-4" />
</Button>
</div>
)}
@@ -757,7 +704,6 @@ export function Webinars() {
</div>
</section>
{/* Webcast CTA Banner */}
<WebcastCTABanner />
</div>
);

View File

@@ -126,7 +126,7 @@ function ResourceCard({ resource, index }: ResourceCardProps) {
backgroundColor: 'var(--color-brand-primary)',
color: 'white'
}}
onClick={() => navigateTo('/learning/articles')}
onClick={() => navigateTo('/self-learner-signup')}
onMouseEnter={(e) => {
e.currentTarget.style.backgroundColor = 'var(--color-brand-accent)';
e.currentTarget.style.color = 'var(--color-brand-black)';
@@ -172,7 +172,7 @@ export function WhitepapersSection() {
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.7, delay: 0.2 }}
viewport={{ once: true }}
>
>
Free Leadership Downloads
</motion.h2>
@@ -187,7 +187,6 @@ export function WhitepapersSection() {
text="Browse All Resources"
onClick={() => navigateTo('/learning/articles')}
ariaLabel="Browse all leadership resources"
className="browse-resources-cta-override"
/>
</motion.div>
</div>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,936 @@
import React, { useEffect, useState } from 'react';
import {
ArrowRight,
CheckCircle,
Settings,
Calendar,
Download,
Network,
Users,
Target,
Brain,
Eye,
TrendingUp,
BarChart3,
Award,
Lightbulb,
Shield,
ChevronDown,
ChevronUp,
ArrowLeft,
Star,
Zap,
Globe,
Crown,
Compass,
Users2,
Building,
Layers,
UserCheck,
Clock,
MessageCircle,
Heart
} from 'lucide-react';
import { Button } from '../ui/button';
import { navigateTo } from '../Router';
import { PrimaryCTAButton } from '../PrimaryCTAButton';
import { BrandedTag } from '../about/BrandedTag';
import { Card, CardContent } from '../ui/card';
import { ImageWithFallback } from '../figma/ImageWithFallback';
import { StandardCTAButton } from '../StandardCTAButton';
import { TestimonialsSection } from '../TestimonialsSection';
import { useGetServiceListQuery } from '../../redux/services/sercicesApi';
import { FullScreenLoader } from '../FullScreenLoader';
// Types based on API response
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
const getIconComponent = (iconUrl: string) => {
const iconMap: Record<string, any> = {
'/icons/pipeline.svg': TrendingUp,
'/icons/succession.svg': Users,
'/icons/hr.svg': Users2,
'/icons/management.svg': Crown,
'/icons/growth.svg': TrendingUp,
'/icons/gap.svg': Target,
'/icons/assessment.svg': BarChart3,
'/icons/development.svg': Brain,
'/icons/outcome.svg': Award,
'/icons/target.svg': Target,
'/icons/engagement.svg': Heart,
'/icons/leader.svg': Crown,
'/icons/performance.svg': TrendingUp,
};
const IconComponent = iconMap[iconUrl] || Target;
return IconComponent;
};
export function LeadershipPipelineDevelopment() {
const [expandedPhase, setExpandedPhase] = useState<number | null>(0);
const { data: apiResponse, isLoading, error } = useGetServiceListQuery({
service_type: 'leadership_pipeline'
});
const apiData = apiResponse?.data as ServicePageData | undefined;
useEffect(() => {
window.scrollTo(0, 0);
}, []);
if (isLoading) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary mx-auto"></div>
<p className="mt-4 text-muted">Loading...</p>
</div>
</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
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 programTimeline = 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 => ({
metric: apiData.impact_section?.impact_stats[0]?.value || '85%',
description: benefit.description,
icon: getIconComponent(benefit.icon_url),
category: benefit.title
})) || [];
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
};
}) || [];
if (isLoading) {
return (
<div className="min-h-screen flex items-center justify-center bg-white">
<FullScreenLoader text="Loading Leadership Pipeline Development..." />
</div>
);
}
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">
<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 || "Our Approach to Leadership Development"}
</h2>
<p className="text-body-lg text-[#6F6F6F] max-w-3xl mx-auto">
{apiData?.approach_section?.description || "A systematic, research-backed methodology that transforms leadership potential into measurable business impact."}
</p>
</div>
{/* Flowchart Container with Connecting Lines */}
<div className="relative mb-16 flex flex-col items-center">
{/* Only render if approach_cards exist and have items */}
{apiData?.approach_section?.approach_cards && apiData.approach_section.approach_cards.length > 0 && (
<>
{/* 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 ? <IconComponent className="w-6 h-6 text-white" /> : <Target 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>
{card.bullets && card.bullets.length > 0 && (
<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 */}
{apiData.approach_section.approach_cards.length > 3 && (
<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 ? <IconComponent className="w-6 h-6 text-white" /> : <Target 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>
{card.bullets && card.bullets.length > 0 && (
<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 ? <OutcomeIcon className="w-10 h-10 text-[#F8C301]" /> : <TrendingUp 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 || "A systematic, measurable leadership pipeline that accelerates talent development and succession readiness."}
</p>
<div className="space-y-2">
{apiData.approach_section.outcomes?.[0]?.bullets && apiData.approach_section.outcomes[0].bullets.length > 0 ? (
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">Proven ROI on Leadership Investment</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 ? <IconComponent className="w-6 h-6 text-white" /> : <Target 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>
{card.bullets && card.bullets.length > 0 && (
<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 ? <OutcomeIcon className="w-10 h-10 text-[#F8C301]" /> : <TrendingUp 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 || "A systematic, measurable leadership pipeline that accelerates talent development and succession readiness."}
</p>
<div className="space-y-2">
{apiData.approach_section.outcomes?.[0]?.bullets && apiData.approach_section.outcomes[0].bullets.length > 0 ? (
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">Proven ROI on Leadership Investment</span>
</div>
)}
</div>
</div>
</div>
</>
)}
</div>
{/* Framework Effectiveness - Stats Section */}
{apiData?.stats_section && apiData.stats_section.stat_cards && apiData.stats_section.stat_cards.length > 0 && (
<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 || "Framework Effectiveness"}</h3>
<p className="text-body text-[#6F6F6F] max-w-2xl mx-auto">
{apiData.stats_section.description || "Measurable results that demonstrate the impact of our leadership development approach."}
</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 ? <IconComponent className="w-7 h-7 text-white" /> : <TrendingUp className="w-7 h-7 text-white" />}
</div>
<div className="text-h2 text-[#04045B] mb-2">{stat.value || "0"}</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">
{programTimeline.map((phase, 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={() => setExpandedPhase(expandedPhase === 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">{phase.phase}</h3>
<div className="flex items-center gap-2 mb-3">
<Clock className="w-4 h-4 text-muted" />
<span className="text-small text-muted">{phase.duration}</span>
</div>
</div>
</div>
<div className="ml-4">
{expandedPhase === index ? (
<ChevronUp className="w-5 h-5 text-muted" />
) : (
<ChevronDown className="w-5 h-5 text-muted" />
)}
</div>
</div>
{expandedPhase === 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">Key Activities</h4>
<ul className="space-y-3">
{phase.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">Deliverables & Outcomes</h4>
<ul className="space-y-3">
{phase.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 Pipeline 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="Pipeline Development Success"
subtitle=""
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 eliminate succession gaps now.
</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

@@ -14,7 +14,6 @@ import { BrandedTag } from '../about/BrandedTag';
import { PrimaryCTAButton } from '../PrimaryCTAButton';
import { TestimonialsSection } from '../TestimonialsSection';
import { CTABannerSection } from '../CTABannerSection';
import Slider from "react-slick";
// import ImageWithFallback from "./ImageWithFallback"; // your custom component
import {

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
"use client";
import * as React from "react";
import * as DialogPrimitive from "@radix-ui/react-dialog@1.1.6";
import { XIcon } from "lucide-react@0.487.0";
import * as DialogPrimitive from "@radix-ui/react-dialog";
import { XIcon } from "lucide-react";
import { cn } from "./utils";

541
src/data/articlesData.ts Normal file
View File

@@ -0,0 +1,541 @@
export interface Article {
id: string;
slug: string;
title: string;
excerpt: string;
content: string;
author: string;
authorTitle: string;
authorAvatar: string;
authorBio?: string;
date: string;
readTime: string;
category: string;
tags: string[];
thumbnail: string;
featured: boolean;
views: string;
likes: number;
}
export const articlesData: Article[] = [
{
id: '1',
slug: 'inspiration-magic-potion-leadership',
title: 'Inspiration the magic potion to leadership',
excerpt: 'Explore how inspiration serves as a fundamental catalyst for effective leadership, transforming both leaders and their teams toward extraordinary achievements.',
content: `
<p>Leadership is not just about managing people and processes; it's about inspiring others to achieve greatness. When leaders tap into the power of inspiration, they unlock potential that transcends ordinary management approaches.</p>
<h2>The Power of Inspiration</h2>
<p>Inspiration moves people beyond compliance to commitment. It transforms tasks into missions and converts employees into passionate advocates for organizational goals.</p>
<blockquote>
<p>"Leadership is not about being in charge. It is about taking care of those in your charge and inspiring them to be their best."</p>
<cite>— Simon Sinek</cite>
</blockquote>
<h2>How Leaders Inspire</h2>
<ul>
<li><strong>Leading by Example:</strong> Embodying the values and behaviors they expect from others.</li>
<li><strong>Sharing Vision:</strong> Communicating a compelling picture of the future that energizes people.</li>
<li><strong>Recognizing Potential:</strong> Seeing and nurturing capabilities in others that they may not see in themselves.</li>
<li><strong>Creating Meaning:</strong> Connecting daily work to larger purposes and impacts.</li>
</ul>
<h2>The Ripple Effect</h2>
<p>When leaders inspire, they create a ripple effect throughout the organization. Inspired employees become ambassadors of positive change, multiplying the leader's impact far beyond direct interactions.</p>
<p>Inspiration is indeed the magic potion that transforms good leadership into extraordinary leadership.</p>
`,
author: 'Ramkumar Krishnaswamy',
authorTitle: 'Leadership Expert & Author',
authorAvatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=150&h=150&fit=crop&crop=face',
authorBio: 'K Ramkumar is a leadership expert, author, and founder of KLC. With over two decades of experience, he specializes in leadership development, organizational transformation, and executive coaching.',
date: '2017-10-13',
readTime: '7 min read',
category: 'Leadership Philosophy',
tags: ['Inspiration', 'Leadership Mindset', 'Motivation', 'Team Engagement'],
thumbnail: 'https://images.unsplash.com/photo-1559027615-cd4628902d4a?w=600&h=400&fit=crop',
featured: true,
views: '4.1k',
likes: 89
},
{
id: '2',
slug: 'lessons-leadership-paradox',
title: 'Lessons on Leadership Paradox',
excerpt: 'Uncover the inherent paradoxes in leadership and learn how embracing these contradictions can make you a more effective and authentic leader.',
content: `
<p>Leadership is filled with paradoxes that can confuse and challenge even the most experienced leaders. The ability to navigate these contradictions often separates exceptional leaders from merely good ones.</p>
<h2>Key Leadership Paradoxes</h2>
<h3>1. Be Confident Yet Humble</h3>
<p>Leaders must project confidence to inspire trust while remaining humble enough to learn from others and acknowledge mistakes.</p>
<h3>2. Think Long-Term While Delivering Short-Term</h3>
<p>Effective leaders balance strategic vision with tactical execution, never sacrificing one for the other.</p>
<h3>3. Be Directive Yet Empowering</h3>
<p>Leaders must provide clear direction while empowering teams to make decisions and take ownership.</p>
<blockquote>
<p>"The test of a first-rate intelligence is the ability to hold two opposed ideas in mind at the same time and still retain the ability to function."</p>
<cite>— F. Scott Fitzgerald</cite>
</blockquote>
<h3>4. Be Consistent Yet Flexible</h3>
<p>Maintain core values while adapting approaches to changing circumstances and contexts.</p>
<h3>5. Be Inclusive Yet Decisive</h3>
<p>Seek diverse perspectives while taking decisive action when needed.</p>
<h2>Embracing the Paradox</h2>
<p>The key to navigating leadership paradoxes is not choosing one extreme over another, but developing the wisdom to know when each approach is appropriate. This requires deep self-awareness, contextual intelligence, and comfort with ambiguity.</p>
<p>Leaders who master these paradoxes create organizations that are both stable and innovative, focused and flexible, disciplined and creative.</p>
`,
author: 'Ramkumar Krishnaswamy',
authorTitle: 'Leadership Expert & Author',
authorAvatar: '/images/k-ramkumar-profile.jpg',
authorBio: 'K Ramkumar is a leadership expert, author, and founder of KLC. With over two decades of experience, he specializes in leadership development, organizational transformation, and executive coaching.',
date: '2017-08-10',
readTime: '7 min read',
category: 'Leadership Strategy',
tags: ['Leadership Paradox', 'Strategic Thinking', 'Decision Making', 'Leadership Development'],
thumbnail: 'https://images.unsplash.com/photo-1552664730-d307ca884978?w=600&h=400&fit=crop',
featured: false,
views: '2.8k',
likes: 52
},
{
id: '3',
slug: 'putting-psychometry-perspective',
title: 'Putting Psychometry in Perspective',
excerpt: 'Gain a comprehensive understanding of psychometric assessments in leadership development and learn how to effectively utilize these tools for organizational success.',
content: `
<p>Psychometric assessments have become increasingly popular in leadership development and talent management. Understanding their proper application is crucial for maximizing their value while avoiding common pitfalls.</p>
<h2>What Are Psychometric Assessments?</h2>
<p>Psychometric assessments are scientifically validated tools designed to measure psychological attributes such as personality traits, cognitive abilities, behavioral styles, and motivational drivers.</p>
<h2>Common Applications in Leadership</h2>
<ul>
<li><strong>Talent Selection:</strong> Identifying candidates with high leadership potential</li>
<li><strong>Development Planning:</strong> Creating personalized development pathways</li>
<li><strong>Team Composition:</strong> Building balanced and complementary teams</li>
<li><strong>Succession Planning:</strong> Identifying and preparing future leaders</li>
</ul>
<h2>Benefits and Limitations</h2>
<p>While psychometric tools provide valuable insights, they should be used as one data point among many. No assessment can fully capture the complexity of human potential or predict performance with complete accuracy.</p>
<blockquote>
<p>"Psychometric assessments are mirrors, not crystal balls. They reflect current patterns but cannot predict future potential with certainty."</p>
<cite>— Dr. Sarah Thompson, Organizational Psychologist</cite>
</blockquote>
<h2>Best Practices</h2>
<p>Use assessments as conversation starters, not definitive labels. Combine quantitative data with qualitative observations. Ensure cultural appropriateness and avoid over-reliance on any single tool.</p>
<h2>The Human Element</h2>
<p>Remember that leadership effectiveness ultimately depends on how individuals apply their capabilities in real-world contexts. Assessments inform, but they don't determine potential or success.</p>
`,
author: 'Ramkumar Krishnaswamy',
authorTitle: 'Leadership Expert & Author',
authorAvatar: '/images/k-ramkumar-profile.jpg',
authorBio: 'K Ramkumar is a leadership expert, author, and founder of KLC. With over two decades of experience, he specializes in leadership development, organizational transformation, and executive coaching.',
date: '2016-12-17',
readTime: '7 min read',
category: 'Assessment & Evaluation',
tags: ['Psychometry', 'Leadership Assessment', 'Talent Management', 'HR Tools'],
thumbnail: 'https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=600&h=400&fit=crop',
featured: false,
views: '2.3k',
likes: 38
},
{
id: '4',
slug: '7-insightful-leadership-podcasts',
title: '7 Insightful Leadership Podcasts One Shouldn\'t Miss',
excerpt: 'Discover seven exceptional leadership podcasts that provide valuable insights, practical strategies, and expert perspectives to enhance your leadership journey.',
content: `
<p>In today's fast-paced world, staying updated with the latest leadership insights can be challenging. Podcasts offer a convenient way to learn from experts while multitasking. Here are seven exceptional leadership podcasts that provide valuable insights and practical strategies.</p>
<h2>1. The Leadership Podcast</h2>
<p>Hosted by Jan Rutherford and Jim Vaselopulos, this podcast delivers practical leadership wisdom through engaging conversations with successful leaders.</p>
<h2>2. HBR IdeaCast</h2>
<p>Harvard Business Review's flagship podcast features interviews with leading thinkers in business and management, offering cutting-edge insights on leadership and strategy.</p>
<h2>3. The Craig Groeschel Leadership Podcast</h2>
<p>Craig Groeschel shares leadership lessons from his experience leading one of the largest and most influential churches in America.</p>
<h2>4. Lead to Win</h2>
<p>Michael Hyatt shares practical wisdom on leading yourself, your team, and your organization to achieve extraordinary results.</p>
<h2>5. The EntreLeadership Podcast</h2>
<p>Hosted by Ken Coleman, this podcast offers actionable insights from successful entrepreneurs and business leaders.</p>
<h2>6. The Tony Robbins Podcast</h2>
<p>Tony Robbins interviews world-class performers from diverse industries, uncovering the strategies and mindsets that drive exceptional success.</p>
<h2>7. Lead with Intention</h2>
<p>This podcast explores intentional leadership practices and strategies for creating meaningful impact in your organization.</p>
<p>These podcasts offer a wealth of knowledge and inspiration for leaders at all levels. Make them part of your continuous learning journey.</p>
`,
author: 'Leadership Centre',
authorTitle: 'Leadership Research Team',
authorAvatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=150&h=150&fit=crop&crop=face',
authorBio: 'The Leadership Research Team at KLC curates and analyzes the latest insights in leadership development, organizational behavior, and management practices.',
date: '2023-07-27',
readTime: '3 min read',
category: 'Leadership Development',
tags: ['Podcasts', 'Leadership Insights', 'Professional Development', 'Learning Resources'],
thumbnail: 'https://images.unsplash.com/photo-1590602847861-f357a9332bbc?w=600&h=400&fit=crop',
featured: true,
views: '3.2k',
likes: 67
},
{
id: '5',
slug: 'hitler-company-budgets-common-ground',
title: 'What\'s Common to Hitler and Company Budgets?',
excerpt: 'A provocative analysis exploring the surprising parallels between authoritarian control mechanisms and corporate budget processes, and their impact on organizational behavior.',
content: `
<p>While the comparison may seem shocking at first, examining the control mechanisms used by authoritarian regimes alongside corporate budget processes reveals surprising parallels in how power and control are exercised through systematic processes.</p>
<h2>Control Through Process</h2>
<p>Both authoritarian systems and rigid budget processes share fundamental characteristics: centralized decision-making, limited autonomy at lower levels, and emphasis on compliance over initiative.</p>
<h2>The Psychology of Control</h2>
<p>When organizations implement overly rigid budget controls, they inadvertently create an environment where fear replaces trust, compliance overshadows innovation, and self-preservation becomes more important than organizational success.</p>
<blockquote>
<p>"The parallels lie not in the scale of impact, but in the underlying psychological mechanisms of control and their effects on human behavior."</p>
<cite>— K Ramkumar</cite>
</blockquote>
<h2>Impact on Organizational Behavior</h2>
<ul>
<li><strong>Risk Aversion:</strong> Excessive controls breed fear of failure and discourage calculated risk-taking</li>
<li><strong>Gaming the System:</strong> People focus on meeting metrics rather than achieving meaningful outcomes</li>
<li><strong>Loss of Initiative:</strong> Micromanagement through budget controls stifles creativity and innovation</li>
<li><strong>Short-term Focus:</strong> Rigid annual budgets favor immediate results over long-term value creation</li>
</ul>
<h2>A Better Approach</h2>
<p>Organizations can maintain financial discipline while fostering empowerment through rolling forecasts, flexible resource allocation, and trust-based accountability systems. The goal is control with empowerment, not control through restriction.</p>
<h2>Learning from History</h2>
<p>History teaches us that excessive control, whether political or organizational, ultimately leads to dysfunction. The most successful organizations balance discipline with freedom, creating environments where people feel empowered to contribute their best.</p>
<p>The question isn't whether we need budgets and controls, but how we implement them in ways that enable rather than constrain human potential.</p>
`,
author: 'Ramkumar Krishnaswamy',
authorTitle: 'Leadership Expert & Author',
authorAvatar: '/images/k-ramkumar-profile.jpg',
authorBio: 'K Ramkumar is a leadership expert, author, and founder of KLC. With over two decades of experience, he specializes in leadership development, organizational transformation, and executive coaching.',
date: '2016-03-14',
readTime: '10 min read',
category: 'Organizational Psychology',
tags: ['Control Systems', 'Organizational Behavior', 'Corporate Culture', 'Management Philosophy'],
thumbnail: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=600&h=400&fit=crop',
featured: false,
views: '3.5k',
likes: 72
},
{
id: '6',
slug: 'busby-way-talent-management-part-1',
title: 'The Busby Way to Talent Management: The Making of "Busby\'s Babes" Part 1',
excerpt: 'How Matt Busby transformed Manchester United through visionary talent management and youth development, creating a legacy that shaped modern football leadership.',
content: `
<p>Manchester United Football club, a premier club today, in 1945 was languishing with only one league title in nearly 40 years. Its coffers were empty. It was saddled with a debt of 15,000 sterling pounds. Its stadium was bombed out, derelict and dilapidated.</p>
<p>It was under these circumstances that the club appointed an army physical training instructor Sgt. Matt Busby as its Manager. Busby immediately recruited Jimmy Murphy as his assistant.</p>
<h2>Establishing Authority and Vision</h2>
<p>Busby first hammered out with the directors of the club a deal, not to interfere with how he ran the team. This was a first for the football club tradition of that period. Managers and coaches were not the super stars which they are today. According to people who were privy to the inside story, they attributed Busby's proximity to Mr. Louis Rocca as the reason for getting this authority. Rocca apart from being credited with the naming of the club in 1902 as Manchester United had grown on to become the "club's fixer" — he was the man for all seasons. Rocca was instrumental in convincing Busby to come to Man U from Liverpool.</p>
<h2>Maximizing Existing Talent</h2>
<p>Busby first took stock of the talent that was at his disposal. His only purchase was Jimmy Delaney from the Celtic. He had realised from his days as a player that when players played outside their position of ability they struggled. Busby discovered his talent when he shifted to a "defending half back" from being an "attacking inside half."</p>
<p>Busby restructured the playing positions of his team. His objective was to maximise the talent at his disposal. He shifted Johnny Carey (Captain) an "inside forward" to "right back" and crafted the famous forward line with Jim Delaney, Jack Rowley, Charlie Mitten and Stan Pearson. He then once again rearranged his forward line by shifting Chilton from "inside forward" to "centre forward" to play alongside Delaney and Rowley.</p>
<blockquote>
<p>"The result was that Manchester United finishing second in the league behind Liverpool, missing the title by just 2 points. The decades of languishing at the bottom of the table were over."</p>
</blockquote>
<p>Manchester United now was a title challenger. In the next 5 years Manchester United finished at a heart breaking number two for 4 years, and eventually won the title in the 1951-52 season. In the meanwhile the club won the FA cup in 1948 after 40 years.</p>
<h2>Building the Youth Academy</h2>
<p>During this period Busby put to practice his vision of nurturing the talent from within. His idea was to identify boys as young as 15 years from the schools and alleys all around, and nurture them. He then went on to recruit a group of assistants to help him gather a youth squad. He brought in Joe Armstrong as the scout because he found in him the talent for "establishing a lot of contacts, an appetite to travel around, an eye for locating ability in school boys and an ability to convince their parents". Thus arose the Manchester United youth academy and the youth team.</p>
<blockquote>
<p>"I was aware of Manchester United because of Busby's youth team which went on to become the famous Busby's Babes and I am proud that I was one of them."</p>
<cite>— Bobby Charlton</cite>
</blockquote>
<h2>Instilling a Unique Playing Style</h2>
<p>Busby instilled in them a style of play, neither adopted nor practised in England during that time. He always encouraged them to play one touch and at best two touch football. He egged them on to stay on the attack. He wanted them to play a flowing football with a perpetual drive forward with the ball moving seamlessly from one to the other with a single touch. He placed a premium on character and loyalty to each other. He instilled in them that scoring goals mattered more than just moving the ball around. So he built his team around the forward line. He instilled an attitude that it was no shame to lose in the quest for a win.</p>
<h2>Blooding Young Talent</h2>
<p>By 1951, as his youth team was developing he started blooding players as young as 19 and 20 years into the senior team and was easing out the aging stars of the past 5 years. Jackie Blanchflower and Roger Bryne made their debut with the senior team in November 1951 and Delaney and Carey were out. In the 1952-53 season the sensational Duncan Edwards from the youth team made his debut at the age of 18 years. He went on to play for England by 1954. Bill Foulkes another youth team talent graduated to play for the seniors. He identified a rare talent from the second division Tommy Taylor and signed him up for Manchester United.</p>
<h2>The Courage to Rebuild</h2>
<p>In 1952-53 Busby tried as many as 30 players in the first division. Manchester United slipped to number 8, a year after winning the league. Bobby Charlton made the grade to the senior team this year. This was the worst league standing in 7 seasons for the club. The season ending game saw Manchester United being thrashed by Middlesbrough 5-0.</p>
<p>However the strategy of blooding the youth academy players was to pay rich dividends in a few years. In just 4 years, with players averaging just 22 years of age United brought the league title back to the club and retained it the next season as well. Finally "Busby's Babes" as they were called were on top of the world.</p>
<h2>Key Leadership Lessons</h2>
<ul>
<li><strong>Vision and Authority:</strong> Busby established clear authority to execute his vision without interference</li>
<li><strong>Talent Optimization:</strong> He maximized existing talent by placing people in positions that suited their abilities</li>
<li><strong>Long-term Thinking:</strong> Built a youth academy despite short-term pressures</li>
<li><strong>Cultural Transformation:</strong> Instilled a unique playing style and team culture</li>
<li><strong>Courage in Transition:</strong> Had the courage to rebuild despite temporary setbacks</li>
</ul>
`,
author: 'K Ramkumar',
authorTitle: 'Founder & Leadership Expert',
authorAvatar: '/images/k-ramkumar-profile.jpg',
authorBio: 'K Ramkumar is the founder of KLC (Kautilya Leadership Center) and a renowned leadership expert. With over two decades of experience in leadership development, organizational transformation, and executive coaching, he has helped thousands of leaders across India and globally develop their leadership capabilities.',
date: '2025-10-09',
readTime: '12 min read',
category: 'Talent Management',
tags: ['Talent Management', 'Youth Development', 'Leadership', 'Team Building', 'Organizational Transformation'],
thumbnail: 'https://images.unsplash.com/photo-1574629810360-7efbbe195018?w=600&h=400&fit=crop',
featured: false,
views: '3.2k',
likes: 68
},
{
id: '7',
slug: 'putting-psychometry-in-perspective',
title: 'Putting Psychometry in perspective',
excerpt: 'An in-depth exploration of the limitations and appropriate use of psychometric tools in leadership assessment and talent selection.',
content: `
<p>The last few months have been an eye opener for me on how ubiquitous is the use of psychometrics as an assessment tool. My new role as the founder of a company dedicated to leadership development and my engagement with various organisations brought this home to me. I have realised that this is led by the well-meaning folks in various organisations, who chase objectivity and want to eliminate subjectivity, read judgement, in selecting managers and leaders. However, during my engagements, I recognised that most of the HR functionaries responsible for talent acquisition and leadership development have not taken the time to sit back and reflect on the limitations of psychometric tools and the best way they can leverage them.</p>
<p>I was no different until KV Kamath, then MD and CEO at ICICI Bank, opened my eyes in July 2002. I was then heading the HR function at ICICI Bank and I too was blindly using a combination of intelligence tests and behaviour profiling tools to select managers and leaders. Kamath, as is his wont, challenged me on this. How could I be sure that these tools were not actually filtering out the best managers and leaders, he asked?</p>
<p>I told him that internationally acclaimed test development organisations have developed these tools and that they were well researched. He asked me to carry out my own research by administering the same tests to 50 leaders who were one level below the board and whose capabilities as leaders was beyond dispute because of their track record over at least 15 years. Most of these leaders are today CEOs of large organisations across the world.</p>
<p>The rest, as they say, is history. Based on the tests, only one among the 50 would have been selected. I was shocked. My mission to deconstruct the world of intelligence tests and psychometrics commenced that day. This article has been brewing in my mind for years.</p>
<p>It is important for business leaders and HR practitioners to take a step back and re-examine the unintended consequences of blindly using off-the-shelf psychometric tools for assessment. This article is intended to help them think about psychometric tools with a new perspective.</p>
<h2>Can human ability and behaviour be measured?</h2>
<p>The search for conclusively predicting human ability is age old. The period following World War II saw the indiscriminate use of IQ tests to find the "whiz kids". This led to institution after institution blindly using IQ tests with scant understanding of their application and limitations.</p>
<p>In his book The Best and the Brightest, journalist-turned-author David Halberstam chronicles how in the 1960s, the high IQ "whiz kids" in John F Kennedy's administration crafted "brilliant policies that defied common sense" with respect to Cuba and Vietnam, which led to disastrous consequences for the US.</p>
<p>This is what Kamath was drawing my attention to. That leaders in industry can become academic and invite disastrous consequences through faulty selection/rejection using inappropriate tools and norms.</p>
<p>Alfred Binet, the inventor of IQ tests, had himself cautioned against using such tests to predict human ability. Eminent psychologists such as Jean Piaget, David McClelland, Richard Boyatzis and Daniel Goleman have questioned the propriety of using IQ tests and other such tools to judge or predict human ability.</p>
<p>I believe that it is important to challenge this lazy and ill-informed practice and put psychometrics in perspective. Can psychometric tools predict someone's success or failure in a job or leadership role? Can these tools "measure" human behaviour? Is there a "unit of measure" of human behaviour? Distance is measured in kilometres, volume in kilolitres, speed in kilometres per hour, and weight in kilograms. How can behaviour have calibrated measures like these?</p>
<p>Human behaviour is indeterminate and is a response to the stimulus from the environment. Its frequency, strength, volatility or stability, flexibility and above all, its impact, cannot be measured, because we have not yet designed a calibrated scale.</p>
<p>Informed professionals will challenge me by referring to the Likert scale. The scale gauges attitudes, values and opinion based on the extent to which respondents agree or disagree with a set of statements.</p>
<p>However, the Likert scale delivers broad judgement at best, not measurement. When more than one person responds to statements about the frequency, strength, volatility or stability of someone's behaviour, the scale helps in judgement. But it does not provide an estimation of the behaviour, leave alone measurement, because no two persons' experience of another person's behaviour can be consistently recalled and reported accurately.</p>
<p>The other issue with the measurement logic is that the scale used for measurement has to be calibrated. This means that every point of measurement can be measured finitely, for us to arrive at a quantitative value whose variance is within acceptable range, irrespective of who measures it.</p>
<h3>Let us take an example:</h3>
<p>He/She understand's the unexpressed motives of others.</p>
<p style="margin-left: 2rem;">Always &nbsp;&nbsp; Most of the times &nbsp;&nbsp; Sometimes &nbsp;&nbsp; Rarely</p>
<p>In a calibrated scale, the distance in terms of measurement value between two measurement indicators on the scale has a fixed value or proportion.</p>
<p>So in this example, how fixed are the measurement values between "Always" and "Most of the time"; and between "Most of the time" and "Sometimes"?</p>
<p>Is it "one unit" between "Always" and "Most of the time"; one unit between "Most of the time" and "Sometimes", and two units between "Always" and "Sometimes"?</p>
<p>"Always" or "Most of the time" and "Sometimes" cannot have a fixed numerical value. This is different from having a scale that says "100 times in a year", "50 times in a year", and "25 times in a year".</p>
<p>The Likert scale notwithstanding, we do not yet know how to measure human behaviour. At best, we can confirm the presence or absence or preference with some degree of frequency and strength of judgment. This is not even like estimating (instead of measuring) the size of a room. When we say "very big", "big", or "small", we are judging and not measuring.</p>
<p>When the output from these psychometric instruments are presented as quantitative numerical values such as indices or normative values, they fool us into believing that we have achieved measured quantitative objectivity.</p>
<h2>Can psychometric tests reliably predict job success or leadership success?</h2>
<p>We should be certain that direct and not surrogate outcome indicators connect a particular behaviour to the specific outcome. Performance or talent ratings are surrogate indicators, and will not suffice. How sure are we that extravert orientation brings success in sales compared with an introvert orientation? Even Myers Briggs Type Indicator (MBTI), the orientation profiling tool, does not claim so. More importantly, what direct measure of selling can be linked to extraversion or introversion?</p>
<p>We need to establish that a particular behaviour is not merely related to a particular outcome, but is the driver which causes the outcome. Hence, correlation will not suffice. Correlation only tells us that when a particular behaviour is present, a particular outcome is also present; it won't tell us which causes which. For example, fever and infection are related, but they do not tell us what caused the infection. At ICICI Bank, by blindly linking analytical and quantitative ability to leadership success, I would have caused great damage had it not been for Kamath's challenge.</p>
<p>When the designers of these tools claim that they have established predictive validity (that is, the outcome of the test predicts later performance), we have to take it with a pinch of salt. Let us say they claim to predict leadership success, should we not ask the following?</p>
<ul>
<li>What is the "measure" of leadership success? Is it the same in all contexts?</li>
<li>Which characteristic in a multi-characteristic tool have they found to have predictive validity for leadership success? For example, let us say, trust, power, perspectives, risk appetite and political savvy are the multiple characteristics that influence leadership outcomes. Should we not ask what algorithm was used to establish predictive validity? It is almost impossible to establish the predictive validity that even a single characteristic—say, trust—has on leadership outcomes, especially when we have no clarity on what is the measure of the outcome. So, most designers use distant measures like the performance rating of an individual or an internal talent rating. This is what I call surrogate measures which are themselves outcomes of gross judgment.</li>
</ul>
<p>Let us grant leeway and accept this approach of surrogate measures. Let us also say that instead of a casual connect there is a relationship connect. Then should we at least not ensure the correlation scores are in excess of .65 to .80? (The closer the score is to 1, the stronger is the correlation.)</p>
<p>Till date I have not come across any psychometric tool which shows a correlation of more than 0.2 even to these surrogate indicators. A 0.2 level of correlation even between two variables is no better than a guess. It doesn't determine what caused the outcome. How risky is it then to use these as selection and elimination tools?</p>
<p>We also have to be sure that a particular outcome is not caused by any other behaviour. In the sales example, let us say for the sake of argument that extraversion drives success. How sure are we that perseverance does not also cause the same outcome? Assuming both drive success in sales, how would we empirically assign weightage to extraversion and perseverance? If we were to assign weightage non-empirically, how can the outcome be objective?</p>
<p>It is also possible that a particular behaviour causes a particular outcome only when another behaviour is present or absent. For example, for nurturance to be effective, care together with faith in others' ability is required, but there should be no competitiveness. What constructs will help us make this empirical? If it cannot be empirical, this is at best qualitative data supported by judgment. It cannot have empirically confirmed predictive validity.</p>
<p>We also have to know precisely the frequency, intensity, stability, or flexibility of behaviour that is necessary to cause the particular outcome. For instance, a certain temperature is required for copper to melt; and some other metal will not melt at that temperature. At what level of intensity is someone passionate? At what level of intensity is she obsessive? How much care is useful and when does care obstruct clinical focus on performance?</p>
<p>All these have to be tested and empirically established through longitudinal studies over long periods of time, with the same set of people, in pre-set contexts, where there is no other potential interference. How long should the longitudinal period be? How many people should be studied? How do we control and isolate the contextual factors? All these become critical before we can claim that we can predict job, role or leadership success based on a set of behaviours or what we call psychometric tools.</p>
<h2>My proposition is not that we should rely on our current subjective judgmental approach and not use any tools.</h2>
<p>We currently blindly use behaviour profiling tools for behaviour measurement and prediction. We use it irresponsibly for selecting and rejecting.</p>
<p>Instead, if we accept that profiling tools do not offer measurement or prediction of outcomes, but are more like tools that generate data to support decisions, we will make better use of them.</p>
<p>Behaviour profilers are not like pathological tests which are truly measurement-based and predictive. They are not 3D imaging tools, which accurately capture all the human features. These are at best thumbnail sketches of people. This is not biometrics. This is like using your hand instead of a thermometer to check for fever. These are not calibrated maps, but a pencil sketch on a piece of paper showing the general route to be taken.</p>
<p>With this perspective, profilers will not create confusion whether the person whose profile we are studying has the characteristics of a Mike Tyson or a Mother Teresa. These tools cannot predict that all aggressive people will create Tyson-like outcomes in a boxing ring or outside of it. Or that all people with compassion will end up like Mother Teresa. More importantly, it cannot tell you how much of aggressiveness or compassion creates Tyson-like or Mother Teresa-like outcomes.</p>
<p>Aggression and compassion, like all behaviours, are neutral. Context and the magnitude of these behaviours determines whether these behaviours are appropriate or not.</p>
<p>Hence, we should stop claiming that behaviour profiling tools can predict performance. They can be used as data gathering tools for making someone aware of their orientations. The output from profilers is valuable for conversations about a person's development, to create insights for them on the consequences of their behavioural orientation in different contexts. Profilers help us reconcile our self-image with how others experience us. This is the best value that profilers deliver to us.</p>
<p>If used as a tool to support decisions, for collecting qualitative data and for development purposes, profilers will add immense value. But to connect them with any degree of confidence to performance outcomes or leadership success, and use them as assessment tools is irresponsible.</p>
<div style="margin-top: 3rem; padding: 1.5rem; background: rgba(248, 195, 1, 0.1); border-radius: 8px; font-style: italic; color: #666;">
<p style="margin: 0;">This article was originally published on Founding Fuel.</p>
</div>
`,
author: 'K Ramkumar',
authorTitle: 'Founder & Leadership Expert',
authorAvatar: '/images/k-ramkumar-profile.jpg',
authorBio: 'K Ramkumar is the founder of KLC (Kautilya Leadership Center) and a renowned leadership expert. With over two decades of experience in leadership development, organizational transformation, and executive coaching, he has helped thousands of leaders across India and globally develop their leadership capabilities.',
date: '2025-10-08',
readTime: '15 min read',
category: 'Assessment & Evaluation',
tags: ['Psychometry', 'Leadership Assessment', 'Talent Management', 'HR Tools', 'Measurement'],
thumbnail: 'https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=600&h=400&fit=crop',
featured: false,
views: '2.8k',
likes: 54
},
{
id: '8',
slug: 'new-lens-on-leadership',
title: 'A New Lens on Leadership',
excerpt: 'Your leadership calls, and how you interpret opportunities and threats, are influenced by your lenses, which are unique and personal to you.',
content: `
<div class="article-poetry" style="background: rgba(4, 4, 91, 0.05); padding: 2rem; border-left: 4px solid #04045B; margin: 2rem 0; border-radius: 8px; font-style: italic;">
<p style="margin-bottom: 0.5rem; line-height: 1.8;">The mighty elephant is hidden inside the wood</p>
<p style="margin-bottom: 0.5rem; line-height: 1.8;">The mighty elephant hides the wood</p>
<p style="margin-bottom: 0.5rem; line-height: 1.8;">The Creator is hidden inside the expansiveness of the universe</p>
<p style="margin-bottom: 0.5rem; line-height: 1.8;">The magic of Creation hides the expansiveness of the universe</p>
<p style="margin-top: 1rem; font-size: 0.875rem; opacity: 0.8;">— A translation of a verse by Tamil saint Thirumoolar</p>
</div>
<p>Each of us perceives the world differently because our brain decodes it differently based on sensory inputs—sight, sound, taste, smell and touch. To make meaning of all this, our brain then overlays emotions on the sensory inputs, followed by another filter—our beliefs. The way we make meaning of the world influences our choices and decisions. It is precisely these perceptual differences that give rise to the many hues of the same thing—our different world views, or the lenses through which we perceive the world.</p>
<p>It is these different lenses that make leaders interpret opportunities and threats so differently. The lens of a leader impacts his perspectives, ambition, risk appetite and orientation to trust. These in turn impact the approach the leader takes with respect to his comprehension and choice of vision or ideology, strategy, innovation, organisation culture and decision making.</p>
<p>Let me illustrate the various ways their lens impacts their decisions.</p>
<h2>1. It makes leaders interpret the same context differently and hold it as their personal realities.</h2>
<p>To give you an example from the world of politics, see the different approaches of American presidential candidates Donald Trump and Hillary Clinton—and our reactions to each.</p>
<p>Trump, the Republican party nominee, sees a world which is dangerous enough for the US to contemplate building the modern Great Wall and erect an iron curtain that will keep its enemies out (and throw out those already in). He is unable to see this as a wall and a curtain that will also isolate Americans. His idea of America is of a land only for the new "native Americans", never mind that the US was built by immigrants.</p>
<p>How could Clinton, the Democratic party nominee, be so blind to the "realities" that Trump sees? How could she deny that the US is probably the most hated nation after Rome? Is she naïve not to know that America's allies want to bite off the hand that feeds them? Is she oblivious to the deep religious and ethnic fault lines across the world and which threaten her homeland? Unlike Trump, she sees hope and opportunity and not fear and despondency. She accepts that the idea of America as envisioned by its founding fathers—that all men are created equal—is still work in progress. Yet she wants "togetherness" and an inclusive America for all.</p>
<h2>2. It makes leaders believe that opposite ideas will achieve the same objective.</h2>
<p>Before Mohandas Karamchand Gandhi, during all the 2,500 years of recorded history, no group of people were able to evict a foreign occupier without military engagement. A mere decade after Gandhi, even with Gandhi's example before them, the African National Congress became disillusioned with about 50 years of other means to overthrow apartheid. Goaded by Nelson Mandela, it chose armed confrontation, until it changed its approach again a decade after.</p>
<p>Communist revolutionary leaders like Vladimir Lenin, Mao Zedong, Ho Chi Minh and Fidel Castro sought to obliterate class inequality by curtailing individual freedom. Others have sought the same outcome through free market, free enterprise and freedom of expression.</p>
<p>Many of us extoll the Chinese model of development. Some leaders believe that democracy and freedom can co-exist with development. Some believe that if it comes to a trade-off between individual freedom and development, they will elect the former. Still others believe that it is agreeable, though not prudent, to sacrifice individual freedom in favour of economic development.</p>
<p>These themes play out in other institutions too. For example, some business leaders believe that they need to restrain employees—serving and those who have left—from critically reviewing them or their organisations. They see this as bringing disrepute to the institution. The values they espouse are, all secrets should remain within the family and the family honour is paramount. They put loyalty above freedom of expression and transparency.</p>
<p>A variant of this world view is that any criticism of the supremo is anti-institutional. The belief here is that the well-being of an institution is inextricably entwined with that of the leader, no matter how inappropriate his actions. The institution here can be a nation, commercial organisation, social organisation or a family. How else will we make sense of the RK Pachauri saga and the choices made by the wise leaders who governed The Energy and Resources Institute?</p>
<h2>3. It makes us justify something in one context and denounce it in another, without any contradictions in our minds.</h2>
<p>The founding fathers of the US embedded the idea of equality in their constitution and yet were blind to racial and gender inequality and oppression. Sir Winston Churchill and the so-called free world fought for liberty and freedom in World War II when the forces arraigned on both sides were all imperial powers. Before then their unsaid position was "my freedom and liberty is my right while yours is not. I will cling to my imperial holdings, but have ethical and moral objections when some other nation flexes its imperial muscle and threatens my sovereignty".</p>
<h2>Moving to the Business Context</h2>
<p>Now let us move to the mundane from the sublime. In the business context the leaders' lens determines the path to market leadership.</p>
<p>For the leadership of one bank, it is balance sheet growth; that of another is prepared to trade-off balance sheet size for profitability. For one packaged consumer goods company it is product innovation; for another it is cost leadership and pricing power. For one firm the opportunity lies in cross-border business expansion; for another it is maximising growth in the local market.</p>
<p>Their lens also colours how they view competence. For many leaders proficiency in English is the indicator. This makes them blind to the fact that in most commercial organisations success in the junior level of leadership is largely driven by problem solving skills, interpersonal effectiveness and personal drive. None of these have anything to do with proficiency in English. This leads us to exclude a large number of competent people from the consideration set because they are not proficient in English.</p>
<p>In 2007 when my team and I were brainstorming at Kashid near Mumbai, we were bold enough to challenge this lens on competence. That is where the now successful ICICI probationary officers programme was born. During the last eight years, ICICI Bank has inducted around 10,000 youth from the interiors of India, who have turned out to be stellar bankers and more importantly, leaders. All that was required was to challenge and grind our lens to see the world of competence differently. What is interesting is that only a year into their training, all these probationary officers became as proficient in English as the best from any convent educated school.</p>
<p>Similarly, the leadership of many institutions see investment in physical distribution as the most efficient and effective way to expand their reach and improve customer service, while a few see the digital route as the answer. Proponents of the physical channel argue that customer service will be impersonal on the digital channel. Their lens prevents them from seeing the reality that old mothers swear by Skype and Facetime to connect with their children abroad and find the experience as deeply personal and intimate. More importantly, where it comes to the established banks in the world, this lens has made them sitting ducks to be disrupted by the newer banks which swear by the digital channel. The leadership of the established banks is failing to see the irrelevance of a physical branch when less than 10% of the total customer base and almost insignificant percentage of the profitable customers ever visit a branch. Unfortunately the leaders in these banks are hesitant to correct their myopic lens.</p>
<h2>Connecting to Leadership</h2>
<p>How does all of this connect to leadership? In my book, leadership comes into play only when we are seeking to transform/alter/change a status quo. (This understanding of leadership is shaped by my personal lens and many readers could have other nuanced understanding of leadership.)</p>
<p>Like I said in the beginning, a leader's lens impacts his perspective, ambition, risk appetite and orientation to trust. These in turn impact his vision and strategy.</p>
<p>Perspective is the heart of leadership and our orientation to trust has a significant impact on perspectives—whether it will be narrow and broad.</p>
<h3>The Magnified Lens</h3>
<p>A hyper magnified (zoomed in) leadership lens leads us to an inductive thought process and a narrow perspective. We seek more and more sensory inputs (data). We are driven by the need for detailing and concreteness. We seek proof that the decision will work. This in turn influences our risk appetite and decision making. A hyper magnified lens is shaped by our orientation to trust. This is dictated by our emotions based on our past experience—such as fear of failure, shame arising from getting something wrong or loss arising out of being cheated. We therefore seek proof before we trust. Proof seeking demands more and more verifiable data.</p>
<h3>The Telescopic Lens</h3>
<p>In contrast, a telescopic (zoomed out) leadership lens leads us to a deductive thought process and a broad perspective. This lens frames a wide-angle picture which connects disparate images and information, and tells a story—of the world as we "want" it to be or we "believe" it can be. This lens influences risk appetite and decision making very differently. A telescopic lens too is shaped by our orientation to trust.</p>
<h2>The Role of Emotions and Beliefs</h2>
<p>Our beliefs and our emotions impact the way we collect and integrate sensory data. Based our beliefs and emotional triggers, we give significance to certain data, filter out others, morph a few, choose the classification into which it will be put into and the connections that will be made to present us with a story (meaning and comprehension).</p>
<p>Since we are working with the lens metaphor, we cannot limit our understanding only to the narrow or broad perspectives. This is a function of focus. Emotions and beliefs also colour our world view—the Trump or Clinton world view; the Marx or the McCarthy world view; the Gandhi or the Subhash Chandra Bose world view. What we refer to as orientations, is the function of the emotion and belief that overlays the sensory data. That is why machine processed data will go through plain glass and not a lens. When we classify leaders as left or right, conservative or liberal, inclusive or exclusive, optimistic or pessimistic, radical or conventional, we are talking about the colour of our lenses (and the colour of the leaders' lenses too).</p>
<h2>Understanding Bias</h2>
<p>When we use the term 'bias' we actually refer to our lens amplifying or diminishing, colouring or distorting certain sensory data. Bias is actually preference for certain characteristics, choices, outcomes or practices. This can become a leader's default setting. Gandhi preferred non-violence while Bose believed that foreign occupation cannot be removed without armed conflict. Leaders who have an orientation for relationship amplify the positives of customer contact in physical channels and find drawbacks with automated channels by amplifying their impersonal nature. So, bias is not only likes and dislikes for people. Leaders' approach to strategy, culture, innovation and decision making thus get impacted by the colour of their lenses.</p>
<h2>The Essence of Leadership</h2>
<p>All leadership processes such as vision, choice set of strategies that we arraign, staying inside or stepping out of a paradigm (innovation), and the calls we eventually make are shaped, directed and controlled by our perspectives—narrow or broad, or the colour of the perspectives. Our lens to the world therefore is the key to our leadership fit for a given leadership context.</p>
<p>Without our unique world view, we would all end up making the same choice given a context. The consequence would be that problems and solutions which that one lens cannot comprehend would remain unsolved forever.</p>
<p>If sensory data alone could shape our lenses, we would have been no different from the other animal species. That emotions and beliefs define the nature and character of our lenses is what makes humans special. It is this that allows us to shape the world around us. Is this not the essence of leadership?</p>
<div style="margin-top: 3rem; padding: 1.5rem; background: rgba(248, 195, 1, 0.1); border-radius: 8px; font-style: italic; color: #666;">
<p style="margin-bottom: 0.5rem;"><strong>Disclaimer:</strong></p>
<p style="margin: 0;">This article was originally published on Founding Fuel, on August 12 '16.</p>
</div>
`,
author: 'K Ramkumar',
authorTitle: 'Founder & Leadership Expert',
authorAvatar: '/images/k-ramkumar-profile.jpg',
authorBio: 'K Ramkumar is the founder of KLC (Kautilya Leadership Center) and a renowned leadership expert. With over two decades of experience in leadership development, organizational transformation, and executive coaching, he has helped thousands of leaders across India and globally develop their leadership capabilities.',
date: '2025-10-07',
readTime: '12 min read',
category: 'Leadership Philosophy',
tags: ['Leadership Lens', 'Perspective', 'Decision Making', 'World View', 'Bias', 'Vision'],
thumbnail: 'https://images.unsplash.com/photo-1560550900-5c10828c40aa?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxsZWFkZXJzaGlwJTIwbGVucyUyMHBlcnNwZWN0aXZlfGVufDF8fHx8MTc1OTk5NTg0N3ww&ixlib=rb-4.1.0&q=80&w=1080',
featured: true,
views: '1.9k',
likes: 41
}
];

View File

@@ -95,7 +95,7 @@ export const sharedWebinarsData: WebinarData[] = [
category: 'Digital Transformation',
tags: ['Leadership', 'Digital Strategy', 'Innovation', 'Change Management'],
thumbnail: 'https://images.unsplash.com/photo-1560472355-536de3962603?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxidXNpbmVzcyUyMGxlYWRlcnNoaXAlMjBzdHJhdGVneXxlbnwwfHx8fDE3MzU4NTQyNzB8MA&ixlib=rb-4.0.3&q=80&w=1080',
status: 'live' as const,
status: 'live',
featured: true,
level: 'Advanced',
format: 'Hybrid',
@@ -193,21 +193,6 @@ export const sharedWebinarsData: WebinarData[] = [
rating: 4.8,
participants: '2,400+',
slug: 'strategic-leadership-programme'
},
{
id: 2,
title: 'Data-Driven Decision Making',
description: 'Learn to make strategic decisions using data analytics and business intelligence',
duration: '8 hours',
format: 'Cohort-based',
image: 'https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=400&h=240&fit=crop',
price: '₹37,267',
originalPrice: '₹45,567',
category: 'Decision Making & Strategy',
level: 'Advanced',
rating: 4.9,
participants: '1,800+',
slug: 'data-driven-decision-making'
}
]
},
@@ -229,7 +214,7 @@ export const sharedWebinarsData: WebinarData[] = [
category: 'Team Development',
tags: ['Team Building', 'Emotional Intelligence', 'Performance', 'Leadership'],
thumbnail: 'https://images.unsplash.com/photo-1522071820081-009f0129c71c?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHx0ZWFtJTIwbGVhZGVyc2hpcCUyMGNvbGxhYm9yYXRpb258ZW58MHx8fHwxNzM1ODU0MjcwfDA&ixlib=rb-4.0.3&q=80&w=1080',
status: 'upcoming' as const,
status: 'upcoming',
featured: true,
level: 'Intermediate',
format: 'In Person',
@@ -302,21 +287,6 @@ export const sharedWebinarsData: WebinarData[] = [
rating: 4.7,
participants: '3,200+',
slug: 'team-leadership-masterclass'
},
{
id: 3,
title: 'Emotional Intelligence for Leaders',
description: 'Develop emotional intelligence to enhance your leadership effectiveness and team relationships',
duration: '5 hours',
format: 'Self-paced',
image: 'https://images.unsplash.com/photo-1559027615-cd4628902d4a?w=400&h=240&fit=crop',
price: '₹14,857',
originalPrice: '₹19,007',
category: 'Personal Development',
level: 'Beginner',
rating: 4.9,
participants: '4,300+',
slug: 'emotional-intelligence-leaders'
}
]
},
@@ -337,8 +307,8 @@ export const sharedWebinarsData: WebinarData[] = [
maxAttendees: 4000,
category: 'Strategy',
tags: ['Strategic Thinking', 'Crisis Management', 'Leadership', 'Planning'],
thumbnail: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxzdHJhdGVnaWMlMjBwbGFubmluZyUyMGxlYWRlcnNoaXB8ZW58MHx8fHwxNzM1ODU0Mjcwfda&ixlib=rb-4.0.3&q=80&w=1080',
status: 'recorded' as const,
thumbnail: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxzdHJhdGVnaWMlMjBwbGFubmluZyUyMGxlYWRlcnNoaXB8ZW58MHx8fHwxNzM1ODU0MjcwfDA&ixlib=rb-4.0.3&q=80&w=1080',
status: 'recorded',
featured: false,
level: 'Advanced',
format: 'Virtual',
@@ -431,8 +401,8 @@ export const sharedWebinarsData: WebinarData[] = [
maxAttendees: 2800,
category: 'Personal Development',
tags: ['Innovation', 'Creativity', 'Communication', 'Leadership'],
thumbnail: 'https://images.unsplash.com/photo-1559027615-cd4628902d4a?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxpbm5vdmF0aW9uJTIwbGVhZGVyc2hpcCUyMGNyZWF0aXZpdHl8ZW58MHx8fHwxNzM1ODU0Mjcwfda&ixlib=rb-4.0.3&q=80&w=1080',
status: 'upcoming' as const,
thumbnail: 'https://images.unsplash.com/photo-1559027615-cd4628902d4a?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxpbm5vdmF0aW9uJTIwbGVhZGVyc2hpcCUyMGNyZWF0aXZpdHl8ZW58MHx8fHwxNzM1ODU0MjcwfDA&ixlib=rb-4.0.3&q=80&w=1080',
status: 'upcoming',
featured: false,
level: 'Intermediate',
format: 'Hybrid',
@@ -528,10 +498,10 @@ export const sharedWebinarsData: WebinarData[] = [
duration: '70 min',
attendees: '2,800+',
maxAttendees: 3500,
category: 'Crisis Management',
category: 'Global Leadership',
tags: ['Global Leadership', 'Cultural Intelligence', 'Communication', 'Diversity'],
thumbnail: 'https://images.unsplash.com/photo-1552664730-d307ca884978?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxnbG9iYWwlMjB0ZWFtJTIwbGVhZGVyc2hpcCUyMGRpdmVyc2l0eXxlbnwwfHx8fDE3MzU4NTQyNzB8da&ixlib=rb-4.0.3&q=80&w=1080',
status: 'upcoming' as const,
thumbnail: 'https://images.unsplash.com/photo-1552664730-d307ca884978?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxnbG9iYWwlMjB0ZWFtJTIwbGVhZGVyc2hpcCUyMGRpdmVyc2l0eXxlbnwwfHx8fDE3MzU4NTQyNzB8MA&ixlib=rb-4.0.3&q=80&w=1080',
status: 'upcoming',
featured: false,
level: 'Advanced',
format: 'Virtual',
@@ -611,6 +581,204 @@ export const sharedWebinarsData: WebinarData[] = [
slug: 'global-leadership-certificate'
}
]
},
{
id: '6',
slug: 'strategic-decision-making',
title: 'Strategic Decision Making: Data-Driven Approaches for Leaders',
description: 'Master the art of making informed, strategic decisions using data analytics, critical thinking frameworks, and proven decision-making methodologies.',
presenter: 'Dr. Michael Thompson',
presenterTitle: 'Data Strategy Consultant',
company: 'Analytics Leadership Partners',
date: '2024-04-12',
time: '11:00',
endTime: '12:15',
timezone: 'EST',
duration: '75 min',
attendees: '1,950+',
maxAttendees: 2200,
category: 'Decision Making',
tags: ['Data Analytics', 'Strategic Thinking', 'Decision Making', 'Leadership'],
thumbnail: 'https://images.unsplash.com/photo-1460925895917-afdab827c52f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxkYXRhJTIwYW5hbHl0aWNzJTIwbGVhZGVyc2hpcCUyMGRlY2lzaW9uJTIwbWFraW5nfGVufDB8fHx8MTczNTg1NDI3MHww&ixlib=rb-4.0.3&q=80&w=1080',
status: 'upcoming',
featured: true,
level: 'Intermediate',
format: 'Virtual',
rating: 4.7,
price: 'Free',
registrationOpen: true,
recordingReady: false,
recordingUrl: '',
zoomUrl: 'https://zoom.us/j/1234567894',
theme: 'Data Leadership',
abstract: 'In an era of information overload, leaders must master the art of data-driven decision making. This session provides practical frameworks for analyzing complex data, avoiding cognitive biases, and making strategic choices that drive organizational success.',
keyTakeaways: [
'Apply data analysis frameworks to complex business problems',
'Identify and mitigate common cognitive biases in decision making',
'Develop decision-making processes that balance speed and accuracy',
'Communicate data-driven decisions effectively to stakeholders',
'Build data-informed cultures within your organization'
],
agenda: [
{
time: '11:00 - 11:15',
title: 'The Data-Driven Leader',
description: 'Understanding the role of data in modern leadership'
},
{
time: '11:15 - 11:35',
title: 'Decision-Making Frameworks',
description: 'Proven methodologies for structured decision making'
},
{
time: '11:35 - 11:55',
title: 'Overcoming Cognitive Biases',
description: 'Strategies for recognizing and mitigating decision-making pitfalls'
},
{
time: '11:55 - 12:10',
title: 'Case Studies & Applications',
description: 'Real-world examples of data-driven leadership success'
},
{
time: '12:10 - 12:15',
title: 'Implementation Roadmap',
description: 'Next steps for enhancing your decision-making capabilities'
}
],
host: {
id: 6,
name: 'Dr. Michael Thompson',
title: 'Data Strategy Consultant',
company: 'Analytics Leadership Partners',
bio: 'Dr. Michael Thompson specializes in helping leaders leverage data for strategic decision making. With a PhD in Business Analytics and 12 years of consulting experience, he has transformed decision-making processes in organizations across multiple industries.',
avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=150&h=150&fit=crop&crop=face',
linkedin: 'https://linkedin.com/in/drmichaelthompson',
email: 'michael@analyticsleadership.com'
},
panelists: [],
faqs: [
{
question: 'Do I need technical data analysis skills for this session?',
answer: 'No technical background required! This session focuses on leadership decision-making frameworks rather than technical data analysis. We\'ll provide accessible tools that any leader can implement immediately.'
}
],
relatedProgrammes: [
{
id: 6,
title: 'Data-Driven Decision Making',
description: 'Learn to make strategic decisions using data analytics and business intelligence',
duration: '8 hours',
format: 'Cohort-based',
image: 'https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=400&h=240&fit=crop',
price: '₹37,267',
originalPrice: '₹45,567',
category: 'Decision Making & Strategy',
level: 'Advanced',
rating: 4.9,
participants: '1,800+',
slug: 'data-driven-decision-making'
}
]
},
{
id: '7',
slug: 'change-management-excellence',
title: 'Change Management Excellence: Leading Organizational Transformation',
description: 'Develop comprehensive change management strategies to successfully lead organizational transformations, manage resistance, and ensure sustainable change adoption.',
presenter: 'Dr. Lisa Washington',
presenterTitle: 'Organizational Change Expert',
company: 'Change Leadership Institute',
date: '2024-04-19',
time: '10:00',
endTime: '11:30',
timezone: 'EST',
duration: '90 min',
attendees: '2,300+',
maxAttendees: 2800,
category: 'Change Management',
tags: ['Change Management', 'Transformation', 'Leadership', 'Organizational Development'],
thumbnail: 'https://images.unsplash.com/photo-1556761175-b413da4baf72?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxjaGFuZ2UlMjBtYW5hZ2VtZW50JTIwbGVhZGVyc2hpcCUyMHRyYW5zZm9ybWF0aW9ufGVufDB8fHx8MTczNTg1NDI3MHww&ixlib=rb-4.0.3&q=80&w=1080',
status: 'recorded',
featured: false,
level: 'Advanced',
format: 'Hybrid',
rating: 4.8,
price: 'Free',
registrationOpen: false,
recordingReady: true,
recordingUrl: 'https://video.example.com/change-management',
zoomUrl: '',
theme: 'Change Leadership',
abstract: 'Successful organizational change requires more than just good planning—it demands exceptional leadership. This session provides comprehensive frameworks for leading change initiatives, managing stakeholder resistance, and creating cultures that embrace continuous transformation.',
keyTakeaways: [
'Develop comprehensive change management strategies for complex initiatives',
'Identify and address common sources of resistance to change',
'Build stakeholder engagement and commitment throughout the change process',
'Create measurement systems to track change adoption and success',
'Develop change-ready organizational cultures that thrive on innovation'
],
agenda: [
{
time: '10:00 - 10:20',
title: 'Change Leadership Fundamentals',
description: 'Understanding the psychology of change and leadership requirements'
},
{
time: '10:20 - 10:40',
title: 'Change Management Frameworks',
description: 'Proven methodologies for planning and executing organizational change'
},
{
time: '10:40 - 10:55',
title: 'Managing Resistance',
description: 'Strategies for identifying and overcoming resistance to change'
},
{
time: '10:55 - 11:15',
title: 'Sustaining Change',
description: 'Ensuring long-term adoption and embedding change into culture'
},
{
time: '11:15 - 11:30',
title: 'Q&A and Case Studies',
description: 'Interactive discussion and real-world application examples'
}
],
host: {
id: 7,
name: 'Dr. Lisa Washington',
title: 'Organizational Change Expert',
company: 'Change Leadership Institute',
bio: 'Dr. Lisa Washington has over 18 years of experience helping organizations navigate major transformations. She holds a doctorate in Organizational Psychology and has authored numerous publications on change leadership and organizational development.',
avatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=150&h=150&fit=crop&crop=face',
linkedin: 'https://linkedin.com/in/drlisawashington',
email: 'lisa@changeleadership.com'
},
panelists: [],
faqs: [
{
question: 'How can I apply these concepts to small-scale changes?',
answer: 'The principles and frameworks covered scale effectively from small team changes to enterprise-wide transformations. We\'ll provide adaptable tools that work across different change magnitudes.'
}
],
relatedProgrammes: [
{
id: 7,
title: 'Change Management Certification',
description: 'Comprehensive change management training for leaders driving organizational transformation',
duration: '10 weeks',
format: 'Online',
image: 'https://images.unsplash.com/photo-1556761175-b413da4baf72?w=400&h=240&fit=crop',
price: '₹49,567',
originalPrice: '₹61,967',
category: 'Change Management',
level: 'Advanced',
rating: 4.7,
participants: '2,300+',
slug: 'change-management-certification'
}
]
}
];

14
src/global.d.ts vendored
View File

@@ -1,5 +1,15 @@
// declarations.d.ts
/// <reference types="vite/client" />
// Vite ENV typing
interface ImportMetaEnv {
readonly VITE_API_URL: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
// Image modules
declare module "*.png" {
const src: string;
export default src;
@@ -23,4 +33,4 @@ declare module "*.svg" {
export { ReactComponent };
const src: string;
export default src;
}
}

View File

@@ -0,0 +1,3 @@
export default {
p2c1c9a80: "M8 3.33398L12.6667 8.00065L8 12.6673",
}

View File

@@ -2,9 +2,16 @@ import { createRoot } from "react-dom/client";
import App from "./App";
import "../src/styles/globals.css";
import { BrowserRouter } from "react-router-dom";
import ScrollToTop from "./components/ScrollToTop";
import { Provider } from "react-redux";
import { store } from "./redux/store/Store";
createRoot(document.getElementById("root")!).render(
<BrowserRouter>
<App />
</BrowserRouter>
<Provider store={store}>
<BrowserRouter>
<ScrollToTop />
<App />
</BrowserRouter>
</Provider>
);

292
src/pages/FAQ.tsx Normal file
View File

@@ -0,0 +1,292 @@
import React, { useState, useEffect } from 'react';
import { Plus, Minus, HelpCircle, Mail } from 'lucide-react';
import { PrimaryCTAButton } from '../components/PrimaryCTAButton';
import { useNavigate } from 'react-router-dom';
import { useGetFaqsQuery, useGetFaqCategoriesQuery } from '../redux/services/faqApi';
interface FAQItemProps {
question: string;
answer: string;
isOpen: boolean;
onToggle: () => void;
tags?: Array<{ tag_name: string; display_order: number }>;
}
interface FAQItemData {
id: string;
question: string;
answer: string;
content_status: string;
content_category_xid: string;
content_category: {
id: string;
category_name: string;
};
faq_tags: Array<{
id: string;
tag_name: string;
display_order: number;
}>;
created_at: string;
updated_at: string;
}
interface CategoryData {
id: string;
category_name: string;
display_order: number;
for_faq: boolean;
for_blog: boolean;
}
const FAQItem: React.FC<FAQItemProps> = ({ question, answer, isOpen, onToggle, tags }) => {
return (
<div className="bg-white border border-gray-200 rounded-lg p-6 mb-4 transition-all duration-300 hover:shadow-md">
<button
onClick={onToggle}
className="w-full text-left flex items-center justify-between"
>
<h3 className="text-h4 pr-4" style={{ color: 'var(--color-black)' }}>
{question}
</h3>
<div className="flex-shrink-0 ml-4">
{isOpen ? (
<Minus
className="w-5 h-5 transition-transform duration-200"
style={{ color: 'var(--color-primary)' }}
/>
) : (
<Plus
className="w-5 h-5 transition-transform duration-200"
style={{ color: 'var(--color-primary)' }}
/>
)}
</div>
</button>
<div
className={`overflow-hidden transition-all duration-300 ease-out ${isOpen ? 'max-h-96 opacity-100 mt-4' : 'max-h-0 opacity-0'
}`}
>
<p className="text-body-lg leading-relaxed mb-4" style={{ color: 'var(--color-black)' }}>
{answer}
</p>
{/* Display tags if available */}
{tags && tags.length > 0 && (
<div className="flex flex-wrap gap-2 mt-3">
{tags.map((tag, index) => (
<span
key={index}
className="px-3 py-1 bg-gray-100 text-gray-600 rounded-full text-small"
>
{tag.tag_name}
</span>
))}
</div>
)}
</div>
</div>
);
};
export function FAQ() {
const [openItems, setOpenItems] = useState<string[]>([]);
const [activeCategory, setActiveCategory] = useState<string>('all');
const [filteredFaqs, setFilteredFaqs] = useState<FAQItemData[]>([]);
const navigate = useNavigate();
// Fetch FAQ categories
const {
data: categoriesResponse,
isLoading: isLoadingCategories
} = useGetFaqCategoriesQuery({
limit: 100,
offset: 0
});
// Fetch FAQs with publish status
const {
data: faqResponse,
isLoading: isLoadingFaqs,
isError,
refetch
} = useGetFaqsQuery({
content_status: 'publish',
limit: 20
});
// Filter categories to only those marked for FAQ
const categories = [
{ id: 'all', label: 'All Questions' },
...(categoriesResponse?.data?.items
?.filter((category: CategoryData) => category.for_faq)
.map((category: CategoryData) => ({
id: category.id, // Use the actual category ID
label: category.category_name,
display_order: category.display_order
}))
.sort((a:any, b:any) => (a.display_order || 0) - (b.display_order || 0)) || [])
];
// Filter FAQs based on active category
useEffect(() => {
if (faqResponse?.data?.items) {
if (activeCategory === 'all') {
setFilteredFaqs(faqResponse.data.items);
} else {
// Filter by content_category_xid using the actual category ID
const filtered = faqResponse.data.items.filter(
(item: FAQItemData) => item.content_category_xid === activeCategory
);
setFilteredFaqs(filtered);
}
}
}, [activeCategory, faqResponse]);
const toggleItem = (id: string) => {
setOpenItems(prev =>
prev.includes(id)
? prev.filter(i => i !== id)
: [...prev, id]
);
};
// Handle loading state
if (isLoadingFaqs || isLoadingCategories) {
return (
<div style={{ backgroundColor: '#FFFFFF' }} 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"></div>
<p className="mt-4 text-gray-600">Loading FAQs...</p>
</div>
</div>
);
}
// Handle error state
if (isError) {
return (
<div style={{ backgroundColor: '#FFFFFF' }} className="min-h-screen flex items-center justify-center">
<div className="text-center">
<HelpCircle className="w-16 h-16 text-red-500 mx-auto mb-4" />
<h2 className="text-h3 mb-2">Failed to load FAQs</h2>
<p className="text-gray-600 mb-4">Please try again later</p>
<PrimaryCTAButton
text="Retry"
onClick={() => refetch()}
className="cta-text-black"
/>
</div>
</div>
);
}
return (
<div style={{ backgroundColor: '#FFFFFF' }} className="min-h-screen">
{/* Header Section */}
<section className="py-20 section-margin-x">
<div className="max-w-4xl mx-auto">
{/* Main Heading */}
<h1 className="text-h1 mb-6" style={{ color: 'var(--color-black)' }}>
FAQs
</h1>
{/* Description */}
<p className="text-body-lg mb-8 leading-relaxed" style={{ color: 'var(--color-gray-muted)' }}>
Everything you need to know about features, membership, and troubleshooting.
</p>
{/* Category Filter Tags - Only show if there are categories */}
{categories.length > 1 && (
<div className="flex flex-wrap gap-3 mb-8">
{categories.map((category) => (
<button
key={category.id}
onClick={() => setActiveCategory(category.id)}
className={`px-4 py-2 rounded-full text-small font-medium transition-all duration-200 ${activeCategory === category.id
? 'text-white'
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
}`}
style={{
backgroundColor: activeCategory === category.id ? 'var(--color-primary)' : undefined,
color: activeCategory === category.id ? 'white' : undefined
}}
>
{category.label}
</button>
))}
</div>
)}
</div>
</section>
{/* FAQ Section */}
<section className="pb-20 section-margin-x">
<div className="max-w-4xl mx-auto">
{filteredFaqs.length > 0 ? (
<div className="space-y-0">
{filteredFaqs.map((faq) => (
<FAQItem
key={faq.id}
question={faq.question}
answer={faq.answer}
// tags={faq.faq_tags}
isOpen={openItems.includes(faq.id)}
onToggle={() => toggleItem(faq.id)}
/>
))}
</div>
) : (
<div className="text-center py-12">
<HelpCircle className="w-16 h-16 text-gray-400 mx-auto mb-4" />
<h3 className="text-h4 mb-2">No FAQs found</h3>
<p className="text-gray-600">
{activeCategory === 'all'
? 'No published FAQs available at the moment.'
: 'No FAQs available in this category.'}
</p>
</div>
)}
</div>
</section>
{/* Contact Support Section */}
<section className="py-16 section-margin-x">
<div className="max-w-4xl mx-auto">
<div className="bg-white border border-gray-200 rounded-xl p-8 text-center">
<div className="w-16 h-16 mx-auto mb-6 rounded-full flex items-center justify-center" style={{ backgroundColor: 'var(--color-primary)' }}>
<HelpCircle className="w-8 h-8" style={{ color: 'white' }} />
</div>
<h2 className="text-h3 mb-4" style={{ color: 'var(--color-black)' }}>
Still have questions?
</h2>
<p className="text-body-lg mb-8 max-w-2xl mx-auto" style={{ color: 'var(--color-gray-muted)' }}>
Can't find the answer you're looking for? Our support team is here to help you with any questions
about our leadership development programs and services.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
<PrimaryCTAButton
text="Contact Support"
onClick={() => navigate('/contact')}
className="cta-text-black"
/>
<a
href="mailto:connect@leadershipcentre.in"
className="flex items-center gap-2 px-6 py-3 text-body-lg transition-colors duration-200 hover:bg-gray-50 rounded-lg"
style={{ color: 'var(--color-primary)' }}
>
<Mail className="w-5 h-5" />
connect@leadershipcentre.in
</a>
</div>
</div>
</div>
</section>
</div>
);
}

View File

@@ -1,32 +1,65 @@
import React from 'react';
import HeroSection from '../components/HeroSection';
import { StatsSection } from '../components/StatsSection';
import { LogosSection } from '../components/LogosSection';
import { ServicesSection } from '../components/ServicesSection';
import { VirtualSpaceSection } from '../components/VirtualSpaceSection';
import { TestimonialsSection } from '../components/TestimonialsSection';
import { UpcomingWebinarsSection } from '../components/UpcomingWebinarsSection';
import { InsightsSection } from '../components/InsightsSection';
import { WhitepapersSection } from '../components/WhitepapersSection';
import { CTABannerSection } from '../components/CTABannerSection';
import React from "react";
import HeroSection from "../components/HeroSection";
import { StatsSection } from "../components/StatsSection";
import { LogosSection } from "../components/LogosSection";
import { ServicesSection } from "../components/ServicesSection";
import { VirtualSpaceSection } from "../components/VirtualSpaceSection";
import { TestimonialsSection } from "../components/TestimonialsSection";
import { InsightsSection } from "../components/InsightsSection";
import { CTABannerSection } from "../components/CTABannerSection";
import { motion } from "motion/react";
import { PrimaryCTAButton } from '../components/PrimaryCTAButton';
import { BrandedTag } from '../components/about/BrandedTag';
import { useNavigate } from 'react-router-dom';
import { PrimaryCTAButton } from "../components/PrimaryCTAButton";
import { BrandedTag } from "../components/about/BrandedTag";
import { useNavigate } from "react-router-dom";
import { useGetHomepageQuery } from "../redux/services/homepageApi";
import { FullScreenLoader } from "../components/FullScreenLoader";
const HomePage: React.FC = () => {
const navigate = useNavigate();
const { data, isLoading } = useGetHomepageQuery({
landing_page_type: "home",
});
const heroSections = data?.hero_sections ?? [];
const stats = data?.stats_sections ?? [];
const highlightCards = data?.highlight_cards ?? [];
const ctaBands = data?.cta_bands ?? [];
const ctaSection = data?.cta_section;
// Transform testimonial section data to match Testimonial interface
const testimonialData = data?.testimonial_section?.map((item: any) => ({
id: item.id,
name: item.name,
role: item.designation,
quote: item.content,
videoUrl: item.video_url,
isVideo: !!item.video_url,
rating: 5, // Default rating, can be updated from API if available
avatar: item.profile_xid ? `https://example.com/avatars/${item.profile_xid}.jpg` : undefined,
})) || [];
if (isLoading) {
return (
<div className="min-h-screen flex items-center justify-center bg-white">
<FullScreenLoader text="Loading Homepage..." />
</div>
);
}
return (
<>
<HeroSection />
<StatsSection />
<HeroSection heroSections={heroSections} isLoading={isLoading} />
{/* Stats Section */}
<StatsSection stats={stats} isLoading={isLoading} />
<LogosSection />
<ServicesSection />
<ServicesSection highlightCards={highlightCards} isLoading={isLoading} />
<div>
<div className="mx-auto text-center py-16 px-4 bg-gradient-to-r from-blue-900 via-gray-400 to-black exp-our-head-tab-sec" >
{/* Branded Tag */}
<div className="mx-auto text-center py-16 px-4 bg-gradient-to-r from-blue-900 via-gray-400 to-black exp-our-head-tab-sec">
<motion.div
initial={{ opacity: 0, y: -20 }}
whileInView={{ opacity: 1, y: 0 }}
@@ -40,7 +73,6 @@ const HomePage: React.FC = () => {
/>
</motion.div>
{/* Main Heading */}
<motion.h2
className="text-5xl font-bold leading-tight mb-4 max-lg:text-4xl max-md:text-3xl text-white"
initial={{ opacity: 0, y: 30 }}
@@ -51,7 +83,6 @@ const HomePage: React.FC = () => {
Experience Our Space Virtually
</motion.h2>
{/* Subheading */}
<motion.p
className="text-lg leading-relaxed max-w-2xl mx-auto max-lg:text-base mb-6 text-white/90"
initial={{ opacity: 0, y: 20 }}
@@ -59,10 +90,10 @@ const HomePage: React.FC = () => {
transition={{ duration: 0.8, delay: 0.4 }}
viewport={{ once: true }}
>
Take a virtual walk through our state-of-the-art facility designed to inspire leadership excellence and foster collaborative learning.
Take a virtual walk through our state-of-the-art facility designed to
inspire leadership excellence and foster collaborative learning.
</motion.p>
{/* Main CTA Button - Explore Our Space */}
<motion.div
className="flex justify-center"
initial={{ opacity: 0, y: 20 }}
@@ -73,19 +104,25 @@ const HomePage: React.FC = () => {
<div className="hero-slide-button">
<PrimaryCTAButton
text="Explore Our Space"
onClick={() => navigate('/services/learning-facility')}
onClick={() => navigate("/services/learning-facility")}
ariaLabel="Explore our virtual learning space and facilities"
/>
</div>
</motion.div>
</div>
<VirtualSpaceSection />
</div>
<TestimonialsSection />
<UpcomingWebinarsSection />
{/* Pass testimonial data to the TestimonialsSection */}
<TestimonialsSection
customTestimonials={testimonialData}
title="What Our Clients Say"
subtitle="Hear from industry leaders who have transformed their organizations with our solutions."
tagText="Client Stories"
/>
<InsightsSection />
<WhitepapersSection />
<CTABannerSection />
<CTABannerSection ctaSection={ctaSection} isLoading={isLoading} />
</>
);
};

View File

@@ -1,104 +0,0 @@
import React, { useState, useEffect, useCallback } from 'react';
import { ChevronLeft, ChevronRight } from 'lucide-react';
// import { navigateTo } from './Router';
import { navigateTo } from '../components/Router';
import svgPaths from "../imports/svg-i1joeov37f";
import { StatsSection } from '../components/StatsSection';
import { LogosSection } from '../components/LogosSection';
import { ServicesSection } from '../components/ServicesSection';
import { ServicesSectionNew } from '../components/ServiceSectionNew';
import { LearningEnvionment } from '../components/LearningEnvionment';
import { motion } from "motion/react";
import { BrandedTag } from '../components/about/BrandedTag';
import { PrimaryCTAButton } from '../components/PrimaryCTAButton';
import { VirtualSpaceSection } from '../components/VirtualSpaceSection';
import { useNavigate } from 'react-router-dom';
import HeroSection from '../components/HeroSection';
interface SlideData {
id: number;
title: string;
backgroundImage: string;
shortTitle: string;
ctaText: string;
}
export default function HomePageNew() {
const navigate = useNavigate()
return (
<>
<HeroSection />
<StatsSection />
<LogosSection />
<ServicesSectionNew />
{/* <LearningEnvionment /> */}
<div>
<div className="mx-auto text-center py-16 px-4 bg-gradient-to-r from-blue-900 via-gray-400 to-black exp-our-head-tab-sec" >
{/* Branded Tag */}
<motion.div
initial={{ opacity: 0, y: -20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
viewport={{ once: true }}
>
<BrandedTag
text="Virtual Learning Environment"
className="justify-center"
variant="white"
/>
</motion.div>
{/* Main Heading */}
<motion.h2
className="text-5xl font-bold leading-tight mb-4 max-lg:text-4xl max-md:text-3xl text-white"
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.2 }}
viewport={{ once: true }}
>
Experience Our Space Virtually
</motion.h2>
{/* Subheading */}
<motion.p
className="text-lg leading-relaxed max-w-2xl mx-auto max-lg:text-base mb-6 text-white/90"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.4 }}
viewport={{ once: true }}
>
Take a virtual walk through our state-of-the-art facility designed to inspire leadership excellence and foster collaborative learning.
</motion.p>
{/* Main CTA Button - Explore Our Space */}
<motion.div
className="flex justify-center"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.6 }}
viewport={{ once: true }}
>
<div className="hero-slide-button">
<PrimaryCTAButton
text="Explore Our Space"
onClick={() => navigate('/services/learning-facility')}
ariaLabel="Explore our virtual learning space and facilities"
/>
</div>
</motion.div>
</div>
<VirtualSpaceSection />
</div>
</>
);
}

948
src/pages/Privacy.tsx Normal file
View File

@@ -0,0 +1,948 @@
import React, { useState, useEffect } from "react";
import {
ChevronRight,
Download,
ExternalLink,
} from "lucide-react";
interface TocItem {
id: string;
title: string;
level: number;
}
export function Privacy() {
const [activeSection, setActiveSection] =
useState<string>("introduction");
const [tocOpen, setTocOpen] = useState(false);
// Table of Contents items
const tocItems: TocItem[] = [
{
id: "introduction",
title: "Introduction",
level: 1,
},
{
id: "general-practice",
title: "General Practice",
level: 1,
},
{
id: "definitions",
title: "Definitions",
level: 1,
},
{
id: "collection-of-data",
title: "Collection of Data",
level: 1,
},
{
id: "usage-of-information",
title: "Usage of Information",
level: 1,
},
{
id: "sharing-of-data",
title: "Sharing of Data or Information",
level: 1,
},
{
id: "retention-storage",
title: "Period of Retention & Information Storage",
level: 1,
},
{
id: "user-rights",
title: "Rights of Users and Subscribers",
level: 1,
},
{
id: "jurisdiction",
title: "Jurisdiction",
level: 1,
},
{
id: "data-breach",
title: "Notifications of Data Breach",
level: 1,
},
{
id: "disputes",
title: "Disputes",
level: 1,
},
{
id: "amendment",
title: "Amendment to the Policy",
level: 1,
},
{
id: "data-protection-officer",
title: "Data Protection Officer",
level: 1,
},
];
// Scroll to section and update active state
const scrollToSection = (sectionId: string) => {
const element = document.getElementById(sectionId);
if (element) {
const offsetTop = element.offsetTop - 100; // Account for sticky nav
window.scrollTo({
top: offsetTop,
behavior: "smooth",
});
setActiveSection(sectionId);
}
};
// Update active section on scroll
useEffect(() => {
const handleScroll = () => {
const sections = tocItems
.map((item) => document.getElementById(item.id))
.filter(Boolean);
const scrollPosition = window.scrollY + 150;
for (let i = sections.length - 1; i >= 0; i--) {
const section = sections[i];
if (section && section.offsetTop <= scrollPosition) {
setActiveSection(section.id);
break;
}
}
};
window.addEventListener("scroll", handleScroll);
return () =>
window.removeEventListener("scroll", handleScroll);
}, []);
// Handle URL fragments
useEffect(() => {
const hash = window.location.hash.replace("#", "");
if (hash && tocItems.find((item) => item.id === hash)) {
setTimeout(() => scrollToSection(hash), 100);
}
}, []);
const lastUpdated = "2025-01-01";
const versionHash = "v25.01";
return (
<div style={{ backgroundColor: "#FFFFFF" }}>
{/* Hero Banner */}
<section
className="py-16 section-margin-x"
style={{ backgroundColor: "#FFFFFF" }}
>
<div className="max-w-4xl mx-auto text-center">
<div className="inline-flex items-center gap-2 px-4 py-2 bg-blue-100 text-blue-800 rounded-full text-small mb-6">
<span className="w-2 h-2 bg-blue-600 rounded-full"></span>
Privacy Document
<span className="ml-2 px-2 py-1 bg-blue-200 text-blue-900 rounded text-xs font-medium">
{versionHash}
</span>
</div>
<h1 className="text-h1 mb-6">Privacy & Cookie Policy</h1>
<p className="text-body-lg text-muted max-w-2xl mx-auto mb-4">
This website 'leadershipcentre.in' ('Website') is owned and operated by Kautilya Leadership Centre Pvt. Ltd ('Kautilya Leadership Centre'), a company incorporated and registered under the laws of the Republic of India.
</p>
<p className="text-small text-muted">
Last updated:{" "}
{new Date(lastUpdated).toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
})}
</p>
</div>
</section>
<div
className="section-margin-x"
style={{ backgroundColor: "#FFFFFF" }}
>
<div className="max-w-6xl mx-auto">
<div className="grid grid-cols-12 gap-8">
{/* Sticky Table of Contents */}
<div className="col-span-12 lg:col-span-3">
<div className="sticky top-24">
<nav
aria-label="On-page navigation"
className="bg-white border border-gray-200 rounded-lg p-6 shadow-sm"
>
<div className="flex items-center justify-between mb-4 lg:hidden">
<h3 className="text-h4">Contents</h3>
<button
onClick={() => setTocOpen(!tocOpen)}
className="p-2 hover:bg-gray-100 rounded-md transition-colors"
aria-expanded={tocOpen}
>
<ChevronRight
className={`w-4 h-4 transition-transform ${tocOpen ? "rotate-90" : ""}`}
/>
</button>
</div>
<div
className={`space-y-2 ${tocOpen ? "block" : "hidden lg:block"}`}
>
{tocItems.map((item) => (
<button
key={item.id}
onClick={() => scrollToSection(item.id)}
className={`block w-full text-left p-2 rounded-md text-small transition-all duration-200 hover:bg-gray-100 ${
activeSection === item.id
? "bg-blue-50 text-primary border-l-2 border-primary pl-3"
: "text-muted hover:text-black"
}`}
>
{item.title}
</button>
))}
</div>
</nav>
</div>
</div>
{/* Main Content */}
<div className="col-span-12 lg:col-span-9">
<div
className="max-w-3xl prose prose-lg"
style={{ maxWidth: "720px" }}
>
{/* Introduction */}
<section
id="introduction"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("introduction")
}
>
<span className="inline-flex items-center gap-2">
Introduction
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body-lg">
Please read carefully the terms of use ('Terms of Use') for using the Electronic Platform.
In case, you ('User') do not agree to any of the terms or conditions, made by Kautilya
Leadership Centre for the Electronic Platform, kindly refrain from using the Electronic Platform.
</p>
<p className="text-body">
Pursuant to the Terms of Use, this privacy & cookie policy ('Policy') of Kautilya Leadership
Centre governs the access to and use of this Electronic Platform, and also sharing, storing
and processing of data and information in the course of availing any services and or Courses
offered by Kautilya Leadership Centre. By continuing to use the Electronic Platform, User
shall be deemed to have read, understood, and be bound by this Policy.
</p>
<div className="bg-blue-50 border-l-4 border-blue-400 p-4 rounded-r-lg">
<p className="text-body text-blue-800">
<strong>Important:</strong> This policy governs how we collect, use, and protect your
personal information. Please ensure you understand your privacy rights and our data practices.
</p>
</div>
</div>
</section>
{/* General Practice */}
<section
id="general-practice"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("general-practice")
}
>
<span className="inline-flex items-center gap-2">
1. General Practice
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body">
<strong>1.1</strong> Kautilya Leadership Centre restricts the collection of data. The data collected and processed is strictly in the course of rendering of services and Courses offered by Kautilya Leadership Centre. This Policy intends to protect User's Information while putting User in control of Processing (as defined hereunder).
</p>
<p className="text-body">
<strong>1.2</strong> For the purposes of, where applicable, Kautilya Leadership Centre is the 'Controller' and is in compliance of the requirements of GDPR.
</p>
<div className="bg-green-50 border-l-4 border-green-400 p-4 rounded-r-lg mt-6">
<p className="text-body text-green-800">
<strong>Privacy First:</strong> We are committed to collecting only the data necessary to
provide our services and maintaining strict control over how your information is processed.
</p>
</div>
</div>
</section>
{/* Definitions */}
<section
id="definitions"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("definitions")
}
>
<span className="inline-flex items-center gap-2">
2. Definitions
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body">
<strong>2.1</strong> <strong>'Applicable Laws'</strong> means any statute, law, regulation, ordinance, rule, judgment as applicable, notification, rule of common law or equity, order, decree, bye-law, government approval, directive, recommendations, guideline, requirement or other governmental restriction, or any similar form of decision of, or determination by, or any interpretation or policy by, any authority having jurisdiction over the matter in question;
</p>
<p className="text-body">
<strong>2.2</strong> <strong>'Cookies'</strong> are small files which are stored on a User's device (computer, mobile, etcetera.). They are designed to hold a modest amount of data specific to a particular client and website, and can be accessed either by the web server or the client computer. For clarity, they store information such as cart details and login id and password, which makes it easier for the User to access and browse the Website;
</p>
<p className="text-body">
<strong>2.3</strong> <strong>'Course'</strong> refers to the leadership, managerial, and other functional courses including, but not limited to videos, textual materials, webinars, seminars, podcasts forming part of such course (whether made available online and/ or offline);
</p>
<p className="text-body">
<strong>2.4</strong> <strong>'Course User'</strong> shall mean a person nominated by the Subscriber to enrol for the Course or any person who enrols for the Course. In case the Subscriber is an individual, the Subscriber shall mean the Course User;
</p>
<p className="text-body">
<strong>2.5</strong> <strong>'Electronic Platform'</strong> means the Website, the Kautilya Leadership Centre mobile applications, television applications, our API's and other related services;
</p>
<p className="text-body">
<strong>2.6</strong> <strong>'Information'</strong> refers to data of a User and includes both Personal Data and Non-personal data;
</p>
<p className="text-body">
<strong>2.7</strong> <strong>'Non-personal data'</strong> means data which are not Personal Data;
</p>
<p className="text-body">
<strong>2.8</strong> <strong>'Personal Data'</strong> means any data relating to an identified or identifiable natural person; an identifiable natural person is one who can be identified, directly or indirectly, in particular by reference to an identifier such as a name, an identification number, location data, an online identifier or to one or more factors specific to the physical, physiological, genetic, mental, economic, cultural or social identity of that natural person;
</p>
<p className="text-body">
<strong>2.9</strong> <strong>'Processing'</strong> or <strong>'Processed'</strong> means any operation or set of operations which is performed on Personal Data or on sets of Personal Data, whether or not by automated means, such as collection, recording, organisation, structuring, storage, adaptation or alteration, retrieval, consultation, use, disclosure by transmission, dissemination or otherwise making available, alignment or combination, restriction, erasure or destruction. Such operation is done by a 'Processor';
</p>
<p className="text-body">
<strong>2.10</strong> <strong>'Subscriber'</strong> shall mean a person who has registered as a Subscriber with the Kautilya Leadership Centre;
</p>
<p className="text-body">
<strong>2.11</strong> <strong>'User'</strong> means and includes, the individual using, accessing and browsing the Electronic Platform, whether or not they purchase the Courses; User shall include a Course User or Subscriber as the case may be;
</p>
<p className="text-body">
<strong>2.12</strong> <strong>'Website'</strong> refers to all web pages, any content such as textual, visual or aural (including modules, videos, including calls to action, text placement, images and other information) made available online and/ or offline through leadershipcentre.in and provided by Kautilya Leadership Centre;
</p>
<div className="bg-gray-50 border border-gray-200 rounded-lg p-6 mt-6">
<h3 className="text-h3 mb-4 text-gray-800">Key Terms Summary</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<p className="text-body font-semibold mb-2">Personal Data:</p>
<p className="text-small text-gray-600">Any information that can identify you as an individual</p>
</div>
<div>
<p className="text-body font-semibold mb-2">Processing:</p>
<p className="text-small text-gray-600">Any operation performed on your personal data</p>
</div>
<div>
<p className="text-body font-semibold mb-2">Cookies:</p>
<p className="text-small text-gray-600">Small files stored on your device to improve your experience</p>
</div>
<div>
<p className="text-body font-semibold mb-2">Electronic Platform:</p>
<p className="text-small text-gray-600">Our website, apps, and related digital services</p>
</div>
</div>
</div>
</div>
</section>
{/* Collection of Data */}
<section
id="collection-of-data"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("collection-of-data")
}
>
<span className="inline-flex items-center gap-2">
3. Collection of Data
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body">
<strong>3.1</strong> Kautilya Leadership Centre collects Information through following means:
</p>
<div className="ml-4 space-y-3">
<p className="text-body">
<strong>a.</strong> Through automated means i.e. by using Cookies, web beacons, Google Analytics, clear gifs, pixels, and other similar technologies ('Data Collection Tools'); and
</p>
<p className="text-body">
<strong>b.</strong> Through non-automated means i.e. Information provided through self-declaratory forms, using psychometric tools etc. Such Information is usually required when the User creates an account with the Kautilya Leadership Centre pursuant to registration, feedback, accesses materials, video recordings or podcast etc.
</p>
</div>
<p className="text-body">
<strong>3.2 Purpose:</strong> The collection of Information is for the purpose of enhancing, improving and personalising the usage of the Electronic Platform and provision of services and Courses offered by Kautilya Leadership Centre. Non-personal data of the User shall be processed for research purposes. Further, the Data Collection Tools allow Kautilya Leadership Centre to detect abuse such as spam or malware, and provide customised services such as ad or search preferences and ease the process of signing up for services and Courses.
</p>
<p className="text-body">
<strong>3.3 Records:</strong>
</p>
<div className="ml-4 space-y-2">
<p className="text-body">
<strong>(i)</strong> Kautilya Leadership Centre also may keep record of communication with the Users.
</p>
<p className="text-body">
<strong>(ii)</strong> Reports and data collected from and of the users for the purpose comparison and building user profile related data and for research and other purposes.
</p>
</div>
<div className="bg-blue-50 border-l-4 border-blue-400 p-4 rounded-r-lg mt-6">
<p className="text-body text-blue-800">
<strong>Data Collection Purpose:</strong> We collect data solely to improve your learning
experience, provide better services, and ensure platform security. All collection is done
with your consent and for legitimate business purposes.
</p>
</div>
</div>
</section>
{/* Usage of Information */}
<section
id="usage-of-information"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("usage-of-information")
}
>
<span className="inline-flex items-center gap-2">
4. Usage of Information
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body">
<strong>4.1</strong> The Electronic Platform Processes Information collected only for the Purposes as set out in 3.2, in compliance with Applicable Laws. The Processing of Information is dependent on User's consent.
</p>
<p className="text-body">
<strong>4.2</strong> The Processing of Information is pursuant to the User's access to the Electronic Platform; It includes Processing User's Course request, delivering orders of the Subscriber, emailing the Users as per their preferences, sharing newsletters, email updates, notifying amendments to this Policy, Terms of Use, processing payment of the Subscriber, managing account preference, updating the Course content, participating in or viewing webinars/seminars or podcast, sharing Information with the expert consultant or Course instructor etc.
</p>
<p className="text-body">
<strong>4.3</strong> The Information collected may also be used to provide customised recommendations, to communicate with the User.
</p>
<p className="text-body">
<strong>4.4</strong> Non- personal data of the User shall be used for research.
</p>
<p className="text-body">
<strong>4.5</strong> Further, all Personal Data is Processed only in compliance with Applicable Laws and for the purposes mentioned in this Section 4 of this Policy.
</p>
<p className="text-body">
<strong>4.6</strong> Kautilya Leadership Centre may request User's consent before using Information for a purpose that does not fall under this Policy.
</p>
<div className="bg-gray-50 border border-gray-200 rounded-lg p-6 mt-6">
<h3 className="text-h3 mb-4">How We Use Your Information</h3>
<ul className="list-disc pl-6 space-y-2 text-body">
<li>Process course enrollments and deliver educational content</li>
<li>Send personalized recommendations and updates</li>
<li>Manage your account preferences and settings</li>
<li>Process payments and handle billing</li>
<li>Communicate important policy changes</li>
<li>Facilitate interactions with instructors and experts</li>
<li>Conduct research to improve our services</li>
</ul>
</div>
</div>
</section>
{/* Sharing of Data or Information */}
<section
id="sharing-of-data"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("sharing-of-data")
}
>
<span className="inline-flex items-center gap-2">
5. Sharing of Data or Information
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body">
<strong>5.1 Consent:</strong> Kautilya Leadership Centre does not Process and or share the User's Information without User's consent to this Policy.
</p>
<p className="text-body">
<strong>5.2 For providing services:</strong> Information may be shared with personnel and affiliates of Kautilya Leadership Centre with appropriate confidentiality and adequate security measures for the purpose of rendering services. This includes, providing customer care support to the User.
</p>
<p className="text-body">
<strong>5.3 Third-party service providers or Processors:</strong> Kautilya Leadership Centre Processes User's Information with third parties in accordance with this Policy and individual agreements with third-party service providers only for the purpose of rendering of services and pursuant to the scope of the Courses offered by Kautilya Leadership Centre, in compliance with Applicable Laws. Such third parties may include consultants, cloud-storage service providers, banks, payment gateway systems, and content providers. Further, Kautilya Leadership Centre tries to ensure that such third-party service providers abide by this Policy and other Applicable Laws.
</p>
<p className="text-body">
<strong>5.4 For legal purposes:</strong> Kautilya Leadership Centre may share User's or Information, with the government or any institution if bound by the law to do so, and with any other entity which may endorse or sponsor the User's acccount (s) on the Electronic Platform.
</p>
<p className="text-body">
<strong>5.5 For processing payments:</strong> Kautilya Leadership Centre uses payment gateway services for online purchases (including refund) related transactions. The payment service provider obtains and process the required Personal and Non-personal data for payment related transactions by self-declaration of the User. Electronic Platform shall not be held liable for any Information, breach of any such Information provided to the payment service provider with regard to payment.
</p>
<p className="text-body">
<strong>5.6 In the event of Re-organisation:</strong> The Information of the Users shall be shared in case of merger, acquisition or reorganization of the Kautilya Leadership Centre. Such Processing shall be in accordance with this Policy and with knowledge (through notification) of the Users.
</p>
<p className="text-body">
<strong>5.7 Sharing of information by the User:</strong> The User may share details of the Course, Electronic Platform or services through links, emails, etcetera, such information, when shared publicly, allows User's content to become accessible through search engines.
</p>
<div className="bg-yellow-50 border-l-4 border-yellow-400 p-4 rounded-r-lg mt-6">
<p className="text-body text-yellow-800">
<strong>Third-Party Sharing:</strong> We only share your information with trusted partners
and service providers who help us deliver our services. All third parties are bound by
strict confidentiality and security requirements.
</p>
</div>
</div>
</section>
{/* Period of Retention and Information Storage */}
<section
id="retention-storage"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("retention-storage")
}
>
<span className="inline-flex items-center gap-2">
6. Period of Retention and Information Storage
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body">
<strong>6.1</strong> Kautilya Leadership Centre only processes or stores Information for the Purpose as long as it is commercially or legally required and for the duration:
</p>
<div className="ml-4 space-y-2">
<p className="text-body">
<strong>a.</strong> the User browses the Electronic Platform;
</p>
<p className="text-body">
<strong>b.</strong> the User maintains an account on the Electronic Platform; and
</p>
<p className="text-body">
<strong>c.</strong> required or permitted under Applicable Laws;
</p>
</div>
<p className="text-body">
such Processing shall be in accordance with this Policy and other Applicable Laws.
</p>
<p className="text-body">
<strong>6.2</strong> The User may withdraw its consent in continuing to provide Information, both Personal Data and Non-Personal Data. In case, the User withdraws its consent, Kautilya Leadership Centre shall erase or delete the Information from their storage and also notify any third party. To clarify, any Information Processed post withdrawal of consent shall only be for compliance with Applicable Laws.
</p>
<div className="bg-green-50 border-l-4 border-green-400 p-4 rounded-r-lg mt-6">
<p className="text-body text-green-800">
<strong>Your Control:</strong> You have the right to withdraw consent and request deletion
of your personal information at any time. We will honor such requests in accordance with
applicable laws and our legal obligations.
</p>
</div>
</div>
</section>
{/* Rights of Users and Subscribers */}
<section
id="user-rights"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("user-rights")
}
>
<span className="inline-flex items-center gap-2">
7. Rights of Users and Subscribers
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<h3 className="text-h3 mb-4">7.1 Rights of the User over information:</h3>
<div className="ml-4 space-y-3">
<p className="text-body">
<strong>(i)</strong> the User has the right to control and deny the access of the Data Collection Tools which can be controlled through web browser and account settings of the User;
</p>
<p className="text-body">
<strong>(ii)</strong> the User has the right to deny Information whenever such Information is required through the Electronic Platform for the purposes specified in Section 4 of this Policy, however, in such event, the Electronic Platform or Kautilya Leadership Centre may not be able to provide the requisite services or Course to the User;
</p>
<p className="text-body">
<strong>(iii)</strong> the User has the right to rectify or update the Information;
</p>
<p className="text-body">
<strong>(iv)</strong> the User has the right to complain to the respective protection authority in case of breach or issues regarding enforceability of User's rights, provided, such breach or issue has been first notified to Kautilya Leadership Centre and a time period of 90 days from the date of such notice of breach or issue to the Kautilya Leadership Centre has been given in order to rectify or provide an explanation for the same; and
</p>
<p className="text-body">
<strong>(v)</strong> the User also reserve the right to give feedback or complain to the data protection officer in accordance with clause 12 of this Policy.
</p>
</div>
<h3 className="text-h3 mb-4 mt-8">7.2 Specific Rights of European Union subjects</h3>
<p className="text-body">
In addition to the abovementioned, this Section additionally is applicable to the User who are residents of European Union. In accordance with GDPR, EU residents hold certain rights over the Information that they provide, as stated below:
</p>
<div className="ml-4 space-y-3">
<p className="text-body">
<strong>(i) Right of data portability</strong> the User has the right to receive details and copy of Information collected from the User in compliance with this Policy. Such data or Information shall be stored in a portable, structured, commonly used and machine-readable format. Upon receiving a written request to access such Information or data Kautilya Leadership Centre shall transfer such Information to the User or any other third-party in accordance with the request and Applicable Laws. Furthermore, Kautilya Leadership Centre is not responsible for the security of the Information, its transmission or its processing once received by the third party. Kautilya Leadership Centre reserves the right to not provide the requested Information if the same would be a breach of another User's rights;
</p>
<p className="text-body">
<strong>(ii) Right to restrict Processing</strong> the User has the right to restrict the processing of the Information provided by the User unless such Information has to be processed in accordance with any law; and
</p>
<p className="text-body">
<strong>(iii) Right to object</strong> the User may object to the processing of Information which is used for direct marketing purposes, unless Kautilya Leadership Centre legitimate grounds for the processing which overrides the interests, rights and freedoms of such User or for the establishment, exercise or defence of legal claims.
</p>
<p className="text-body">
<strong>(iv) Right to Erasure</strong> Pursuant to Section 6.2 of this Policy, the User may request Kautilya Leadership Centre to erase all the Information Processed by Kautilya Leadership Centre relating to the User by contacting the support service in accordance with Section 7.1(vi) above.
</p>
</div>
<h3 className="text-h3 mb-4 mt-8">7.3 Specific Right for USA and Canada citizens</h3>
<p className="text-body">
The User has the right to request access to the Information that the Electronic Platform collects and know whether such Information is being processed. However, Kautilya Leadership Centre retains the right to deny such request for access in cases where it may affect the right of any other Users or is exempted under any Applicable Law.
</p>
<p className="text-body">
<strong>7.4</strong> In order to claim or enforce the rights under the Sections 7.1 and 7.2, such User may be required to provide adequate and verifiable identity proof when called upon to do so.
</p>
<p className="text-body">
<strong>7.5</strong> In case any of the rights of a User, in compliance with Applicable Laws of respective jurisdiction, is not provided in this Policy, such User may contact Kautilya Leadership Centre's data protection officer in accordance with Section 12 of this Policy.
</p>
<div className="bg-blue-50 border border-blue-200 rounded-lg p-6 mt-6">
<h3 className="text-h3 mb-4 text-blue-800">Your Privacy Rights Summary</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<p className="text-body font-semibold mb-2">Control & Deny Access:</p>
<p className="text-small text-blue-700">Manage cookies and data collection through browser settings</p>
</div>
<div>
<p className="text-body font-semibold mb-2">Rectify & Update:</p>
<p className="text-small text-blue-700">Correct or update your personal information</p>
</div>
<div>
<p className="text-body font-semibold mb-2">Data Portability (EU):</p>
<p className="text-small text-blue-700">Receive your data in a portable format</p>
</div>
<div>
<p className="text-body font-semibold mb-2">Right to Erasure:</p>
<p className="text-small text-blue-700">Request deletion of your personal information</p>
</div>
</div>
</div>
</div>
</section>
{/* Jurisdiction */}
<section
id="jurisdiction"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("jurisdiction")
}
>
<span className="inline-flex items-center gap-2">
8. Jurisdiction
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body">
<strong>8.1</strong> Any dispute regarding this Policy shall be subject to the exclusive jurisdiction of the courts at Mumbai, Maharashtra.
</p>
<p className="text-body">
<strong>8.2</strong> Subject to Section 10 and in compliance with Applicable Laws, the User, in case is a resident outside India, may be entitled certain rights to raise complaints with the respective data protection authority within their jurisdiction.
</p>
<div className="bg-gray-50 border border-gray-200 rounded-lg p-6">
<p className="text-body">
<strong>Exclusive Jurisdiction:</strong> Mumbai, Maharashtra, India
</p>
<p className="text-body mt-2">
Any disputes arising from this privacy policy shall be subject to the exclusive
jurisdiction of the courts in Mumbai, Maharashtra, India.
</p>
</div>
</div>
</section>
{/* Notifications of Data Breach */}
<section
id="data-breach"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("data-breach")
}
>
<span className="inline-flex items-center gap-2">
9. Notifications of Data Breach
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body">
<strong>9.1</strong> Although, adequate safety measures and compliance is exercised, in the unlikely event of breach of the Information provided by the User to Kautilya Leadership Centre, Kautilya Leadership Centre shall notify and the assist all such affected Users promptly and in compliance with Applicable Laws.
</p>
<p className="text-body">
<strong>9.2</strong> In case, a User learns of such a breach of Information, it may contact the data protection officer of Kautilya Leadership Centre and this in accordance with Section 12 of this Policy.
</p>
<div className="bg-red-50 border-l-4 border-red-400 p-4 rounded-r-lg mt-6">
<p className="text-body text-red-800">
<strong>Security Commitment:</strong> We implement robust security measures to protect your
data. In the unlikely event of a breach, we will notify affected users promptly and take
immediate action to secure your information.
</p>
</div>
</div>
</section>
{/* Disputes */}
<section
id="disputes"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("disputes")
}
>
<span className="inline-flex items-center gap-2">
10. Disputes
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body">
<strong>10.1</strong> In the event of any dispute in respect of or concerning or connected with the interpretation or implementation or arising out this Policy, including any question regarding their existence, validity or termination, the same shall be at the first instance be amicably settled through good faith negotiations between the Parties.
</p>
<p className="text-body">
<strong>10.2</strong> In case that a resolution of the dispute is not achieved within 30 (thirty) days from the date such dispute arises, as notified in writing by any party to the other party, then such dispute shall be referred to and finally resolved by arbitration by a sole arbitrator, administered by the Mumbai International Arbitration Centre ('MICA') in accordance with the Arbitration Rules of the Mumbai International Arbitration Centre ('MICA Rules') for the time being in force.
</p>
<p className="text-body">
<strong>10.3</strong> The award of the arbitration shall be final and binding on the Parties hereto and the seat and venue of arbitration shall be Mumbai, India. Language of the arbitration shall be in English.
</p>
<p className="text-body">
<strong>10.4</strong> The arbitrator shall give a reasoned decision or award, and shall allocate or appropriation cost, expenses and disbursement of the arbitration as the arbitrator may deem fair.
</p>
<div className="bg-blue-50 border-l-4 border-blue-400 p-4 rounded-r-lg mt-6">
<p className="text-body text-blue-800">
<strong>Dispute Resolution:</strong> We prefer to resolve disputes through direct communication.
If formal procedures are needed, disputes will be resolved through arbitration in Mumbai, India.
</p>
</div>
</div>
</section>
{/* Amendment to the Policy */}
<section
id="amendment"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("amendment")
}
>
<span className="inline-flex items-center gap-2">
11. Amendment to the Policy
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body">
<strong>11.1</strong> Kautilya Leadership Centre reserves the right to change or revise the Policy and terms of this Policy at any time by posting any changes or a revised Policy on this Electronic Platform, without advance notice to the User.
</p>
<p className="text-body">
<strong>11.2</strong> Such amendment or the changed and or revised Policy will be effective immediately after it is posted on this Electronic Platform. Use of the Electronic Platform following the posting any such amendment or of a revised Policy will constitute the User's acceptance to any such changes or revisions.
</p>
<p className="text-body">
<strong>11.3</strong> Notwithstanding the above, such amendment shall be notified to the User in such manner as prescribed by law, if and when such requirement to notify the User is necessitated by law.
</p>
<div className="bg-yellow-50 border-l-4 border-yellow-400 p-4 rounded-r-lg mt-6">
<p className="text-body text-yellow-800">
<strong>Policy Updates:</strong> We may update this privacy policy from time to time.
Changes will be effective immediately upon posting. We encourage you to review this
policy periodically for any updates.
</p>
</div>
</div>
</section>
{/* Data Protection Officer */}
<section
id="data-protection-officer"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("data-protection-officer")
}
>
<span className="inline-flex items-center gap-2">
12. Data Protection Officer
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body">
In case of any concerns or issues in relation to this Policy or Electronic Platform, the User may contact Kautilya Leadership Centre's data protection officer can be contacted at connect@leadershipcentre.in
</p>
<div className="bg-green-50 border border-green-200 rounded-lg p-6">
<h3 className="text-h3 mb-4 text-green-800">Contact Our Data Protection Officer</h3>
<div className="space-y-2">
<p className="text-body text-green-800">
<strong>Email:</strong> <a href="mailto:connect@leadershipcentre.in" className="underline hover:text-green-900">connect@leadershipcentre.in</a>
</p>
<p className="text-body text-green-800">
<strong>Organization:</strong> Kautilya Leadership Centre Pvt. Ltd
</p>
<p className="text-body text-green-800">
<strong>Purpose:</strong> Privacy concerns, data protection queries, policy questions
</p>
</div>
</div>
<div className="bg-blue-50 border-l-4 border-blue-400 p-4 rounded-r-lg mt-6">
<p className="text-body text-blue-800">
<strong>Privacy Support:</strong> Our data protection officer is available to address your
privacy concerns, answer questions about this policy, and assist with exercising your
privacy rights.
</p>
</div>
</div>
</section>
{/* Important Notice */}
<section className="mb-12">
<div className="bg-gradient-to-r from-blue-50 to-green-50 border-2 border-primary rounded-lg p-8">
<h3 className="text-h3 mb-4 text-primary">
Privacy Compliance & Your Rights
</h3>
<p className="text-body mb-4">
This Privacy & Cookie Policy is effective as of the last updated date mentioned above.
By using our Electronic Platform, you acknowledge that you have read, understood, and agree
to be bound by this policy.
</p>
<p className="text-body text-muted">
For questions about your privacy rights, data processing, or this policy, please contact
our Data Protection Officer. We are committed to protecting your privacy and ensuring
compliance with all applicable data protection laws.
</p>
</div>
</section>
{/* Download Section */}
<section className="mb-12">
<div className="text-center bg-gray-50 border border-gray-200 rounded-lg p-8">
<h3 className="text-h3 mb-4">Download Privacy & Cookie Policy</h3>
<p className="text-body text-muted mb-6">
Save a copy of this privacy policy for your records
</p>
<button
onClick={() => window.print()}
className="inline-flex items-center gap-2 px-6 py-3 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors"
>
<Download className="w-4 h-4" />
Download PDF
</button>
</div>
</section>
</div>
</div>
</div>
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,760 @@
import React, { useState, useEffect } from "react";
import {
ChevronRight,
Download,
ExternalLink,
} from "lucide-react";
interface TocItem {
id: string;
title: string;
level: number;
}
export function TermsCondition() {
const [activeSection, setActiveSection] =
useState<string>("introduction");
const [tocOpen, setTocOpen] = useState(false);
// Table of Contents items
const tocItems: TocItem[] = [
{
id: "introduction",
title: "Introduction",
level: 1,
},
{
id: "limits-of-use",
title: "Limits of Use",
level: 1,
},
{
id: "services-offered",
title: "Services Offered",
level: 1,
},
{
id: "disclaimer-warranties",
title: "Disclaimer of Warranties & Liability",
level: 1,
},
{
id: "privacy-policy",
title: "Privacy Policy",
level: 1,
},
{
id: "termination",
title: "Termination",
level: 1,
},
{
id: "intellectual-property",
title: "Intellectual Property Rights",
level: 1,
},
{
id: "third-party-links",
title: "Links to Third-Party",
level: 1,
},
{
id: "indemnity",
title: "Indemnity",
level: 1,
},
{
id: "amendment",
title: "Amendment",
level: 1,
},
{
id: "governing-law",
title: "Governing Laws & Jurisdiction",
level: 1,
},
{
id: "grievances",
title: "Grievances",
level: 1,
},
];
// Scroll to section and update active state
const scrollToSection = (sectionId: string) => {
const element = document.getElementById(sectionId);
if (element) {
const offsetTop = element.offsetTop - 100; // Account for sticky nav
window.scrollTo({
top: offsetTop,
behavior: "smooth",
});
setActiveSection(sectionId);
}
};
// Update active section on scroll
useEffect(() => {
const handleScroll = () => {
const sections = tocItems
.map((item) => document.getElementById(item.id))
.filter(Boolean);
const scrollPosition = window.scrollY + 150;
for (let i = sections.length - 1; i >= 0; i--) {
const section = sections[i];
if (section && section.offsetTop <= scrollPosition) {
setActiveSection(section.id);
break;
}
}
};
window.addEventListener("scroll", handleScroll);
return () =>
window.removeEventListener("scroll", handleScroll);
}, []);
// Handle URL fragments
useEffect(() => {
const hash = window.location.hash.replace("#", "");
if (hash && tocItems.find((item) => item.id === hash)) {
setTimeout(() => scrollToSection(hash), 100);
}
}, []);
const lastUpdated = "2025-01-01";
const versionHash = "v25.01";
return (
<div style={{ backgroundColor: "#FFFFFF" }}>
{/* Hero Banner */}
<section
className="py-16 section-margin-x"
style={{ backgroundColor: "#FFFFFF" }}
>
<div className="max-w-4xl mx-auto text-center">
<div className="inline-flex items-center gap-2 px-4 py-2 bg-blue-100 text-blue-800 rounded-full text-small mb-6">
<span className="w-2 h-2 bg-blue-600 rounded-full"></span>
Legal Document
<span className="ml-2 px-2 py-1 bg-blue-200 text-blue-900 rounded text-xs font-medium">
{versionHash}
</span>
</div>
<h1 className="text-h1 mb-6">Terms & Conditions</h1>
<p className="text-body-lg text-muted max-w-2xl mx-auto mb-4">
This website 'leadershipcentre.in' ('Website') is owned and operated by Kautilya Leadership Centre Pvt. Ltd ('Leadership Centre'), a company incorporated and registered under the laws of India.
</p>
<p className="text-small text-muted">
Last updated:{" "}
{new Date(lastUpdated).toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
})}
</p>
</div>
</section>
<div
className="section-margin-x"
style={{ backgroundColor: "#FFFFFF" }}
>
<div className="max-w-6xl mx-auto">
<div className="grid grid-cols-12 gap-8">
{/* Sticky Table of Contents */}
<div className="col-span-12 lg:col-span-3">
<div className="sticky top-24">
<nav
aria-label="On-page navigation"
className="bg-white border border-gray-200 rounded-lg p-6 shadow-sm"
>
<div className="flex items-center justify-between mb-4 lg:hidden">
<h3 className="text-h4">Contents</h3>
<button
onClick={() => setTocOpen(!tocOpen)}
className="p-2 hover:bg-gray-100 rounded-md transition-colors"
aria-expanded={tocOpen}
>
<ChevronRight
className={`w-4 h-4 transition-transform ${tocOpen ? "rotate-90" : ""}`}
/>
</button>
</div>
<div
className={`space-y-2 ${tocOpen ? "block" : "hidden lg:block"}`}
>
{tocItems.map((item) => (
<button
key={item.id}
onClick={() => scrollToSection(item.id)}
className={`block w-full text-left p-2 rounded-md text-small transition-all duration-200 hover:bg-gray-100 ${
activeSection === item.id
? "bg-blue-50 text-primary border-l-2 border-primary pl-3"
: "text-muted hover:text-black"
}`}
>
{item.title}
</button>
))}
</div>
</nav>
</div>
</div>
{/* Main Content */}
<div className="col-span-12 lg:col-span-9">
<div
className="max-w-3xl prose prose-lg"
style={{ maxWidth: "720px" }}
>
{/* Introduction */}
<section
id="introduction"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("introduction")
}
>
<span className="inline-flex items-center gap-2">
1. Introduction
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body-lg">
Please read carefully these terms of use ('Terms of Use') for using the Website.
In case, you ('User') do not agree to any of the terms or conditions, made by
Kautilya Leadership Centre for the Website, kindly refrain from using the Website.
</p>
<p className="text-body">
By continuing to use the Website, User shall be deemed to have agreed that User
is of legal age to enter into a binding contract, have read, consented, understood
and is bound to the Terms of Use hereunder.
</p>
<h3 className="text-h3 mb-4 mt-8">1.1 Parties</h3>
<p className="text-body">
The parties to the Terms of Use are Users of the Website and Kautilya Leadership Centre.
User and Kautilya Leadership Centre hereinafter may be individually referred to as
'Party' and collectively as 'Parties'.
</p>
<h3 className="text-h3 mb-4 mt-8">1.2 Applicability of Terms of Use</h3>
<p className="text-body">
The Terms of Use govern the Website's usage and states the rights and obligations
of the Parties arising thereof.
</p>
<h3 className="text-h3 mb-4 mt-8">1.3 Ownership of the Website and the content</h3>
<p className="text-body">
The Website is owned and controlled by Kautilya Leadership Centre subject to provisions
hereunder. All web pages, any content such as textual, visual or aural (including modules,
videos, including calls to action, text placement, images and other information) available
online and or offline shall be property of Kautilya Leadership Centre.
</p>
<div className="bg-yellow-50 border-l-4 border-yellow-400 p-4 rounded-r-lg">
<p className="text-body text-yellow-800">
<strong>Important:</strong> These terms create a legally binding agreement
between you and Kautilya Leadership Centre. Please ensure you understand
your rights and obligations.
</p>
</div>
</div>
</section>
{/* Limits of Use */}
<section
id="limits-of-use"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("limits-of-use")
}
>
<span className="inline-flex items-center gap-2">
2. Limits of Use
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body">
<strong>2.1</strong> The User acknowledges that the User shall use the Website only for personal, non-commercial use.
</p>
<p className="text-body">
<strong>2.2</strong> The User shall use the website only for lawful purposes. User shall not use the website: in breach of any national or international law or regulation; in a fraudulent and unlawful manner; and to transmit, send or upload any material or data that contains any viruses, trojan horses, malware, worms, time-bombs, keystroke loggers, spyware, adware or any other harmful programs or computer code designed to adversely affect the operation of this website or any computer hardware or software.
</p>
<p className="text-body">
<strong>2.3</strong> The User shall not: copy, reproduce, duplicate or re-sell any part of the Website; charge others for the usage of the Website; and use or re-use any part of the Website in any way that may be illegally detrimental to the business and autonomy of Kautilya Leadership Centre, unless with the explicit written consent of the Kautilya Leadership Centre.
</p>
<h3 className="text-h3 mb-4 mt-8">Access to minors</h3>
<p className="text-body">
<strong>2.4.1</strong> For the purpose of this Terms of Use and the Privacy and Cookie Policy, a minor shall mean a natural person below the age of 18 (eighteen).
</p>
<p className="text-body">
<strong>2.4.2</strong> The Website may be accessed by minors for educational purposes only.
</p>
<p className="text-body">
<strong>2.4.3</strong> The parent or guardian of the minor shall provide their own details while creating an account with Kautilya Leadership Centre.
</p>
<p className="text-body">
<strong>2.4.4</strong> Kautilya Leadership Centre does not knowingly collect any data from minors. Upon knowledge that it holds Personal Data (Privacy and Cookie Policy) of a minor, Kautilya Leadership Centre shall erase the same. Parents or Guardians who believe that Kautilya Leadership Centre may have collected personal information of a minor shall inform Kautilya Leadership Centre.
</p>
<div className="bg-red-50 border-l-4 border-red-400 p-4 rounded-r-lg mt-6">
<p className="text-body text-red-800">
<strong>Warning:</strong> Violation of these terms may result in immediate
termination of your access to our services and potential legal action.
</p>
</div>
</div>
</section>
{/* Services Offered */}
<section
id="services-offered"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("services-offered")
}
>
<span className="inline-flex items-center gap-2">
3. Services Offered
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body">
<strong>3.1</strong> This Website is an information gateway to the learning programs ('Course') that Kautilya Leadership Centre extends to eligible Users ('Services'). Apart from providing Services, the Website also hosts the platform for the online payment for subscribing to or buying Course(s) from the Kautilya Leadership Centre for the User(s) who may subscribe for the Services offered by Kautilya Leadership Centre ('Subscriber').
</p>
<p className="text-body">
<strong>3.2</strong> The User(s) or the Subscriber(s) may interact with the agents or employees of Kautilya Leadership Centre using the 'Help' or 'Contact Us' sections on the Website. User(s) are solely responsible for their interactions with the consultants of the Website and any content that the User may share with them.
</p>
<p className="text-body">
<strong>3.3</strong> Kautilya Leadership Centre is not liable for any damage or harm resulting from any posts by or interactions between Users.
</p>
<p className="text-body">
<strong>3.4</strong> Kautilya Leadership Centre reserves the right, but has no obligation, to monitor interactions between the Website (including its consultants) and the Users.
</p>
<div className="bg-blue-50 border-l-4 border-blue-400 p-4 rounded-r-lg mt-6">
<p className="text-body text-blue-800">
<strong>Note:</strong> All interactions and communications through our platform
are subject to monitoring for quality assurance and legal compliance purposes.
</p>
</div>
</div>
</section>
{/* Disclaimer of Warranties & Liability */}
<section
id="disclaimer-warranties"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("disclaimer-warranties")
}
>
<span className="inline-flex items-center gap-2">
4. Disclaimer of Warranties and Liability
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body">
<strong>4.1</strong> While Kautilya Leadership Centre has endeavoured to ensure that all the information on the Website is correct, Kautilya Leadership Centre neither warrants nor makes any representations regarding the quality, accuracy or completeness of any data, information, product, or website (whether any third-party website or this Website) or Service.
</p>
<p className="text-body">
<strong>4.2</strong> Kautilya Leadership Centre and its representatives, directors, employees, agents and affiliates shall not be liable for any loss:
</p>
<ul className="list-disc pl-6 space-y-2 text-body ml-4">
<li>(i) of revenue, income, any direct, special, indirect or consequential loss or damage incurred by the User in connection with the Website; and</li>
<li>(ii) in connection with the use, inability to use, or results of the use of the Website, any webpage or website linked to it and any materials posted on it, including, loss of income or revenue; loss of business; loss of profits or contracts; loss of anticipated savings; loss of data; loss of goodwill; wasted management or office time; and</li>
<li>(iii) of any other kind, however arising and whether caused by tort (including negligence), breach of contract or otherwise, even if foreseeable.</li>
</ul>
<p className="text-body">
<strong>4.3</strong> Kautilya Leadership Centre shall not be liable for any possible hacks (including the database), bugs, malware or faults in our system which may affect the Users, their computer hardware or software in any possible way.
</p>
<p className="text-body">
<strong>4.4</strong> The User agrees to be responsible for their own use of the Website and will use or access this Website in compliance with all applicable laws, regulations and rules of India including, but not limited to, the Information Technology Act, 2000 and the Copyright Act, 1957 and the rules notified thereunder.
</p>
<p className="text-body">
<strong>4.5</strong> The User or Subscriber shall take the responsibility to obtain valid permissions or licenses required on his or her respective devices to be able to utilize the course(s). Kautilya Leadership Centre does not take any responsibility for any act of the User or Subscriber in breach of any such licenses.
</p>
<div className="bg-yellow-50 border-l-4 border-yellow-400 p-4 rounded-r-lg mt-6">
<p className="text-body text-yellow-800">
<strong>Important Legal Notice:</strong> The information provided on this website
is for educational purposes only and does not constitute professional advice.
KLC makes no warranties about the completeness, reliability, or accuracy of this information.
</p>
</div>
</div>
</section>
{/* Privacy Policy */}
<section
id="privacy-policy"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("privacy-policy")
}
>
<span className="inline-flex items-center gap-2">
5. Privacy Policy
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body">
<strong>5.1</strong> The User hereby consents, expresses and agrees that it has read and fully understands the Privacy and Cookie Policy of Kautilya Leadership Centre in respect of the Website. User further consents that the terms of the Privacy and Cookie Policy are acceptable to the User.
</p>
<p className="text-body">
<strong>5.2</strong> Kautilya Leadership Centre reserves the right to update, change or modify the Privacy Policy at any time. The Privacy Policy shall come to effect from the date of such update, change or modification.
</p>
<div className="bg-blue-50 border-l-4 border-blue-400 p-4 rounded-r-lg mt-6">
<p className="text-body text-blue-800">
<strong>Your Privacy Matters:</strong> We are committed to protecting your personal
information. Please review our comprehensive Privacy Policy for detailed information
about how we collect, use, and protect your data.
</p>
</div>
</div>
</section>
{/* Termination */}
<section
id="termination"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("termination")
}
>
<span className="inline-flex items-center gap-2">
6. Termination
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body">
<strong>6.1</strong> Kautilya Leadership Centre retains the right to terminate the User's access to the Website at any time at its sole and absolute discretion, if the User's conduct is in anyway detrimental to the business and or autonomy of Kautilya Leadership Centre.
</p>
<p className="text-body">
<strong>6.2</strong> Kautilya Leadership Centre reserves the right to terminate or discontinue the Website at its sole discretion. Such cessation of operations, whether permanent or temporary, shall be notified to the User in such manner as prescribed by law, if and when such requirement to notify the User is necessitated by law.
</p>
<div className="bg-red-50 border-l-4 border-red-400 p-4 rounded-r-lg mt-6">
<p className="text-body text-red-800">
<strong>Important:</strong> We reserve the right to suspend or terminate access
to our services without prior notice for violations of these terms or conduct
that harms our platform or other users.
</p>
</div>
</div>
</section>
{/* Intellectual Property Rights */}
<section
id="intellectual-property"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("intellectual-property")
}
>
<span className="inline-flex items-center gap-2">
7. Intellectual Property Rights
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body">
<strong>7.1</strong> Unless otherwise indicated or anything contained to the contrary or any proprietary material owned by a third party and so expressly mentioned, Kautilya Leadership Centre owns all Intellectual Property Rights to and into the trademarks 'Kautilya Leadership Centre Pvt. Ltd' and 'leadershipcentre.in', and the Website, including, without limitation, any and all rights, title and interest in and to copyright, related rights, logos, patents, utility models, designs, know-how, trade secrets and inventions (including any patent/s pending), goodwill, source code, meta tags, databases, text, content, graphics, icons, and hyperlinks. User acknowledges and agrees that it shall not use, reproduce or distribute any content from the Website belonging to Kautilya Leadership Centre without obtaining prior written consent.
</p>
<p className="text-body">
<strong>7.2</strong> Notwithstanding the foregoing, it is expressly clarified that User will be solely responsible for any content provided or uploaded by the User when accessing the Website, including any text, data, information, images, photographs, music, sound, video or any other material which may be accessible through the User's post, or any other content that the User may have uploaded, transmitted or stored when accessing the Website.
</p>
<div className="bg-blue-50 border border-blue-200 rounded-lg p-6">
<p className="text-body text-blue-800">
<strong>Copyright Notice:</strong> © 2025 Kautilya Leadership Centre. All rights reserved.
This website and its contents are protected by copyright, trademark, and other intellectual property laws.
</p>
</div>
</div>
</section>
{/* Links to Third-Party */}
<section
id="third-party-links"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("third-party-links")
}
>
<span className="inline-flex items-center gap-2">
8. Links to Third-Party
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body">
<strong>8.1</strong> Any links to third-party websites are provided solely as a convenience to the User, Kautilya Leadership Centre does not endorse the contents on any such third-party websites. Kautilya Leadership Centre is not responsible for the content of, or any damage that may result from User's access to or reliance on such third-party websites and shall be at the sole risk of the User.
</p>
<div className="bg-yellow-50 border-l-4 border-yellow-400 p-4 rounded-r-lg mt-6">
<p className="text-body text-yellow-800">
<strong>Caution:</strong> We are not responsible for the content, privacy policies,
or practices of external sites. Please review their terms and policies before use.
</p>
</div>
</div>
</section>
{/* Indemnity */}
<section
id="indemnity"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("indemnity")
}
>
<span className="inline-flex items-center gap-2">
9. Indemnity
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body">
<strong>9.1</strong> Kautilya Leadership Centre retains the right to take legal course against the User, if the User's act is illegal and or detrimental to the business and or autonomy of Kautilya Leadership Centre or the Website.
</p>
<p className="text-body">
<strong>9.2</strong> The User agrees to indemnify Kautilya Leadership Centre, its affiliates, officers, directors, employees and agents from and against any and all third-party claims, liabilities, damages, losses (direct and indirect) or expenses, including reasonable legal costs, arising out of or in any way connected with the usage of the Website and/or any of its contents, the violation of the terms of these Terms of Use, and violation of any third-party rights.
</p>
<div className="bg-red-50 border-l-4 border-red-400 p-4 rounded-r-lg mt-6">
<p className="text-body text-red-800">
<strong>Legal Protection:</strong> You agree to protect and hold harmless
Kautilya Leadership Centre from any claims arising from your use of our services
or violation of these terms.
</p>
</div>
</div>
</section>
{/* Amendment */}
<section
id="amendment"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("amendment")
}
>
<span className="inline-flex items-center gap-2">
10. Amendment
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body">
<strong>10.1</strong> Kautilya Leadership Centre reserves the right to modify the Terms of Use at any time by posting any modifications on this Website, without advance notice to the User.
</p>
<p className="text-body">
<strong>10.2</strong> Any modification to the Terms of Use will be effective immediately after it is posted on this Website. Use of the Website following the posting of any such modification will constitute the User's acceptance of any such modification.
</p>
<p className="text-body">
<strong>10.3</strong> Notwithstanding the above, such modification shall be notified to the User in such manner as prescribed by law, if and when such requirement to notify the User is necessitated by law.
</p>
<div className="bg-blue-50 border-l-4 border-blue-400 p-4 rounded-r-lg mt-6">
<p className="text-body text-blue-800">
<strong>Your Responsibility:</strong> It is your responsibility to periodically
review these terms for changes. Continued use of our website after modifications
constitutes acceptance of the updated terms.
</p>
</div>
</div>
</section>
{/* Governing Laws & Jurisdiction */}
<section
id="governing-law"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("governing-law")
}
>
<span className="inline-flex items-center gap-2">
11. Governing Laws and Jurisdiction
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body">
<strong>11.1</strong> These Terms of Use shall be governed and construed in accordance with the laws of the Republic of India. Any dispute regarding this Agreement shall be subject to the exclusive jurisdiction of the courts in Mumbai, Maharashtra.
</p>
<div className="bg-gray-50 border border-gray-200 rounded-lg p-6">
<p className="text-body">
<strong>Exclusive Jurisdiction:</strong> Mumbai, Maharashtra, India
</p>
<p className="text-body mt-2">
Any disputes arising from these terms or your use of our services shall be
subject to the exclusive jurisdiction of the courts in Mumbai, Maharashtra, India.
</p>
</div>
<div className="bg-blue-50 border-l-4 border-blue-400 p-4 rounded-r-lg mt-6">
<p className="text-body text-blue-800">
<strong>Legal Framework:</strong> These terms are governed by Indian law and
any legal proceedings must be conducted in Mumbai, Maharashtra courts.
</p>
</div>
</div>
</section>
{/* Grievances */}
<section
id="grievances"
className="mb-12 scroll-mt-24"
>
<h2
className="text-h2 mb-6 group cursor-pointer"
onClick={() =>
scrollToSection("grievances")
}
>
<span className="inline-flex items-center gap-2">
12. Grievances
<ExternalLink className="w-5 h-5 opacity-0 group-hover:opacity-100 transition-opacity" />
</span>
</h2>
<div className="space-y-4">
<p className="text-body">
<strong>12.1</strong> For any queries or grievances regarding Terms of Use, the courses offered on the Website and the subsequent Privacy Policy etc. the user may contact Ms. Diju Shali at connect@leadershipcentre.in
</p>
<div className="bg-green-50 border border-green-200 rounded-lg p-6">
<h3 className="text-h3 mb-4 text-green-800">Contact Information</h3>
<div className="space-y-2">
<p className="text-body text-green-800">
<strong>Grievance Officer:</strong> Ms. Diju Shali
</p>
<p className="text-body text-green-800">
<strong>Email:</strong> <a href="mailto:connect@leadershipcentre.in" className="underline hover:text-green-900">connect@leadershipcentre.in</a>
</p>
<p className="text-body text-green-800">
<strong>Organization:</strong> Kautilya Leadership Centre Pvt. Ltd
</p>
</div>
</div>
<div className="bg-blue-50 border-l-4 border-blue-400 p-4 rounded-r-lg mt-6">
<p className="text-body text-blue-800">
<strong>Support Commitment:</strong> We are committed to addressing your concerns
promptly and fairly. Please provide detailed information about your query or grievance
to help us assist you better.
</p>
</div>
</div>
</section>
{/* Important Notice */}
<section className="mb-12">
<div className="bg-gradient-to-r from-blue-50 to-yellow-50 border-2 border-primary rounded-lg p-8">
<h3 className="text-h3 mb-4 text-primary">
Legal Compliance & Updates
</h3>
<p className="text-body mb-4">
These Terms of Use are effective as of the last updated date mentioned above.
By using our website, you acknowledge that you have read, understood, and agree
to be bound by these terms.
</p>
<p className="text-body text-muted">
For the most current version of our Terms of Use, please visit this page regularly.
If you have any questions about these terms, please contact us using the information provided above.
</p>
</div>
</section>
{/* Download Section */}
<section className="mb-12">
<div className="text-center bg-gray-50 border border-gray-200 rounded-lg p-8">
<h3 className="text-h3 mb-4">Download Terms & Conditions</h3>
<p className="text-body text-muted mb-6">
Save a copy of these terms for your records
</p>
<button
onClick={() => window.print()}
className="inline-flex items-center gap-2 px-6 py-3 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors"
>
<Download className="w-4 h-4" />
Download PDF
</button>
</div>
</section>
</div>
</div>
</div>
</div>
</div>
</div>
);
}

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