Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53dfa83aa3 | ||
|
|
6d31c3b96f | ||
| 3fc97d24fc | |||
|
|
ec9cbca147 | ||
|
|
d69747addc | ||
|
|
637ca3bc03 | ||
| a1c1d90491 | |||
|
|
b907162457 | ||
|
|
f0b4719282 | ||
|
|
6f72f1c828 | ||
|
|
3e1f2ca425 | ||
|
|
dc072361f8 | ||
|
|
b5a8f0f9c3 | ||
|
|
06518400a7 | ||
|
|
f540b267f8 | ||
|
|
609edb06bf | ||
|
|
35de76d259 | ||
|
|
5f2f319a33 | ||
|
|
d2548f2408 | ||
|
|
d3e301cbbb | ||
|
|
5a11fa4771 | ||
|
|
7b8fe79917 | ||
|
|
c67ace8edb | ||
|
|
9163e046d0 | ||
|
|
38a7b6a260 | ||
|
|
43b894604d | ||
|
|
8020a17fdd | ||
|
|
819f7a6865 | ||
|
|
2a857ef729 | ||
|
|
cff8173987 |
34
.gitea/workflows/build.yml
Normal 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
|
||||||
67
.gitea/workflows/compressimage.yml
Normal 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 }}
|
||||||
39
.gitea/workflows/sonar.yml
Normal 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
@@ -53,6 +53,7 @@
|
|||||||
"sonner": "^2.0.3",
|
"sonner": "^2.0.3",
|
||||||
"tailwind-merge": "*",
|
"tailwind-merge": "*",
|
||||||
"tailwindcss": "^4.1.12",
|
"tailwindcss": "^4.1.12",
|
||||||
|
"three": "^0.183.2",
|
||||||
"vaul": "^1.1.2"
|
"vaul": "^1.1.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -60,6 +61,7 @@
|
|||||||
"@types/react": "^19.1.12",
|
"@types/react": "^19.1.12",
|
||||||
"@types/react-dom": "^19.1.8",
|
"@types/react-dom": "^19.1.8",
|
||||||
"@types/react-slick": "^0.23.13",
|
"@types/react-slick": "^0.23.13",
|
||||||
|
"@types/three": "^0.183.1",
|
||||||
"@vitejs/plugin-react": "^5.0.2",
|
"@vitejs/plugin-react": "^5.0.2",
|
||||||
"@vitejs/plugin-react-swc": "^3.10.2",
|
"@vitejs/plugin-react-swc": "^3.10.2",
|
||||||
"vite": "^6.3.5"
|
"vite": "^6.3.5"
|
||||||
|
|||||||
28
src/App.tsx
@@ -22,7 +22,6 @@ import { SelfLearnerSignUp } from "./components/SelfLearnerSignUp";
|
|||||||
import { Consulting } from "./components/services/Consulting";
|
import { Consulting } from "./components/services/Consulting";
|
||||||
import { CultureCompetence } from "./components/services/CultureCompetence";
|
import { CultureCompetence } from "./components/services/CultureCompetence";
|
||||||
import { ExecutiveCoaching } from "./components/services/ExecutiveCoaching";
|
import { ExecutiveCoaching } from "./components/services/ExecutiveCoaching";
|
||||||
import { LeadershipDevelopment } from "./components/services/LeadershipDevelopment";
|
|
||||||
import { LearningFacility } from "./components/services/LearningFacility";
|
import { LearningFacility } from "./components/services/LearningFacility";
|
||||||
import { ManagementDevelopment } from "./components/services/ManagementDevelopment";
|
import { ManagementDevelopment } from "./components/services/ManagementDevelopment";
|
||||||
import { Terms } from "./components/Terms";
|
import { Terms } from "./components/Terms";
|
||||||
@@ -32,9 +31,15 @@ import { WebinarsPage } from "./components/WebinarsPage";
|
|||||||
import HomePage from './pages/HomePage';
|
import HomePage from './pages/HomePage';
|
||||||
import { AboutUs } from './components/AboutUs';
|
import { AboutUs } from './components/AboutUs';
|
||||||
import { Services } from './components/Services';
|
import { Services } from './components/Services';
|
||||||
import { LearningFacilityNew } from './components/LearningFacilityNew';
|
|
||||||
import HomePageNew from './pages/HomePageNew';
|
|
||||||
import { FooterNew } from './components/FooterNew';
|
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 EnrollPlaceholder from "./components/EnrollPlaceholder";
|
||||||
// import ForgotPasswordPlaceholder from "./components/ForgotPasswordPlaceholder";
|
// import ForgotPasswordPlaceholder from "./components/ForgotPasswordPlaceholder";
|
||||||
// import DashboardPlaceholder from "./components/DashboardPlaceholder";
|
// import DashboardPlaceholder from "./components/DashboardPlaceholder";
|
||||||
@@ -58,13 +63,15 @@ export default function App() {
|
|||||||
<Route path="/leadership-journey" element={<LeadershipJourneyPage />} />
|
<Route path="/leadership-journey" element={<LeadershipJourneyPage />} />
|
||||||
|
|
||||||
{/* Services Pages */}
|
{/* 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/consulting" element={<Consulting />} />
|
||||||
<Route path="/services/culture-competence" element={<CultureCompetence />} />
|
<Route path="/services/culture-competence" element={<CultureCompetence />} />
|
||||||
<Route path="/services/executive-coaching" element={<ExecutiveCoaching />} />
|
<Route path="/services/executive-coaching" element={<ExecutiveCoaching />} />
|
||||||
<Route path="/services/management-development" element={<ManagementDevelopment />} />
|
<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" element={<Services />} />
|
||||||
|
<Route path="/services/kautilya-facility" element={<KautilyaFacility />} />
|
||||||
|
|
||||||
{/* About Us Pages */}
|
{/* About Us Pages */}
|
||||||
<Route path="/about/our-vision" element={<OurVision />} />
|
<Route path="/about/our-vision" element={<OurVision />} />
|
||||||
@@ -79,6 +86,7 @@ export default function App() {
|
|||||||
|
|
||||||
{/* Webinars Pages */}
|
{/* Webinars Pages */}
|
||||||
<Route path="/webinars" element={<WebinarsPage />} />
|
<Route path="/webinars" element={<WebinarsPage />} />
|
||||||
|
<Route path="/webinars/:webinar_id" element={<WebinarDetail />} />
|
||||||
<Route path="/webinars-legacy" element={<WebinarsListing />} />
|
<Route path="/webinars-legacy" element={<WebinarsListing />} />
|
||||||
|
|
||||||
{/* Learning Online */}
|
{/* Learning Online */}
|
||||||
@@ -101,15 +109,19 @@ export default function App() {
|
|||||||
<Route path="/contact" element={<Contact />} />
|
<Route path="/contact" element={<Contact />} />
|
||||||
|
|
||||||
{/* Dynamic Routes */}
|
{/* Dynamic Routes */}
|
||||||
<Route path="/learning/articles/:slug" element={<BlogDetail />} />
|
<Route path="/learning/articles/:slugAndId" element={<BlogDetail />} />
|
||||||
<Route path="/learning/blogs/:slug" element={<BlogDetail />} />
|
{/* <Route path="/learning/blogs/:slug" element={<BlogDetail />} /> */}
|
||||||
{/* <Route path="/learning/webcast/:slug" element={<WebinarDetail />} />
|
{/* <Route path="/learning/webcast/:slug" element={<WebinarDetail />} />
|
||||||
<Route path="/webinar/:slug" element={<WebinarDetail />} /> */}
|
<Route path="/webinar/:slug" element={<WebinarDetail />} /> */}
|
||||||
<Route path="/course/:slug" element={<ProgrammeDetail />} />
|
<Route path="/course/:slug" element={<ProgrammeDetail />} />
|
||||||
<Route path="/programme/:slug" element={<ProgrammeDetail />} />
|
<Route path="/programme/:slug" element={<ProgrammeDetail />} />
|
||||||
|
|
||||||
{/* Learning Facility */}
|
{/* 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 */}
|
{/* Placeholder Pages */}
|
||||||
{/* <Route path="/enroll" element={<EnrollPlaceholder />} />
|
{/* <Route path="/enroll" element={<EnrollPlaceholder />} />
|
||||||
|
|||||||
BIN
src/assets/Aparna-Nair.png
Normal file
|
After Width: | Height: | Size: 289 KiB |
BIN
src/assets/Balaji-Chandrakumar.jpeg
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
src/assets/Campus1.jpg
Normal file
|
After Width: | Height: | Size: 3.7 MiB |
BIN
src/assets/Campus2.jpg
Normal file
|
After Width: | Height: | Size: 3.7 MiB |
BIN
src/assets/Campus3.jpg
Normal file
|
After Width: | Height: | Size: 6.4 MiB |
BIN
src/assets/Campus4.jpg
Normal file
|
After Width: | Height: | Size: 4.2 MiB |
BIN
src/assets/Campus5.jpg
Normal file
|
After Width: | Height: | Size: 4.3 MiB |
BIN
src/assets/Campus6.jpg
Normal file
|
After Width: | Height: | Size: 3.6 MiB |
BIN
src/assets/Classroom1.jpg
Normal file
|
After Width: | Height: | Size: 3.3 MiB |
BIN
src/assets/Classroom2.jpg
Normal file
|
After Width: | Height: | Size: 2.8 MiB |
BIN
src/assets/Classroom3.jpg
Normal file
|
After Width: | Height: | Size: 3.9 MiB |
BIN
src/assets/Classroom4.jpg
Normal file
|
After Width: | Height: | Size: 3.4 MiB |
BIN
src/assets/Classroom5.jpg
Normal file
|
After Width: | Height: | Size: 3.5 MiB |
BIN
src/assets/Diju.jpeg
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
src/assets/Eco-Conscious Design.jpg
Normal file
|
After Width: | Height: | Size: 6.4 MiB |
BIN
src/assets/Fitness&Recreation-centre.jpg
Normal file
|
After Width: | Height: | Size: 3.8 MiB |
BIN
src/assets/K-Ramkumar.png
Normal file
|
After Width: | Height: | Size: 457 KiB |
BIN
src/assets/Kautilya.png
Normal file
|
After Width: | Height: | Size: 363 KiB |
BIN
src/assets/R-Muralidharan.png
Normal file
|
After Width: | Height: | Size: 289 KiB |
BIN
src/assets/Ramesh-Padmanabhan.jpeg
Normal file
|
After Width: | Height: | Size: 32 KiB |
1
src/assets/accenture.svg
Normal 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 |
1
src/assets/adani-logo.svg
Normal 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
@@ -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
|
After Width: | Height: | Size: 2.8 MiB |
BIN
src/assets/campus-arial-view.jpg
Normal file
|
After Width: | Height: | Size: 4.6 MiB |
1
src/assets/ceat-logo.svg
Normal 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 |
BIN
src/assets/dining-experience.jpg
Normal file
|
After Width: | Height: | Size: 3.0 MiB |
BIN
src/assets/exe-boardroom.jpg
Normal file
|
After Width: | Height: | Size: 4.7 MiB |
BIN
src/assets/expert-support.jpg
Normal file
|
After Width: | Height: | Size: 3.9 MiB |
1
src/assets/flipkart.svg
Normal file
|
After Width: | Height: | Size: 21 KiB |
1
src/assets/hsbc.svg
Normal 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 |
1
src/assets/idbi-bank-logo.svg
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
src/assets/kautilya-learning-story.jpg
Normal file
|
After Width: | Height: | Size: 4.1 MiB |
1
src/assets/larsen-toubro-logo.svg
Normal 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
@@ -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 |
BIN
src/assets/luxury-accommodation.jpg
Normal file
|
After Width: | Height: | Size: 3.4 MiB |
BIN
src/assets/morning.jpg
Normal file
|
After Width: | Height: | Size: 3.7 MiB |
BIN
src/assets/outdoor-amphitheater.jpg
Normal file
|
After Width: | Height: | Size: 4.5 MiB |
BIN
src/assets/panoramas/cayley_interior.jpg
Normal file
|
After Width: | Height: | Size: 778 KiB |
BIN
src/assets/panoramas/cedar_bridge_sunset.jpg
Normal file
|
After Width: | Height: | Size: 387 KiB |
BIN
src/assets/private-space.jpg
Normal file
|
After Width: | Height: | Size: 3.3 MiB |
1
src/assets/tata-motors.svg
Normal 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 |
BIN
src/assets/v-Swaminathan.jpg
Normal file
|
After Width: | Height: | Size: 53 KiB |
@@ -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 {
|
import {
|
||||||
Search,
|
|
||||||
Calendar,
|
|
||||||
User,
|
|
||||||
ArrowRight,
|
|
||||||
BookOpen,
|
BookOpen,
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
Filter,
|
Filter,
|
||||||
Grid,
|
Grid,
|
||||||
List,
|
List,
|
||||||
SortAsc,
|
Search,
|
||||||
Clock,
|
|
||||||
Eye,
|
|
||||||
Star,
|
|
||||||
ChevronLeft,
|
|
||||||
ChevronRight,
|
|
||||||
X
|
X
|
||||||
} from 'lucide-react';
|
} 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
|
// Define category type with ID and name
|
||||||
const articles = [
|
interface CategoryOption {
|
||||||
{
|
id: string;
|
||||||
id: '1',
|
name: string;
|
||||||
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.',
|
type DateRange =
|
||||||
content: 'Leadership development is evolving rapidly in response to changing workplace dynamics, technological advances, and new generations entering the workforce...',
|
| 'all_time'
|
||||||
author: 'Sarah Johnson',
|
| 'last_7_days'
|
||||||
authorTitle: 'Senior Leadership Consultant',
|
| 'last_30_days'
|
||||||
authorAvatar: 'https://images.unsplash.com/photo-1494790108755-2616b612b47c?w=150&h=150&fit=crop&crop=face',
|
| 'last_3_months'
|
||||||
date: '2024-02-20',
|
| 'last_6_months';
|
||||||
readTime: '8 min read',
|
|
||||||
category: 'Leadership Development',
|
type SortBy =
|
||||||
tags: ['Future of Work', 'Leadership Trends', 'Innovation', 'Strategy'],
|
| 'most_recent'
|
||||||
thumbnail: 'https://images.unsplash.com/photo-1552664730-d307ca884978?w=600&h=400&fit=crop',
|
| 'oldest_first'
|
||||||
featured: true,
|
| 'title_az';
|
||||||
views: '2.4k',
|
|
||||||
likes: 45
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
slug: 'emotional-intelligence-in-leadership',
|
|
||||||
title: 'Emotional Intelligence: The Key to Effective Leadership',
|
|
||||||
excerpt: 'Discover how emotional intelligence impacts leadership effectiveness and learn practical strategies to develop your EQ.',
|
|
||||||
content: 'Emotional intelligence has become increasingly recognized as a critical factor in leadership success...',
|
|
||||||
author: 'Dr. Michael Chen',
|
|
||||||
authorTitle: 'Executive Coach',
|
|
||||||
authorAvatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=150&h=150&fit=crop&crop=face',
|
|
||||||
date: '2024-02-18',
|
|
||||||
readTime: '6 min read',
|
|
||||||
category: 'Personal Development',
|
|
||||||
tags: ['Emotional Intelligence', 'Leadership Skills', 'Communication', 'Self-Awareness'],
|
|
||||||
thumbnail: 'https://images.unsplash.com/photo-1559027615-cd4628902d4a?w=600&h=400&fit=crop',
|
|
||||||
featured: true,
|
|
||||||
views: '1.8k',
|
|
||||||
likes: 32
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
slug: 'building-high-performing-teams',
|
|
||||||
title: 'Building High-Performing Teams: A Leader\'s Guide',
|
|
||||||
excerpt: 'Learn the essential strategies for creating and maintaining high-performing teams that deliver exceptional results.',
|
|
||||||
content: 'High-performing teams don\'t happen by accident. They require intentional leadership, clear purpose, and the right environment...',
|
|
||||||
author: 'Lisa Rodriguez',
|
|
||||||
authorTitle: 'Team Development Specialist',
|
|
||||||
authorAvatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=150&h=150&fit=crop&crop=face',
|
|
||||||
date: '2024-02-15',
|
|
||||||
readTime: '10 min read',
|
|
||||||
category: 'Team Development',
|
|
||||||
tags: ['Team Building', 'Performance', 'Collaboration', 'Leadership'],
|
|
||||||
thumbnail: 'https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=600&h=400&fit=crop',
|
|
||||||
featured: false,
|
|
||||||
views: '3.1k',
|
|
||||||
likes: 58
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '4',
|
|
||||||
slug: 'digital-transformation-leadership',
|
|
||||||
title: 'Leading Digital Transformation: A Strategic Approach',
|
|
||||||
excerpt: 'Navigate the complexities of digital transformation with proven leadership strategies and best practices.',
|
|
||||||
content: 'Digital transformation is reshaping industries and organizations worldwide. Successful transformation requires strong leadership...',
|
|
||||||
author: 'David Park',
|
|
||||||
authorTitle: 'Digital Strategy Consultant',
|
|
||||||
authorAvatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=150&h=150&fit=crop&crop=face',
|
|
||||||
date: '2024-02-12',
|
|
||||||
readTime: '12 min read',
|
|
||||||
category: 'Digital Transformation',
|
|
||||||
tags: ['Digital Strategy', 'Change Management', 'Technology', 'Innovation'],
|
|
||||||
thumbnail: 'https://images.unsplash.com/photo-1560472355-536de3962603?w=600&h=400&fit=crop',
|
|
||||||
featured: false,
|
|
||||||
views: '2.7k',
|
|
||||||
likes: 41
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '5',
|
|
||||||
slug: 'crisis-leadership-strategies',
|
|
||||||
title: 'Crisis Leadership: Navigating Uncertainty with Confidence',
|
|
||||||
excerpt: 'Master the art of crisis leadership and learn how to guide your organization through challenging times.',
|
|
||||||
content: 'Crisis situations test the mettle of leaders and reveal the true strength of an organization...',
|
|
||||||
author: 'Jennifer Adams',
|
|
||||||
authorTitle: 'Crisis Management Expert',
|
|
||||||
authorAvatar: 'https://images.unsplash.com/photo-1580489944761-15a19d654956?w=150&h=150&fit=crop&crop=face',
|
|
||||||
date: '2024-02-10',
|
|
||||||
readTime: '9 min read',
|
|
||||||
category: 'Crisis Management',
|
|
||||||
tags: ['Crisis Leadership', 'Risk Management', 'Decision Making', 'Communication'],
|
|
||||||
thumbnail: 'https://images.unsplash.com/photo-1584697964358-3e14ca57658b?w=600&h=400&fit=crop',
|
|
||||||
featured: false,
|
|
||||||
views: '1.9k',
|
|
||||||
likes: 35
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '6',
|
|
||||||
slug: 'sustainable-leadership-practices',
|
|
||||||
title: 'Sustainable Leadership: Building for the Long Term',
|
|
||||||
excerpt: 'Explore sustainable leadership practices that create lasting value for organizations and stakeholders.',
|
|
||||||
content: 'Sustainable leadership goes beyond short-term gains to create lasting value for all stakeholders...',
|
|
||||||
author: 'Robert Kim',
|
|
||||||
authorTitle: 'Sustainability Consultant',
|
|
||||||
authorAvatar: 'https://images.unsplash.com/photo-1560250097-0b93528c311a?w=150&h=150&fit=crop&crop=face',
|
|
||||||
date: '2024-02-08',
|
|
||||||
readTime: '7 min read',
|
|
||||||
category: 'Strategy',
|
|
||||||
tags: ['Sustainability', 'ESG', 'Long-term Thinking', 'Stakeholder Value'],
|
|
||||||
thumbnail: 'https://images.unsplash.com/photo-1542601906990-b4d3fb778b09?w=600&h=400&fit=crop',
|
|
||||||
featured: false,
|
|
||||||
views: '1.5k',
|
|
||||||
likes: 28
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
export function Articles() {
|
export function Articles() {
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [selectedCategory, setSelectedCategory] = useState('All Categories');
|
const [selectedCategory, setSelectedCategory] = useState<CategoryOption>({ id: 'all', name: 'All Categories' });
|
||||||
const [selectedAuthor, setSelectedAuthor] = useState('All Authors');
|
|
||||||
const [selectedReadTime, setSelectedReadTime] = useState('All Read Times');
|
const [selectedReadTime, setSelectedReadTime] = useState('All Read Times');
|
||||||
const [selectedDateRange, setSelectedDateRange] = useState('All Time');
|
const [selectedDateRange, setSelectedDateRange] = useState<DateRange>('all_time');
|
||||||
const [selectedTopic, setSelectedTopic] = useState('All Topics');
|
const [selectedTopic, setSelectedTopic] = useState<{
|
||||||
const [sortBy, setSortBy] = useState('Most Recent');
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}>({
|
||||||
|
id: 'all',
|
||||||
|
name: 'All Topics'
|
||||||
|
});
|
||||||
|
const [sortBy, setSortBy] = useState<SortBy>('most_recent');
|
||||||
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid');
|
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid');
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
const [debouncedSearch, setDebouncedSearch] = useState('');
|
||||||
const articlesPerPage = 6;
|
const articlesPerPage = 6;
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const [allTags, setAllTags] = useState<{ id: string; name: string }[]>([]);
|
||||||
|
|
||||||
// Get unique values for filters
|
// Fetch categories for filter dropdown
|
||||||
const categories = ['All Categories', ...Array.from(new Set(articles.map(article => article.category)))];
|
const {
|
||||||
const authors = ['All Authors', ...Array.from(new Set(articles.map(article => article.author)))];
|
data: categoriesData,
|
||||||
const readTimes = ['All Read Times', 'Under 5 min', '5-10 min', 'Over 10 min'];
|
isLoading: isLoadingCategories
|
||||||
const dateRanges = ['All Time', 'Last 7 days', 'Last 30 days', 'Last 3 months'];
|
} = useGetFaqCategoriesQuery({
|
||||||
const allTags = Array.from(new Set(articles.flatMap(article => article.tags)));
|
limit: 100,
|
||||||
const sortOptions = [
|
offset: 0
|
||||||
{ value: 'Most Recent', label: 'Most Recent' },
|
|
||||||
{ value: 'oldest', label: 'Oldest First' },
|
|
||||||
{ value: 'title', label: 'Title A-Z' },
|
|
||||||
{ value: 'readTime', label: 'Read Time' },
|
|
||||||
{ value: 'popular', label: 'Most Popular' }
|
|
||||||
];
|
|
||||||
|
|
||||||
// Filter and sort articles
|
|
||||||
const filteredArticles = articles.filter(article => {
|
|
||||||
const matchesSearch = article.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
||||||
article.excerpt.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
||||||
article.author.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
||||||
article.tags.some(tag => tag.toLowerCase().includes(searchTerm.toLowerCase()));
|
|
||||||
const matchesCategory = selectedCategory === 'All Categories' || article.category === selectedCategory;
|
|
||||||
const matchesAuthor = selectedAuthor === 'All Authors' || article.author === selectedAuthor;
|
|
||||||
|
|
||||||
// Read time filter
|
|
||||||
const readTimeMinutes = parseInt(article.readTime);
|
|
||||||
const matchesReadTime = selectedReadTime === 'All Read Times' ||
|
|
||||||
(selectedReadTime === 'Under 5 min' && readTimeMinutes < 5) ||
|
|
||||||
(selectedReadTime === '5-10 min' && readTimeMinutes >= 5 && readTimeMinutes <= 10) ||
|
|
||||||
(selectedReadTime === 'Over 10 min' && readTimeMinutes > 10);
|
|
||||||
|
|
||||||
// Date range filter
|
|
||||||
const articleDate = new Date(article.date);
|
|
||||||
const now = new Date();
|
|
||||||
const matchesDateRange = selectedDateRange === 'All Time' ||
|
|
||||||
(selectedDateRange === 'Last 7 days' && (now.getTime() - articleDate.getTime()) <= 7 * 24 * 60 * 60 * 1000) ||
|
|
||||||
(selectedDateRange === 'Last 30 days' && (now.getTime() - articleDate.getTime()) <= 30 * 24 * 60 * 60 * 1000) ||
|
|
||||||
(selectedDateRange === 'Last 3 months' && (now.getTime() - articleDate.getTime()) <= 90 * 24 * 60 * 60 * 1000);
|
|
||||||
|
|
||||||
// Topic filter
|
|
||||||
const matchesTopic = selectedTopic === 'All Topics' ||
|
|
||||||
article.tags.includes(selectedTopic);
|
|
||||||
|
|
||||||
return matchesSearch && matchesCategory && matchesAuthor && matchesReadTime && matchesDateRange && matchesTopic;
|
|
||||||
}).sort((a, b) => {
|
|
||||||
switch (sortBy) {
|
|
||||||
case 'Most Recent':
|
|
||||||
return new Date(b.date).getTime() - new Date(a.date).getTime();
|
|
||||||
case 'oldest':
|
|
||||||
return new Date(a.date).getTime() - new Date(b.date).getTime();
|
|
||||||
case 'title':
|
|
||||||
return a.title.localeCompare(b.title);
|
|
||||||
case 'readTime':
|
|
||||||
return parseInt(a.readTime) - parseInt(b.readTime);
|
|
||||||
case 'popular':
|
|
||||||
return parseInt(b.views) - parseInt(a.views);
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Paginate results
|
// Filter categories to only those for blog and create options with id and name
|
||||||
const totalPages = Math.ceil(filteredArticles.length / articlesPerPage);
|
const categories: CategoryOption[] = [
|
||||||
const startIndex = (currentPage - 1) * articlesPerPage;
|
{ id: 'all', name: 'All Categories' },
|
||||||
const currentArticles = filteredArticles.slice(startIndex, startIndex + articlesPerPage);
|
...(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) => {
|
const formatDate = (dateString: string) => {
|
||||||
return new Date(dateString).toLocaleDateString('en-US', {
|
return new Date(dateString).toLocaleDateString('en-US', {
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
@@ -222,27 +130,94 @@ export function Articles() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearAllFilters = () => {
|
// Calculate read time based on content length (approx)
|
||||||
setSearchTerm('');
|
const calculateReadTime = (content: string): string => {
|
||||||
setSelectedCategory('All Categories');
|
const wordsPerMinute = 200;
|
||||||
setSelectedAuthor('All Authors');
|
const wordCount = content.split(/\s+/).length;
|
||||||
setSelectedReadTime('All Read Times');
|
const minutes = Math.ceil(wordCount / wordsPerMinute);
|
||||||
setSelectedDateRange('All Time');
|
return `${minutes} min read`;
|
||||||
setSelectedTopic('All Topics');
|
|
||||||
setSortBy('Most Recent');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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 ||
|
const finalFilteredArticles = getReadTimeFilteredArticles();
|
||||||
selectedCategory !== 'All Categories' ||
|
|
||||||
selectedAuthor !== 'All Authors' ||
|
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' ||
|
selectedReadTime !== 'All Read Times' ||
|
||||||
selectedDateRange !== 'All Time' ||
|
selectedDateRange !== 'all_time' ||
|
||||||
selectedTopic !== 'All Topics';
|
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 (
|
return (
|
||||||
<div style={{ backgroundColor: '#FFFFFF' }}>
|
<div style={{ backgroundColor: '#FFFFFF' }} ref={containerRef}>
|
||||||
{/* Hero Section */}
|
{/* Hero Section */}
|
||||||
<section className="relative py-28 overflow-hidden">
|
<section className="relative py-28 overflow-hidden">
|
||||||
<div className="absolute inset-0">
|
<div className="absolute inset-0">
|
||||||
@@ -277,7 +252,7 @@ export function Articles() {
|
|||||||
<div className="section-margin-x">
|
<div className="section-margin-x">
|
||||||
<div className="grid grid-cols-3 gap-8 text-center">
|
<div className="grid grid-cols-3 gap-8 text-center">
|
||||||
<div>
|
<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 className="text-small-white">Expert Articles</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -285,8 +260,8 @@ export function Articles() {
|
|||||||
<div className="text-small-white">Categories</div>
|
<div className="text-small-white">Categories</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-h2-white mb-2">25,400</div>
|
<div className="text-h2-white mb-2">{allTags.length}+</div>
|
||||||
<div className="text-small-white">Total Reads</div>
|
<div className="text-small-white">Topics</div>
|
||||||
</div>
|
</div>
|
||||||
</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" />
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search articles, authors, topics..."
|
placeholder="Search articles..."
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
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"
|
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,8 +295,7 @@ export function Articles() {
|
|||||||
<div className="flex items-center border border-gray-300 rounded-lg overflow-hidden">
|
<div className="flex items-center border border-gray-300 rounded-lg overflow-hidden">
|
||||||
<button
|
<button
|
||||||
onClick={() => setViewMode('grid')}
|
onClick={() => setViewMode('grid')}
|
||||||
className={`p-2 transition-colors ${
|
className={`p-2 transition-colors ${viewMode === 'grid'
|
||||||
viewMode === 'grid'
|
|
||||||
? 'text-white'
|
? 'text-white'
|
||||||
: 'bg-white text-gray-600 hover:bg-gray-50'
|
: 'bg-white text-gray-600 hover:bg-gray-50'
|
||||||
}`}
|
}`}
|
||||||
@@ -334,8 +308,7 @@ export function Articles() {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setViewMode('list')}
|
onClick={() => setViewMode('list')}
|
||||||
className={`p-2 transition-colors ${
|
className={`p-2 transition-colors ${viewMode === 'list'
|
||||||
viewMode === 'list'
|
|
||||||
? 'text-white'
|
? 'text-white'
|
||||||
: 'bg-white text-gray-600 hover:bg-gray-50'
|
: 'bg-white text-gray-600 hover:bg-gray-50'
|
||||||
}`}
|
}`}
|
||||||
@@ -406,46 +379,37 @@ export function Articles() {
|
|||||||
<label className="block text-small mb-2 font-medium text-gray-700">
|
<label className="block text-small mb-2 font-medium text-gray-700">
|
||||||
Category
|
Category
|
||||||
</label>
|
</label>
|
||||||
<Select value={selectedCategory} onValueChange={setSelectedCategory}>
|
<Select
|
||||||
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors" style={{ '&:focus': { borderColor: 'var(--color-primary)' } }}>
|
value={selectedCategory.id}
|
||||||
<SelectValue placeholder="All Categories" />
|
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>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{categories.map((category) => (
|
{categories.map((category) => (
|
||||||
<SelectItem key={category} value={category} className="text-small">
|
<SelectItem key={category.id} value={category.id} className="text-small">
|
||||||
{category}
|
{category.name}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Author Filter */}
|
{/* Read Time Filter - Client-side only */}
|
||||||
<div className="filter-section">
|
|
||||||
<label className="block text-small mb-2 font-medium text-gray-700">
|
|
||||||
Author
|
|
||||||
</label>
|
|
||||||
<Select value={selectedAuthor} onValueChange={setSelectedAuthor}>
|
|
||||||
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors" style={{ '&:focus': { borderColor: 'var(--color-primary)' } }}>
|
|
||||||
<SelectValue placeholder="All Authors" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{authors.map((author) => (
|
|
||||||
<SelectItem key={author} value={author} className="text-small">
|
|
||||||
{author}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Read Time Filter */}
|
|
||||||
<div className="filter-section">
|
<div className="filter-section">
|
||||||
<label className="block text-small mb-2 font-medium text-gray-700">
|
<label className="block text-small mb-2 font-medium text-gray-700">
|
||||||
Read Time
|
Read Time
|
||||||
</label>
|
</label>
|
||||||
<Select value={selectedReadTime} onValueChange={setSelectedReadTime}>
|
<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" />
|
<SelectValue placeholder="All Read Times" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
@@ -464,13 +428,13 @@ export function Articles() {
|
|||||||
Date Range
|
Date Range
|
||||||
</label>
|
</label>
|
||||||
<Select value={selectedDateRange} onValueChange={setSelectedDateRange}>
|
<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" />
|
<SelectValue placeholder="All Time" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{dateRanges.map((dateRange) => (
|
{dateRanges.map((range) => (
|
||||||
<SelectItem key={dateRange} value={dateRange} className="text-small">
|
<SelectItem key={range.value} value={range.value}>
|
||||||
{dateRange}
|
{range.label}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
@@ -482,17 +446,28 @@ export function Articles() {
|
|||||||
<label className="block text-small mb-2 font-medium text-gray-700">
|
<label className="block text-small mb-2 font-medium text-gray-700">
|
||||||
Topic
|
Topic
|
||||||
</label>
|
</label>
|
||||||
<Select value={selectedTopic} onValueChange={setSelectedTopic}>
|
<Select
|
||||||
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors" style={{ '&:focus': { borderColor: 'var(--color-primary)' } }}>
|
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" />
|
<SelectValue placeholder="All Topics" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="All Topics" className="text-small">
|
<SelectItem value="all">
|
||||||
All Topics
|
All Topics
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
|
|
||||||
{allTags.map((tag) => (
|
{allTags.map((tag) => (
|
||||||
<SelectItem key={tag} value={tag} className="text-small">
|
<SelectItem key={tag.id} value={tag.id}>
|
||||||
{tag}
|
{tag.name}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
@@ -507,73 +482,85 @@ export function Articles() {
|
|||||||
{/* Right Content Area - Scrollable Articles */}
|
{/* Right Content Area - Scrollable Articles */}
|
||||||
<div className="col-span-12 lg:col-span-9">
|
<div className="col-span-12 lg:col-span-9">
|
||||||
<div className="mb-4 text-small text-muted">
|
<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>
|
</div>
|
||||||
|
|
||||||
{/* Articles Results */}
|
{/* Articles Results */}
|
||||||
{currentArticles.length === 0 ? (
|
{finalFilteredArticles.length === 0 ? (
|
||||||
<div className="text-center py-12">
|
<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">
|
<p className="text-body-lg text-muted">
|
||||||
No articles found matching your criteria.
|
No articles found matching your criteria.
|
||||||
</p>
|
</p>
|
||||||
|
{hasActiveFilters && (
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={clearAllFilters}
|
||||||
|
className="mt-4"
|
||||||
|
>
|
||||||
|
Clear Filters
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{/* Grid View */}
|
{/* Grid View */}
|
||||||
{viewMode === 'grid' && (
|
{viewMode === 'grid' && (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
{currentArticles.map((article) => (
|
{finalFilteredArticles.map((article: BlogItem) => (
|
||||||
<Card
|
<Card
|
||||||
key={article.id}
|
key={article.id}
|
||||||
className="overflow-hidden hover:shadow-lg transition-all duration-300 cursor-pointer group"
|
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">
|
<div className="aspect-video w-full bg-gray-100 overflow-hidden relative">
|
||||||
<ImageWithFallback
|
<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}
|
alt={article.title}
|
||||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
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>
|
</div>
|
||||||
<CardContent className="p-6">
|
<CardContent className="p-6">
|
||||||
<div className="flex items-center gap-2 mb-3">
|
<div className="flex items-center gap-2 mb-3">
|
||||||
<Badge variant="outline" className="text-small">
|
<Badge variant="outline" className="text-small">
|
||||||
{article.category}
|
{article.content_category}
|
||||||
</Badge>
|
</Badge>
|
||||||
<span className="text-small text-muted">{article.readTime}</span>
|
<span className="text-small text-muted">
|
||||||
|
{calculateReadTime(article.content)}
|
||||||
|
</span>
|
||||||
</div>
|
</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}
|
{article.title}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<p className="text-small text-muted mb-4 line-clamp-3">
|
<p className="text-small text-muted mb-4 line-clamp-3">
|
||||||
{article.excerpt}
|
{article.short_description || article.content.substring(0, 150) + '...'}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-end gap-2">
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<ImageWithFallback
|
|
||||||
src={article.authorAvatar}
|
|
||||||
alt={article.author}
|
|
||||||
className="w-6 h-6 rounded-full object-cover"
|
|
||||||
/>
|
|
||||||
<span className="text-small">{article.author}</span>
|
|
||||||
</div>
|
|
||||||
<div className="text-small text-muted">
|
<div className="text-small text-muted">
|
||||||
{formatDate(article.date)}
|
{formatDate(article.updated_at)}
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
@@ -583,29 +570,23 @@ export function Articles() {
|
|||||||
{/* List View */}
|
{/* List View */}
|
||||||
{viewMode === 'list' && (
|
{viewMode === 'list' && (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{currentArticles.map((article) => (
|
{finalFilteredArticles.map((article: BlogItem) => (
|
||||||
<Card
|
<Card
|
||||||
key={article.id}
|
key={article.id}
|
||||||
className="overflow-hidden hover:shadow-lg transition-all duration-300 cursor-pointer group"
|
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="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">
|
<div className="md:w-80 h-48 md:h-auto bg-gray-100 overflow-hidden relative flex-shrink-0">
|
||||||
<ImageWithFallback
|
<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}
|
alt={article.title}
|
||||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
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>
|
||||||
|
|
||||||
<div className="flex-1 p-6">
|
<div className="flex-1 p-6">
|
||||||
@@ -613,38 +594,39 @@ export function Articles() {
|
|||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<Badge variant="outline" className="text-small">
|
<Badge variant="outline" className="text-small">
|
||||||
{article.category}
|
{article.content_category}
|
||||||
</Badge>
|
</Badge>
|
||||||
<span className="text-small text-muted">{article.readTime}</span>
|
<span className="text-small text-muted">
|
||||||
<div className="flex items-center gap-1 text-small text-muted">
|
{calculateReadTime(article.content)}
|
||||||
<Eye className="w-3 h-3" />
|
</span>
|
||||||
<span>{article.views}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</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}
|
{article.title}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<p className="text-body text-muted mb-3">
|
<p className="text-body text-muted mb-3">
|
||||||
{article.excerpt}
|
{article.short_description || article.content.substring(0, 200) + '...'}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="flex items-center justify-between">
|
<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">
|
<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">
|
||||||
|
{article.blog_tags.slice(0, 2).map((tag, idx) => (
|
||||||
|
<Badge key={idx} variant="secondary" className="text-xs">
|
||||||
|
{tag.tag_name}
|
||||||
|
</Badge>
|
||||||
|
))}
|
||||||
|
{article.blog_tags.length > 2 && (
|
||||||
|
<span className="text-xs text-gray-500">+{article.blog_tags.length - 2}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -657,29 +639,71 @@ export function Articles() {
|
|||||||
|
|
||||||
{/* Pagination */}
|
{/* Pagination */}
|
||||||
{totalPages > 1 && (
|
{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
|
<Button
|
||||||
variant="outline"
|
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}
|
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
|
Previous
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<span className="text-body">
|
<div className="flex items-center gap-1">
|
||||||
Page {currentPage} of {totalPages}
|
{Array.from({ length: totalPages }, (_, i) => {
|
||||||
</span>
|
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
|
<Button
|
||||||
variant="outline"
|
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}
|
disabled={currentPage === totalPages}
|
||||||
className="text-body"
|
className="flex items-center gap-1 border-gray-300 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
Next
|
Next
|
||||||
<ChevronRight className="w-4 h-4 ml-2" />
|
<ChevronRight className="w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import { Button } from './ui/button';
|
import { Button } from './ui/button';
|
||||||
import { Card, CardContent } from './ui/card';
|
import { Card, CardContent } from './ui/card';
|
||||||
import { Badge } from './ui/badge';
|
import { Badge } from './ui/badge';
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from './ui/avatar';
|
import { Avatar, AvatarFallback, AvatarImage } from './ui/avatar';
|
||||||
import { navigateTo } from './Router';
|
|
||||||
import { ImageWithFallback } from './figma/ImageWithFallback';
|
import { ImageWithFallback } from './figma/ImageWithFallback';
|
||||||
import { CTABannerSection } from './CTABannerSection';
|
import { CTABannerSection } from './CTABannerSection';
|
||||||
import { useCart } from './CartContext';
|
import { useCart } from './CartContext';
|
||||||
import {
|
import {
|
||||||
Calendar,
|
Calendar,
|
||||||
Clock,
|
Clock,
|
||||||
ArrowRight,
|
|
||||||
ChevronUp,
|
ChevronUp,
|
||||||
Share2,
|
|
||||||
Bookmark,
|
Bookmark,
|
||||||
Twitter,
|
Twitter,
|
||||||
Facebook,
|
Facebook,
|
||||||
@@ -21,410 +19,77 @@ import {
|
|||||||
Heart,
|
Heart,
|
||||||
Eye,
|
Eye,
|
||||||
BookOpen,
|
BookOpen,
|
||||||
ArrowLeft,
|
ArrowLeft
|
||||||
Star,
|
|
||||||
MessageCircle,
|
|
||||||
Users,
|
|
||||||
TrendingUp,
|
|
||||||
Award
|
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { useGetBlogByIDQuery, useGetBlogsQuery } from '../redux/services/blogApi';
|
||||||
interface BlogPost {
|
import { FullScreenLoader } from './FullScreenLoader';
|
||||||
id: string;
|
import { extractIdFromSlug, extractSlugFromSlugAndId, getSlugWithId } from '../utils/urlHelpers';
|
||||||
title: string;
|
|
||||||
slug: string;
|
|
||||||
excerpt: string;
|
|
||||||
content: string;
|
|
||||||
author: string;
|
|
||||||
authorBio: string;
|
|
||||||
authorImage: string;
|
|
||||||
publishedDate: string;
|
|
||||||
updatedDate?: string;
|
|
||||||
readTime: string;
|
|
||||||
views: number;
|
|
||||||
likes: number;
|
|
||||||
category: string;
|
|
||||||
tags: string[];
|
|
||||||
image: string;
|
|
||||||
featured: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RelatedPost {
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
slug: string;
|
|
||||||
excerpt: string;
|
|
||||||
author: string;
|
|
||||||
publishedDate: string;
|
|
||||||
readTime: string;
|
|
||||||
category: string;
|
|
||||||
image: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BlogDetailProps {
|
interface BlogDetailProps {
|
||||||
params?: {
|
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) {
|
export function BlogDetail({ params }: BlogDetailProps) {
|
||||||
const slug = params?.slug || '';
|
const { slugAndId } = useParams<{ slugAndId: string }>();
|
||||||
|
const navigate = useNavigate();
|
||||||
const [scrollProgress, setScrollProgress] = useState(0);
|
const [scrollProgress, setScrollProgress] = useState(0);
|
||||||
const [showBackToTop, setShowBackToTop] = useState(false);
|
const [showBackToTop, setShowBackToTop] = useState(false);
|
||||||
const [isLiked, setIsLiked] = useState(false);
|
const [isLiked, setIsLiked] = useState(false);
|
||||||
const [isBookmarked, setIsBookmarked] = useState(false);
|
const [isBookmarked, setIsBookmarked] = useState(false);
|
||||||
const { addToCart } = useCart();
|
const { addToCart } = useCart();
|
||||||
|
|
||||||
// Get blog post data based on slug
|
// Extract full ID from URL using the new function
|
||||||
const blogPost = getBlogPostBySlug(slug);
|
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(() => {
|
useEffect(() => {
|
||||||
|
if (blogPost?.title) {
|
||||||
document.title = `${blogPost.title} | KLC Blog`;
|
document.title = `${blogPost.title} | KLC Blog`;
|
||||||
|
}
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
|
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
@@ -438,7 +103,7 @@ export function BlogDetail({ params }: BlogDetailProps) {
|
|||||||
|
|
||||||
window.addEventListener('scroll', handleScroll);
|
window.addEventListener('scroll', handleScroll);
|
||||||
return () => window.removeEventListener('scroll', handleScroll);
|
return () => window.removeEventListener('scroll', handleScroll);
|
||||||
}, [blogPost.title]);
|
}, [blogPost?.title]);
|
||||||
|
|
||||||
const formatDate = (dateString: string) => {
|
const formatDate = (dateString: string) => {
|
||||||
return new Date(dateString).toLocaleDateString('en-US', {
|
return new Date(dateString).toLocaleDateString('en-US', {
|
||||||
@@ -450,7 +115,7 @@ export function BlogDetail({ params }: BlogDetailProps) {
|
|||||||
|
|
||||||
const handleShare = (platform: string) => {
|
const handleShare = (platform: string) => {
|
||||||
const url = window.location.href;
|
const url = window.location.href;
|
||||||
const title = blogPost.title;
|
const title = blogPost?.title || '';
|
||||||
|
|
||||||
const shareUrls = {
|
const shareUrls = {
|
||||||
twitter: `https://twitter.com/intent/tweet?text=${encodeURIComponent(title)}&url=${encodeURIComponent(url)}`,
|
twitter: `https://twitter.com/intent/tweet?text=${encodeURIComponent(title)}&url=${encodeURIComponent(url)}`,
|
||||||
@@ -471,6 +136,64 @@ export function BlogDetail({ params }: BlogDetailProps) {
|
|||||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
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 (
|
return (
|
||||||
<div className="min-h-screen" style={{ backgroundColor: '#FFFFFF' }}>
|
<div className="min-h-screen" style={{ backgroundColor: '#FFFFFF' }}>
|
||||||
{/* Scroll Progress Bar */}
|
{/* Scroll Progress Bar */}
|
||||||
@@ -506,7 +229,7 @@ export function BlogDetail({ params }: BlogDetailProps) {
|
|||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => navigateTo('/learning/articles')}
|
onClick={() => navigate('/learning/articles')}
|
||||||
className="p-0 h-auto font-medium hover:bg-transparent transition-colors"
|
className="p-0 h-auto font-medium hover:bg-transparent transition-colors"
|
||||||
style={{ color: '#6F6F6F' }}
|
style={{ color: '#6F6F6F' }}
|
||||||
>
|
>
|
||||||
@@ -514,7 +237,7 @@ export function BlogDetail({ params }: BlogDetailProps) {
|
|||||||
Back to Articles
|
Back to Articles
|
||||||
</Button>
|
</Button>
|
||||||
<span className="text-[#E5E7EB]">•</span>
|
<span className="text-[#E5E7EB]">•</span>
|
||||||
<span>{blogPost.category}</span>
|
<span>{blogPost.content_category || 'Article'}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -533,7 +256,7 @@ export function BlogDetail({ params }: BlogDetailProps) {
|
|||||||
color: '#04045B'
|
color: '#04045B'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{blogPost.category}
|
{blogPost.content_category || 'Article'}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|
||||||
{/* Improved Typography Hierarchy */}
|
{/* Improved Typography Hierarchy */}
|
||||||
@@ -544,7 +267,7 @@ export function BlogDetail({ params }: BlogDetailProps) {
|
|||||||
{/* Constrained Width Excerpt for Better Readability */}
|
{/* Constrained Width Excerpt for Better Readability */}
|
||||||
<div className="max-w-3xl">
|
<div className="max-w-3xl">
|
||||||
<p className="text-body-lg leading-relaxed" style={{ color: '#6F6F6F' }}>
|
<p className="text-body-lg leading-relaxed" style={{ color: '#6F6F6F' }}>
|
||||||
{blogPost.excerpt}
|
{blogPost.short_description || blogPost.content.substring(0, 200) + '...'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -557,33 +280,33 @@ export function BlogDetail({ params }: BlogDetailProps) {
|
|||||||
{/* Author Info with Improved Layout */}
|
{/* Author Info with Improved Layout */}
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<Avatar className="w-14 h-14 ring-2 ring-white shadow-md">
|
<Avatar className="w-14 h-14 ring-2 ring-white shadow-md">
|
||||||
<AvatarImage src={blogPost.authorImage} alt={blogPost.author} />
|
<AvatarImage src="https://images.unsplash.com/photo-1494790108755-2616b612b47c?w=150&h=150&fit=crop" alt="Author" />
|
||||||
<AvatarFallback className="text-subhead font-medium">{blogPost.author.split(' ').map(n => n[0]).join('')}</AvatarFallback>
|
<AvatarFallback className="text-subhead font-medium">KLC</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<div>
|
<div>
|
||||||
<div className="text-subhead font-medium mb-1" style={{ color: '#26231A' }}>
|
<div className="text-subhead font-medium mb-1" style={{ color: '#26231A' }}>
|
||||||
{blogPost.author}
|
KLC Team
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Cleaner Meta Information with Subtle Dividers */}
|
{/* Cleaner Meta Information with Subtle Dividers */}
|
||||||
<div className="flex items-center gap-4 text-small" style={{ color: '#6F6F6F' }}>
|
<div className="flex items-center gap-4 text-small" style={{ color: '#6F6F6F' }}>
|
||||||
<span className="flex items-center gap-1.5">
|
<span className="flex items-center gap-1.5">
|
||||||
<Calendar className="w-4 h-4" />
|
<Calendar className="w-4 h-4" />
|
||||||
{formatDate(blogPost.publishedDate)}
|
{formatDate(blogPost.updated_at || new Date().toISOString())}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div className="w-1 h-1 rounded-full" style={{ backgroundColor: '#E5E7EB' }}></div>
|
<div className="w-1 h-1 rounded-full" style={{ backgroundColor: '#E5E7EB' }}></div>
|
||||||
|
|
||||||
<span className="flex items-center gap-1.5">
|
<span className="flex items-center gap-1.5">
|
||||||
<Clock className="w-4 h-4" />
|
<Clock className="w-4 h-4" />
|
||||||
{blogPost.readTime}
|
{calculateReadTime(blogPost.content)}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div className="w-1 h-1 rounded-full" style={{ backgroundColor: '#E5E7EB' }}></div>
|
<div className="w-1 h-1 rounded-full" style={{ backgroundColor: '#E5E7EB' }}></div>
|
||||||
|
|
||||||
<span className="flex items-center gap-1.5">
|
<span className="flex items-center gap-1.5">
|
||||||
<Eye className="w-4 h-4" />
|
<Eye className="w-4 h-4" />
|
||||||
{blogPost.views.toLocaleString()}
|
0
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -598,7 +321,7 @@ export function BlogDetail({ params }: BlogDetailProps) {
|
|||||||
className={`transition-colors ${isLiked ? 'text-red-500' : 'text-[#6F6F6F]'}`}
|
className={`transition-colors ${isLiked ? 'text-red-500' : 'text-[#6F6F6F]'}`}
|
||||||
>
|
>
|
||||||
<Heart className={`w-4 h-4 mr-2 ${isLiked ? 'fill-current' : ''}`} />
|
<Heart className={`w-4 h-4 mr-2 ${isLiked ? 'fill-current' : ''}`} />
|
||||||
{blogPost.likes + (isLiked ? 1 : 0)}
|
0
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@@ -628,7 +351,7 @@ export function BlogDetail({ params }: BlogDetailProps) {
|
|||||||
{/* Featured Image with Better Aspect Ratio */}
|
{/* Featured Image with Better Aspect Ratio */}
|
||||||
<div className="aspect-[16/9] rounded-xl overflow-hidden mt-8 shadow-lg">
|
<div className="aspect-[16/9] rounded-xl overflow-hidden mt-8 shadow-lg">
|
||||||
<ImageWithFallback
|
<ImageWithFallback
|
||||||
src={blogPost.image}
|
src={blogPost.banner_img || 'https://images.unsplash.com/photo-1552664730-d307ca884978?w=1200&h=600&fit=crop'}
|
||||||
alt={blogPost.title}
|
alt={blogPost.title}
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
/>
|
/>
|
||||||
@@ -666,21 +389,22 @@ export function BlogDetail({ params }: BlogDetailProps) {
|
|||||||
fontFamily: 'var(--font-family-base)',
|
fontFamily: 'var(--font-family-base)',
|
||||||
color: '#26231A',
|
color: '#26231A',
|
||||||
width: '100%'
|
width: '100%'
|
||||||
}}
|
} as React.CSSProperties}
|
||||||
dangerouslySetInnerHTML={{ __html: blogPost.content }}
|
dangerouslySetInnerHTML={{ __html: blogPost.content }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
{/* Enhanced Tag Pills with Hover States */}
|
{/* Enhanced Tag Pills with Hover States */}
|
||||||
|
{blogPost.blog_tags && blogPost.blog_tags.length > 0 && (
|
||||||
<div className="mb-16">
|
<div className="mb-16">
|
||||||
<h3 className="text-subhead mb-6 font-medium" style={{ color: '#26231A' }}>
|
<h3 className="text-subhead mb-6 font-medium" style={{ color: '#26231A' }}>
|
||||||
Topics covered in this article
|
Topics covered in this article
|
||||||
</h3>
|
</h3>
|
||||||
<div className="flex flex-wrap gap-3">
|
<div className="flex flex-wrap gap-3">
|
||||||
{blogPost.tags.map((tag) => (
|
{blogPost.blog_tags.map((tag: any) => (
|
||||||
<Badge
|
<Badge
|
||||||
key={tag}
|
key={tag.tag_name}
|
||||||
className="transition-all duration-200 text-body px-4 py-2 font-medium"
|
className="transition-all duration-200 text-body px-4 py-2 font-medium"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: 'rgba(4, 4, 91, 0.08)',
|
backgroundColor: 'rgba(4, 4, 91, 0.08)',
|
||||||
@@ -688,26 +412,27 @@ export function BlogDetail({ params }: BlogDetailProps) {
|
|||||||
border: '1px solid rgba(4, 4, 91, 0.15)'
|
border: '1px solid rgba(4, 4, 91, 0.15)'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{tag}
|
{tag.tag_name}
|
||||||
</Badge>
|
</Badge>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Enhanced Author Bio Card */}
|
{/* Enhanced Author Bio Card */}
|
||||||
<Card className="mb-16 shadow-md border-0" style={{ backgroundColor: '#FFFFFF' }}>
|
<Card className="mb-16 shadow-md border-0" style={{ backgroundColor: '#FFFFFF' }}>
|
||||||
<CardContent className="p-8">
|
<CardContent className="p-8">
|
||||||
<div className="flex items-start gap-6">
|
<div className="flex items-start gap-6">
|
||||||
<Avatar className="w-20 h-20 ring-4 ring-white shadow-lg">
|
<Avatar className="w-20 h-20 ring-4 ring-white shadow-lg">
|
||||||
<AvatarImage src={blogPost.authorImage} alt={blogPost.author} />
|
<AvatarImage src="https://images.unsplash.com/photo-1494790108755-2616b612b47c?w=150&h=150&fit=crop" alt="Author" />
|
||||||
<AvatarFallback className="text-lg font-medium">{blogPost.author.split(' ').map(n => n[0]).join('')}</AvatarFallback>
|
<AvatarFallback className="text-lg font-medium">KLC</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<h4 className="text-h4 mb-3 font-semibold" style={{ color: '#26231A' }}>
|
<h4 className="text-h4 mb-3 font-semibold" style={{ color: '#26231A' }}>
|
||||||
About {blogPost.author}
|
About KLC Team
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-body leading-relaxed mb-6" style={{ color: '#6F6F6F' }}>
|
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -717,6 +442,7 @@ export function BlogDetail({ params }: BlogDetailProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Related Articles Section with Balanced Grid Layout */}
|
{/* Related Articles Section with Balanced Grid Layout */}
|
||||||
|
{relatedPosts.length > 0 && (
|
||||||
<section className="py-20" style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}>
|
<section className="py-20" style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}>
|
||||||
<div className="section-margin-x">
|
<div className="section-margin-x">
|
||||||
<div className="max-w-6xl mx-auto">
|
<div className="max-w-6xl mx-auto">
|
||||||
@@ -735,11 +461,15 @@ export function BlogDetail({ params }: BlogDetailProps) {
|
|||||||
|
|
||||||
{/* Balanced Card Grid with Equal Spacing */}
|
{/* Balanced Card Grid with Equal Spacing */}
|
||||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||||
{relatedPosts.map((post) => (
|
{relatedPosts.map((post: any) => (
|
||||||
<Card
|
<Card
|
||||||
key={post.id}
|
key={post.id}
|
||||||
className="overflow-hidden hover:shadow-xl transition-all duration-300 cursor-pointer group border-0"
|
className="overflow-hidden hover:shadow-xl transition-all duration-300 cursor-pointer group border-0"
|
||||||
onClick={() => navigateTo(`/learning/articles/${post.slug}`)}
|
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' }}
|
style={{ backgroundColor: '#FFFFFF' }}
|
||||||
>
|
>
|
||||||
<div className="aspect-[16/10] w-full bg-gray-100 overflow-hidden relative">
|
<div className="aspect-[16/10] w-full bg-gray-100 overflow-hidden relative">
|
||||||
@@ -806,6 +536,7 @@ export function BlogDetail({ params }: BlogDetailProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* CTA Section */}
|
{/* CTA Section */}
|
||||||
<CTABannerSection />
|
<CTABannerSection />
|
||||||
|
|||||||
@@ -4,14 +4,44 @@ import { BrandedTag } from "./about/BrandedTag";
|
|||||||
import { PrimaryCTAButton } from "./PrimaryCTAButton";
|
import { PrimaryCTAButton } from "./PrimaryCTAButton";
|
||||||
import { navigateTo } from "./Router";
|
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 (
|
return (
|
||||||
<section className="relative h-[700px] overflow-hidden">
|
<section className="relative h-[700px] overflow-hidden">
|
||||||
{/* Background Image */}
|
{/* Background Image */}
|
||||||
<div className="absolute inset-0">
|
<div className="absolute inset-0">
|
||||||
<ImageWithFallback
|
<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"
|
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="Professional team collaborating in modern office"
|
alt="Background image for call to action section"
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -36,9 +66,9 @@ export function CTABannerSection() {
|
|||||||
|
|
||||||
{/* Main Headline */}
|
{/* Main Headline */}
|
||||||
<h2
|
<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
|
<span
|
||||||
className="italic"
|
className="italic"
|
||||||
style={{ color: 'var(--color-brand-accent)' }}
|
style={{ color: 'var(--color-brand-accent)' }}
|
||||||
@@ -48,20 +78,22 @@ export function CTABannerSection() {
|
|||||||
to start your development journey now.
|
to start your development journey now.
|
||||||
</h2>
|
</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
|
<PrimaryCTAButton
|
||||||
text="Schedule a Consultation"
|
text={ctaSection.cta_text || "Schedule a Consultation"}
|
||||||
onClick={() => navigateTo('/contact?topic=consulting')}
|
onClick={() => navigateTo(ctaSection.cta_destination || '/contact?topic=consulting')}
|
||||||
ariaLabel="Schedule a consultation with our leadership experts"
|
ariaLabel="Schedule a consultation with our leadership experts"
|
||||||
className="cta-banner-yellow"
|
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>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -14,21 +14,22 @@ export interface CartItem {
|
|||||||
originalPrice?: string;
|
originalPrice?: string;
|
||||||
category: string;
|
category: string;
|
||||||
level: string;
|
level: string;
|
||||||
|
type?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CartPopupProps {
|
interface CartPopupProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
cartItems: CartItem[]; // 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
|
// onRemoveItem: (itemId: string) => void; // Legacy prop - no longer used but kept for backward compatibility
|
||||||
recentlyAddedItem?: CartItem | null;
|
recentlyAddedItem?: CartItem | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CartPopup({
|
export function CartPopup({
|
||||||
isOpen,
|
isOpen,
|
||||||
onClose,
|
onClose,
|
||||||
cartItems: legacyCartItems, // Renamed to avoid confusion
|
// cartItems: legacyCartItems, // Renamed to avoid confusion
|
||||||
onRemoveItem: legacyOnRemoveItem, // Renamed to avoid confusion
|
// onRemoveItem: legacyOnRemoveItem, // Renamed to avoid confusion
|
||||||
recentlyAddedItem
|
recentlyAddedItem
|
||||||
}: CartPopupProps) {
|
}: CartPopupProps) {
|
||||||
const [showSuccess, setShowSuccess] = useState(false);
|
const [showSuccess, setShowSuccess] = useState(false);
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import { BrandedTag } from './about/BrandedTag';
|
|||||||
import { PrimaryCTAButton } from './PrimaryCTAButton';
|
import { PrimaryCTAButton } from './PrimaryCTAButton';
|
||||||
import { ImageWithFallback } from './figma/ImageWithFallback';
|
import { ImageWithFallback } from './figma/ImageWithFallback';
|
||||||
import { navigateTo } from './Router';
|
import { navigateTo } from './Router';
|
||||||
|
import kautilyabg from '../assets/Kautilya.png';
|
||||||
|
import { useCreateLeadMutation, useGetLeadCategoriesQuery } from '../redux/services/contactUsApi';
|
||||||
|
|
||||||
interface ContactProps {
|
interface ContactProps {
|
||||||
topic?: string;
|
topic?: string;
|
||||||
@@ -15,29 +17,35 @@ interface ContactProps {
|
|||||||
|
|
||||||
export function Contact({ topic }: ContactProps) {
|
export function Contact({ topic }: ContactProps) {
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
firstName: '',
|
name: '',
|
||||||
lastName: '',
|
mobileNumber: '',
|
||||||
email: '',
|
emailId: '',
|
||||||
phone: '',
|
interestedIn: '',
|
||||||
company: '',
|
message: ''
|
||||||
jobTitle: '',
|
|
||||||
subject: topic || '',
|
|
||||||
message: '',
|
|
||||||
source: ''
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const [isSubmitted, setIsSubmitted] = useState(false);
|
const [isSubmitted, setIsSubmitted] = useState(false);
|
||||||
const [isSubmitting, setIsSubmitting] = 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(() => {
|
useEffect(() => {
|
||||||
console.log('Contact component mounted with topic:', topic);
|
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) {
|
if (topic) {
|
||||||
const subject = getTopicSubject(topic);
|
const interestedIn = getTopicSubject(topic);
|
||||||
console.log('Setting form subject to:', subject);
|
console.log('Setting form interestedIn to:', interestedIn);
|
||||||
setFormData(prev => ({
|
setFormData(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
subject: subject
|
interestedIn: interestedIn
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}, [topic]);
|
}, [topic]);
|
||||||
@@ -68,26 +76,42 @@ export function Contact({ topic }: ContactProps) {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
|
|
||||||
// Simulate form submission
|
try {
|
||||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
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);
|
setIsSubmitted(true);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Lead creation failed", error);
|
||||||
|
}
|
||||||
|
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isSubmitted) {
|
if (isSubmitted) {
|
||||||
return (
|
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="section-margin-x py-24">
|
||||||
<div className="max-w-2xl mx-auto text-center">
|
<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">
|
<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" />
|
<CheckCircle2 className="w-10 h-10 text-green-600" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 className="text-h2 mb-4">Thank You!</h1>
|
<h1 className="text-h2 mb-4">Thank You!</h1>
|
||||||
|
|
||||||
<p className="text-body-lg text-muted mb-8">
|
<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.
|
Our leadership development experts are excited to help transform your organization.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<div className="flex justify-center">
|
||||||
<PrimaryCTAButton
|
<PrimaryCTAButton
|
||||||
text="Return to Home"
|
text="Return to Home"
|
||||||
onClick={() => navigateTo('/')}
|
onClick={() => navigateTo('/')}
|
||||||
@@ -96,23 +120,24 @@ export function Contact({ topic }: ContactProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen" style={{ backgroundColor: '#F7F7FD' }}>
|
<div className="min-h-screen" style={{ backgroundColor: '#FFFFFF' }}>
|
||||||
{/* Hero Section with CTA Banner Styling */}
|
{/* Hero Section with Google Map Background */}
|
||||||
<section className="relative h-[600px] overflow-hidden">
|
<section className="relative h-[600px] overflow-hidden">
|
||||||
{/* Background Image */}
|
{/* Google Map Background */}
|
||||||
<div className="absolute inset-0">
|
<div className="absolute inset-0">
|
||||||
<ImageWithFallback
|
<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"
|
src={kautilyabg}
|
||||||
alt="Professional business meeting and consultation"
|
alt="Google Maps showing office location and navigation"
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* 40% black overlay for better text readability */}
|
{/* Dark overlay for better text readability */}
|
||||||
<div className="absolute inset-0 bg-black/70" />
|
<div className="absolute inset-0 bg-black/60" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content Container */}
|
{/* Content Container */}
|
||||||
@@ -120,22 +145,16 @@ export function Contact({ topic }: ContactProps) {
|
|||||||
{/* Hero Content Block */}
|
{/* Hero Content Block */}
|
||||||
<div className="text-center max-w-4xl mx-auto">
|
<div className="text-center max-w-4xl mx-auto">
|
||||||
{/* Branded Tag */}
|
{/* Branded Tag */}
|
||||||
<BrandedTag text="Get In Touch" variant="white" />
|
<BrandedTag text="Contact Us" variant="white" />
|
||||||
|
|
||||||
{/* Main Headline */}
|
{/* Main Headline */}
|
||||||
<h1 className="text-h1-white mb-6">
|
<h1 className="text-h1-white mb-6">
|
||||||
Ready to transform your leadership?
|
Reach us
|
||||||
<span
|
|
||||||
className="italic"
|
|
||||||
style={{ color: 'var(--color-brand-accent)' }}
|
|
||||||
>
|
|
||||||
{" "}Let's connect{" "}
|
|
||||||
</span>
|
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
{/* Supporting Text */}
|
{/* Supporting Text */}
|
||||||
<p className="text-body-lg-white opacity-90 max-w-3xl mx-auto">
|
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -161,7 +180,7 @@ export function Contact({ topic }: ContactProps) {
|
|||||||
|
|
||||||
{/* Contact Details */}
|
{/* Contact Details */}
|
||||||
<div className="p-8 space-y-8">
|
<div className="p-8 space-y-8">
|
||||||
{/* Office Location */}
|
{/* Corporate Office */}
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-4">
|
||||||
<div
|
<div
|
||||||
className="w-12 h-12 rounded-lg flex items-center justify-center flex-shrink-0"
|
className="w-12 h-12 rounded-lg flex items-center justify-center flex-shrink-0"
|
||||||
@@ -170,28 +189,31 @@ export function Contact({ topic }: ContactProps) {
|
|||||||
<MapPin 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>
|
||||||
<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">
|
<p className="text-body text-muted">
|
||||||
123 Leadership Avenue<br />
|
Leadership Centre Pvt. Ltd.<br />
|
||||||
Business District, New Delhi<br />
|
No. 107, 1st Floor<br />
|
||||||
110001, India
|
T.V. Industrial Estate, Worli<br />
|
||||||
|
Mumbai 400030
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Phone */}
|
{/* Registered Office */}
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-4">
|
||||||
<div
|
<div
|
||||||
className="w-12 h-12 rounded-lg flex items-center justify-center flex-shrink-0"
|
className="w-12 h-12 rounded-lg flex items-center justify-center flex-shrink-0"
|
||||||
style={{ backgroundColor: 'rgba(248, 195, 1, 0.1)' }}
|
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>
|
||||||
<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">
|
<p className="text-body text-muted">
|
||||||
+91 11 4567 8900<br />
|
Kautilya Leadership Services<br />
|
||||||
+91 98765 43210
|
R.S. No. 5/6, Savaroli Kharpada Road<br />
|
||||||
|
Dhamani Taluk, Khalapur District<br />
|
||||||
|
Raigad – 410202
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -207,26 +229,25 @@ export function Contact({ topic }: ContactProps) {
|
|||||||
<div>
|
<div>
|
||||||
<h3 className="text-h3 mb-2">Email</h3>
|
<h3 className="text-h3 mb-2">Email</h3>
|
||||||
<p className="text-body text-muted">
|
<p className="text-body text-muted">
|
||||||
info@klc.edu<br />
|
connect@leadershipcentre.in<br />
|
||||||
leadership@klc.edu
|
connect@kautilyaservices.in
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Business Hours */}
|
{/* Phone */}
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-4">
|
||||||
<div
|
<div
|
||||||
className="w-12 h-12 rounded-lg flex items-center justify-center flex-shrink-0"
|
className="w-12 h-12 rounded-lg flex items-center justify-center flex-shrink-0"
|
||||||
style={{ backgroundColor: 'rgba(248, 195, 1, 0.1)' }}
|
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>
|
||||||
<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">
|
<p className="text-body text-muted">
|
||||||
Monday - Friday: 9:00 AM - 6:00 PM<br />
|
+91-22-2084 0097<br />
|
||||||
Saturday: 10:00 AM - 4:00 PM<br />
|
+91-8928738661
|
||||||
Sunday: Closed
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -280,7 +301,7 @@ export function Contact({ topic }: ContactProps) {
|
|||||||
>
|
>
|
||||||
<Send className="w-5 h-5" style={{ color: 'var(--color-brand-primary)' }} />
|
<Send className="w-5 h-5" style={{ color: 'var(--color-brand-primary)' }} />
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-h3">Send us a Message</h2>
|
<h2 className="text-h3">Enquiry Form</h2>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-body text-muted">
|
<p className="text-body text-muted">
|
||||||
Fill out the form below and we'll get back to you within 24 hours.
|
Fill out the form below and we'll get back to you within 24 hours.
|
||||||
@@ -290,17 +311,16 @@ export function Contact({ topic }: ContactProps) {
|
|||||||
{/* Form Content */}
|
{/* Form Content */}
|
||||||
<div className="p-8">
|
<div className="p-8">
|
||||||
<form onSubmit={handleSubmit} className="space-y-6">
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
||||||
<div>
|
<div>
|
||||||
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
|
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
|
||||||
First Name *
|
Name *
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
required
|
required
|
||||||
value={formData.firstName}
|
value={formData.name}
|
||||||
onChange={(e) => handleInputChange('firstName', e.target.value)}
|
onChange={(e) => handleInputChange('name', e.target.value)}
|
||||||
placeholder="John"
|
placeholder="Enter your full name"
|
||||||
className="w-full h-12 border-gray-200 focus:border-blue-500 focus:ring-blue-500"
|
className="w-full h-12 border-gray-200 focus:border-blue-500 focus:ring-blue-500"
|
||||||
style={{
|
style={{
|
||||||
fontFamily: 'var(--font-family-base)',
|
fontFamily: 'var(--font-family-base)',
|
||||||
@@ -308,51 +328,16 @@ export function Contact({ topic }: ContactProps) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</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>
|
<div>
|
||||||
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
|
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
|
||||||
Email Address *
|
Mobile Number *
|
||||||
</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>
|
</label>
|
||||||
<Input
|
<Input
|
||||||
type="tel"
|
type="tel"
|
||||||
value={formData.phone}
|
required
|
||||||
onChange={(e) => handleInputChange('phone', e.target.value)}
|
value={formData.mobileNumber}
|
||||||
|
onChange={(e) => handleInputChange('mobileNumber', e.target.value)}
|
||||||
placeholder="+91 98765 43210"
|
placeholder="+91 98765 43210"
|
||||||
className="w-full h-12 border-gray-200 focus:border-blue-500 focus:ring-blue-500"
|
className="w-full h-12 border-gray-200 focus:border-blue-500 focus:ring-blue-500"
|
||||||
style={{
|
style={{
|
||||||
@@ -361,18 +346,17 @@ export function Contact({ topic }: ContactProps) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
||||||
<div>
|
<div>
|
||||||
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
|
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
|
||||||
Company/Organization
|
Email Id *
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="email"
|
||||||
value={formData.company}
|
required
|
||||||
onChange={(e) => handleInputChange('company', e.target.value)}
|
value={formData.emailId}
|
||||||
placeholder="Company Name"
|
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"
|
className="w-full h-12 border-gray-200 focus:border-blue-500 focus:ring-blue-500"
|
||||||
style={{
|
style={{
|
||||||
fontFamily: 'var(--font-family-base)',
|
fontFamily: 'var(--font-family-base)',
|
||||||
@@ -380,32 +364,14 @@ export function Contact({ topic }: ContactProps) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</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>
|
<div>
|
||||||
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
|
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
|
||||||
Subject/Interest Area *
|
Interested In *
|
||||||
</label>
|
</label>
|
||||||
<Select
|
<Select
|
||||||
value={formData.subject}
|
value={formData.interestedIn}
|
||||||
onValueChange={(value) => handleInputChange('subject', value)}
|
onValueChange={(value: string) => handleInputChange('interestedIn', value)}
|
||||||
>
|
>
|
||||||
<SelectTrigger
|
<SelectTrigger
|
||||||
className="w-full h-12 border-gray-200 focus:border-blue-500 focus:ring-blue-500"
|
className="w-full h-12 border-gray-200 focus:border-blue-500 focus:ring-blue-500"
|
||||||
@@ -414,60 +380,29 @@ export function Contact({ topic }: ContactProps) {
|
|||||||
fontSize: 'var(--font-body)'
|
fontSize: 'var(--font-body)'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectValue placeholder="Select a subject" />
|
<SelectValue placeholder="Select your area of interest" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<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>
|
<SelectContent>
|
||||||
<SelectItem value="Website">Website</SelectItem>
|
{categories.map((cat: any) => (
|
||||||
<SelectItem value="Google Search">Google Search</SelectItem>
|
<SelectItem key={cat.id} value={cat.id}>
|
||||||
<SelectItem value="Social Media">Social Media</SelectItem>
|
{cat.category_type}
|
||||||
<SelectItem value="Referral">Referral</SelectItem>
|
</SelectItem>
|
||||||
<SelectItem value="Event/Conference">Event/Conference</SelectItem>
|
))}
|
||||||
<SelectItem value="LinkedIn">LinkedIn</SelectItem>
|
</SelectContent>
|
||||||
<SelectItem value="Email Marketing">Email Marketing</SelectItem>
|
|
||||||
<SelectItem value="Other">Other</SelectItem>
|
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
|
<label className="text-body font-medium mb-3 block" style={{ color: 'var(--color-black)' }}>
|
||||||
Message *
|
Enter Message *
|
||||||
</label>
|
</label>
|
||||||
<Textarea
|
<Textarea
|
||||||
required
|
required
|
||||||
value={formData.message}
|
value={formData.message}
|
||||||
onChange={(e) => handleInputChange('message', e.target.value)}
|
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}
|
rows={6}
|
||||||
className="w-full border-gray-200 focus:border-blue-500 focus:ring-blue-500 resize-none"
|
className="w-full border-gray-200 focus:border-blue-500 focus:ring-blue-500 resize-none"
|
||||||
style={{
|
style={{
|
||||||
@@ -484,9 +419,8 @@ export function Contact({ topic }: ContactProps) {
|
|||||||
</div>
|
</div>
|
||||||
<PrimaryCTAButton
|
<PrimaryCTAButton
|
||||||
text={isSubmitting ? "Sending Message..." : "Send Message"}
|
text={isSubmitting ? "Sending Message..." : "Send Message"}
|
||||||
onClick={() => {}}
|
onClick={() => { handleSubmit }}
|
||||||
ariaLabel="Send contact message"
|
ariaLabel="Send contact message"
|
||||||
disabled={isSubmitting}
|
|
||||||
className="w-full sm:w-auto"
|
className="w-full sm:w-auto"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -608,7 +608,7 @@ export function CorporateSignUp() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Need Help Section - Separate Section Below */}
|
{/* Need Help Section - Separate Section Below */}
|
||||||
<div
|
{/* <div
|
||||||
className="section-margin-x py-12"
|
className="section-margin-x py-12"
|
||||||
style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}
|
style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}
|
||||||
>
|
>
|
||||||
@@ -649,7 +649,7 @@ export function CorporateSignUp() {
|
|||||||
Contact Sales Team
|
Contact Sales Team
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ export interface Course {
|
|||||||
level: string;
|
level: string;
|
||||||
format: string;
|
format: string;
|
||||||
rating: number;
|
rating: number;
|
||||||
participants: string;
|
reviews: string;
|
||||||
category: string;
|
category: string;
|
||||||
description: string;
|
description: string;
|
||||||
price: string;
|
price: string;
|
||||||
@@ -89,20 +89,6 @@ export function CourseCard({ course, onClick, className, onAddToCart }: CourseCa
|
|||||||
{course.category}
|
{course.category}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
|
|
||||||
{/* Card Content - Reduced horizontal padding */}
|
{/* Card Content - Reduced horizontal padding */}
|
||||||
@@ -118,7 +104,10 @@ export function CourseCard({ course, onClick, className, onAddToCart }: CourseCa
|
|||||||
fontFamily: 'var(--font-family-base)'
|
fontFamily: 'var(--font-family-base)'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{course.title}
|
{course.title
|
||||||
|
?.split(' ')
|
||||||
|
.slice(0, 3)
|
||||||
|
.join(' ') + (course.title.split(' ').length > 3 ? '...' : '')}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
{/* Course Description - Limited to 2 lines with ellipsis */}
|
{/* Course Description - Limited to 2 lines with ellipsis */}
|
||||||
@@ -161,7 +150,7 @@ export function CourseCard({ course, onClick, className, onAddToCart }: CourseCa
|
|||||||
color: 'var(--color-gray-muted)',
|
color: 'var(--color-gray-muted)',
|
||||||
fontWeight: '500'
|
fontWeight: '500'
|
||||||
}}>
|
}}>
|
||||||
{course.participants}
|
{course.reviews}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -208,7 +197,7 @@ export function CourseCard({ course, onClick, className, onAddToCart }: CourseCa
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{course.originalPrice && (
|
{/* {course.originalPrice && (
|
||||||
<div className="text-right">
|
<div className="text-right">
|
||||||
<span
|
<span
|
||||||
className="text-green-600 font-semibold text-sm"
|
className="text-green-600 font-semibold text-sm"
|
||||||
@@ -219,59 +208,61 @@ export function CourseCard({ course, onClick, className, onAddToCart }: CourseCa
|
|||||||
Save {Math.round(((parseFloat(course.originalPrice.replace('$', '')) - parseFloat(course.price.replace('$', ''))) / parseFloat(course.originalPrice.replace('$', ''))) * 100)}%
|
Save {Math.round(((parseFloat(course.originalPrice.replace('$', '')) - parseFloat(course.price.replace('$', ''))) / parseFloat(course.originalPrice.replace('$', ''))) * 100)}%
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)} */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Action Buttons - Horizontal Layout with reduced gap */}
|
{/* Action Buttons - Horizontal Layout with reduced gap */}
|
||||||
<div className="flex flex-row gap-2 mt-auto">
|
<div className="flex flex-row gap-2 mt-auto">
|
||||||
{/* Add to Cart Button - Outline Blue */}
|
{/* Add to Cart */}
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={handleAddToCart}
|
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={{
|
style={{
|
||||||
borderColor: '#04045B',
|
borderColor: '#04045B',
|
||||||
color: '#04045B',
|
color: '#04045B',
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
fontSize: 'var(--font-body)',
|
fontSize: '12px', // ⬅️ reduced
|
||||||
fontFamily: 'var(--font-family-base)',
|
fontFamily: 'var(--font-family-base)',
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
borderWidth: '2px'
|
borderWidth: '1px',
|
||||||
|
padding: '8px'
|
||||||
}}
|
}}
|
||||||
onMouseEnter={(e) => {
|
onMouseEnter={(e: any) => {
|
||||||
e.currentTarget.style.backgroundColor = '#04045B';
|
e.currentTarget.style.backgroundColor = '#04045B';
|
||||||
e.currentTarget.style.color = 'white';
|
e.currentTarget.style.color = 'white';
|
||||||
}}
|
}}
|
||||||
onMouseLeave={(e) => {
|
onMouseLeave={(e: any) => {
|
||||||
e.currentTarget.style.backgroundColor = 'transparent';
|
e.currentTarget.style.backgroundColor = 'transparent';
|
||||||
e.currentTarget.style.color = '#04045B';
|
e.currentTarget.style.color = '#04045B';
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ShoppingCart className="w-4 h-4" />
|
<ShoppingCart className="w-3.5 h-3.5" /> {/* ⬅️ smaller icon */}
|
||||||
Add to Cart
|
Add to Cart
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{/* Learn More Button - Solid Blue */}
|
{/* Learn More */}
|
||||||
<Button
|
<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={{
|
style={{
|
||||||
backgroundColor: '#04045B',
|
backgroundColor: '#04045B',
|
||||||
color: 'white',
|
color: 'white',
|
||||||
fontSize: 'var(--font-body)',
|
fontSize: '12px', // ⬅️ reduced
|
||||||
fontFamily: 'var(--font-family-base)',
|
fontFamily: 'var(--font-family-base)',
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
border: 'none'
|
border: 'none',
|
||||||
|
padding: '8px'
|
||||||
}}
|
}}
|
||||||
onMouseEnter={(e) => {
|
onMouseEnter={(e: any) => {
|
||||||
e.currentTarget.style.backgroundColor = '#030359';
|
e.currentTarget.style.backgroundColor = '#030359';
|
||||||
}}
|
}}
|
||||||
onMouseLeave={(e) => {
|
onMouseLeave={(e: any) => {
|
||||||
e.currentTarget.style.backgroundColor = '#04045B';
|
e.currentTarget.style.backgroundColor = '#04045B';
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Learn More
|
Learn More
|
||||||
<ArrowRight className="w-4 h-4" />
|
<ArrowRight className="w-3.5 h-3.5" /> {/* ⬅️ smaller icon */}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export function FooterNew() {
|
|||||||
Webcast
|
Webcast
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => navigateTo('/about')}
|
onClick={() => navigateTo('/about-us')}
|
||||||
className="block text-small-white transition-all duration-300 text-left"
|
className="block text-small-white transition-all duration-300 text-left"
|
||||||
style={{
|
style={{
|
||||||
color: 'white',
|
color: 'white',
|
||||||
@@ -72,7 +72,7 @@ export function FooterNew() {
|
|||||||
About Us
|
About Us
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => navigateTo('/learning/blogs')}
|
onClick={() => navigateTo('/learning/articles')}
|
||||||
className="block text-small-white transition-all duration-300 text-left"
|
className="block text-small-white transition-all duration-300 text-left"
|
||||||
style={{
|
style={{
|
||||||
color: 'white',
|
color: 'white',
|
||||||
@@ -108,7 +108,7 @@ export function FooterNew() {
|
|||||||
FAQs
|
FAQs
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => navigateTo('/services/learning-facility')}
|
onClick={() => navigateTo('/learning-facility')}
|
||||||
className="block text-small-white transition-all duration-300 text-left"
|
className="block text-small-white transition-all duration-300 text-left"
|
||||||
style={{
|
style={{
|
||||||
color: 'white',
|
color: 'white',
|
||||||
@@ -243,14 +243,14 @@ export function FooterNew() {
|
|||||||
{/* Legal Links */}
|
{/* Legal Links */}
|
||||||
<div className="flex flex-col lg:flex-row lg:items-center space-y-2 lg:space-y-0 lg:space-x-8">
|
<div className="flex flex-col lg:flex-row lg:items-center space-y-2 lg:space-y-0 lg:space-x-8">
|
||||||
<button
|
<button
|
||||||
onClick={() => navigateTo('/privacy')}
|
onClick={() => navigateTo('/privacy-policy')}
|
||||||
className="text-body-white transition-colors duration-300 hover:text-yellow-300 text-left"
|
className="text-body-white transition-colors duration-300 hover:text-yellow-300 text-left cursor-pointer"
|
||||||
style={{ color: 'rgba(255, 255, 255, 0.85)' }}
|
style={{ color: 'rgba(255, 255, 255, 0.85)' }}
|
||||||
>
|
>
|
||||||
Privacy and Cookie Policy
|
Privacy and Cookie Policy
|
||||||
</button>
|
</button>
|
||||||
<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"
|
className="text-body-white transition-colors duration-300 cursor-pointer text-left hover:text-yellow-300"
|
||||||
style={{ color: 'rgba(255, 255, 255, 0.85)' }}
|
style={{ color: 'rgba(255, 255, 255, 0.85)' }}
|
||||||
>
|
>
|
||||||
|
|||||||
22
src/components/FullScreenLoader.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,65 +1,62 @@
|
|||||||
import React, { useState, useEffect, useCallback } from 'react';
|
import React, { useState, useEffect, useCallback } from "react";
|
||||||
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||||
import { navigateTo } from './Router';
|
import { navigateTo } from "./Router";
|
||||||
import svgPaths from "../imports/svg-i1joeov37f";
|
import PrimaryCTAButton from "./PrimaryCTAButton";
|
||||||
|
|
||||||
|
interface HeroSectionItem {
|
||||||
|
id: string;
|
||||||
|
headline: string;
|
||||||
|
subtext: string;
|
||||||
|
background_image_url: string;
|
||||||
|
cta_text: string;
|
||||||
|
cta_destination: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface SlideData {
|
interface SlideData {
|
||||||
id: number;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
backgroundImage: string;
|
backgroundImage: string;
|
||||||
shortTitle: string;
|
shortTitle: string;
|
||||||
ctaText: string;
|
ctaText: string;
|
||||||
|
route: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function HeroSection() {
|
interface HeroSectionProps {
|
||||||
const [currentSlide, setCurrentSlide] = useState(0);
|
heroSections: HeroSectionItem[];
|
||||||
const [isAutoPlaying, setIsAutoPlaying] = useState(true);
|
isLoading: boolean;
|
||||||
const [progressValues, setProgressValues] = useState([0, 0, 0, 0, 0]);
|
}
|
||||||
|
|
||||||
const slides: SlideData[] = [
|
export default function HeroSection({
|
||||||
{
|
heroSections,
|
||||||
id: 1,
|
isLoading,
|
||||||
title: "Build Leaders Who Drive Business Growth",
|
}: HeroSectionProps) {
|
||||||
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",
|
const slides: SlideData[] = heroSections.map((item) => ({
|
||||||
shortTitle: "Leadership Development Programs",
|
id: item.id,
|
||||||
ctaText: "Explore Leadership Journeys"
|
title: item.headline,
|
||||||
},
|
backgroundImage: item.background_image_url,
|
||||||
{
|
shortTitle: item.subtext,
|
||||||
id: 2,
|
ctaText: item.cta_text,
|
||||||
title: "Strengthen the Backbone: Your Managers",
|
route: item.cta_destination,
|
||||||
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"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const totalSlides = slides.length;
|
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(() => {
|
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(() => {
|
const interval = setInterval(() => {
|
||||||
setCurrentSlide((prev) => (prev + 1) % totalSlides);
|
setCurrentSlide((prev) => (prev + 1) % totalSlides);
|
||||||
@@ -68,48 +65,50 @@ export default function HeroSection() {
|
|||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, [isAutoPlaying, totalSlides]);
|
}, [isAutoPlaying, totalSlides]);
|
||||||
|
|
||||||
// Progress bar animation
|
/* Progress animation */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isAutoPlaying) return;
|
if (!isAutoPlaying || totalSlides === 0) return;
|
||||||
|
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
setProgressValues(prev => {
|
setProgressValues((prev) => {
|
||||||
const newProgress = [...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) {
|
if (newProgress[currentSlide] >= 100) {
|
||||||
newProgress[currentSlide] = 0;
|
newProgress[currentSlide] = 0;
|
||||||
// Reset other slides
|
|
||||||
newProgress.forEach((_, index) => {
|
newProgress.forEach((_, index) => {
|
||||||
if (index !== currentSlide) {
|
if (index !== currentSlide) newProgress[index] = 0;
|
||||||
newProgress[index] = 0;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return newProgress;
|
return newProgress;
|
||||||
});
|
});
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, [currentSlide, isAutoPlaying]);
|
}, [currentSlide, isAutoPlaying, totalSlides]);
|
||||||
|
|
||||||
// Reset progress when manually changing slides
|
/* Reset progress when slide changes */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setProgressValues(prev => {
|
setProgressValues(new Array(totalSlides).fill(0));
|
||||||
const newProgress = [0, 0, 0, 0, 0];
|
}, [currentSlide, totalSlides]);
|
||||||
newProgress[currentSlide] = 0;
|
|
||||||
return newProgress;
|
|
||||||
});
|
|
||||||
}, [currentSlide]);
|
|
||||||
|
|
||||||
const goToSlide = useCallback((slideIndex: number) => {
|
const goToSlide = useCallback(
|
||||||
|
(slideIndex: number) => {
|
||||||
if (slideIndex !== currentSlide) {
|
if (slideIndex !== currentSlide) {
|
||||||
setCurrentSlide(slideIndex);
|
setCurrentSlide(slideIndex);
|
||||||
setIsAutoPlaying(false);
|
setIsAutoPlaying(false);
|
||||||
// Resume auto-play after manual interaction
|
|
||||||
setTimeout(() => setIsAutoPlaying(true), 3000);
|
setTimeout(() => setIsAutoPlaying(true), 3000);
|
||||||
}
|
}
|
||||||
}, [currentSlide]);
|
},
|
||||||
|
[currentSlide]
|
||||||
|
);
|
||||||
|
|
||||||
const nextSlide = useCallback(() => {
|
const nextSlide = useCallback(() => {
|
||||||
const next = (currentSlide + 1) % totalSlides;
|
const next = (currentSlide + 1) % totalSlides;
|
||||||
@@ -121,10 +120,11 @@ export default function HeroSection() {
|
|||||||
goToSlide(prev);
|
goToSlide(prev);
|
||||||
}, [currentSlide, totalSlides, goToSlide]);
|
}, [currentSlide, totalSlides, goToSlide]);
|
||||||
|
|
||||||
// Pause auto-play on hover
|
|
||||||
const handleMouseEnter = () => setIsAutoPlaying(false);
|
const handleMouseEnter = () => setIsAutoPlaying(false);
|
||||||
const handleMouseLeave = () => setIsAutoPlaying(true);
|
const handleMouseLeave = () => setIsAutoPlaying(true);
|
||||||
|
|
||||||
|
if (isLoading || slides.length === 0) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
className="hero-section"
|
className="hero-section"
|
||||||
@@ -135,9 +135,9 @@ export default function HeroSection() {
|
|||||||
{slides.map((slide, index) => (
|
{slides.map((slide, index) => (
|
||||||
<div
|
<div
|
||||||
key={slide.id}
|
key={slide.id}
|
||||||
className={`hero-slide ${index === currentSlide ? 'active' : ''}`}
|
className={`hero-slide ${index === currentSlide ? "active" : ""}`}
|
||||||
style={{
|
style={{
|
||||||
backgroundImage: `url('${slide.backgroundImage}')`
|
backgroundImage: `url('${slide.backgroundImage}')`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="hero-overlay" />
|
<div className="hero-overlay" />
|
||||||
@@ -147,120 +147,20 @@ export default function HeroSection() {
|
|||||||
{/* Hero Content */}
|
{/* Hero Content */}
|
||||||
<div className="hero-content">
|
<div className="hero-content">
|
||||||
<div className="hero-text-section">
|
<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}
|
{slides[currentSlide].title}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
{/* Dynamic CTA Button - Enhanced with Proper Navigation */}
|
<PrimaryCTAButton
|
||||||
<button
|
text={slides[currentSlide].ctaText}
|
||||||
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"
|
onClick={() => navigateTo(slides[currentSlide].route)}
|
||||||
style={{
|
ariaLabel="Learn more about KLC"
|
||||||
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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bottom Navigation */}
|
{/* Bottom Navigation */}
|
||||||
<div className="hero-navigation">
|
<div className="hero-navigation">
|
||||||
{/* Progress Section */}
|
|
||||||
<div className="hero-progress-container">
|
<div className="hero-progress-container">
|
||||||
{slides.map((slide, index) => (
|
{slides.map((slide, index) => (
|
||||||
<div
|
<div
|
||||||
@@ -268,30 +168,41 @@ export default function HeroSection() {
|
|||||||
className="hero-progress-item"
|
className="hero-progress-item"
|
||||||
onClick={() => goToSlide(index)}
|
onClick={() => goToSlide(index)}
|
||||||
>
|
>
|
||||||
{/* Progress Bar */}
|
|
||||||
<div
|
<div
|
||||||
className={`hero-progress-segment ${index === currentSlide ? 'active' : ''}`}
|
className={`hero-progress-segment ${
|
||||||
|
index === currentSlide ? "active" : ""
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="hero-progress-fill"
|
className="hero-progress-fill"
|
||||||
style={{ width: index === currentSlide ? `${progressValues[index]}%` : '0%' }}
|
style={{
|
||||||
|
width:
|
||||||
|
index === currentSlide
|
||||||
|
? `${progressValues[index] ?? 0}%`
|
||||||
|
: "0%",
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Progress Number */}
|
<div
|
||||||
<div className={`hero-progress-number ${index === currentSlide ? 'active' : ''}`}>
|
className={`hero-progress-number ${
|
||||||
{String(index + 1).padStart(2, '0')}
|
index === currentSlide ? "active" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{String(index + 1).padStart(2, "0")}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Progress Text */}
|
<div
|
||||||
<div className={`hero-progress-text ${index === currentSlide ? 'active' : ''}`}>
|
className={`hero-progress-text ${
|
||||||
|
index === currentSlide ? "active" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
{slide.shortTitle}
|
{slide.shortTitle}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Navigation Arrows */}
|
|
||||||
<div className="hero-controls">
|
<div className="hero-controls">
|
||||||
<button
|
<button
|
||||||
className="hero-nav-button"
|
className="hero-nav-button"
|
||||||
@@ -300,6 +211,7 @@ export default function HeroSection() {
|
|||||||
>
|
>
|
||||||
<ChevronLeft className="w-5 h-5" />
|
<ChevronLeft className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className="hero-nav-button"
|
className="hero-nav-button"
|
||||||
onClick={nextSlide}
|
onClick={nextSlide}
|
||||||
|
|||||||
@@ -1,43 +1,38 @@
|
|||||||
import { ArrowUpRight } from "lucide-react";
|
import { ArrowUpRight } from "lucide-react";
|
||||||
import { ImageWithFallback } from "./figma/ImageWithFallback";
|
import { ImageWithFallback } from "./figma/ImageWithFallback";
|
||||||
import { BrandedTag } from "./about/BrandedTag";
|
import { BrandedTag } from "./about/BrandedTag";
|
||||||
import { PrimaryCTAButton } from "./PrimaryCTAButton";
|
import { StandardCTAButton } from "./StandardCTAButton";
|
||||||
import { navigateTo } from "./Router";
|
import { navigateTo } from "./Router";
|
||||||
|
import { useGetFeaturedBlogsQuery } from "../redux/services/homepageApi";
|
||||||
|
import { FullScreenLoader } from "./FullScreenLoader";
|
||||||
|
import { getSlugWithId } from "../utils/urlHelpers";
|
||||||
|
|
||||||
interface InsightCard {
|
// Interface for featured blog items from API
|
||||||
id: number;
|
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;
|
title: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
date: string;
|
date: string;
|
||||||
tags: string[];
|
tags: string[];
|
||||||
image: 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
|
// Insight Tag Component
|
||||||
function InsightTag({ text }: { text: string }) {
|
function InsightTag({ text }: { text: string }) {
|
||||||
return (
|
return (
|
||||||
@@ -72,22 +67,41 @@ function CalendarIcon() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explore All Button Component - Updated to redirect to articles page
|
// Explore All Button Component
|
||||||
function ExploreAllButton() {
|
function ExploreAllButton() {
|
||||||
return (
|
return (
|
||||||
<PrimaryCTAButton
|
<StandardCTAButton
|
||||||
text="Explore All"
|
text="Explore All"
|
||||||
onClick={() => navigateTo('/learning/articles')}
|
onClick={() => navigateTo('/learning/articles')}
|
||||||
ariaLabel="Explore all leadership insights and ideas"
|
ariaLabel="Explore all leadership insights and ideas"
|
||||||
className="explore-all-cta-override"
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Large Insight Card Component
|
// 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 (
|
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 */}
|
{/* Background Image */}
|
||||||
<div className="absolute inset-0 transition-transform duration-300 group-hover:scale-105">
|
<div className="absolute inset-0 transition-transform duration-300 group-hover:scale-105">
|
||||||
<ImageWithFallback
|
<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">
|
<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 */}
|
{/* Top section with tags and arrow */}
|
||||||
<div className="insight-card-header flex items-start justify-between mb-4">
|
<div className="insight-card-header flex items-start justify-between mb-4">
|
||||||
<div className="insight-card-tags flex gap-2">
|
<div className="insight-card-tags flex gap-2 flex-wrap">
|
||||||
{card.tags.map((tag, index) => (
|
{card.tags.slice(0, 2).map((tag, index) => (
|
||||||
<InsightTag key={index} text={tag} />
|
<InsightTag key={index} text={tag} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -134,9 +148,29 @@ function LargeInsightCard({ card }: { card: InsightCard }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Small Insight Card Component
|
// 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 (
|
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 */}
|
{/* Background Image */}
|
||||||
<div className="absolute inset-0 transition-transform duration-300 group-hover:scale-105">
|
<div className="absolute inset-0 transition-transform duration-300 group-hover:scale-105">
|
||||||
<ImageWithFallback
|
<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">
|
<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 */}
|
{/* Top section with tags and arrow */}
|
||||||
<div className="insight-card-header flex items-start justify-between mb-3">
|
<div className="insight-card-header flex items-start justify-between mb-3">
|
||||||
<div className="insight-card-tags flex gap-2">
|
<div className="insight-card-tags flex gap-2 flex-wrap">
|
||||||
{card.tags.map((tag, index) => (
|
{card.tags.slice(0, 2).map((tag, index) => (
|
||||||
<span
|
<span
|
||||||
key={index}
|
key={index}
|
||||||
className="px-2.5 py-1 text-xs font-medium rounded-full"
|
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() {
|
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 (
|
return (
|
||||||
<section
|
<section
|
||||||
className="py-24"
|
className="py-24"
|
||||||
@@ -200,7 +324,6 @@ export function InsightsSection() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="max-w-7xl mx-auto section-margin-x">
|
<div className="max-w-7xl mx-auto section-margin-x">
|
||||||
{/* Branded Tag */}
|
|
||||||
<BrandedTag text="Leadership Insights" />
|
<BrandedTag text="Leadership Insights" />
|
||||||
|
|
||||||
<div className="insights-container">
|
<div className="insights-container">
|
||||||
@@ -218,15 +341,20 @@ export function InsightsSection() {
|
|||||||
{/* Main Grid Layout */}
|
{/* Main Grid Layout */}
|
||||||
<div className="insights-grid grid grid-cols-1 lg:grid-cols-2 gap-4 md:gap-6 lg:gap-8">
|
<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 */}
|
{/* Large Card - Takes full height on left */}
|
||||||
|
{hasLargeCard && (
|
||||||
<div className="lg:row-span-2">
|
<div className="lg:row-span-2">
|
||||||
<LargeInsightCard card={insightCards[0]} />
|
<LargeInsightCard card={largeCard} />
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Small Cards - Stack on right */}
|
{/* Small Cards - Stack on right */}
|
||||||
|
{hasSmallCards && (
|
||||||
<div className="flex flex-col gap-4 md:gap-6 lg:gap-8">
|
<div className="flex flex-col gap-4 md:gap-6 lg:gap-8">
|
||||||
<SmallInsightCard card={insightCards[1]} />
|
{smallCards.map((card:any, index:any) => (
|
||||||
<SmallInsightCard card={insightCards[2]} />
|
<SmallInsightCard key={card.id || index} card={card} />
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
919
src/components/KautilyaFacility.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
2106
src/components/LearningFacilityPage.tsx
Normal file
175
src/components/Loader.tsx
Normal 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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,27 +1,25 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import imgImage36 from "figma:asset/6bdf8056f51bbdc6dd9dab9044a6579a254bd02c.png";
|
import accenture from "../assets/accenture.svg";
|
||||||
import imgImage39 from "figma:asset/037c4659b7b0bf15b1dfdcd4868cb42e8257e838.png";
|
import adani from "../assets/adani-logo.svg";
|
||||||
import imgImage43 from "figma:asset/c57ec1f4466f68e607139a3cd6d52f7e2f372408.png";
|
import axis from "../assets/axis-bank.svg";
|
||||||
import imgImage45 from "figma:asset/4833274f0a593cd31fdefe553b70bb016de281af.png";
|
import ceat from "../assets/ceat-logo.svg";
|
||||||
import imgImage38 from "figma:asset/d5bab6ea4f3d8cef3b0425c45cfee7faea19fdbc.png";
|
import hsbc from "../assets/hsbc.svg";
|
||||||
import imgImage47 from "figma:asset/e8fad960112d5eba554c3969d08891ebe4d4b9c7.png";
|
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";
|
import Frame1597884933 from "../imports/Frame1597884933-44-374";
|
||||||
|
|
||||||
// Logo data using Frame1597884944 logos with proper dimensions
|
// Logo data using Frame1597884944 logos with proper dimensions
|
||||||
const logoData = [
|
const logoData = [
|
||||||
{ src: imgImage36, name: 'CANMOOR', width: 302, height: 54 }, // CANMOOR
|
{ src: accenture, name: 'CANMOOR', width: 302, height: 54 }, // CANMOOR
|
||||||
{ src: imgImage45, name: 'BlackRock', width: 210, height: 54 }, // BlackRock
|
{ src: ceat, name: 'BlackRock', width: 210, height: 54 }, // BlackRock
|
||||||
{ src: imgImage38, name: 'Royal London', width: 145, height: 54 }, // Royal London
|
{ src: hsbc, name: 'Royal London', width: 145, height: 54 }, // Royal London
|
||||||
{ src: imgImage39, name: 'Abstract', width: 172, height: 54 }, // Abstract
|
{ src: adani, name: 'Abstract', width: 172, height: 54 }, // Abstract
|
||||||
{ src: imgImage47, name: 'ARES', width: 163, height: 54 }, // ARES
|
{ src: larsen, name: 'ARES', width: 163, height: 54 }, // ARES
|
||||||
{ src: imgImage43, name: 'KADANS', width: 206, height: 54 }, // KADANS
|
{ src: axis, name: 'KADANS', width: 206, height: 54 }, // KADANS
|
||||||
// Repeat logos for more variety in scrolling
|
{ src: levis, name: 'levis', width: 206, height: 54 }, // KADANS
|
||||||
{ src: imgImage36, name: 'CANMOOR', width: 302, height: 54 }, // CANMOOR (repeat)
|
{ src: tata, name: 'tata', width: 206, height: 54 },
|
||||||
{ src: imgImage45, name: 'BlackRock', width: 210, height: 54 }, // BlackRock (repeat)
|
|
||||||
{ src: imgImage38, name: 'Royal London', width: 145, height: 54 }, // Royal London (repeat)
|
|
||||||
{ src: imgImage39, name: 'Abstract', width: 172, height: 54 }, // Abstract (repeat)
|
|
||||||
{ src: imgImage47, name: 'ARES', width: 163, height: 54 }, // ARES (repeat)
|
|
||||||
{ src: imgImage43, name: 'KADANS', width: 206, height: 54 }, // KADANS (repeat)
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// Top row logos - Original 6 unique logos
|
// Top row logos - Original 6 unique logos
|
||||||
@@ -90,7 +88,7 @@ export function LogosSection() {
|
|||||||
>
|
>
|
||||||
{/* Top Row - Scrolling Left to Right */}
|
{/* Top Row - Scrolling Left to Right */}
|
||||||
<div
|
<div
|
||||||
className="relative h-[54px] mb-16 overflow-hidden"
|
className="relative h-[54px] overflow-hidden"
|
||||||
role="list"
|
role="list"
|
||||||
aria-label="Client logos row 1"
|
aria-label="Client logos row 1"
|
||||||
>
|
>
|
||||||
@@ -124,43 +122,6 @@ export function LogosSection() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bottom Row - Scrolling Right to Left */}
|
|
||||||
<div
|
|
||||||
className="relative h-[54px] overflow-hidden"
|
|
||||||
role="list"
|
|
||||||
aria-label="Client logos row 2"
|
|
||||||
>
|
|
||||||
<div className="flex items-center h-full will-change-transform">
|
|
||||||
<div
|
|
||||||
className="scroll-right flex items-center h-full"
|
|
||||||
style={{
|
|
||||||
width: '400%',
|
|
||||||
gap: '80px',
|
|
||||||
paddingLeft: '40px',
|
|
||||||
paddingRight: '40px'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/* Create multiple sets for seamless infinite scroll */}
|
|
||||||
{[1, 2, 3, 4].map((setNumber) => (
|
|
||||||
<div
|
|
||||||
key={`bottom-set-${setNumber}`}
|
|
||||||
className="flex items-center h-full"
|
|
||||||
style={{ gap: '80px' }}
|
|
||||||
>
|
|
||||||
{bottomRowLogos.map((logo, index) => (
|
|
||||||
<LogoItem
|
|
||||||
key={`bottom-${setNumber}-${index}`}
|
|
||||||
logo={logo}
|
|
||||||
index={index}
|
|
||||||
duplicate={setNumber > 1}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import klcLogo from '../assets/klc-logo-dark.png';
|
|||||||
import {
|
import {
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
BookMarked,
|
BookMarked,
|
||||||
|
Building,
|
||||||
|
Building2,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
Eye,
|
Eye,
|
||||||
@@ -57,48 +59,62 @@ interface NavLink {
|
|||||||
|
|
||||||
const navigationItems: NavLink[] = [
|
const navigationItems: NavLink[] = [
|
||||||
{
|
{
|
||||||
label: 'Services',
|
label: 'Our Services',
|
||||||
// items: [
|
items: [
|
||||||
// {
|
{
|
||||||
// label: 'Leadership Development',
|
label: 'Our Services',
|
||||||
// href: '/services/leadership-development',
|
href: '/services',
|
||||||
// description: 'Executive and senior leadership programs',
|
description: 'Complete overview of our leadership development services',
|
||||||
// icon: Target
|
icon: Building
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// 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: 'About Us',
|
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: [
|
// items: [
|
||||||
// {
|
// {
|
||||||
|
// label: 'About Us',
|
||||||
|
// href: '/about',
|
||||||
|
// description: 'Learn about our story, vision, and leadership approach',
|
||||||
|
// icon: Building
|
||||||
|
// },
|
||||||
|
// {
|
||||||
// label: 'Our Vision',
|
// label: 'Our Vision',
|
||||||
// href: '/about/our-vision',
|
// href: '/about/our-vision',
|
||||||
// description: 'Our mission to transform leadership globally',
|
// description: 'Our mission to transform leadership globally',
|
||||||
@@ -117,6 +133,9 @@ const navigationItems: NavLink[] = [
|
|||||||
// icon: Star
|
// icon: Star
|
||||||
// }
|
// }
|
||||||
// ]
|
// ]
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
label: 'About Us',
|
||||||
href: '/about-us'
|
href: '/about-us'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -173,17 +192,30 @@ function NavLink({ item, isMobile = false }: { item: NavLink; isMobile?: boolean
|
|||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
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 (
|
return (
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={() => navigate(item.href!)}
|
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={{
|
style={{
|
||||||
fontSize: '14px',
|
fontSize: '13px',
|
||||||
fontWeight: 'normal',
|
fontWeight: 'normal',
|
||||||
fontFamily: 'var(--font-family-base)',
|
fontFamily: 'var(--font-family-base)',
|
||||||
color: 'var(--color-black)'
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{item.icon && <item.icon className="w-4 h-4 mr-2" />}
|
{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) {
|
if (isMobile) {
|
||||||
return (
|
return (
|
||||||
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
|
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<CollapsibleTrigger asChild>
|
<CollapsibleTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
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={{
|
style={{
|
||||||
fontSize: '14px',
|
fontSize: '14px',
|
||||||
fontWeight: 'normal',
|
fontWeight: 'normal',
|
||||||
fontFamily: 'var(--font-family-base)',
|
fontFamily: 'var(--font-family-base)',
|
||||||
color: 'var(--color-black)'
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className="flex items-center">
|
<span className="flex items-center">
|
||||||
{item.icon && <item.icon className="w-4 h-4 mr-2" />}
|
{item.icon && <item.icon className="w-4 h-4 mr-2" />}
|
||||||
{item.label}
|
{item.label}
|
||||||
</span>
|
</span>
|
||||||
<ChevronRight className={`w-4 h-4 transition-transform ${isOpen ? 'rotate-90' : ''}`} />
|
<ChevronRight
|
||||||
|
className={`w-4 h-4 transition-transform ${isOpen ? 'rotate-90' : ''}`}
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</CollapsibleTrigger>
|
</CollapsibleTrigger>
|
||||||
<CollapsibleContent className="pl-4 space-y-1">
|
<CollapsibleContent className="pl-4 space-y-1">
|
||||||
{item.items?.map((subItem) => (
|
{item.items?.map((subItem) => {
|
||||||
|
const isSubItemActive = isActive(subItem.href);
|
||||||
|
|
||||||
|
return (
|
||||||
<Button
|
<Button
|
||||||
key={subItem.href}
|
key={subItem.href}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={() => navigate(subItem.href)}
|
onClick={() => navigate(subItem.href)}
|
||||||
className="w-full justify-start min-h-[44px] pl-6"
|
className={`w-full justify-start min-h-[44px] pl-6 ${isSubItemActive ? 'bg-gray-100 text-[#04045b] font-medium' : ''
|
||||||
|
}`}
|
||||||
style={{
|
style={{
|
||||||
fontSize: '14px',
|
fontSize: '14px',
|
||||||
fontWeight: 'normal',
|
fontWeight: 'normal',
|
||||||
fontFamily: 'var(--font-family-base)',
|
fontFamily: 'var(--font-family-base)',
|
||||||
color: 'var(--color-black)'
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{subItem.icon && <subItem.icon className="w-4 h-4 mr-2" />}
|
{subItem.icon && <subItem.icon className="w-4 h-4 mr-2" />}
|
||||||
{subItem.label}
|
{subItem.label}
|
||||||
</Button>
|
</Button>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</CollapsibleContent>
|
</CollapsibleContent>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------- Desktop dropdown ----------
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
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={{
|
style={{
|
||||||
fontSize: '14px',
|
fontSize: '14px',
|
||||||
fontWeight: 'normal',
|
fontWeight: 'normal',
|
||||||
fontFamily: 'var(--font-family-base)',
|
fontFamily: 'var(--font-family-base)',
|
||||||
color: 'var(--color-black)'
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{item.icon && <item.icon className="w-4 h-4 mr-2" />}
|
{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" />
|
<ChevronDown className="w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
|
|
||||||
<DropdownMenuContent
|
<DropdownMenuContent
|
||||||
align="start"
|
align="start"
|
||||||
className="w-80"
|
className="w-80"
|
||||||
@@ -261,25 +302,29 @@ function NavLink({ item, isMobile = false }: { item: NavLink; isMobile?: boolean
|
|||||||
backgroundColor: 'var(--color-bg-white)',
|
backgroundColor: 'var(--color-bg-white)',
|
||||||
border: `1px solid var(--color-border)`,
|
border: `1px solid var(--color-border)`,
|
||||||
borderRadius: '12px',
|
borderRadius: '12px',
|
||||||
boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
|
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)'
|
fontFamily: 'var(--font-family-base)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{item.items?.map((subItem) => (
|
{item.items?.map((subItem) => {
|
||||||
|
const isSubItemActive = isActive(subItem.href);
|
||||||
|
|
||||||
|
return (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
key={subItem.href}
|
key={subItem.href}
|
||||||
onClick={() => navigate(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"
|
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={{
|
style={{
|
||||||
fontFamily: 'var(--font-family-base)'
|
fontFamily: 'var(--font-family-base)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{subItem.icon && (
|
{subItem.icon && (
|
||||||
<div
|
<div
|
||||||
className="w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0 mt-1"
|
className="w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0 mt-1"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: 'rgba(4, 4, 91, 0.1)',
|
backgroundColor: 'rgba(4,4,91,0.1)',
|
||||||
color: 'var(--color-brand-primary)'
|
color: 'var(--color-brand-primary)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<subItem.icon className="w-4 h-4" />
|
<subItem.icon className="w-4 h-4" />
|
||||||
@@ -290,9 +335,9 @@ function NavLink({ item, isMobile = false }: { item: NavLink; isMobile?: boolean
|
|||||||
className="font-medium"
|
className="font-medium"
|
||||||
style={{
|
style={{
|
||||||
fontSize: '14px',
|
fontSize: '14px',
|
||||||
fontWeight: '400',
|
fontWeight: isSubItemActive ? '500' : '400',
|
||||||
color: 'var(--color-black)',
|
color: isSubItemActive ? '#04045b' : 'var(--color-black)',
|
||||||
fontFamily: 'var(--font-family-base)'
|
fontFamily: 'var(--font-family-base)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{subItem.label}
|
{subItem.label}
|
||||||
@@ -304,7 +349,7 @@ function NavLink({ item, isMobile = false }: { item: NavLink; isMobile?: boolean
|
|||||||
fontSize: '12px',
|
fontSize: '12px',
|
||||||
color: 'var(--color-gray-muted)',
|
color: 'var(--color-gray-muted)',
|
||||||
fontFamily: 'var(--font-family-base)',
|
fontFamily: 'var(--font-family-base)',
|
||||||
lineHeight: 'var(--line-height-small)'
|
lineHeight: 'var(--line-height-small)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{subItem.description}
|
{subItem.description}
|
||||||
@@ -312,12 +357,14 @@ function NavLink({ item, isMobile = false }: { item: NavLink; isMobile?: boolean
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function ProfileDropdown({ user }: { user: any }) {
|
function ProfileDropdown({ user }: { user: any }) {
|
||||||
const { signOut } = useAuth();
|
const { signOut } = useAuth();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -537,7 +584,7 @@ export function Navigation({ currentPage }: NavigationProps) {
|
|||||||
) : (
|
) : (
|
||||||
<div className="hidden md:flex items-center">
|
<div className="hidden md:flex items-center">
|
||||||
<Button
|
<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"
|
className="management-dev-glassmorphic-btn text-body px-8 py-4 min-h-[52px] border transition-all duration-300 group"
|
||||||
style={{
|
style={{
|
||||||
fontFamily: 'var(--font-family-base)',
|
fontFamily: 'var(--font-family-base)',
|
||||||
|
|||||||
@@ -1,146 +1,101 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useState } from 'react';
|
||||||
import svgPaths from "../imports/svg-i1joeov37f";
|
import { ArrowRight } from 'lucide-react';
|
||||||
import { navigateTo } from './Router';
|
|
||||||
|
|
||||||
interface PrimaryCTAButtonProps {
|
interface PrimaryCTAButtonProps {
|
||||||
text: string;
|
text: string;
|
||||||
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
onClick: () => void;
|
||||||
className?: string;
|
|
||||||
ariaLabel?: string;
|
ariaLabel?: string;
|
||||||
debugId?: string; // Add debug identifier
|
className?: string;
|
||||||
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PrimaryCTAButton: React.FC<PrimaryCTAButtonProps> = ({
|
export function PrimaryCTAButton({
|
||||||
text,
|
text,
|
||||||
onClick,
|
onClick,
|
||||||
className = '',
|
|
||||||
ariaLabel,
|
ariaLabel,
|
||||||
debugId = 'unknown'
|
className = '',
|
||||||
}) => {
|
disabled = false
|
||||||
// Debug: Log when component mounts
|
}: PrimaryCTAButtonProps) {
|
||||||
useEffect(() => {
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
console.log(`PrimaryCTAButton ${debugId} mounted`);
|
|
||||||
}, [debugId]);
|
|
||||||
|
|
||||||
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
||||||
e.preventDefault();
|
|
||||||
console.log(`Primary CTA Button ${debugId} clicked with text: "${text}"`); // Debug log
|
|
||||||
// Handle webinars navigation
|
|
||||||
if (text === "Join Our Webinars" || text === "Explore Webinars" || text === "View Webinars") {
|
|
||||||
console.log(`Navigating to webinars page for ${debugId}`);
|
|
||||||
navigateTo('/webinars');
|
|
||||||
} else if (text === "Register Free" || text === "Watch Replay" || text === "Launch in Zoom") {
|
|
||||||
// These are handled by individual webinar detail pages
|
|
||||||
console.log(`Webinar CTA handled by detail page for ${debugId}`);
|
|
||||||
} else if (onClick) {
|
|
||||||
console.log(`Custom onClick handler found for ${debugId}, executing it`); // Debug log
|
|
||||||
onClick(e);
|
|
||||||
} else {
|
|
||||||
console.log(`No custom onClick for ${debugId}, navigating to webcast page`); // Debug log
|
|
||||||
// Navigate to webcast page by default
|
|
||||||
navigateTo('/learning/webcast');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<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}`}
|
onClick={onClick}
|
||||||
style={{
|
disabled={disabled}
|
||||||
background: 'transparent',
|
|
||||||
border: 'none',
|
|
||||||
width: 'fit-content' // Perfect width - no extra horizontal space
|
|
||||||
}}
|
|
||||||
onClick={handleClick}
|
|
||||||
aria-label={ariaLabel || text}
|
aria-label={ariaLabel || text}
|
||||||
>
|
className={`primary-cta-button ${className}`}
|
||||||
{/* Icon Container with Slide Animation */}
|
onMouseEnter={() => setIsHovered(true)}
|
||||||
<div className="relative shrink-0 size-[50px] overflow-hidden">
|
onMouseLeave={() => setIsHovered(false)}
|
||||||
{/* 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={{
|
style={{
|
||||||
color: '#ffffff',
|
display: 'flex',
|
||||||
fontFamily: 'Inter, sans-serif',
|
alignItems: 'center',
|
||||||
fontSize: '20px',
|
justifyContent: 'center',
|
||||||
fontWeight: '400',
|
gap: '12px',
|
||||||
lineHeight: '28px'
|
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'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{text}
|
{text}
|
||||||
</div>
|
</span>
|
||||||
|
|
||||||
{/* Secondary Text - Slides in from bottom */}
|
{/* Arrow icon with enhanced animation */}
|
||||||
<div
|
<span
|
||||||
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={{
|
style={{
|
||||||
color: '#ffffff',
|
display: 'flex',
|
||||||
fontFamily: 'Inter, sans-serif',
|
alignItems: 'center',
|
||||||
fontSize: '20px',
|
justifyContent: 'center',
|
||||||
fontWeight: '400',
|
width: '20px',
|
||||||
lineHeight: '28px'
|
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)'
|
||||||
|
})
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{text}
|
<ArrowRight
|
||||||
</div>
|
size={14}
|
||||||
</div>
|
style={{
|
||||||
|
color: '#FFFFFF',
|
||||||
|
transition: 'all 300ms ease-in-out'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
// Named exports for backward compatibility
|
||||||
|
export const PrimaryCTAButtonProps = PrimaryCTAButton;
|
||||||
|
export default PrimaryCTAButton;
|
||||||
14
src/components/ScrollToTop.tsx
Normal 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;
|
||||||
@@ -520,14 +520,14 @@ export function SelfLearnerSignIn() {
|
|||||||
Looking for corporate access?{' '}
|
Looking for corporate access?{' '}
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
onClick={() => navigateTo('/corporate-login')}
|
onClick={() => navigateTo('/contact')}
|
||||||
className="text-blue-600 hover:text-blue-700 transition-colors font-medium"
|
className="text-blue-600 hover:text-blue-700 transition-colors font-medium"
|
||||||
style={{
|
style={{
|
||||||
fontSize: 'var(--font-small)',
|
fontSize: 'var(--font-small)',
|
||||||
fontFamily: 'var(--font-family-base)'
|
fontFamily: 'var(--font-family-base)'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Corporate Sign In
|
Contact Us
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -539,7 +539,7 @@ export function SelfLearnerSignIn() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Need Help Section - Separate Section Below */}
|
{/* Need Help Section - Separate Section Below */}
|
||||||
<div
|
{/* <div
|
||||||
className="section-margin-x py-12"
|
className="section-margin-x py-12"
|
||||||
style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}
|
style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}
|
||||||
>
|
>
|
||||||
@@ -580,7 +580,7 @@ export function SelfLearnerSignIn() {
|
|||||||
Speak with Learning Advisor
|
Speak with Learning Advisor
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -394,11 +394,15 @@ export function SelfLearnerSignUp() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
I agree to the{' '}
|
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
|
Terms of Service
|
||||||
</span>{' '}
|
</span>{' '}
|
||||||
and{' '}
|
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
|
Privacy Policy
|
||||||
</span>
|
</span>
|
||||||
</Label>
|
</Label>
|
||||||
@@ -457,7 +461,7 @@ export function SelfLearnerSignUp() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Need Help Section - Separate Section Below */}
|
{/* Need Help Section - Separate Section Below */}
|
||||||
<div
|
{/* <div
|
||||||
className="section-margin-x py-12"
|
className="section-margin-x py-12"
|
||||||
style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}
|
style={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}
|
||||||
>
|
>
|
||||||
@@ -498,7 +502,7 @@ export function SelfLearnerSignUp() {
|
|||||||
Speak with Learning Advisor
|
Speak with Learning Advisor
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -18,10 +18,9 @@ import {
|
|||||||
Users2,
|
Users2,
|
||||||
Briefcase
|
Briefcase
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { BrandedTag } from './about/BrandedTag';
|
import PrimaryCTAButton from './PrimaryCTAButton';
|
||||||
import { PrimaryCTAButton } from './PrimaryCTAButton';
|
|
||||||
import { CTABannerSection } from './CTABannerSection';
|
|
||||||
import { navigateTo } from './Router';
|
import { navigateTo } from './Router';
|
||||||
|
import { CTABannerSection } from './CTABannerSection';
|
||||||
|
|
||||||
export function Services() {
|
export function Services() {
|
||||||
const [isVisible, setIsVisible] = useState(false);
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
@@ -36,7 +35,7 @@ export function Services() {
|
|||||||
icon: Users,
|
icon: Users,
|
||||||
title: 'Leadership Development Programs (LDP)',
|
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.',
|
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',
|
ctaText: 'Learn More',
|
||||||
downloadTitle: 'Case Study: How a BFSI Major Built its Next-Gen Leadership Pipeline',
|
downloadTitle: 'Case Study: How a BFSI Major Built its Next-Gen Leadership Pipeline',
|
||||||
route: '/services/leadership-development'
|
route: '/services/leadership-development'
|
||||||
},
|
},
|
||||||
@@ -45,7 +44,7 @@ export function Services() {
|
|||||||
icon: Target,
|
icon: Target,
|
||||||
title: 'Assessments & Leadership Pipeline',
|
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.',
|
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',
|
ctaText: 'Learn More',
|
||||||
downloadTitle: 'Case Study: Leadership Assessment & Succession Planning in a Top NBFC',
|
downloadTitle: 'Case Study: Leadership Assessment & Succession Planning in a Top NBFC',
|
||||||
route: '/contact'
|
route: '/contact'
|
||||||
},
|
},
|
||||||
@@ -54,7 +53,7 @@ export function Services() {
|
|||||||
icon: TrendingUp,
|
icon: TrendingUp,
|
||||||
title: 'Management Development Programs (MDP)',
|
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.',
|
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',
|
ctaText: 'Learn More',
|
||||||
downloadTitle: 'Case Study: Developing 400 Mid-Level Managers at a Global Manufacturing Firm',
|
downloadTitle: 'Case Study: Developing 400 Mid-Level Managers at a Global Manufacturing Firm',
|
||||||
route: '/services/management-development'
|
route: '/services/management-development'
|
||||||
},
|
},
|
||||||
@@ -63,7 +62,7 @@ export function Services() {
|
|||||||
icon: Building2,
|
icon: Building2,
|
||||||
title: 'Culture & Competence Consulting',
|
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.',
|
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',
|
ctaText: 'Learn More',
|
||||||
downloadTitle: 'Case Study: Embedding a Culture of Customer-Centricity in an ITES Giant',
|
downloadTitle: 'Case Study: Embedding a Culture of Customer-Centricity in an ITES Giant',
|
||||||
route: '/services/culture-competence'
|
route: '/services/culture-competence'
|
||||||
},
|
},
|
||||||
@@ -72,9 +71,18 @@ export function Services() {
|
|||||||
icon: MessageCircle,
|
icon: MessageCircle,
|
||||||
title: 'Coaching & Mentoring',
|
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.',
|
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',
|
ctaText: 'Learn More',
|
||||||
downloadTitle: 'Case Study: Coaching Senior Leaders Through Organisational Transition',
|
downloadTitle: 'Case Study: Coaching Senior Leaders Through Organisational Transition',
|
||||||
route: '/services/executive-coaching'
|
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'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -99,7 +107,7 @@ export function Services() {
|
|||||||
return (
|
return (
|
||||||
<div style={{ backgroundColor: '#FFFFFF', fontFamily: 'var(--font-family-base)' }}>
|
<div style={{ backgroundColor: '#FFFFFF', fontFamily: 'var(--font-family-base)' }}>
|
||||||
{/* Hero Section */}
|
{/* Hero Section */}
|
||||||
<section className="relative min-h-[85vh] flex flex-col">
|
<section className="relative flex flex-col py-24 min-h-[85vh]">
|
||||||
<div className="absolute inset-0 z-0">
|
<div className="absolute inset-0 z-0">
|
||||||
<div
|
<div
|
||||||
className="w-full h-full bg-cover bg-center bg-no-repeat"
|
className="w-full h-full bg-cover bg-center bg-no-repeat"
|
||||||
@@ -110,9 +118,9 @@ export function Services() {
|
|||||||
<div className="absolute inset-0 bg-gradient-to-r from-black/80 via-black/70 to-black/60"></div>
|
<div className="absolute inset-0 bg-gradient-to-r from-black/80 via-black/70 to-black/60"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="relative z-10 flex-1 flex items-center">
|
<div className="relative z-10 flex-1 flex items-left">
|
||||||
<div className="container mx-auto section-margin-x">
|
<div className="container mx-auto section-margin-x">
|
||||||
<div className="text-center max-w-5xl mx-auto">
|
<div className="text-center max-w-6xl mx-auto">
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 30 }}
|
initial={{ opacity: 0, y: 30 }}
|
||||||
animate={{ opacity: isVisible ? 1 : 0, y: isVisible ? 0 : 30 }}
|
animate={{ opacity: isVisible ? 1 : 0, y: isVisible ? 0 : 30 }}
|
||||||
@@ -123,18 +131,18 @@ export function Services() {
|
|||||||
<span className="text">Our Services</span>
|
<span className="text">Our Services</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 className="text-h1-white mb-6">
|
<h1 className="text-h1-white mb-6 text-left">
|
||||||
Building Leadership Capacity<br />
|
Building Leadership Capacity<br />
|
||||||
That Drives Results
|
That Drives Results
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p className="text-body-lg-white mb-8 max-w-3xl mx-auto">
|
<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,
|
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,
|
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.
|
consulting, coaching, assessments, and digital learning — each designed with research, context, and practice at the core.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="flex justify-center">
|
<div className="flex justify-left">
|
||||||
<PrimaryCTAButton
|
<PrimaryCTAButton
|
||||||
text="Explore Our Approach"
|
text="Explore Our Approach"
|
||||||
onClick={() => navigateTo('/contact')}
|
onClick={() => navigateTo('/contact')}
|
||||||
@@ -148,13 +156,14 @@ export function Services() {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
{/* Services Overview Section */}
|
{/* Services Overview Section */}
|
||||||
<section className="py-20 lg:py-28" style={{ backgroundColor: '#FFFFFF' }}>
|
<section className="py-20 lg:py-28" style={{ backgroundColor: '#FFFFFF' }}>
|
||||||
<div className="section-margin-x">
|
<div className="section-margin-x">
|
||||||
<div className="max-w-7xl mx-auto">
|
<div className="max-w-7xl mx-auto">
|
||||||
|
|
||||||
{/* Services Grid */}
|
{/* Services Grid */}
|
||||||
<div className="space-y-24">
|
<div className="space-y-40">
|
||||||
{services.map((service, index) => (
|
{services.map((service, index) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={service.id}
|
key={service.id}
|
||||||
@@ -164,77 +173,136 @@ export function Services() {
|
|||||||
viewport={{ once: true }}
|
viewport={{ once: true }}
|
||||||
className="max-w-7xl mx-auto"
|
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' : ''
|
<div className="relative">
|
||||||
}`}>
|
|
||||||
|
|
||||||
{/* Content Section */}
|
{/* 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'
|
<div className="relative pr-0 lg:pr-[520px]">
|
||||||
}`}>
|
{/* Icon and Title - Single Line (Figma Layout) */}
|
||||||
{/* Icon and Title - Side by Side */}
|
<div className="flex items-center gap-[15.5px] mb-8">
|
||||||
<div className="flex items-start gap-4 mb-6">
|
|
||||||
<div
|
<div
|
||||||
className="w-16 h-16 rounded-xl flex items-center justify-center flex-shrink-0"
|
className="w-16 h-16 rounded-[14px] flex items-center justify-center flex-shrink-0"
|
||||||
style={{ backgroundColor: 'var(--color-primary)' }}
|
style={{ backgroundColor: 'var(--color-primary)' }}
|
||||||
>
|
>
|
||||||
<service.icon className="w-8 h-8 text-white" />
|
<service.icon className="w-8 h-8 text-white" />
|
||||||
</div>
|
</div>
|
||||||
|
<h2 className="text-h2 leading-[57.6px] tracking-[-0.48px]" style={{ fontSize: '48px' }}>{service.title}</h2>
|
||||||
<h2 className="text-h2 leading-tight">{service.title}</h2>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Description */}
|
{/* Brief Overview */}
|
||||||
<p className="text-body-lg text-muted mb-8 leading-relaxed">
|
<p className="text-body-lg text-muted leading-relaxed mb-8">
|
||||||
{service.description}
|
{service.description.split('.')[0]}.
|
||||||
</p>
|
</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 */}
|
{/* CTA Button */}
|
||||||
<div className="mb-8 primary-cta-container-left cta-left-locked">
|
<div className="primary-cta-container-left cta-left-locked">
|
||||||
<PrimaryCTAButton
|
<PrimaryCTAButton
|
||||||
text={service.ctaText}
|
text="Explore This Service"
|
||||||
onClick={() => navigateTo('/contact')}
|
onClick={() => navigateTo(service.route)}
|
||||||
ariaLabel={`Contact us about ${service.title}`}
|
ariaLabel={`Learn more about ${service.title}`}
|
||||||
className="cta-text-black"
|
className="cta-text-black"
|
||||||
/>
|
/>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
|
|
||||||
{/* Image Section */}
|
{/* Image Section - Positioned at Bottom Right (Figma Design) */}
|
||||||
<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="absolute bottom-0 right-0 w-[496px] h-[479px] hidden lg:block overflow-hidden rounded-[16px]">
|
||||||
}`}>
|
|
||||||
<div className="relative">
|
|
||||||
<div
|
<div
|
||||||
className="w-full aspect-[4/3] rounded-2xl bg-cover bg-center bg-no-repeat"
|
className="absolute h-[479px] left-[-71px] top-0 w-[639px] rounded-[16px] bg-cover bg-center bg-no-repeat"
|
||||||
style={{
|
style={{
|
||||||
backgroundImage: `url('https://images.unsplash.com/photo-${index === 0 ? '1522071820-d3ca7b99e0dd' : // leadership team
|
backgroundImage: `url('https://images.unsplash.com/photo-${index === 0 ? '1758270705696-ec9caffc73dd' : // leadership team
|
||||||
index === 1 ? '1551836022-d5d88e9218df' : // assessment/analytics
|
index === 1 ? '1551836022-d5d88e9218df' : // assessment/analytics
|
||||||
index === 2 ? '1600880292203-757bb62b4baf' : // management meeting
|
index === 2 ? '1600880292203-757bb62b4baf' : // management meeting
|
||||||
index === 3 ? '1552664730-d307ca884978' : // culture/team
|
index === 3 ? '1552664730-d307ca884978' : // culture/team
|
||||||
'1573496359142-b8d87734a5a2' // coaching
|
index === 4 ? '1573496359142-b8d87734a5a2' : // coaching
|
||||||
|
'1582213782179-e0d53f98f2ca' // leadership campus/facility
|
||||||
}?w=800&h=600&fit=crop')`
|
}?w=800&h=600&fit=crop')`
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-primary/20 to-transparent rounded-2xl"></div>
|
<div className="absolute inset-0 bg-gradient-to-br from-primary/20 to-transparent rounded-[16px]"></div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -7,69 +7,43 @@ import {
|
|||||||
Globe,
|
Globe,
|
||||||
MessageSquare,
|
MessageSquare,
|
||||||
GraduationCap,
|
GraduationCap,
|
||||||
|
TrendingUp,
|
||||||
|
Building,
|
||||||
ArrowRight
|
ArrowRight
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { BrandedTag } from "./about/BrandedTag";
|
import { BrandedTag } from "./about/BrandedTag";
|
||||||
import { PrimaryCTAButton } from "./PrimaryCTAButton";
|
import { StandardCTAButton } from "./StandardCTAButton";
|
||||||
import { navigateTo } from "./Router";
|
import { navigateTo } from "./Router";
|
||||||
|
import { ImageWithFallback } from "./figma/ImageWithFallback";
|
||||||
|
|
||||||
// Services data
|
interface HighlightCard {
|
||||||
const recognitionItems = [
|
card_title: string;
|
||||||
{
|
icon_url: string;
|
||||||
id: 1,
|
accessible_label: string;
|
||||||
title: "Leadership Development",
|
body_text: string;
|
||||||
description: "Comprehensive programs designed to cultivate strategic thinking and emotional intelligence. Develop capabilities that drive organizational success through authentic leadership practices.",
|
display_order: number;
|
||||||
icon: <Users size={28} />,
|
}
|
||||||
badge: "CORE PROGRAM",
|
|
||||||
badgeColor: "#F8C301"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
title: "Management Development",
|
|
||||||
description: "Essential skills training for first-time and experienced managers seeking growth. Focus on communication, delegation, and performance management excellence.",
|
|
||||||
icon: <Settings size={28} />,
|
|
||||||
badge: "POPULAR",
|
|
||||||
badgeColor: "#04045B"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
title: "Culture Competence",
|
|
||||||
description: "Build cultural awareness and inclusive practices that enhance team collaboration. Navigate cultural differences with confidence and create inclusive environments.",
|
|
||||||
icon: <Globe size={28} />,
|
|
||||||
badge: "GLOBAL FOCUS",
|
|
||||||
badgeColor: "#F8C301"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
title: "Executive Coaching",
|
|
||||||
description: "One-on-one personalized development for senior leaders and high-potential talent. Strategic guidance for complex leadership challenges and career advancement.",
|
|
||||||
icon: <User size={28} />,
|
|
||||||
badge: "PREMIUM",
|
|
||||||
badgeColor: "#04045B"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
title: "Communication Excellence",
|
|
||||||
description: "Master the art of influential communication across all organizational levels. Develop presentation skills, difficult conversation navigation, and stakeholder engagement.",
|
|
||||||
icon: <MessageSquare size={28} />,
|
|
||||||
badge: "ESSENTIAL",
|
|
||||||
badgeColor: "#F8C301"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 6,
|
|
||||||
title: "Change Leadership",
|
|
||||||
description: "Guide organizations through transformation with confidence and clarity. Learn frameworks for managing resistance, building momentum, and sustaining change initiatives.",
|
|
||||||
icon: <GraduationCap size={28} />,
|
|
||||||
badge: "STRATEGIC",
|
|
||||||
badgeColor: "#04045B"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
export function ServicesSection() {
|
interface ServicesSectionProps {
|
||||||
|
highlightCards?: HighlightCard[];
|
||||||
|
isLoading?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ServicesSection({ highlightCards = [], isLoading = false }: ServicesSectionProps) {
|
||||||
const [isVisible, setIsVisible] = useState(false);
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
const cardRefs = useRef<(HTMLDivElement | null)[]>([]);
|
const cardRefs = useRef<(HTMLDivElement | null)[]>([]);
|
||||||
const sectionRef = 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
|
// Add card refs helper
|
||||||
const addCardRef = (el: HTMLDivElement | null, index: number) => {
|
const addCardRef = (el: HTMLDivElement | null, index: number) => {
|
||||||
cardRefs.current[index] = el;
|
cardRefs.current[index] = el;
|
||||||
@@ -106,34 +80,65 @@ 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 (
|
return (
|
||||||
<section
|
<section
|
||||||
ref={sectionRef}
|
ref={sectionRef}
|
||||||
className="py-16 lg:py-20"
|
className="py-16 lg:py-20"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: '#F7F7FD',
|
backgroundColor: '#fff',
|
||||||
fontFamily: 'var(--font-family-brand)'
|
fontFamily: 'var(--font-family-brand)'
|
||||||
}}
|
}}
|
||||||
aria-labelledby="recognition-section-heading"
|
aria-labelledby="recognition-section-heading"
|
||||||
>
|
>
|
||||||
<div className="section-margin-x">
|
<div className="section-margin-x">
|
||||||
<div className="max-w-7xl mx-auto">
|
<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 */}
|
{/* Left Side - Sticky Content */}
|
||||||
<div className="col-span-5 sticky top-24 self-start">
|
<div className="col-span-5 sticky top-24 self-start">
|
||||||
<div className="recognition-header pr-8">
|
<div className="recognition-header pr-8">
|
||||||
<BrandedTag
|
<BrandedTag
|
||||||
text="Leadership Development Programs"
|
text="Our Services"
|
||||||
/>
|
/>
|
||||||
<h2
|
<h2
|
||||||
id="recognition-section-heading"
|
id="recognition-section-heading"
|
||||||
className="text-h2 mb-6"
|
className="text-h2 mb-6"
|
||||||
>
|
>
|
||||||
Services That Shape Stronger Leaders
|
Shaping Leaders, Cultures, and Institutions
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-body-lg text-muted">
|
<p className="text-body-lg text-muted mb-8">
|
||||||
Our comprehensive leadership development programs are designed to build future-ready leaders who thrive in complexity and drive measurable organizational impact.
|
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>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -144,11 +149,11 @@ export function ServicesSection() {
|
|||||||
role="list"
|
role="list"
|
||||||
aria-label="Leadership development services"
|
aria-label="Leadership development services"
|
||||||
>
|
>
|
||||||
{recognitionItems.map((item, index) => (
|
{serviceItems.map((item, index) => (
|
||||||
<div
|
<div
|
||||||
key={item.id}
|
key={item.id}
|
||||||
ref={(el) => addCardRef(el, index)}
|
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"
|
role="listitem"
|
||||||
aria-labelledby={`recognition-title-${item.id}`}
|
aria-labelledby={`recognition-title-${item.id}`}
|
||||||
aria-describedby={`recognition-desc-${item.id}`}
|
aria-describedby={`recognition-desc-${item.id}`}
|
||||||
@@ -158,6 +163,7 @@ export function ServicesSection() {
|
|||||||
transitionDelay: `${(index + 1) * 150}ms`,
|
transitionDelay: `${(index + 1) * 150}ms`,
|
||||||
opacity: isVisible ? 1 : 0
|
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"
|
className="p-8 transition-all duration-300 hover:shadow-xl hover:-translate-y-1 border bg-white"
|
||||||
@@ -167,30 +173,26 @@ export function ServicesSection() {
|
|||||||
fontFamily: 'var(--font-family-brand)'
|
fontFamily: 'var(--font-family-brand)'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex items-start justify-between mb-6">
|
<div className="flex items-start mb-6">
|
||||||
<div
|
<div
|
||||||
className="w-14 h-14 flex items-center justify-center transition-transform duration-300 group-hover:scale-110"
|
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)',
|
backgroundColor: 'var(--color-brand-primary)',
|
||||||
borderRadius: '12px',
|
borderRadius: '12px',
|
||||||
color: 'white'
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{item.icon}
|
{/* Image icon from icon_url */}
|
||||||
</div>
|
<img
|
||||||
{item.badge && (
|
src={item.iconUrl}
|
||||||
<div
|
alt={item.accessibleLabel || item.title}
|
||||||
className="px-3 py-1 text-xs font-bold uppercase tracking-wider"
|
className="w-8 h-8 object-contain filter brightness-0 invert" // Makes white icon on colored background
|
||||||
style={{
|
onError={(e) => {
|
||||||
backgroundColor: item.badgeColor,
|
// Fallback if image fails to load
|
||||||
color: item.badgeColor === '#F8C301' ? 'var(--color-brand-black)' : 'white',
|
e.currentTarget.style.display = 'none';
|
||||||
borderRadius: '20px',
|
// You could add a fallback icon here if needed
|
||||||
fontFamily: 'var(--font-family-brand)'
|
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
{item.badge}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="recognition-card-content">
|
<div className="recognition-card-content">
|
||||||
@@ -214,21 +216,102 @@ export function ServicesSection() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* CTA Button - Updated to redirect to leadership journey */}
|
{/* Mobile Layout - Stacked Header + Horizontal Scrollable Cards */}
|
||||||
<div className="flex justify-center mt-16">
|
<div className="lg:hidden">
|
||||||
<motion.div
|
{/* Mobile Header */}
|
||||||
initial={{ opacity: 0, y: 20 }}
|
<div className="text-center mb-8">
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
<BrandedTag
|
||||||
transition={{ duration: 0.6, delay: 0.6 }}
|
text="Our Services"
|
||||||
viewport={{ once: true }}
|
|
||||||
>
|
|
||||||
<PrimaryCTAButton
|
|
||||||
text="Get Started Today"
|
|
||||||
onClick={() => navigateTo('/leadership-journey')}
|
|
||||||
ariaLabel="Get started with leadership development programs"
|
|
||||||
className="get-started-cta-override"
|
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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 { BrandedTag } from "./about/BrandedTag";
|
||||||
import { PrimaryCTAButton } from "./PrimaryCTAButton";
|
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 {
|
interface StatItemProps {
|
||||||
end: number;
|
end: number;
|
||||||
@@ -17,124 +27,104 @@ function StatItem({ end, suffix, label, duration = 2000 }: StatItemProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-0">
|
<div className="mb-0">
|
||||||
{/* Top line */}
|
|
||||||
<div
|
<div
|
||||||
className="w-full h-[1px] mb-4"
|
className="w-full h-[1px] mb-4"
|
||||||
style={{ backgroundColor: 'var(--color-brand-gray-muted)' }}
|
style={{ backgroundColor: "var(--color-brand-gray-muted)" }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Large number */}
|
<span
|
||||||
<div
|
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className="stats-number mb-3 leading-none"
|
className="stats-number mb-3 leading-none"
|
||||||
style={{
|
style={{
|
||||||
color: 'var(--color-brand-primary)',
|
color: "var(--color-brand-primary)",
|
||||||
fontFamily: 'var(--font-family-base)'
|
fontFamily: "var(--font-family-base)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{count}
|
{count}
|
||||||
</div>
|
</span>
|
||||||
|
|
||||||
{/* Yellow square and label */}
|
|
||||||
<div className="flex items-center mb-4">
|
<div className="flex items-center mb-4">
|
||||||
<div
|
<div
|
||||||
className="w-2 h-2 mr-3 flex-shrink-0"
|
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"
|
className="text-eyebrow"
|
||||||
style={{ color: 'var(--color-brand-black)' }}
|
style={{ color: "var(--color-brand-black)" }}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bottom line */}
|
|
||||||
<div
|
<div
|
||||||
className="w-full h-[1px]"
|
className="w-full h-[1px]"
|
||||||
style={{ backgroundColor: 'var(--color-brand-gray-muted)' }}
|
style={{ backgroundColor: "var(--color-brand-gray-muted)" }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</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 (
|
return (
|
||||||
<section
|
<section
|
||||||
className="py-20"
|
className="py-20"
|
||||||
style={{ backgroundColor: 'var(--color-brand-bg-light)' }}
|
style={{ backgroundColor: "var(--color-brand-bg-light)" }}
|
||||||
>
|
>
|
||||||
<div className="section-margin-x">
|
<div className="section-margin-x">
|
||||||
<div className="max-w-7xl mx-auto">
|
<div className="max-w-7xl mx-auto">
|
||||||
<div className="mb-12 lg:mb-16 md:mb-12 sm:mb-8">
|
<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="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">
|
<div className="lg:col-span-6 md:col-span-8 sm:col-span-12">
|
||||||
<h2 className="text-h2 mb-8">
|
<h2 className="text-h2 mb-8">
|
||||||
Driving impact by building future-ready leaders who thrive in
|
Your Partner in Leadership, Culture, and Capability Building
|
||||||
complexity and lead with confidence.
|
|
||||||
</h2>
|
</h2>
|
||||||
{/* CTA Button */}
|
|
||||||
<PrimaryCTAButton
|
<PrimaryCTAButton
|
||||||
text="About Us"
|
text="About Us"
|
||||||
onClick={() => console.log('About us clicked')}
|
onClick={() => console.log("About us clicked")}
|
||||||
ariaLabel="Learn more about KLC"
|
ariaLabel="Learn more about KLC"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Desktop Statistics */}
|
{/* Desktop */}
|
||||||
<div className="hidden lg:block lg:col-start-9 lg:col-end-13">
|
<div className="hidden lg:block lg:col-start-9 lg:col-end-13">
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
|
{sortedStats.map((stat) => (
|
||||||
<StatItem
|
<StatItem
|
||||||
end={27000}
|
key={stat.id}
|
||||||
suffix="+"
|
end={stat.number}
|
||||||
label="LEADERS DEVELOPED"
|
suffix={stat.suffix}
|
||||||
duration={2500}
|
label={stat.label}
|
||||||
/>
|
|
||||||
<StatItem
|
|
||||||
end={150}
|
|
||||||
suffix="+"
|
|
||||||
label="CORPORATE CLIENTS"
|
|
||||||
duration={2000}
|
|
||||||
/>
|
|
||||||
<StatItem
|
|
||||||
end={20}
|
|
||||||
suffix="+"
|
|
||||||
label="COUNTRIES SERVED"
|
|
||||||
duration={1800}
|
|
||||||
/>
|
/>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Mobile Statistics - Show below content on mobile/tablet */}
|
{/* Mobile */}
|
||||||
<div className="block lg:hidden mt-12">
|
<div className="block lg:hidden mt-12">
|
||||||
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 sm:gap-8">
|
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 sm:gap-8">
|
||||||
|
{sortedStats.map((stat) => (
|
||||||
<StatItem
|
<StatItem
|
||||||
end={27000}
|
key={stat.id}
|
||||||
suffix="+"
|
end={stat.number}
|
||||||
label="LEADERS DEVELOPED"
|
suffix={stat.suffix}
|
||||||
duration={2500}
|
label={stat.label}
|
||||||
/>
|
|
||||||
<StatItem
|
|
||||||
end={150}
|
|
||||||
suffix="+"
|
|
||||||
label="CORPORATE CLIENTS"
|
|
||||||
duration={2000}
|
|
||||||
/>
|
|
||||||
<StatItem
|
|
||||||
end={20}
|
|
||||||
suffix="+"
|
|
||||||
label="COUNTRIES SERVED"
|
|
||||||
duration={1800}
|
|
||||||
/>
|
/>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
344
src/components/TeamMemberModal.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import { useState, useRef, useEffect } from "react";
|
|||||||
import { BrandedTag } from "./about/BrandedTag";
|
import { BrandedTag } from "./about/BrandedTag";
|
||||||
|
|
||||||
interface Testimonial {
|
interface Testimonial {
|
||||||
id?: number;
|
id?: number | string;
|
||||||
name: string;
|
name: string;
|
||||||
role: string;
|
role: string;
|
||||||
company?: string;
|
company?: string;
|
||||||
@@ -16,8 +16,13 @@ interface Testimonial {
|
|||||||
isVideo?: boolean;
|
isVideo?: boolean;
|
||||||
videoThumbnail?: string;
|
videoThumbnail?: string;
|
||||||
videoUrl?: string;
|
videoUrl?: string;
|
||||||
|
designation?: string;
|
||||||
|
content?: string;
|
||||||
|
video_url?: string;
|
||||||
|
profile_xid?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default testimonials as fallback
|
||||||
const defaultTestimonialsData: Testimonial[] = [
|
const defaultTestimonialsData: Testimonial[] = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
@@ -52,38 +57,6 @@ const defaultTestimonialsData: Testimonial[] = [
|
|||||||
isVideo: true,
|
isVideo: true,
|
||||||
videoThumbnail: "https://images.unsplash.com/photo-1560472355-109703aa3edc?w=600&h=300&fit=crop",
|
videoThumbnail: "https://images.unsplash.com/photo-1560472355-109703aa3edc?w=600&h=300&fit=crop",
|
||||||
videoUrl: "https://example.com/testimonial-video-2.mp4"
|
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 }: {
|
function TestimonialCard({ testimonial, onPlayVideo }: {
|
||||||
testimonial: Testimonial;
|
testimonial: Testimonial;
|
||||||
onPlayVideo: (videoUrl: string) => void;
|
onPlayVideo: (videoUrl: string) => void;
|
||||||
}) {
|
}) {
|
||||||
const avatarSrc = testimonial.avatar || testimonial.image;
|
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 (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
@@ -162,14 +141,14 @@ function TestimonialCard({ testimonial, onPlayVideo }: {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Video Testimonials */}
|
{/* Video Testimonials */}
|
||||||
{testimonial.isVideo ? (
|
{isVideo ? (
|
||||||
<div
|
<div
|
||||||
className="relative h-full cursor-pointer overflow-hidden group rounded-xl"
|
className="relative h-full cursor-pointer overflow-hidden group rounded-xl"
|
||||||
onClick={() => onPlayVideo(testimonial.videoUrl || "")}
|
onClick={() => onPlayVideo(videoUrl)}
|
||||||
>
|
>
|
||||||
<ImageWithFallback
|
<ImageWithFallback
|
||||||
src={testimonial.videoThumbnail || avatarSrc || ""}
|
src={testimonial.videoThumbnail || avatarSrc || "https://images.unsplash.com/photo-1552664730-d307ca884978?w=600&h=300&fit=crop"}
|
||||||
alt={`${testimonial.name} video testimonial`}
|
alt={`${name} video testimonial`}
|
||||||
className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
|
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">
|
<div className="w-10 h-10 rounded-full overflow-hidden bg-white shadow-lg flex-shrink-0">
|
||||||
<ImageWithFallback
|
<ImageWithFallback
|
||||||
src={avatarSrc || ""}
|
src={avatarSrc || ""}
|
||||||
alt={testimonial.name}
|
alt={name}
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<h4 className="font-semibold text-white mb-1 text-sm">
|
<h4 className="font-semibold text-white mb-1 text-sm">
|
||||||
{testimonial.name}
|
{name}
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-xs text-white/80 truncate">
|
<p className="text-xs text-white/80 truncate">
|
||||||
{testimonial.role}
|
{role}
|
||||||
{testimonial.company && ` • ${testimonial.company}`}
|
{testimonial.company && ` • ${testimonial.company}`}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -223,7 +202,7 @@ function TestimonialCard({ testimonial, onPlayVideo }: {
|
|||||||
<Star
|
<Star
|
||||||
key={star}
|
key={star}
|
||||||
size={14}
|
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>
|
</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">
|
<div className="w-12 h-12 rounded-full overflow-hidden bg-gray-100 flex-shrink-0">
|
||||||
<ImageWithFallback
|
<ImageWithFallback
|
||||||
src={avatarSrc || ""}
|
src={avatarSrc || ""}
|
||||||
alt={testimonial.name}
|
alt={name}
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<h4 className="font-semibold text-black mb-1 text-sm">
|
<h4 className="font-semibold text-black mb-1 text-sm">
|
||||||
{testimonial.name}
|
{name}
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-xs text-gray-600">
|
<p className="text-xs text-gray-600">
|
||||||
{testimonial.role}
|
{role}
|
||||||
</p>
|
</p>
|
||||||
{testimonial.company && (
|
{testimonial.company && (
|
||||||
<p className="text-xs text-gray-500 font-medium">
|
<p className="text-xs text-gray-500 font-medium">
|
||||||
@@ -264,7 +243,7 @@ function TestimonialCard({ testimonial, onPlayVideo }: {
|
|||||||
<Star
|
<Star
|
||||||
key={star}
|
key={star}
|
||||||
size={14}
|
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>
|
</div>
|
||||||
@@ -277,7 +256,7 @@ function TestimonialCard({ testimonial, onPlayVideo }: {
|
|||||||
"
|
"
|
||||||
</span>
|
</span>
|
||||||
<span className="relative z-10">
|
<span className="relative z-10">
|
||||||
{testimonial.quote}
|
{quote}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import { Label } from "./ui/label";
|
|||||||
import { Textarea } from "./ui/textarea";
|
import { Textarea } from "./ui/textarea";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select";
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select";
|
||||||
import { navigateTo } from "./Router";
|
import { navigateTo } from "./Router";
|
||||||
|
import kautilya from "../assets/Kautilya.png";
|
||||||
// Calendar helper functions
|
// Calendar helper functions
|
||||||
const getDaysInMonth = (date: Date) => {
|
const getDaysInMonth = (date: Date) => {
|
||||||
return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
|
return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
|
||||||
@@ -104,10 +104,9 @@ interface BookingFormData {
|
|||||||
interface FacilityCardProps {
|
interface FacilityCardProps {
|
||||||
facility: typeof facilities[0];
|
facility: typeof facilities[0];
|
||||||
index: number;
|
index: number;
|
||||||
onBookNow: (facility: typeof facilities[0]) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function FacilityCard({ facility, index, onBookNow }: FacilityCardProps) {
|
function FacilityCard({ facility, index }: FacilityCardProps) {
|
||||||
const IconComponent = facility.icon;
|
const IconComponent = facility.icon;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -153,14 +152,10 @@ function FacilityCard({ facility, index, onBookNow }: FacilityCardProps) {
|
|||||||
{facility.description}
|
{facility.description}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Book Now Button */}
|
{/* Capacity Info */}
|
||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
<div className="hero-slide-button">
|
<div className="bg-white/20 backdrop-blur-sm rounded-lg px-4 py-2 border border-white/30">
|
||||||
<PrimaryCTAButton
|
<p className="text-white text-sm font-medium">Capacity: {facility.capacity} people</p>
|
||||||
text="Book Now"
|
|
||||||
onClick={() => onBookNow(facility)}
|
|
||||||
ariaLabel={`Book ${facility.name} now`}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -330,7 +325,7 @@ function BookingModal({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Calendar Footer - Compact */}
|
{/* 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 justify-center gap-3 text-xs text-gray-500">
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<div className="w-2 h-2 rounded-full bg-primary"></div>
|
<div className="w-2 h-2 rounded-full bg-primary"></div>
|
||||||
@@ -341,7 +336,7 @@ function BookingModal({
|
|||||||
<span>Available</span>
|
<span>Available</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -371,7 +366,7 @@ function BookingModal({
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-body lg:text-subhead mb-1">
|
<h2 className="text-body lg:text-subhead mb-1">
|
||||||
{facility.name} Virtual Tour & Booking
|
Kautilya Leadership Centre
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-small text-muted">
|
<p className="text-small text-muted">
|
||||||
Capacity: {facility.capacity} people
|
Capacity: {facility.capacity} people
|
||||||
@@ -391,7 +386,7 @@ function BookingModal({
|
|||||||
{/* Modal Content - Side by Side Layout No Scroll */}
|
{/* Modal Content - Side by Side Layout No Scroll */}
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 flex-1 min-h-0">
|
<div className="grid grid-cols-1 lg:grid-cols-2 flex-1 min-h-0">
|
||||||
{/* Left Side - Virtual Tour */}
|
{/* 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">
|
<div className="flex flex-col h-full space-y-2 lg:space-y-3">
|
||||||
{/* Video Section - Compact */}
|
{/* Video Section - Compact */}
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
@@ -422,33 +417,20 @@ function BookingModal({
|
|||||||
data-tour-type="360-viewer" // Change to "video" for video fallback
|
data-tour-type="360-viewer" // Change to "video" for video fallback
|
||||||
>
|
>
|
||||||
{/* Fallback: Video iframe - Replace this entire section with 360 viewer */}
|
{/* Fallback: Video iframe - Replace this entire section with 360 viewer */}
|
||||||
<iframe
|
{/* <iframe
|
||||||
src={facility.videoUrl}
|
src={facility.videoUrl}
|
||||||
title={`${facility.name} Virtual Tour`}
|
title={`${facility.name} Virtual Tour`}
|
||||||
className="w-full h-full"
|
className="w-full h-full"
|
||||||
allowFullScreen
|
allowFullScreen
|
||||||
frameBorder="0"
|
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>
|
</div>
|
||||||
|
|
||||||
{/* Interactive Controls Overlay (optional) */}
|
{/* Interactive Controls Overlay (optional) */}
|
||||||
@@ -504,7 +486,7 @@ function BookingModal({
|
|||||||
{/* Action Button - Compact */}
|
{/* Action Button - Compact */}
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => navigateTo('/services/learning-facility')}
|
onClick={() => navigateTo('/learning-facility')}
|
||||||
className="w-full h-8 lg:h-10 text-xs lg:text-small"
|
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" />
|
<Building className="w-3 h-3 lg:w-4 lg:h-4 mr-1" />
|
||||||
@@ -597,7 +579,7 @@ function BookingModal({
|
|||||||
<Label htmlFor="role" className="text-xs font-normal text-black mb-1 block">
|
<Label htmlFor="role" className="text-xs font-normal text-black mb-1 block">
|
||||||
Your Role *
|
Your Role *
|
||||||
</Label>
|
</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">
|
<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" />
|
<SelectValue placeholder="Role" className="text-gray-400 opacity-50" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
@@ -615,7 +597,7 @@ function BookingModal({
|
|||||||
<Label htmlFor="teamSize" className="text-xs font-normal text-black mb-1 block">
|
<Label htmlFor="teamSize" className="text-xs font-normal text-black mb-1 block">
|
||||||
Expected Team Size *
|
Expected Team Size *
|
||||||
</Label>
|
</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">
|
<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" />
|
<SelectValue placeholder="Size" className="text-gray-400 opacity-50" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
@@ -641,7 +623,7 @@ function BookingModal({
|
|||||||
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{/* Selected Facility Info */}
|
{/* 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="flex items-center gap-2">
|
||||||
<div
|
<div
|
||||||
className="w-6 h-6 rounded flex items-center justify-center"
|
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>
|
<p className="text-xs text-gray-600">Capacity: {facility.capacity} people</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> */}
|
||||||
|
|
||||||
{/* Calendar Date Selection */}
|
{/* Calendar Date Selection */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@@ -731,8 +713,9 @@ export function VirtualSpaceSection() {
|
|||||||
const [selectedFacility, setSelectedFacility] = useState<typeof facilities[0] | null>(null);
|
const [selectedFacility, setSelectedFacility] = useState<typeof facilities[0] | null>(null);
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
|
||||||
const handleBookNow = (facility: typeof facilities[0]) => {
|
const handleBookNow = () => {
|
||||||
setSelectedFacility(facility);
|
// Set the first facility as default for booking
|
||||||
|
setSelectedFacility(facilities[0]);
|
||||||
setIsModalOpen(true);
|
setIsModalOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -752,7 +735,6 @@ export function VirtualSpaceSection() {
|
|||||||
key={facility.id}
|
key={facility.id}
|
||||||
facility={facility}
|
facility={facility}
|
||||||
index={index}
|
index={index}
|
||||||
onBookNow={handleBookNow}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -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.
|
Take a virtual walk through our state-of-the-art facility designed to inspire leadership excellence and foster collaborative learning.
|
||||||
</motion.p>
|
</motion.p>
|
||||||
|
|
||||||
{/* Main CTA Button - Explore Our Space */}
|
{/* Main CTA Button - Book Now */}
|
||||||
<motion.div
|
<motion.div
|
||||||
className="flex justify-center"
|
className="flex justify-center"
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
@@ -806,9 +788,9 @@ export function VirtualSpaceSection() {
|
|||||||
>
|
>
|
||||||
<div className="hero-slide-button">
|
<div className="hero-slide-button">
|
||||||
<PrimaryCTAButton
|
<PrimaryCTAButton
|
||||||
text="Explore Our Space"
|
text="Book Now"
|
||||||
onClick={() => navigateTo('/services/learning-facility')}
|
onClick={handleBookNow}
|
||||||
ariaLabel="Explore our virtual learning space and facilities"
|
ariaLabel="Book our virtual learning space and facilities"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|||||||
355
src/components/VirtualTour360.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -41,19 +41,53 @@ import {
|
|||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { motion, AnimatePresence } from 'motion/react';
|
import { motion, AnimatePresence } from 'motion/react';
|
||||||
import { navigateTo } from './Router';
|
import { navigateTo } from './Router';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
import { ImageWithFallback } from './figma/ImageWithFallback';
|
import { ImageWithFallback } from './figma/ImageWithFallback';
|
||||||
import { BrandedTag } from './about/BrandedTag';
|
import { BrandedTag } from './about/BrandedTag';
|
||||||
import { PrimaryCTAButton } from './PrimaryCTAButton';
|
import { PrimaryCTAButton } from './PrimaryCTAButton';
|
||||||
import { toast } from 'sonner@2.0.3';
|
import { toast } from 'sonner';
|
||||||
import { getWebinarBySlug, sharedWebinarsData, type WebinarData } from '../data/webinarsData';
|
import { useGetWebinarByIdQuery } from '../redux/services/webinarApi';
|
||||||
|
|
||||||
interface WebinarDetailProps {
|
interface WebinarDetailProps {
|
||||||
params: { slug: string };
|
params?: { webinar_id: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function WebinarDetail({ params }: WebinarDetailProps) {
|
// Helper function to format date
|
||||||
// Get webinar data from shared data source
|
const formatDate = (dateTimeString: string) => {
|
||||||
const [webinar, setWebinar] = useState<WebinarData | null>(null);
|
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 [timeLeft, setTimeLeft] = useState({ days: 0, hours: 0, minutes: 0, seconds: 0 });
|
||||||
const [isRegistered, setIsRegistered] = useState(false);
|
const [isRegistered, setIsRegistered] = useState(false);
|
||||||
const [showRegistrationForm, setShowRegistrationForm] = useState(false);
|
const [showRegistrationForm, setShowRegistrationForm] = useState(false);
|
||||||
@@ -75,22 +109,76 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
|||||||
const videoRef = useRef<HTMLVideoElement>(null);
|
const videoRef = useRef<HTMLVideoElement>(null);
|
||||||
const shareMenuRef = useRef<HTMLDivElement>(null);
|
const shareMenuRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
// Load webinar data on component mount
|
// Transform API data to match your component's expected format
|
||||||
useEffect(() => {
|
const webinar = webinarData?.data ? {
|
||||||
const webinarData = getWebinarBySlug(params.slug);
|
id: webinarData.data.id,
|
||||||
if (webinarData) {
|
title: webinarData.data.session_title,
|
||||||
setWebinar(webinarData);
|
description: webinarData.data.description || '',
|
||||||
} else {
|
thumbnail: webinarData.data.media?.file_name
|
||||||
// Fallback: redirect to webinars page if webinar not found
|
? `/api/media/${webinarData.data.media.id}`
|
||||||
navigateTo('/webinars');
|
: '/images/default-webinar.jpg',
|
||||||
}
|
theme: webinarData.data.webinar_category_id || 'Webinar',
|
||||||
}, [params.slug]);
|
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
|
// Countdown timer
|
||||||
useEffect(() => {
|
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 updateCountdown = () => {
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime();
|
||||||
@@ -110,7 +198,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
|||||||
const interval = setInterval(updateCountdown, 1000);
|
const interval = setInterval(updateCountdown, 1000);
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, [webinar?.date, webinar?.time, webinar?.status]);
|
}, [webinar, webinarData?.data.session_datetime]);
|
||||||
|
|
||||||
// Close share menu when clicking outside
|
// Close share menu when clicking outside
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -129,7 +217,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setIsSubmittingRegistration(true);
|
setIsSubmittingRegistration(true);
|
||||||
|
|
||||||
// Simulate API call
|
// TODO: Integrate with your registration API endpoint
|
||||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||||
|
|
||||||
setIsSubmittingRegistration(false);
|
setIsSubmittingRegistration(false);
|
||||||
@@ -163,7 +251,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
|||||||
if (!webinar) return { text: 'Loading...', onClick: () => {}, disabled: true };
|
if (!webinar) return { text: 'Loading...', onClick: () => {}, disabled: true };
|
||||||
|
|
||||||
const now = new Date();
|
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);
|
const tenMinutesBefore = new Date(webinarDate.getTime() - 10 * 60 * 1000);
|
||||||
|
|
||||||
switch (webinar.status) {
|
switch (webinar.status) {
|
||||||
@@ -177,7 +265,10 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
|||||||
if (now >= tenMinutesBefore) {
|
if (now >= tenMinutesBefore) {
|
||||||
return {
|
return {
|
||||||
text: 'Launch in Zoom',
|
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
|
disabled: false
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
@@ -256,7 +347,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Show loading state while webinar data is loading
|
// Show loading state while webinar data is loading
|
||||||
if (!webinar) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex items-center justify-center" style={{ backgroundColor: '#FFFFFF' }}>
|
<div className="min-h-screen flex items-center justify-center" style={{ backgroundColor: '#FFFFFF' }}>
|
||||||
<div className="text-center">
|
<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 ctaProps = getCTAProps();
|
||||||
|
const sessionDateTime = webinarData?.data.session_datetime ? new Date(webinarData.data.session_datetime) : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen" style={{ backgroundColor: '#FFFFFF' }}>
|
<div className="min-h-screen" style={{ backgroundColor: '#FFFFFF' }}>
|
||||||
@@ -298,7 +408,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Countdown Timer for Upcoming Webinars */}
|
{/* Countdown Timer for Upcoming Webinars */}
|
||||||
{webinar.status === 'upcoming' && (
|
{webinar.status === 'upcoming' && sessionDateTime && (
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<div className="grid grid-cols-4 gap-4 max-w-md mb-4">
|
<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>
|
||||||
<div className="text-small-white opacity-80">
|
<div className="text-small-white opacity-80">
|
||||||
Starts {new Date(`${webinar.date}T${webinar.time}:00`).toLocaleDateString('en-US', {
|
Starts {webinar.date} at {webinar.time}
|
||||||
weekday: 'long',
|
|
||||||
year: 'numeric',
|
|
||||||
month: 'long',
|
|
||||||
day: 'numeric'
|
|
||||||
})} at {webinar.time} {webinar.timezone}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -337,11 +442,11 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
|||||||
<span className="text-small font-medium">LIVE NOW</span>
|
<span className="text-small font-medium">LIVE NOW</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-small-white">
|
<div className="text-small-white">
|
||||||
{webinar.attendees} people watching
|
Max capacity: {webinar.maxAttendees} attendees
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-body-lg-white">
|
<div className="text-body-lg-white">
|
||||||
Session in progress • Ends at {webinar.endTime} {webinar.timezone}
|
Session in progress
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -356,7 +461,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-body-lg-white">
|
<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',
|
month: 'long',
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
year: 'numeric'
|
year: 'numeric'
|
||||||
@@ -425,7 +530,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
|||||||
<div className="flex items-center gap-2 text-muted">
|
<div className="flex items-center gap-2 text-muted">
|
||||||
<Users className="w-4 h-4" />
|
<Users className="w-4 h-4" />
|
||||||
<span className="text-small">
|
<span className="text-small">
|
||||||
{webinar.attendees} {webinar.status === 'live' ? 'watching' : 'registered'}
|
{webinar.attendees} {webinar.status === 'live' ? 'watching' : 'capacity'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -445,6 +550,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
|||||||
{webinar.abstract}
|
{webinar.abstract}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
{webinar.keyTakeaways.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-h3 mb-4">What You'll Learn</h3>
|
<h3 className="text-h3 mb-4">What You'll Learn</h3>
|
||||||
<ul className="space-y-3">
|
<ul className="space-y-3">
|
||||||
@@ -456,10 +562,12 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
|||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Agenda Timeline */}
|
{/* Agenda Timeline */}
|
||||||
|
{webinar.agenda.length > 0 && (
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-h2 mb-6">Session Agenda</h2>
|
<h2 className="text-h2 mb-6">Session Agenda</h2>
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
@@ -484,6 +592,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Host & Panelists */}
|
{/* Host & Panelists */}
|
||||||
<section>
|
<section>
|
||||||
@@ -503,6 +612,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
|||||||
<h3 className="text-h4 mb-1">{webinar.host.name}</h3>
|
<h3 className="text-h4 mb-1">{webinar.host.name}</h3>
|
||||||
<p className="text-small text-[#04045B] font-medium">HOST</p>
|
<p className="text-small text-[#04045B] font-medium">HOST</p>
|
||||||
</div>
|
</div>
|
||||||
|
{webinar.host.linkedin !== '#' && (
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -511,6 +621,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
|||||||
>
|
>
|
||||||
<ExternalLink className="w-4 h-4" />
|
<ExternalLink className="w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-small text-muted mb-1">{webinar.host.title}</p>
|
<p className="text-small text-muted mb-1">{webinar.host.title}</p>
|
||||||
<p className="text-small text-muted">{webinar.host.company}</p>
|
<p className="text-small text-muted">{webinar.host.company}</p>
|
||||||
@@ -535,6 +646,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
|||||||
<h3 className="text-h4 mb-1">{panelist.name}</h3>
|
<h3 className="text-h4 mb-1">{panelist.name}</h3>
|
||||||
<p className="text-small text-[#F8C301] font-medium">SPEAKER</p>
|
<p className="text-small text-[#F8C301] font-medium">SPEAKER</p>
|
||||||
</div>
|
</div>
|
||||||
|
{panelist.linkedin !== '#' && (
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -543,6 +655,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
|||||||
>
|
>
|
||||||
<ExternalLink className="w-4 h-4" />
|
<ExternalLink className="w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-small text-muted mb-1">{panelist.title}</p>
|
<p className="text-small text-muted mb-1">{panelist.title}</p>
|
||||||
<p className="text-small text-muted">{panelist.company}</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-center mb-6">
|
||||||
<div className="text-h3 mb-2">{webinar.price}</div>
|
<div className="text-h3 mb-2">{webinar.price}</div>
|
||||||
<div className="text-small text-muted">
|
<div className="text-small text-muted">
|
||||||
{webinar.maxAttendees - parseInt(webinar.attendees.replace(/\D/g, '')) || 0} spots remaining
|
{webinar.maxAttendees} spots available
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -612,15 +725,8 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
|||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Calendar className="w-5 h-5 text-[#04045B]" />
|
<Calendar className="w-5 h-5 text-[#04045B]" />
|
||||||
<div>
|
<div>
|
||||||
<div className="text-body font-medium">
|
<div className="text-body font-medium">{webinar.date}</div>
|
||||||
{new Date(`${webinar.date}T${webinar.time}:00`).toLocaleDateString('en-US', {
|
<div className="text-small text-muted">{webinar.time}</div>
|
||||||
weekday: 'long',
|
|
||||||
year: 'numeric',
|
|
||||||
month: 'long',
|
|
||||||
day: 'numeric'
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<div className="text-small text-muted">{webinar.time} {webinar.timezone}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -636,7 +742,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
|||||||
|
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Users className="w-5 h-5 text-[#04045B]" />
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -863,9 +969,9 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
|||||||
{/* Add to Cart Button - Outline Blue */}
|
{/* Add to Cart Button - Outline Blue */}
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={(e) => {
|
onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
e.stopPropagation();
|
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"
|
className="flex-1 flex items-center justify-center gap-2 h-11 rounded-lg transition-all duration-200 font-medium"
|
||||||
style={{
|
style={{
|
||||||
@@ -877,11 +983,11 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
|||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
borderWidth: '2px'
|
borderWidth: '2px'
|
||||||
}}
|
}}
|
||||||
onMouseEnter={(e) => {
|
onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
e.currentTarget.style.backgroundColor = '#04045B';
|
e.currentTarget.style.backgroundColor = '#04045B';
|
||||||
e.currentTarget.style.color = 'white';
|
e.currentTarget.style.color = 'white';
|
||||||
}}
|
}}
|
||||||
onMouseLeave={(e) => {
|
onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
e.currentTarget.style.backgroundColor = 'transparent';
|
e.currentTarget.style.backgroundColor = 'transparent';
|
||||||
e.currentTarget.style.color = '#04045B';
|
e.currentTarget.style.color = '#04045B';
|
||||||
}}
|
}}
|
||||||
@@ -901,10 +1007,10 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
|||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
border: 'none'
|
border: 'none'
|
||||||
}}
|
}}
|
||||||
onMouseEnter={(e) => {
|
onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
e.currentTarget.style.backgroundColor = '#030359';
|
e.currentTarget.style.backgroundColor = '#030359';
|
||||||
}}
|
}}
|
||||||
onMouseLeave={(e) => {
|
onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
e.currentTarget.style.backgroundColor = '#04045B';
|
e.currentTarget.style.backgroundColor = '#04045B';
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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 {
|
import {
|
||||||
Search,
|
|
||||||
Calendar,
|
|
||||||
Clock,
|
|
||||||
Users,
|
|
||||||
Play,
|
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
Clock,
|
||||||
|
Eye,
|
||||||
Filter,
|
Filter,
|
||||||
Grid,
|
Grid,
|
||||||
List,
|
List,
|
||||||
SortAsc,
|
Play,
|
||||||
Eye,
|
Search,
|
||||||
Star,
|
Star,
|
||||||
ChevronLeft,
|
Users,
|
||||||
ChevronRight,
|
|
||||||
X
|
X
|
||||||
} from 'lucide-react';
|
} 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() {
|
export function Webinars() {
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [selectedCategory, setSelectedCategory] = useState('All Categories');
|
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[]>([]);
|
const [selectedStatuses, setSelectedStatuses] = useState<string[]>([]);
|
||||||
|
|
||||||
// Updated state for duration slider (min, max in minutes)
|
|
||||||
const [durationRange, setDurationRange] = useState([0, 120]);
|
const [durationRange, setDurationRange] = useState([0, 120]);
|
||||||
|
|
||||||
// Attendee range slider state
|
|
||||||
const [attendeeRange, setAttendeeRange] = useState([0, 5000]);
|
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 [viewType, setViewType] = useState<'grid' | 'list'>('grid');
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const webinarsPerPage = 6;
|
const webinarsPerPage = 6;
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
// Use shared webinars data instead of local mock data
|
// Fetch webinars from API
|
||||||
const webinars = sharedWebinarsData;
|
const {
|
||||||
|
data: webinarResponse,
|
||||||
// Get unique values for filters from shared data
|
isLoading,
|
||||||
const categories = ['All Categories', ...Array.from(new Set(webinars.map(webinar => webinar.category)))];
|
isError,
|
||||||
const formats = ['All Formats', ...Array.from(new Set(webinars.map(webinar => webinar.format)))];
|
} = useWebinarListQuery({
|
||||||
const levels = ['All Levels', ...Array.from(new Set(webinars.map(webinar => webinar.level)))];
|
limit: 100,
|
||||||
|
offset: 0,
|
||||||
// Status options for pills - updated to match shared data structure
|
search: searchTerm || undefined,
|
||||||
const statusOptions = [
|
status: selectedStatuses.length > 0 ? selectedStatuses : undefined,
|
||||||
{ value: 'upcoming', label: '📅 Upcoming', color: 'bg-blue-100 text-blue-800 border-blue-200' },
|
minDuration: durationRange[0] > 0 ? durationRange[0] : undefined,
|
||||||
{ value: 'live', label: '🔴 Live', color: 'bg-red-100 text-red-800 border-red-200' },
|
maxDuration: durationRange[1] < 120 ? durationRange[1] : undefined,
|
||||||
{ value: 'recorded', label: '▶️ Recorded', color: 'bg-green-100 text-green-800 border-green-200' },
|
minAttendees: attendeeRange[0] > 0 ? attendeeRange[0] : undefined,
|
||||||
{ value: 'featured', label: '⭐ Featured', color: 'bg-yellow-100 text-yellow-800 border-yellow-200' }
|
maxAttendees: attendeeRange[1] < 5000 ? attendeeRange[1] : undefined,
|
||||||
];
|
sortBy: sortBy as any,
|
||||||
|
|
||||||
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 webinars = webinarResponse?.data?.items || [];
|
||||||
const durationMinutes = parseDuration(webinar.duration);
|
|
||||||
const matchesDuration = durationMinutes >= durationRange[0] && durationMinutes <= durationRange[1];
|
|
||||||
|
|
||||||
// Attendee filter using range
|
// Get random tags for each webinar (3 random tags from staticTags)
|
||||||
const attendeeCount = parseAttendees(webinar.attendees);
|
const getRandomTags = (seed: string) => {
|
||||||
const matchesAttendees = attendeeCount >= attendeeRange[0] && attendeeCount <= attendeeRange[1];
|
// 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);
|
||||||
return matchesSearch && matchesCategory && matchesFormat && matchesLevel && matchesStatus && matchesDuration && matchesAttendees;
|
const shuffled = [...staticTags];
|
||||||
}).sort((a, b) => {
|
for (let i = shuffled.length - 1; i > 0; i--) {
|
||||||
switch (sortBy) {
|
const j = Math.floor(Math.random() * (i + 1));
|
||||||
case 'newest':
|
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
});
|
return shuffled.slice(0, 3);
|
||||||
|
|
||||||
// Statistics
|
|
||||||
const stats = {
|
|
||||||
total: webinars.length,
|
|
||||||
upcoming: webinars.filter(w => w.status === 'upcoming').length,
|
|
||||||
live: webinars.filter(w => w.status === 'live').length,
|
|
||||||
recorded: webinars.filter(w => w.status === 'recorded').length,
|
|
||||||
featured: webinars.filter(w => w.featured).length,
|
|
||||||
categories: new Set(webinars.map(w => w.category)).size
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Paginate results
|
// Get unique categories from API data
|
||||||
const totalPages = Math.ceil(filteredWebinars.length / webinarsPerPage);
|
const categories = [
|
||||||
const currentWebinars = filteredWebinars.slice((currentPage - 1) * webinarsPerPage, currentPage * webinarsPerPage);
|
'All Categories',
|
||||||
|
...Array.from(new Set(webinars.map(webinar => webinar.session_title?.split(' ')[0] || 'General')))
|
||||||
|
];
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
const formatDate = (dateString: string) => {
|
const formatDate = (dateString: string) => {
|
||||||
return new Date(dateString).toLocaleDateString('en-US', {
|
return new Date(dateString).toLocaleDateString('en-US', {
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
@@ -148,26 +103,81 @@ 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 = () => {
|
const clearAllFilters = () => {
|
||||||
setSearchTerm('');
|
setSearchTerm('');
|
||||||
setSelectedCategory('All Categories');
|
setSelectedCategory('All Categories');
|
||||||
setSelectedFormat('All Formats');
|
|
||||||
setSelectedLevel('All Levels');
|
|
||||||
setSelectedStatuses([]);
|
setSelectedStatuses([]);
|
||||||
setDurationRange([0, 120]);
|
setDurationRange([0, 120]);
|
||||||
setAttendeeRange([0, 5000]);
|
setAttendeeRange([0, 5000]);
|
||||||
setSortBy('Most Popular');
|
setSortBy('most_popular');
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasActiveFilters = searchTerm ||
|
const hasActiveFilters = searchTerm ||
|
||||||
selectedCategory !== 'All Categories' ||
|
selectedCategory !== 'All Categories' ||
|
||||||
selectedFormat !== 'All Formats' ||
|
|
||||||
selectedLevel !== 'All Levels' ||
|
|
||||||
selectedStatuses.length > 0 ||
|
selectedStatuses.length > 0 ||
|
||||||
durationRange[0] !== 0 || durationRange[1] !== 120 ||
|
durationRange[0] !== 0 || durationRange[1] !== 120 ||
|
||||||
attendeeRange[0] !== 0 || attendeeRange[1] !== 5000;
|
attendeeRange[0] !== 0 || attendeeRange[1] !== 5000;
|
||||||
|
|
||||||
// Status pill toggle function
|
|
||||||
const toggleStatus = (status: string) => {
|
const toggleStatus = (status: string) => {
|
||||||
setSelectedStatuses(prev =>
|
setSelectedStatuses(prev =>
|
||||||
prev.includes(status)
|
prev.includes(status)
|
||||||
@@ -176,90 +186,78 @@ export function Webinars() {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Updated WebinarCard component that navigates to consistent route
|
useEffect(() => {
|
||||||
const WebinarCard = ({ webinar }: { webinar: WebinarData }) => {
|
setCurrentPage(1);
|
||||||
|
}, [searchTerm, selectedCategory, selectedStatuses, durationRange, attendeeRange, sortBy]);
|
||||||
|
|
||||||
|
const WebinarCard = ({ webinar }: { webinar: WebinarItem }) => {
|
||||||
const handleCardClick = () => {
|
const handleCardClick = () => {
|
||||||
// Navigate to consistent webinar detail route
|
if (webinar.webinar_status !== 'cancelled') {
|
||||||
navigateTo(`/webinar/${webinar.slug}`);
|
navigateTo(`/webinars/${webinar.id}`);
|
||||||
};
|
|
||||||
|
|
||||||
const getStatusBadge = () => {
|
|
||||||
switch (webinar.status) {
|
|
||||||
case 'live':
|
|
||||||
return <Badge className="bg-red-600 text-white animate-pulse">LIVE</Badge>;
|
|
||||||
case 'upcoming':
|
|
||||||
return <Badge className="bg-blue-600 text-white">UPCOMING</Badge>;
|
|
||||||
case 'recorded':
|
|
||||||
return <Badge className="bg-green-600 text-white">RECORDED</Badge>;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getActionText = () => {
|
const isCancelled = webinar.webinar_status === 'cancelled';
|
||||||
switch (webinar.status) {
|
const webinarTags = getRandomTags(webinar.id);
|
||||||
case 'live':
|
|
||||||
return 'Join Now';
|
|
||||||
case 'upcoming':
|
|
||||||
return 'Register';
|
|
||||||
case 'recorded':
|
|
||||||
return 'Watch Recording';
|
|
||||||
default:
|
|
||||||
return 'Learn More';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (viewType === 'list') {
|
if (viewType === 'list') {
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
className="mb-4 cursor-pointer transition-all duration-300 hover:shadow-lg hover:transform hover:-translate-y-1"
|
className={`mb-4 cursor-pointer transition-all duration-300 hover:shadow-lg hover:transform hover:-translate-y-1 ${isCancelled ? 'opacity-75' : ''}`}
|
||||||
onClick={handleCardClick}
|
onClick={handleCardClick}
|
||||||
|
style={isCancelled ? { cursor: 'not-allowed' } : {}}
|
||||||
>
|
>
|
||||||
<CardContent className="p-6">
|
<CardContent className="p-6">
|
||||||
<div className="flex gap-6">
|
<div className="flex gap-6">
|
||||||
{/* Thumbnail */}
|
{/* Thumbnail */}
|
||||||
<div className="flex-shrink-0 w-32 h-24 rounded-lg overflow-hidden">
|
<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">
|
||||||
<ImageWithFallback
|
<Play className="w-8 h-8 text-gray-400" />
|
||||||
src={webinar.thumbnail}
|
|
||||||
alt={webinar.title}
|
|
||||||
className="w-full h-full object-cover"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex justify-between items-start mb-2">
|
<div className="flex justify-between items-start mb-2">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{getStatusBadge()}
|
{getStatusBadge(webinar.webinar_status)}
|
||||||
{webinar.featured && (
|
|
||||||
<Badge className="bg-yellow-100 text-yellow-800">Featured</Badge>
|
|
||||||
)}
|
|
||||||
</div>
|
</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>
|
</div>
|
||||||
|
|
||||||
<h3 className="text-h4 mb-2 line-clamp-2">{webinar.title}</h3>
|
<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}</p>
|
<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 justify-between">
|
||||||
<div className="flex items-center gap-4 text-small text-gray-500">
|
<div className="flex items-center gap-4 text-small text-gray-500">
|
||||||
<span className="flex items-center gap-1">
|
<span className="flex items-center gap-1">
|
||||||
<Users className="w-4 h-4" />
|
<Users className="w-4 h-4" />
|
||||||
{webinar.presenter}
|
{webinar.owner || 'Kautilya Leadership'}
|
||||||
</span>
|
</span>
|
||||||
<span className="flex items-center gap-1">
|
<span className="flex items-center gap-1">
|
||||||
<Clock className="w-4 h-4" />
|
<Clock className="w-4 h-4" />
|
||||||
{webinar.duration}
|
{formatDuration(webinar.duration_minutes)}
|
||||||
</span>
|
</span>
|
||||||
<span className="flex items-center gap-1">
|
<span className="flex items-center gap-1">
|
||||||
<Eye className="w-4 h-4" />
|
<Eye className="w-4 h-4" />
|
||||||
{webinar.attendees}
|
Max {webinar.max_attendee.toLocaleString()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 text-primary font-medium">
|
{!isCancelled && (
|
||||||
<span className="text-small">{getActionText()}</span>
|
<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" />
|
<ArrowRight className="w-4 h-4" />
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -268,100 +266,122 @@ export function Webinars() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Grid View
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
className="cursor-pointer transition-all duration-300 hover:shadow-lg hover:transform hover:-translate-y-2 group overflow-hidden"
|
className={`cursor-pointer transition-all duration-300 hover:shadow-lg hover:transform hover:-translate-y-2 group overflow-hidden ${isCancelled ? 'opacity-75' : ''}`}
|
||||||
onClick={handleCardClick}
|
onClick={handleCardClick}
|
||||||
|
style={isCancelled ? { cursor: 'not-allowed' } : {}}
|
||||||
>
|
>
|
||||||
{/* Image */}
|
{/* Image */}
|
||||||
<div className="aspect-video relative overflow-hidden">
|
<div className="aspect-video relative overflow-hidden bg-gradient-to-br from-gray-100 to-gray-200">
|
||||||
<ImageWithFallback
|
<div className="w-full h-full flex items-center justify-center">
|
||||||
src={webinar.thumbnail}
|
<Play className="w-12 h-12 text-gray-400" />
|
||||||
alt={webinar.title}
|
</div>
|
||||||
className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Status Badge */}
|
{/* Status Badge */}
|
||||||
<div className="absolute top-4 left-4">
|
<div className="absolute top-4 left-4">
|
||||||
{getStatusBadge()}
|
{getStatusBadge(webinar.webinar_status)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Featured Badge */}
|
|
||||||
{webinar.featured && (
|
|
||||||
<div className="absolute top-4 right-4">
|
|
||||||
<Badge className="bg-yellow-100 text-yellow-800 border border-yellow-200">
|
|
||||||
<Star className="w-3 h-3 mr-1" />
|
|
||||||
Featured
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Play Icon Overlay */}
|
{/* 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="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">
|
<div className="bg-white bg-opacity-90 rounded-full p-3">
|
||||||
<Play className="w-6 h-6 text-gray-800" />
|
<Play className="w-6 h-6 text-gray-800" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<CardContent className="p-6">
|
<CardContent className="p-6">
|
||||||
<div className="flex items-center justify-between mb-2">
|
<div className="flex items-center justify-between mb-2">
|
||||||
<Badge variant="secondary" className="text-xs">
|
<Badge variant="secondary" className="text-xs">
|
||||||
{webinar.category}
|
{webinar.recurring_webinar ? 'Recurring' : 'One-time'}
|
||||||
</Badge>
|
</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>
|
</div>
|
||||||
|
|
||||||
<h3 className="text-h4 mb-3 line-clamp-2 group-hover:text-primary transition-colors">
|
<h3 className="text-h4 mb-3 line-clamp-2 group-hover:text-primary transition-colors">
|
||||||
{webinar.title}
|
{webinar.session_title}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<p className="text-body text-gray-600 mb-4 line-clamp-2">
|
<p className="text-body text-gray-600 mb-4 line-clamp-2">
|
||||||
{webinar.description}
|
{webinar.description || 'No description available'}
|
||||||
</p>
|
</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="space-y-3">
|
||||||
<div className="flex items-center gap-2 text-small text-gray-500">
|
<div className="flex items-center gap-2 text-small text-gray-500">
|
||||||
<Users className="w-4 h-4" />
|
<Users className="w-4 h-4" />
|
||||||
<span>{webinar.presenter}</span>
|
<span>{webinar.owner || 'Kautilya Leadership'}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center justify-between text-small text-gray-500">
|
<div className="flex items-center justify-between text-small text-gray-500">
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<Clock className="w-4 h-4" />
|
<Clock className="w-4 h-4" />
|
||||||
<span>{webinar.duration}</span>
|
<span>{formatDuration(webinar.duration_minutes)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<Eye className="w-4 h-4" />
|
<Eye className="w-4 h-4" />
|
||||||
<span>{webinar.attendees}</span>
|
<span>Max {webinar.max_attendee.toLocaleString()}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{!isCancelled && (
|
||||||
<div className="flex items-center justify-between mt-4 pt-4 border-t">
|
<div className="flex items-center justify-between mt-4 pt-4 border-t">
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1 text-xs text-gray-500">
|
||||||
{webinar.tags.slice(0, 2).map((tag, index) => (
|
{webinar.require_registration && (
|
||||||
<Badge key={index} variant="outline" className="text-xs">
|
<>
|
||||||
{tag}
|
<Star className="w-3 h-3 text-yellow-500" />
|
||||||
</Badge>
|
<span>Registration Required</span>
|
||||||
))}
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 text-primary font-medium group-hover:translate-x-1 transition-transform">
|
<div className="flex items-center gap-2 font-medium group-hover:translate-x-1 transition-transform" style={{ color: '#04045b' }}>
|
||||||
<span className="text-small">{getActionText()}</span>
|
<span className="text-small">{getActionText(webinar.webinar_status)}</span>
|
||||||
<ArrowRight className="w-4 h-4" />
|
<ArrowRight className="w-4 h-4" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</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 (
|
return (
|
||||||
<div style={{ backgroundColor: '#FFFFFF' }}>
|
<div style={{ backgroundColor: '#FFFFFF' }}>
|
||||||
{/* Hero Section with Background Image */}
|
{/* Hero Section */}
|
||||||
<section className="relative h-[400px] overflow-hidden">
|
<section className="relative h-[400px] overflow-hidden">
|
||||||
{/* Background Image */}
|
|
||||||
<div className="absolute inset-0">
|
<div className="absolute inset-0">
|
||||||
<ImageWithFallback
|
<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"
|
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,14 +391,12 @@ export function Webinars() {
|
|||||||
<div className="absolute inset-0 bg-black/60" />
|
<div className="absolute inset-0 bg-black/60" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Hero Content */}
|
|
||||||
<div className="relative h-full flex flex-col justify-center section-margin-x">
|
<div className="relative h-full flex flex-col justify-center section-margin-x">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<h1 className="text-h1-white mb-6">
|
<h1 className="text-h1-white mb-6">
|
||||||
Leadership Webcasts &<br />
|
Leadership Webcasts &<br />
|
||||||
Expert Insights
|
Expert Insights
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p className="text-body-lg-white max-w-3xl mx-auto">
|
<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.
|
to elevate your leadership journey and drive organizational excellence.
|
||||||
@@ -386,7 +404,6 @@ export function Webinars() {
|
|||||||
</div>
|
</div>
|
||||||
</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="bg-black/80 backdrop-blur-sm px-8 py-6">
|
||||||
<div className="section-margin-x">
|
<div className="section-margin-x">
|
||||||
@@ -413,12 +430,11 @@ export function Webinars() {
|
|||||||
<section className="py-8" style={{ backgroundColor: '#FFFFFF' }}>
|
<section className="py-8" style={{ backgroundColor: '#FFFFFF' }}>
|
||||||
<div className="section-margin-x">
|
<div className="section-margin-x">
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 mb-6">
|
<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">
|
<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" />
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search webcasts..."
|
placeholder="Search webinars..."
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
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"
|
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>
|
</div>
|
||||||
|
|
||||||
{/* View Toggle and Sort */}
|
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<div className="flex items-center border border-gray-300 rounded-lg overflow-hidden">
|
<div className="flex items-center border border-gray-300 rounded-lg overflow-hidden">
|
||||||
<button
|
<button
|
||||||
onClick={() => setViewType('grid')}
|
onClick={() => setViewType('grid')}
|
||||||
className={`p-2 transition-colors ${
|
className={`p-2 transition-colors ${viewType === 'grid'
|
||||||
viewType === 'grid'
|
|
||||||
? 'text-white'
|
? 'text-white'
|
||||||
: 'bg-white text-gray-600 hover:bg-gray-50'
|
: 'bg-white text-gray-600 hover:bg-gray-50'
|
||||||
}`}
|
}`}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: viewType === 'grid' ? 'var(--color-primary)' : undefined
|
backgroundColor: viewType === 'grid' ? '#04045b' : undefined
|
||||||
}}
|
}}
|
||||||
aria-label="Grid view"
|
aria-label="Grid view"
|
||||||
>
|
>
|
||||||
@@ -449,13 +463,12 @@ export function Webinars() {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setViewType('list')}
|
onClick={() => setViewType('list')}
|
||||||
className={`p-2 transition-colors ${
|
className={`p-2 transition-colors ${viewType === 'list'
|
||||||
viewType === 'list'
|
|
||||||
? 'text-white'
|
? 'text-white'
|
||||||
: 'bg-white text-gray-600 hover:bg-gray-50'
|
: 'bg-white text-gray-600 hover:bg-gray-50'
|
||||||
}`}
|
}`}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: viewType === 'list' ? 'var(--color-primary)' : undefined
|
backgroundColor: viewType === 'list' ? '#04045b' : undefined
|
||||||
}}
|
}}
|
||||||
aria-label="List view"
|
aria-label="List view"
|
||||||
>
|
>
|
||||||
@@ -480,31 +493,28 @@ export function Webinars() {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Main Content Section with Sidebar */}
|
{/* Main Content Section */}
|
||||||
<section className="pb-16" style={{ backgroundColor: '#FFFFFF' }}>
|
<section className="pb-16" style={{ backgroundColor: '#FFFFFF' }}>
|
||||||
<div className="section-margin-x">
|
<div className="section-margin-x">
|
||||||
<div className="grid grid-cols-12 gap-8">
|
<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="col-span-12 lg:col-span-3">
|
||||||
<div className="sticky top-4">
|
<div className="sticky top-4">
|
||||||
<Card className="bg-white border border-gray-200 rounded-lg shadow-md overflow-hidden">
|
<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="bg-gray-50 px-4 py-3 border-b border-gray-200">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="p-1.5 rounded-md" style={{ backgroundColor: 'rgba(4, 4, 91, 0.1)' }}>
|
<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>
|
</div>
|
||||||
<h3 className="text-body font-semibold text-gray-800">
|
<h3 className="text-body font-semibold text-gray-800">Filters</h3>
|
||||||
Filters
|
|
||||||
</h3>
|
|
||||||
</div>
|
</div>
|
||||||
{hasActiveFilters && (
|
{hasActiveFilters && (
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={clearAllFilters}
|
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" />
|
<X className="w-3 h-3 mr-1" />
|
||||||
Clear
|
Clear
|
||||||
@@ -513,67 +523,9 @@ export function Webinars() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Filter Content */}
|
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* Category Filter */}
|
{/* Status Filter */}
|
||||||
<div className="filter-section">
|
|
||||||
<label className="block text-small mb-2 font-medium text-gray-700">
|
|
||||||
Category
|
|
||||||
</label>
|
|
||||||
<Select value={selectedCategory} onValueChange={setSelectedCategory}>
|
|
||||||
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors">
|
|
||||||
<SelectValue placeholder="All Categories" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{categories.map((category) => (
|
|
||||||
<SelectItem key={category} value={category} className="text-small">
|
|
||||||
{category}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Format Filter */}
|
|
||||||
<div className="filter-section">
|
|
||||||
<label className="block text-small mb-2 font-medium text-gray-700">
|
|
||||||
Format
|
|
||||||
</label>
|
|
||||||
<Select value={selectedFormat} onValueChange={setSelectedFormat}>
|
|
||||||
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors">
|
|
||||||
<SelectValue placeholder="All Formats" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{formats.map((format) => (
|
|
||||||
<SelectItem key={format} value={format} className="text-small">
|
|
||||||
{format}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Level Filter */}
|
|
||||||
<div className="filter-section">
|
|
||||||
<label className="block text-small mb-2 font-medium text-gray-700">
|
|
||||||
Level
|
|
||||||
</label>
|
|
||||||
<Select value={selectedLevel} onValueChange={setSelectedLevel}>
|
|
||||||
<SelectTrigger className="w-full text-small h-9 border-gray-300 hover:border-gray-400 transition-colors">
|
|
||||||
<SelectValue placeholder="All Levels" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{levels.map((level) => (
|
|
||||||
<SelectItem key={level} value={level} className="text-small">
|
|
||||||
{level}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Status Filter - Multi-select Pills */}
|
|
||||||
<div className="filter-section">
|
<div className="filter-section">
|
||||||
<label className="block text-small mb-3 font-medium text-gray-700">
|
<label className="block text-small mb-3 font-medium text-gray-700">
|
||||||
Status
|
Status
|
||||||
@@ -602,10 +554,10 @@ export function Webinars() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Duration Filter - Slider */}
|
{/* Duration Filter */}
|
||||||
<div className="filter-section">
|
<div className="filter-section">
|
||||||
<label className="block text-small mb-3 font-medium text-gray-700">
|
<label className="block text-small mb-3 font-medium text-gray-700">
|
||||||
Duration
|
Duration (minutes)
|
||||||
</label>
|
</label>
|
||||||
<div className="px-2">
|
<div className="px-2">
|
||||||
<Slider
|
<Slider
|
||||||
@@ -620,19 +572,13 @@ export function Webinars() {
|
|||||||
<span>{durationRange[0]} min</span>
|
<span>{durationRange[0]} min</span>
|
||||||
<span>{durationRange[1]} min</span>
|
<span>{durationRange[1]} min</span>
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Attendee Count Filter - Slider */}
|
{/* Attendee Filter */}
|
||||||
<div className="filter-section">
|
<div className="filter-section">
|
||||||
<label className="block text-small mb-3 font-medium text-gray-700">
|
<label className="block text-small mb-3 font-medium text-gray-700">
|
||||||
Attendees
|
Max Attendees
|
||||||
</label>
|
</label>
|
||||||
<div className="px-2">
|
<div className="px-2">
|
||||||
<Slider
|
<Slider
|
||||||
@@ -647,12 +593,6 @@ export function Webinars() {
|
|||||||
<span>{attendeeRange[0].toLocaleString()}</span>
|
<span>{attendeeRange[0].toLocaleString()}</span>
|
||||||
<span>{attendeeRange[1].toLocaleString()}+</span>
|
<span>{attendeeRange[1].toLocaleString()}+</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-1 text-center text-xs text-gray-400">
|
|
||||||
{attendeeRange[0] === 0 && attendeeRange[1] === 5000
|
|
||||||
? 'Any size'
|
|
||||||
: `${attendeeRange[0].toLocaleString()}-${attendeeRange[1].toLocaleString()}+`
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -663,24 +603,22 @@ export function Webinars() {
|
|||||||
|
|
||||||
{/* Right Main Content */}
|
{/* Right Main Content */}
|
||||||
<div className="col-span-12 lg:col-span-9">
|
<div className="col-span-12 lg:col-span-9">
|
||||||
{/* Results Header */}
|
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex items-center justify-between mb-6">
|
||||||
<div className="text-body text-gray-600">
|
<div className="text-body text-gray-600">
|
||||||
Showing {currentWebinars.length} of {filteredWebinars.length} webcasts
|
Showing {currentWebinars.length} of {filteredWebinars.length} webinars
|
||||||
</div>
|
</div>
|
||||||
<div className="text-small text-gray-500">
|
<div className="text-small text-gray-500">
|
||||||
Page {currentPage} of {totalPages}
|
Page {currentPage} of {totalPages}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content Area */}
|
|
||||||
<div ref={containerRef}>
|
<div ref={containerRef}>
|
||||||
{currentWebinars.length === 0 ? (
|
{currentWebinars.length === 0 ? (
|
||||||
<div className="text-center py-12">
|
<div className="text-center py-12">
|
||||||
<div className="text-gray-400 mb-4">
|
<div className="text-gray-400 mb-4">
|
||||||
<Search className="w-12 h-12 mx-auto mb-4" />
|
<Search className="w-12 h-12 mx-auto mb-4" />
|
||||||
</div>
|
</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">
|
<p className="text-body text-gray-600 mb-4">
|
||||||
Try adjusting your filters or search terms
|
Try adjusting your filters or search terms
|
||||||
</p>
|
</p>
|
||||||
@@ -692,7 +630,6 @@ export function Webinars() {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{/* Grid View */}
|
|
||||||
{viewType === 'grid' ? (
|
{viewType === 'grid' ? (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6 mb-8">
|
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6 mb-8">
|
||||||
{currentWebinars.map((webinar) => (
|
{currentWebinars.map((webinar) => (
|
||||||
@@ -700,7 +637,6 @@ export function Webinars() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
/* List View */
|
|
||||||
<div className="space-y-4 mb-8">
|
<div className="space-y-4 mb-8">
|
||||||
{currentWebinars.map((webinar) => (
|
{currentWebinars.map((webinar) => (
|
||||||
<WebinarCard key={webinar.id} webinar={webinar} />
|
<WebinarCard key={webinar.id} webinar={webinar} />
|
||||||
@@ -717,27 +653,38 @@ export function Webinars() {
|
|||||||
onClick={() => setCurrentPage(prev => Math.max(1, prev - 1))}
|
onClick={() => setCurrentPage(prev => Math.max(1, prev - 1))}
|
||||||
disabled={currentPage === 1}
|
disabled={currentPage === 1}
|
||||||
>
|
>
|
||||||
<ChevronLeft className="w-4 h-4 mr-1" />
|
<ChevronLeft className="w-4 h-4" />
|
||||||
Previous
|
Previous
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
{Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
|
{Array.from({ length: Math.min(totalPages, 5) }, (_, i) => {
|
||||||
const page = Math.max(1, Math.min(totalPages - 4, currentPage - 2)) + i;
|
const page = i + 1;
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
key={page}
|
key={page}
|
||||||
variant={currentPage === page ? "default" : "outline"}
|
variant={currentPage === page ? "default" : "outline"}
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setCurrentPage(page)}
|
onClick={() => setCurrentPage(page)}
|
||||||
className="w-10"
|
className="min-w-10"
|
||||||
|
style={currentPage === page ? { backgroundColor: '#04045b' } : {}}
|
||||||
>
|
>
|
||||||
{page}
|
{page}
|
||||||
</Button>
|
</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>
|
</div>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -745,7 +692,7 @@ export function Webinars() {
|
|||||||
disabled={currentPage === totalPages}
|
disabled={currentPage === totalPages}
|
||||||
>
|
>
|
||||||
Next
|
Next
|
||||||
<ChevronRight className="w-4 h-4 ml-1" />
|
<ChevronRight className="w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -757,7 +704,6 @@ export function Webinars() {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Webcast CTA Banner */}
|
|
||||||
<WebcastCTABanner />
|
<WebcastCTABanner />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ function ResourceCard({ resource, index }: ResourceCardProps) {
|
|||||||
backgroundColor: 'var(--color-brand-primary)',
|
backgroundColor: 'var(--color-brand-primary)',
|
||||||
color: 'white'
|
color: 'white'
|
||||||
}}
|
}}
|
||||||
onClick={() => navigateTo('/learning/articles')}
|
onClick={() => navigateTo('/self-learner-signup')}
|
||||||
onMouseEnter={(e) => {
|
onMouseEnter={(e) => {
|
||||||
e.currentTarget.style.backgroundColor = 'var(--color-brand-accent)';
|
e.currentTarget.style.backgroundColor = 'var(--color-brand-accent)';
|
||||||
e.currentTarget.style.color = 'var(--color-brand-black)';
|
e.currentTarget.style.color = 'var(--color-brand-black)';
|
||||||
@@ -187,7 +187,6 @@ export function WhitepapersSection() {
|
|||||||
text="Browse All Resources"
|
text="Browse All Resources"
|
||||||
onClick={() => navigateTo('/learning/articles')}
|
onClick={() => navigateTo('/learning/articles')}
|
||||||
ariaLabel="Browse all leadership resources"
|
ariaLabel="Browse all leadership resources"
|
||||||
className="browse-resources-cta-override"
|
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
936
src/components/services/LeadershipPipelineDevelopment.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -14,7 +14,6 @@ import { BrandedTag } from '../about/BrandedTag';
|
|||||||
import { PrimaryCTAButton } from '../PrimaryCTAButton';
|
import { PrimaryCTAButton } from '../PrimaryCTAButton';
|
||||||
import { TestimonialsSection } from '../TestimonialsSection';
|
import { TestimonialsSection } from '../TestimonialsSection';
|
||||||
import { CTABannerSection } from '../CTABannerSection';
|
import { CTABannerSection } from '../CTABannerSection';
|
||||||
import Slider from "react-slick";
|
|
||||||
// import ImageWithFallback from "./ImageWithFallback"; // your custom component
|
// import ImageWithFallback from "./ImageWithFallback"; // your custom component
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as DialogPrimitive from "@radix-ui/react-dialog@1.1.6";
|
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
||||||
import { XIcon } from "lucide-react@0.487.0";
|
import { XIcon } from "lucide-react";
|
||||||
|
|
||||||
import { cn } from "./utils";
|
import { cn } from "./utils";
|
||||||
|
|
||||||
|
|||||||
541
src/data/articlesData.ts
Normal 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 Most of the times Sometimes 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
|
||||||
|
}
|
||||||
|
];
|
||||||
@@ -95,7 +95,7 @@ export const sharedWebinarsData: WebinarData[] = [
|
|||||||
category: 'Digital Transformation',
|
category: 'Digital Transformation',
|
||||||
tags: ['Leadership', 'Digital Strategy', 'Innovation', 'Change Management'],
|
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',
|
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,
|
featured: true,
|
||||||
level: 'Advanced',
|
level: 'Advanced',
|
||||||
format: 'Hybrid',
|
format: 'Hybrid',
|
||||||
@@ -193,21 +193,6 @@ export const sharedWebinarsData: WebinarData[] = [
|
|||||||
rating: 4.8,
|
rating: 4.8,
|
||||||
participants: '2,400+',
|
participants: '2,400+',
|
||||||
slug: 'strategic-leadership-programme'
|
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',
|
category: 'Team Development',
|
||||||
tags: ['Team Building', 'Emotional Intelligence', 'Performance', 'Leadership'],
|
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',
|
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,
|
featured: true,
|
||||||
level: 'Intermediate',
|
level: 'Intermediate',
|
||||||
format: 'In Person',
|
format: 'In Person',
|
||||||
@@ -302,21 +287,6 @@ export const sharedWebinarsData: WebinarData[] = [
|
|||||||
rating: 4.7,
|
rating: 4.7,
|
||||||
participants: '3,200+',
|
participants: '3,200+',
|
||||||
slug: 'team-leadership-masterclass'
|
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,
|
maxAttendees: 4000,
|
||||||
category: 'Strategy',
|
category: 'Strategy',
|
||||||
tags: ['Strategic Thinking', 'Crisis Management', 'Leadership', 'Planning'],
|
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',
|
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,
|
status: 'recorded',
|
||||||
featured: false,
|
featured: false,
|
||||||
level: 'Advanced',
|
level: 'Advanced',
|
||||||
format: 'Virtual',
|
format: 'Virtual',
|
||||||
@@ -431,8 +401,8 @@ export const sharedWebinarsData: WebinarData[] = [
|
|||||||
maxAttendees: 2800,
|
maxAttendees: 2800,
|
||||||
category: 'Personal Development',
|
category: 'Personal Development',
|
||||||
tags: ['Innovation', 'Creativity', 'Communication', 'Leadership'],
|
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',
|
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,
|
status: 'upcoming',
|
||||||
featured: false,
|
featured: false,
|
||||||
level: 'Intermediate',
|
level: 'Intermediate',
|
||||||
format: 'Hybrid',
|
format: 'Hybrid',
|
||||||
@@ -528,10 +498,10 @@ export const sharedWebinarsData: WebinarData[] = [
|
|||||||
duration: '70 min',
|
duration: '70 min',
|
||||||
attendees: '2,800+',
|
attendees: '2,800+',
|
||||||
maxAttendees: 3500,
|
maxAttendees: 3500,
|
||||||
category: 'Crisis Management',
|
category: 'Global Leadership',
|
||||||
tags: ['Global Leadership', 'Cultural Intelligence', 'Communication', 'Diversity'],
|
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',
|
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' as const,
|
status: 'upcoming',
|
||||||
featured: false,
|
featured: false,
|
||||||
level: 'Advanced',
|
level: 'Advanced',
|
||||||
format: 'Virtual',
|
format: 'Virtual',
|
||||||
@@ -611,6 +581,204 @@ export const sharedWebinarsData: WebinarData[] = [
|
|||||||
slug: 'global-leadership-certificate'
|
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'
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
12
src/global.d.ts
vendored
@@ -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" {
|
declare module "*.png" {
|
||||||
const src: string;
|
const src: string;
|
||||||
export default src;
|
export default src;
|
||||||
|
|||||||
3
src/imports/svg-kw7r0ellyk.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default {
|
||||||
|
p2c1c9a80: "M8 3.33398L12.6667 8.00065L8 12.6673",
|
||||||
|
}
|
||||||
@@ -2,9 +2,16 @@ import { createRoot } from "react-dom/client";
|
|||||||
import App from "./App";
|
import App from "./App";
|
||||||
import "../src/styles/globals.css";
|
import "../src/styles/globals.css";
|
||||||
import { BrowserRouter } from "react-router-dom";
|
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(
|
createRoot(document.getElementById("root")!).render(
|
||||||
|
<Provider store={store}>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
|
<ScrollToTop />
|
||||||
<App />
|
<App />
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
|
</Provider>
|
||||||
);
|
);
|
||||||
292
src/pages/FAQ.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,32 +1,65 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import HeroSection from '../components/HeroSection';
|
import HeroSection from "../components/HeroSection";
|
||||||
import { StatsSection } from '../components/StatsSection';
|
import { StatsSection } from "../components/StatsSection";
|
||||||
import { LogosSection } from '../components/LogosSection';
|
import { LogosSection } from "../components/LogosSection";
|
||||||
import { ServicesSection } from '../components/ServicesSection';
|
import { ServicesSection } from "../components/ServicesSection";
|
||||||
import { VirtualSpaceSection } from '../components/VirtualSpaceSection';
|
import { VirtualSpaceSection } from "../components/VirtualSpaceSection";
|
||||||
import { TestimonialsSection } from '../components/TestimonialsSection';
|
import { TestimonialsSection } from "../components/TestimonialsSection";
|
||||||
import { UpcomingWebinarsSection } from '../components/UpcomingWebinarsSection';
|
import { InsightsSection } from "../components/InsightsSection";
|
||||||
import { InsightsSection } from '../components/InsightsSection';
|
import { CTABannerSection } from "../components/CTABannerSection";
|
||||||
import { WhitepapersSection } from '../components/WhitepapersSection';
|
|
||||||
import { CTABannerSection } from '../components/CTABannerSection';
|
|
||||||
import { motion } from "motion/react";
|
import { motion } from "motion/react";
|
||||||
import { PrimaryCTAButton } from '../components/PrimaryCTAButton';
|
import { PrimaryCTAButton } from "../components/PrimaryCTAButton";
|
||||||
import { BrandedTag } from '../components/about/BrandedTag';
|
import { BrandedTag } from "../components/about/BrandedTag";
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { useGetHomepageQuery } from "../redux/services/homepageApi";
|
||||||
|
import { FullScreenLoader } from "../components/FullScreenLoader";
|
||||||
|
|
||||||
const HomePage: React.FC = () => {
|
const HomePage: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<HeroSection />
|
<HeroSection heroSections={heroSections} isLoading={isLoading} />
|
||||||
<StatsSection />
|
|
||||||
|
{/* Stats Section */}
|
||||||
|
<StatsSection stats={stats} isLoading={isLoading} />
|
||||||
|
|
||||||
<LogosSection />
|
<LogosSection />
|
||||||
<ServicesSection />
|
<ServicesSection highlightCards={highlightCards} isLoading={isLoading} />
|
||||||
|
|
||||||
<div>
|
<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" >
|
<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
|
<motion.div
|
||||||
initial={{ opacity: 0, y: -20 }}
|
initial={{ opacity: 0, y: -20 }}
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
@@ -40,7 +73,6 @@ const HomePage: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
</motion.div>
|
</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"
|
className="text-5xl font-bold leading-tight mb-4 max-lg:text-4xl max-md:text-3xl text-white"
|
||||||
initial={{ opacity: 0, y: 30 }}
|
initial={{ opacity: 0, y: 30 }}
|
||||||
@@ -51,7 +83,6 @@ const HomePage: React.FC = () => {
|
|||||||
Experience Our Space Virtually
|
Experience Our Space Virtually
|
||||||
</motion.h2>
|
</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"
|
className="text-lg leading-relaxed max-w-2xl mx-auto max-lg:text-base mb-6 text-white/90"
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
@@ -59,10 +90,10 @@ const HomePage: React.FC = () => {
|
|||||||
transition={{ duration: 0.8, delay: 0.4 }}
|
transition={{ duration: 0.8, delay: 0.4 }}
|
||||||
viewport={{ once: true }}
|
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>
|
</motion.p>
|
||||||
|
|
||||||
{/* Main CTA Button - Explore Our Space */}
|
|
||||||
<motion.div
|
<motion.div
|
||||||
className="flex justify-center"
|
className="flex justify-center"
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
@@ -73,19 +104,25 @@ const HomePage: React.FC = () => {
|
|||||||
<div className="hero-slide-button">
|
<div className="hero-slide-button">
|
||||||
<PrimaryCTAButton
|
<PrimaryCTAButton
|
||||||
text="Explore Our Space"
|
text="Explore Our Space"
|
||||||
onClick={() => navigate('/services/learning-facility')}
|
onClick={() => navigate("/services/learning-facility")}
|
||||||
ariaLabel="Explore our virtual learning space and facilities"
|
ariaLabel="Explore our virtual learning space and facilities"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<VirtualSpaceSection />
|
<VirtualSpaceSection />
|
||||||
</div>
|
</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 />
|
<InsightsSection />
|
||||||
<WhitepapersSection />
|
<CTABannerSection ctaSection={ctaSection} isLoading={isLoading} />
|
||||||
<CTABannerSection />
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
760
src/pages/TermsCondition.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||