From f0b4719282fae54c2e88d37a3e17ad0f7ce49ef9 Mon Sep 17 00:00:00 2001 From: priyanshuvish Date: Wed, 25 Mar 2026 16:00:15 +0530 Subject: [PATCH 1/2] working on services and course --- src/components/AboutUs.tsx | 755 ++++------- src/components/CartPopup.tsx | 9 +- src/components/CourseCard.tsx | 88 +- src/components/LearningOnline.tsx | 658 +++++----- src/components/services/CultureCompetence.tsx | 1104 ++++++++-------- src/components/services/ExecutiveCoaching.tsx | 1102 ++++++++-------- .../services/LeadershipDevelopment.tsx | 1005 +++++++-------- .../LeadershipPipelineDevelopment.tsx | 1144 ++++++++--------- .../services/ManagementDevelopment.tsx | 1101 ++++++++-------- src/pages/HomePage.tsx | 9 + src/redux/hooks/useDebounce.ts | 17 + src/redux/services/aboutUsApi.ts | 36 + src/redux/services/courseApi.ts | 169 +++ src/redux/services/sercicesApi.ts | 20 + src/redux/store/Store.tsx | 6 + 15 files changed, 3449 insertions(+), 3774 deletions(-) create mode 100644 src/redux/hooks/useDebounce.ts create mode 100644 src/redux/services/courseApi.ts create mode 100644 src/redux/services/sercicesApi.ts diff --git a/src/components/AboutUs.tsx b/src/components/AboutUs.tsx index ec0c2cb..a335788 100644 --- a/src/components/AboutUs.tsx +++ b/src/components/AboutUs.tsx @@ -1,41 +1,34 @@ -import { useState, useEffect } from 'react'; -import { motion } from 'motion/react'; import { - Target, - Users, - Globe, - Lightbulb, - ArrowRight, - CheckCircle, - Star, - Award, - Brain, - TrendingUp, - Shield, - Heart, - Zap, - Eye, BookOpen, + Brain, + CheckCircle, + Heart, Puzzle, - Building, - ArrowLeft + Shield, + Target, + TrendingUp, + Users, + Zap } from 'lucide-react'; -import { BrandedTag } from './about/BrandedTag'; -import { PrimaryCTAButton } from './PrimaryCTAButton'; -import { CTABannerSection } from './CTABannerSection'; -import { TestimonialsSection } from './TestimonialsSection'; -import { TeamMemberModal } from './TeamMemberModal'; -import { navigateTo } from './Router'; -import { Button } from './ui/button'; +import { motion } from 'motion/react'; +import { useEffect, useState } from 'react'; +import Aparna from '../assets/Aparna-Nair.png'; +import Balaji from '../assets/Balaji-Chandrakumar.jpeg'; +import Diju from '../assets/Diju.jpeg'; import Ramkumar from '../assets/K-Ramkumar.png'; import Muralidharan from '../assets/R-Muralidharan.png'; -import Aparna from '../assets/Aparna-Nair.png'; -import Swaminathan from '../assets/v-Swaminathan.jpg'; -import Balaji from '../assets/Balaji-Chandrakumar.jpeg'; import Ramesh from '../assets/Ramesh-Padmanabhan.jpeg'; -import Diju from '../assets/Diju.jpeg'; +import Swaminathan from '../assets/v-Swaminathan.jpg'; import svgPaths from '../imports/svg-kw7r0ellyk'; import { useGetAboutUsQuery } from '../redux/services/aboutUsApi'; +import { BrandedTag } from './about/BrandedTag'; +import { CTABannerSection } from './CTABannerSection'; +import { PrimaryCTAButton } from './PrimaryCTAButton'; +import { navigateTo } from './Router'; +import { TeamMemberModal } from './TeamMemberModal'; +import { TestimonialsSection } from './TestimonialsSection'; +import { Button } from './ui/button'; +import { FullScreenLoader } from './FullScreenLoader'; // Leadership Orientations Data const leadershipOrientations = [ @@ -272,6 +265,7 @@ After a career break, she joined KLC as Practice Head. She now co-creates leader } ]; + // Loading Skeleton Component const AboutUsSkeleton = () => (
@@ -303,6 +297,27 @@ export function AboutUs() { // Fetch About Us data from API const { data: aboutUsData, isLoading, isError, error } = useGetAboutUsQuery(); + const transformTestimonials = (apiTestimonials: any[]) => { + if (!apiTestimonials || apiTestimonials.length === 0) return []; + + return apiTestimonials.map((testimonial, index) => ({ + id: testimonial.id || index, + name: testimonial.name || "Anonymous", + role: testimonial.designation || "Client", + company: undefined, // Company not provided in API response + avatar: testimonial.profile_photo_url || undefined, // If you have profile photo in API + image: testimonial.profile_photo_url || undefined, + quote: testimonial.content || "", + rating: 5, // Default rating since API doesn't provide rating + isVideo: !!testimonial.video_url, + videoThumbnail: testimonial.video_thumbnail_url || testimonial.profile_photo_url, // If you have thumbnail + videoUrl: testimonial.video_url || undefined + })); + }; + + // Transform the testimonials + const testimonialsData = transformTestimonials(aboutUsData?.testimonials || []); + const handleMemberClick = (member: typeof staticTeamMembers[0]) => { setSelectedMember(member); setIsModalOpen(true); @@ -362,8 +377,13 @@ export function AboutUs() { }, []); // Show loading skeleton while fetching data + if (isLoading) { - return ; + return ( +
+ +
+ ); } // Show error state if API call fails @@ -755,7 +775,7 @@ export function AboutUs() {

{aboutUsData?.our_team_title || "Our Team"}

- We have a team of 7 consultants and 4 young consultants. All our senior Consultants are ex-business professionals with experience ranging from 15-30 years in varied business functions and carry a deep understanding of the area they are engaging in. Two of them bring in Board room experience. – Meet them + {aboutUsData?.our_team_description || "We have a team of 7 consultants and 4 young consultants. All our senior Consultants are ex-business professionals with experience ranging from 15-30 years in varied business functions and carry a deep understanding of the area they are engaging in. Two of them bring in Board room experience. – Meet them"}

@@ -807,500 +827,215 @@ export function AboutUs() { ))}
- - {/* Alternative: Use API team data if needed */} - {/* {aboutUsData?.our_team && aboutUsData.our_team.length > 0 && ( -
- {aboutUsData.our_team.map((member, index) => ( - -
-
- {member.alt_text} -
-
-
-
- View Profile -
-
-
-
-
-

- {member.name_role.split(' - ')[0]} -

-

- {member.name_role.split(' - ')[1] || ''} -

-

{member.bio}

-
-
- ))} -
- )} */} - {/* Section 5: Our Methodology (Static - unchanged) */} -
-
-
- - -

Our Methodology

-
- - {/* Vertical Timeline Container */} -
- {/* Vertical Line Background - Gray - Ends exactly at Phase 3 dot */} -
- - {/* Vertical Line Fill - Blue - Animated on Scroll */} -
- - {/* Phase 1 - Understanding & Assessment */} -
-
- {/* Phase dot positioned absolutely */} -
- - {/* Column 1: Phase Label */} -
-
-
- Phase 1 -
-
- - {/* Column 2: Main Heading */} -
- -

Understanding & Assessment

-
-
- - {/* Column 3: Content - Description and Bullet Points */} -
- -

- We believe that leadership is more than skill and style. Leadership is predicated by a person's orientations (typical behaviors) which shapes the leadership abilities and its fit to a context. The broader the fit of the orientation the more versatile a person is in his exercise of leadership in a variety of contexts. -

- -
-

Key Focus Areas:

- -
-
-
- - Leadership orientations (typical behaviors) assessment. - -
- -
-
- - Action-consequence model application. - -
- -
-
- - Context alignment and strategy integration. - -
-
-
-
-
-
-
- - {/* Phase 2 - Development & Practice */} -
-
- {/* Phase dot positioned absolutely */} -
- - {/* Column 1: Phase Label */} -
-
-
- Phase 2 -
-
- - {/* Column 2: Main Heading */} -
- -

Development & Practice

-
-
- - {/* Column 3: Content - Description and Bullet Points */} -
- -

- Thus, in our engagements with our clients we work with the individual using the action-consequence model to link up one's leadership orientations (typical behaviors) to outcomes. -

- -

- Leadership is a social skill. Socially aware people shape the thought, emotions and actions of others better. The key to social-awareness is self-awareness. We help our learners become aware, gain insights and discover their fullest leadership potential. This in turn builds organizational leadership capacity & capabilities. -

- -
-

Development Process:

- -
-
-
- - Self-awareness and insight facilitation. - -
- -
-
- - Leadership potential discovery sessions. - -
- -
-
- - Organizational capacity building. - -
-
-
-
-
-
-
- - {/* Phase 3 - Implementation & Mastery */} -
-
- {/* Phase dot positioned absolutely */} -
- - {/* Column 1: Phase Label */} -
-
-
- Phase 3 -
-
- - {/* Column 2: Main Heading */} -
- -

Implementation & Mastery

-
-
- - {/* Column 3: Content - Description and Bullet Points */} -
- -

- Our approach to learning works on the 'Orientation Toning process', which is using 'Restraint and Release' of thought, emotions and actions. This release is moderated according to the demands of a context, without being tied down by one's default settings. -

- -
-

Mastery Framework:

- -
-
-
- - Orientation Toning process implementation. - -
- -
-
- - Context-responsive leadership adaptation. - -
- -
-
- - Sustainable behavior change integration. - -
-
-
-
-
-
-
- - {/* Our Philosophy - Full Width Figma Design */} -
- {/* Phase dot positioned absolutely */} -
- -
- -
-
- {/* Left Section: Heading */} -
-

Our Philosophy

-
- - {/* Right Section: Content */} -
- {/* Philosophy Description */} -

- Our philosophy is that leadership begins with orientations — the inner drivers and behaviors that shape who we are. These orientations combine to create leadership abilities, which in turn lead to meaningful outcomes. At Kautilya Leadership Centre, we see leadership as a journey of connecting orientations to abilities and outcomes through structured development. -

- - {/* Three Philosophy Items */} -
- {/* Leadership Orientations */} -
-
- - - - - - -
-
-

- Leadership Orientations -

-

- Who am I? (inner drivers & behaviors) -

-
-
- - {/* Leadership Abilities */} -
-
- - - - - - -
-
-

- Leadership Abilities -

-

- What I can do (competencies & skills) -

-
-
- - {/* Leadership Outcomes */} -
-
- - - - - - -
-
-

- Leadership Outcomes -

-

- What I deliver (results for self & organization) -

-
-
-
-
-
-
-
-
-
- + {/* Section 5: Our Methodology - Dynamic from API */} + {aboutUsData?.methodology && ( +
+
+
- navigateTo('/contact')} - ariaLabel="Contact us to design your leadership journey" - className="cta-text-black" - /> + + {aboutUsData.methodology.subtitle && ( +

{aboutUsData.methodology.subtitle}

+ )}
+ + {/* Vertical Timeline Container */} +
+ {/* Vertical Line Background - Gray */} +
+ + {/* Vertical Line Fill - Blue - Animated on Scroll */} +
+ + {/* Map through phases from API - Create a copy before sorting */} + {[...(aboutUsData.methodology.phases || [])] + .sort((a, b) => (a.display_order || 0) - (b.display_order || 0)) + .map((phase, phaseIndex) => ( +
+
+ {/* Phase dot positioned absolutely */} +
+ + {/* Column 1: Phase Label */} +
+
+
+ {phase.phase_label || `Phase ${phase.phase_number}`} +
+
+ + {/* Column 2: Main Heading */} +
+ +

{phase.title}

+
+
+ + {/* Column 3: Content - Description and Bullet Points */} +
+ +

+ {phase.description} +

+ + {phase.bullet_title && phase.bullets && phase.bullets.length > 0 && ( +
+

{phase.bullet_title}

+
+ {phase.bullets.map((bullet, bulletIndex) => ( +
+
+ + {bullet} + +
+ ))} +
+
+ )} +
+
+
+
+ ))} + + {/* Our Philosophy - Dynamic from API */} + {aboutUsData?.philosophy && ( +
+ {/* Phase dot positioned absolutely */} +
+ +
+ +
+
+ {/* Left Section: Heading */} +
+

{aboutUsData.philosophy.title || "Our Philosophy"}

+
+ + {/* Right Section: Content */} +
+ {/* Philosophy Description */} +

+ {aboutUsData.philosophy.description} +

+ + {/* Philosophy Points */} + {aboutUsData.philosophy.points && aboutUsData.philosophy.points.length > 0 && ( +
+ {aboutUsData.philosophy.points.map((point, pointIndex) => ( +
+
+ + + + + + +
+
+

+ {point} +

+
+
+ ))} +
+ )} +
+
+
+
+
+
+ )} + + + navigateTo('/contact')} + ariaLabel="Contact us to design your leadership journey" + className="cta-text-black" + /> + +
-
-
+ + )} {/* Testimonials Section */} void; - cartItems: CartItem[]; // Legacy prop - no longer used but kept for backward compatibility - onRemoveItem: (itemId: string) => void; // Legacy prop - no longer used but kept for backward compatibility + // cartItems: CartItem[]; // Legacy prop - no longer used but kept for backward compatibility + // onRemoveItem: (itemId: string) => void; // Legacy prop - no longer used but kept for backward compatibility recentlyAddedItem?: CartItem | null; } export function CartPopup({ isOpen, onClose, - cartItems: legacyCartItems, // Renamed to avoid confusion - onRemoveItem: legacyOnRemoveItem, // Renamed to avoid confusion + // cartItems: legacyCartItems, // Renamed to avoid confusion + // onRemoveItem: legacyOnRemoveItem, // Renamed to avoid confusion recentlyAddedItem }: CartPopupProps) { const [showSuccess, setShowSuccess] = useState(false); diff --git a/src/components/CourseCard.tsx b/src/components/CourseCard.tsx index 280ebea..a6c06d9 100644 --- a/src/components/CourseCard.tsx +++ b/src/components/CourseCard.tsx @@ -1,9 +1,9 @@ import React from 'react'; import { Button } from './ui/button'; import { Badge } from './ui/badge'; -import { - Users, - Clock, +import { + Users, + Clock, Star, ArrowRight, ShoppingCart @@ -47,7 +47,7 @@ export function CourseCard({ course, onClick, className, onAddToCart }: CourseCa const handleAddToCart = (e: React.MouseEvent) => { e.stopPropagation(); // Prevent card click when clicking Add to Cart - + if (onAddToCart) { const cartItem: CartItem = { id: course.id, @@ -75,7 +75,7 @@ export function CourseCard({ course, onClick, className, onAddToCart }: CourseCa className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300" />
-
-
- - {course.level} - -
- + {/* Card Content - Reduced horizontal padding */}
{/* Course Title */} -

{course.title}

- + {/* Course Description - Limited to 2 lines with ellipsis */} -

{course.description}

- + {/* Course Meta Information - Reduced bottom margin */}
-
-
- +
-
- {course.originalPrice && ( - )}
- {course.originalPrice && ( + {/* {course.originalPrice && (
- )} + )} */}
- + {/* Action Buttons - Horizontal Layout with reduced gap */}
- {/* Add to Cart Button - Outline Blue */} + {/* Add to Cart */} - - {/* Learn More Button - Solid Blue */} + + {/* Learn More */}
diff --git a/src/components/LearningOnline.tsx b/src/components/LearningOnline.tsx index ca3d425..46f94d7 100644 --- a/src/components/LearningOnline.tsx +++ b/src/components/LearningOnline.tsx @@ -1,206 +1,48 @@ -import React, { useState, useRef, useEffect } from 'react'; -import { Button } from './ui/button'; -import { Badge } from './ui/badge'; -import { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs'; -import { Card, CardContent, CardHeader, CardTitle } from './ui/card'; -import { Input } from './ui/input'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select'; import { - Play, - Users, - Clock, - ChevronRight, ChevronLeft, - GraduationCap, - MessageCircle, - Zap, - Video, - Smartphone, - Award, - Building2, - BookOpen, - Star, - Globe, - Target, - TrendingUp, - Lightbulb, - CheckCircle, - ArrowRight, - Calendar, - Search, + ChevronRight, + Clock, + DollarSign, Filter, Grid, List, - X, - DollarSign + Search, + Star, + Users, + X } from 'lucide-react'; import { motion } from 'motion/react'; -import { navigateTo } from './Router'; -import { ImageWithFallback } from './figma/ImageWithFallback'; -import { BrandedTag } from './about/BrandedTag'; -import { PrimaryCTAButton } from './PrimaryCTAButton'; -import { CourseCard } from './CourseCard'; -import { CartPopup, CartItem } from './CartPopup'; +import { useState, useEffect, useMemo, useCallback } from 'react'; import { useCart } from './CartContext'; +import { CartItem, CartPopup } from './CartPopup'; +import { CourseCard } from './CourseCard'; +import { ImageWithFallback } from './figma/ImageWithFallback'; +import { navigateTo } from './Router'; +import { Badge } from './ui/badge'; +import { Button } from './ui/button'; +import { Card } from './ui/card'; +import { Input } from './ui/input'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select'; +import { useGetCoursesQuery, Course, GetCoursesParams, useGetCourseCategoriesQuery, CourseCategory } from '../redux/services/courseApi'; +import { useDebounce } from '../redux/hooks/useDebounce'; -// Course Categories -const courseCategories = [ - 'Leadership Fundamentals', - 'Decision Making & Strategy', - 'Perspective & Risk', - 'Communication & Influence', - 'Change & Innovation' -]; +// Helper function to parse rupee price from string (keep as is) +const parsePriceToNumber = (priceStr: string | number): number => { + if (typeof priceStr === 'number') return priceStr; + const numericStr = priceStr.toString().replace(/[^0-9.-]/g, ''); + return parseFloat(numericStr) || 0; +}; -// Featured Courses Data - Updated with Rupee pricing -const featuredCourses = [ - { - id: '1', - title: 'Strategic Leadership Foundations', - thumbnail: 'https://images.unsplash.com/photo-1552664730-d307ca884978?w=400&h=250&fit=crop', - duration: '12 hours', - level: 'Intermediate', - format: 'Self-paced', - rating: 4.8, - participants: '2,400+', - category: 'Leadership Fundamentals', - description: 'Master the core principles of strategic leadership and organizational vision.', - price: '₹24,817', - originalPrice: '₹33,117' - }, - { - id: '2', - title: 'Data-Driven Decision Making', - thumbnail: 'https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=400&h=250&fit=crop', - duration: '8 hours', - level: 'Advanced', - format: 'Cohort-based', - rating: 4.9, - participants: '1,800+', - category: 'Decision Making & Strategy', - description: 'Learn to make strategic decisions using data analytics and business intelligence.', - price: '₹37,267', - originalPrice: '₹45,567' - }, - { - id: '3', - title: 'Risk Assessment & Management', - thumbnail: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=400&h=250&fit=crop', - duration: '10 hours', - level: 'Intermediate', - format: 'Self-paced', - rating: 4.7, - participants: '3,200+', - category: 'Perspective & Risk', - description: 'Develop expertise in identifying, analyzing, and mitigating organizational risks.', - price: '₹28,967', - originalPrice: '₹37,267' - }, - { - id: '4', - title: 'Influential Communication', - thumbnail: 'https://images.unsplash.com/photo-1556761175-b413da4baf72?w=400&h=250&fit=crop', - duration: '6 hours', - level: 'Beginner', - format: 'Self-paced', - rating: 4.8, - participants: '5,100+', - category: 'Communication & Influence', - description: 'Master the art of persuasive communication and stakeholder engagement.', - price: '₹16,517', - originalPrice: '₹20,667' - }, - { - id: '5', - title: 'Leading Innovation & Change', - thumbnail: 'https://images.unsplash.com/photo-1542744173-8e7e53415bb0?w=400&h=250&fit=crop', - duration: '14 hours', - level: 'Advanced', - format: 'Cohort-based', - rating: 4.9, - participants: '1,950+', - category: 'Change & Innovation', - description: 'Drive organizational transformation and foster a culture of innovation.', - price: '₹45,567', - originalPrice: '₹53,867' - }, - { - id: '6', - title: 'Digital Leadership Essentials', - thumbnail: 'https://images.unsplash.com/photo-1551434678-e076c223a692?w=400&h=250&fit=crop', - duration: '9 hours', - level: 'Intermediate', - format: 'Self-paced', - rating: 4.6, - participants: '2,800+', - category: 'Leadership Fundamentals', - description: 'Navigate the digital transformation as a modern leader.', - price: '₹23,157', - originalPrice: '₹28,967' - }, - { - id: '7', - title: 'Crisis Leadership Strategies', - thumbnail: 'https://images.unsplash.com/photo-1584697964358-3e14ca57658b?w=400&h=250&fit=crop', - duration: '7 hours', - level: 'Advanced', - format: 'Cohort-based', - rating: 4.7, - participants: '1,200+', - category: 'Leadership Fundamentals', - description: 'Navigate uncertainty and lead your team through challenging situations with confidence.', - price: '₹33,117', - originalPrice: '₹41,417' - }, - { - id: '8', - title: 'Emotional Intelligence for Leaders', - thumbnail: 'https://images.unsplash.com/photo-1559027615-cd4628902d4a?w=400&h=250&fit=crop', - duration: '5 hours', - level: 'Beginner', - format: 'Self-paced', - rating: 4.9, - participants: '4,300+', - category: 'Communication & Influence', - description: 'Develop emotional intelligence to enhance your leadership effectiveness.', - price: '₹14,857', - originalPrice: '₹19,007' - }, - { - id: 'ldp-foundations', - title: 'Strategic Leadership Development Program: Foundations', - thumbnail: 'https://images.unsplash.com/photo-1588912914078-2fe5224fd8b8?w=400&h=250&fit=crop', - duration: '40 hours', - level: 'Intermediate', - format: 'Self-paced', - rating: 4.8, - participants: '1,247+', - category: 'Leadership Development', - description: 'Master the fundamentals of effective leadership through evidence-based practices and real-world case studies.', - price: '$599', - originalPrice: '$799' - }, - { - id: '9', - title: 'Strategic Risk Analysis', - thumbnail: 'https://images.unsplash.com/photo-1560472355-536de3962603?w=400&h=250&fit=crop', - duration: '11 hours', - level: 'Advanced', - format: 'Self-paced', - rating: 4.8, - participants: '1,500+', - category: 'Perspective & Risk', - description: 'Master advanced risk analysis techniques for strategic decision-making.', - price: '₹39,757', - originalPrice: '₹49,717' - } -]; +// Format price with Rupee symbol (keep as is) +const formatPrice = (price: number): string => { + return `₹${price.toLocaleString('en-IN')}`; +}; export function LearningOnline() { + // UI state const [searchTerm, setSearchTerm] = useState(''); - const [selectedCategory, setSelectedCategory] = useState('All Categories'); - const [selectedLevel, setSelectedLevel] = useState('All Levels'); - const [selectedFormat, setSelectedFormat] = useState('All Formats'); + const [selectedCategoryId, setSelectedCategoryId] = useState(''); + const [selectedCategoryName, setSelectedCategoryName] = useState('All Categories'); const [selectedPriceRange, setSelectedPriceRange] = useState('All Prices'); const [selectedDuration, setSelectedDuration] = useState('All Durations'); const [selectedRating, setSelectedRating] = useState('All Ratings'); @@ -209,98 +51,248 @@ export function LearningOnline() { const [currentPage, setCurrentPage] = useState(1); const coursesPerPage = 9; - // Cart functionality - using global cart context - const { addToCart } = useCart(); - const [isCartPopupOpen, setIsCartPopupOpen] = useState(false); - const [recentlyAddedItem, setRecentlyAddedItem] = useState(null); + // Debounced search term to avoid too many API calls + const debouncedSearchTerm = useDebounce(searchTerm, 500); - // Get unique values for filters - Updated for Rupees - const categories = ['All Categories', ...courseCategories]; - const levels = ['All Levels', ...Array.from(new Set(featuredCourses.map(course => course.level)))]; - const formats = ['All Formats', ...Array.from(new Set(featuredCourses.map(course => course.format)))]; - const priceRanges = ['All Prices', 'Under ₹20,000', '₹20,000 - ₹35,000', '₹35,000 - ₹50,000', 'Over ₹50,000']; - const durations = ['All Durations', 'Under 6 hours', '6-10 hours', '10-15 hours', 'Over 15 hours']; - const ratings = ['All Ratings', '4.5+ Stars', '4.0+ Stars', '3.5+ Stars']; - - const sortOptions = [ - { value: 'Most Popular', label: 'Most Popular' }, - { value: 'newest', label: 'Newest First' }, - { value: 'title', label: 'Title A-Z' }, - { value: 'price_low', label: 'Price: Low to High' }, - { value: 'price_high', label: 'Price: High to Low' }, - { value: 'rating', label: 'Highest Rated' }, - { value: 'duration', label: 'Duration' } - ]; - - // Helper function to parse rupee price - const parseRupeePrice = (priceStr: string) => { - return parseFloat(priceStr.replace('₹', '').replace(/,/g, '')); - }; - - // Filter and sort courses - const filteredCourses = featuredCourses.filter(course => { - const matchesSearch = course.title.toLowerCase().includes(searchTerm.toLowerCase()) || - course.description.toLowerCase().includes(searchTerm.toLowerCase()) || - course.category.toLowerCase().includes(searchTerm.toLowerCase()); - - const matchesCategory = selectedCategory === 'All Categories' || course.category === selectedCategory; - const matchesLevel = selectedLevel === 'All Levels' || course.level === selectedLevel; - const matchesFormat = selectedFormat === 'All Formats' || course.format === selectedFormat; - - // Price filter - Updated for Rupees - const price = parseRupeePrice(course.price); - const matchesPrice = selectedPriceRange === 'All Prices' || - (selectedPriceRange === 'Under ₹20,000' && price < 20000) || - (selectedPriceRange === '₹20,000 - ₹35,000' && price >= 20000 && price <= 35000) || - (selectedPriceRange === '₹35,000 - ₹50,000' && price >= 35000 && price <= 50000) || - (selectedPriceRange === 'Over ₹50,000' && price > 50000); - - // Duration filter - const durationHours = parseInt(course.duration); - const matchesDuration = selectedDuration === 'All Durations' || - (selectedDuration === 'Under 6 hours' && durationHours < 6) || - (selectedDuration === '6-10 hours' && durationHours >= 6 && durationHours <= 10) || - (selectedDuration === '10-15 hours' && durationHours >= 10 && durationHours <= 15) || - (selectedDuration === 'Over 15 hours' && durationHours > 15); - - // Rating filter - const matchesRating = selectedRating === 'All Ratings' || - (selectedRating === '4.5+ Stars' && course.rating >= 4.5) || - (selectedRating === '4.0+ Stars' && course.rating >= 4.0) || - (selectedRating === '3.5+ Stars' && course.rating >= 3.5); - - return matchesSearch && matchesCategory && matchesLevel && matchesFormat && matchesPrice && matchesDuration && matchesRating; - }).sort((a, b) => { - switch (sortBy) { - case 'Most Popular': - return parseInt(b.participants.replace(/[^\d]/g, '')) - parseInt(a.participants.replace(/[^\d]/g, '')); - case 'newest': - return a.id.localeCompare(b.id); // Assuming newer courses have higher IDs - case 'title': - return a.title.localeCompare(b.title); - case 'price_low': - return parseRupeePrice(a.price) - parseRupeePrice(b.price); - case 'price_high': - return parseRupeePrice(b.price) - parseRupeePrice(a.price); - case 'rating': - return b.rating - a.rating; - case 'duration': - return parseInt(a.duration) - parseInt(b.duration); - default: - return 0; - } + // Fetch course categories + const { data: categoriesData, isLoading: categoriesLoading } = useGetCourseCategoriesQuery({ + limit: 100, + offset: 0 }); - // Paginate results - const totalPages = Math.ceil(filteredCourses.length / coursesPerPage); + const sortOptions = [ + { value: 'most_popular', label: 'Most Popular' }, + { value: 'newest', label: 'Newest First' }, + { value: 'title_asc', label: 'Title A-Z' }, + { value: 'price_asc', label: 'Price: Low to High' }, + { value: 'price_desc', label: 'Price: High to Low' }, + { value: 'rating_desc', label: 'Highest Rated' }, + { value: 'duration_asc', label: 'Duration' } + ]; + + const priceRanges = [ + 'All Prices', + 'Under ₹20,000', + '₹20,000 - ₹35,000', + '₹35,000 - ₹50,000', + 'Over ₹50,000' + ]; + + const durations = [ + 'All Durations', + 'Under 6 hours', + '6-10 hours', + '10-15 hours', + 'Over 15 hours' + ]; + + const ratings = [ + 'All Ratings', + '4.5+ Stars', + '4.0+ Stars', + '3.5+ Stars' + ]; + + // Build categories list + const categories = useMemo(() => { + const cats = [{ id: '', name: 'All Categories' }]; + if (categoriesData?.data?.items) { + categoriesData.data.items.forEach((cat: CourseCategory) => { + cats.push({ id: cat.id, name: cat.category_name }); + }); + } + return cats; + }, [categoriesData]); + + // Helper function to convert UI price range to API format + const getPriceRangeForApi = useCallback((priceRange: string): string | undefined => { + switch (priceRange) { + case 'Under ₹20,000': + return '0-20000'; + case '₹20,000 - ₹35,000': + return '20000-35000'; + case '₹35,000 - ₹50,000': + return '35000-50000'; + case 'Over ₹50,000': + return '50000-999999'; + default: + return undefined; + } + }, []); + + // Helper function to convert UI duration to API format + const getDurationForApi = useCallback((duration: string): string | undefined => { + switch (duration) { + case 'Under 6 hours': + return '0-6'; + case '6-10 hours': + return '6-10'; + case '10-15 hours': + return '10-15'; + case 'Over 15 hours': + return '15-999'; + default: + return undefined; + } + }, []); + + // Helper function to convert UI rating to API format + const getRatingForApi = useCallback((rating: string): number | undefined => { + switch (rating) { + case '4.5+ Stars': + return 4.5; + case '4.0+ Stars': + return 4.0; + case '3.5+ Stars': + return 3.5; + default: + return undefined; + } + }, []); + + // Helper function to convert sort option to API format + const getSortByForApi = useCallback((sort: string): string | undefined => { + switch (sort) { + case 'Most Popular': + return 'popular'; + case 'newest': + return 'newest'; + case 'title': + return 'title_asc'; + case 'price_low': + return 'price_asc'; + case 'price_high': + return 'price_desc'; + case 'rating': + return 'rating_desc'; + case 'duration': + return 'duration_asc'; + default: + return undefined; + } + }, []); + + // Build API filters based on current UI state + const apiFilters: GetCoursesParams = useMemo(() => { + const filters: GetCoursesParams = { + limit: 100, + offset: 0, + status: 'publish' + }; + + // Category filter + if (selectedCategoryId) { + filters.course_category = [selectedCategoryId]; + } + + // Search query + if (debouncedSearchTerm) { + filters.search_query = debouncedSearchTerm; + } + + // Price range + const apiPriceRange = getPriceRangeForApi(selectedPriceRange); + if (apiPriceRange) { + filters.price_range = apiPriceRange; + } + + // Duration range + const apiDurationRange = getDurationForApi(selectedDuration); + if (apiDurationRange) { + filters.duration_range = apiDurationRange; + } + + // Rating + const apiRating = getRatingForApi(selectedRating); + if (apiRating !== undefined) { + filters.min_rating = apiRating; + } + + // Sort by + const apiSortBy = getSortByForApi(sortBy); + if (apiSortBy) { + filters.sort_by = apiSortBy; + } + + return filters; + }, [ + selectedCategoryId, + debouncedSearchTerm, + selectedPriceRange, + selectedDuration, + selectedRating, + sortBy, + getPriceRangeForApi, + getDurationForApi, + getRatingForApi, + getSortByForApi + ]); + + // Fetch courses with API filters + const { + data: coursesData, + isLoading: coursesLoading, + isError, + isFetching // To show loading indicator while fetching + } = useGetCoursesQuery(apiFilters); + + // Reset to page 1 when filters change + useEffect(() => { + setCurrentPage(1); + }, [ + selectedCategoryId, + debouncedSearchTerm, + selectedPriceRange, + selectedDuration, + selectedRating, + sortBy + ]); + + // Transform API response to course format + const courses = useMemo(() => { + if (!coursesData?.data?.items) return []; + + return coursesData.data.items.map((course: Course) => ({ + id: course.id, + title: course.course_name, + thumbnail: course.thumbnail_img || 'https://images.unsplash.com/photo-1552664730-d307ca884978?w=400&h=250&fit=crop', + duration: `${course.total_duration || 0} hours`, + level: 'Intermediate', + format: course.retail_type === 'public' ? 'Cohort-based' : 'Self-paced', + rating: course.avg_rating || 4.5, + participants: `${Math.floor(Math.random() * 5000) + 100}+`, + category: course.course_category_name || 'General', + categoryId: course.course_category_xid || '', + description: course.course_desc || `Master ${course.course_name} with our comprehensive program.`, + price: formatPrice(course.price), + originalPrice: formatPrice(course.price * 1.25), + course_status: course.course_status + })); + }, [coursesData]); + + // Get total courses count from API response + const totalCoursesCount = coursesData?.data?.pagination_info?.total_count || 0; + + // Paginate the courses (since API returns all courses based on filters, we paginate client-side) + const totalPages = Math.ceil(totalCoursesCount / coursesPerPage); const startIndex = (currentPage - 1) * coursesPerPage; - const currentCourses = filteredCourses.slice(startIndex, startIndex + coursesPerPage); + const currentCourses = courses.slice(startIndex, startIndex + coursesPerPage); + + // Handle category change + const handleCategoryChange = (value: string) => { + const selectedCat = categories.find(cat => cat.name === value); + if (selectedCat) { + setSelectedCategoryName(selectedCat.name); + setSelectedCategoryId(selectedCat.id); + } else { + setSelectedCategoryName('All Categories'); + setSelectedCategoryId(''); + } + }; const clearAllFilters = () => { setSearchTerm(''); - setSelectedCategory('All Categories'); - setSelectedLevel('All Levels'); - setSelectedFormat('All Formats'); + handleCategoryChange('All Categories'); setSelectedPriceRange('All Prices'); setSelectedDuration('All Durations'); setSelectedRating('All Ratings'); @@ -308,14 +300,16 @@ export function LearningOnline() { }; const hasActiveFilters = searchTerm || - selectedCategory !== 'All Categories' || - selectedLevel !== 'All Levels' || - selectedFormat !== 'All Formats' || + selectedCategoryName !== 'All Categories' || selectedPriceRange !== 'All Prices' || selectedDuration !== 'All Durations' || selectedRating !== 'All Ratings'; - // Cart functions - using global cart context + // Cart functionality + const { addToCart } = useCart(); + const [isCartPopupOpen, setIsCartPopupOpen] = useState(false); + const [recentlyAddedItem, setRecentlyAddedItem] = useState(null); + const handleAddToCart = (item: CartItem) => { addToCart(item); setRecentlyAddedItem(item); @@ -327,9 +321,34 @@ export function LearningOnline() { setRecentlyAddedItem(null); }; + // Show loading state + if (coursesLoading || categoriesLoading) { + return ( +
+
+
+

Loading courses...

+
+
+ ); + } + + // Show error state + if (isError) { + return ( +
+
+

Error Loading Courses

+

Failed to load courses. Please try again later.

+ +
+
+ ); + } + return (
- {/* Hero Banner – Digital Learning - Blog Style */} + {/* Hero Banner (keep as is) */}
- {/* Eyebrow Text */}
DIGITAL LEARNING PLATFORM
- - {/* Main Header */}

Discover Your Leadership
Potential Online

- - {/* Sub Text */}

Our Leadership Courses are structured packages which are targeted towards building your leadership abilities. Each course is a wholesome package which not only helps you gain awareness about your leadership style but also gives insights to build your leadership abilities. Every course contains curated content targeted towards a specific leadership ability. Each course consists of our proprietary profiling instruments – Leadership Profilers, conceptual videos and experiences of leaders – Leadership Webcasts, as well as additional content to supplement learning. @@ -371,11 +385,10 @@ export function LearningOnline() {

- {/* Search and Controls Section */} + {/* Search and Controls Section (keep as is) */}
- {/* Search Bar */}
- {/* View Toggle and Sort */}
- - {/* Level Filter */} -
- - -
- - {/* Format Filter */} -
- - setBrochureFormData({...brochureFormData, company: e.target.value})} + onChange={(e) => setBrochureFormData({ ...brochureFormData, company: e.target.value })} />
@@ -1526,10 +2045,10 @@ export function ProgrammeDetail({ slug }: ProgrammeDetailProps) { setBrochureFormData({...brochureFormData, designation: e.target.value})} + onChange={(e) => setBrochureFormData({ ...brochureFormData, designation: e.target.value })} />
- +
diff --git a/src/components/WebinarDetail.tsx b/src/components/WebinarDetail.tsx index 197cbbf..ff176b3 100644 --- a/src/components/WebinarDetail.tsx +++ b/src/components/WebinarDetail.tsx @@ -44,7 +44,7 @@ import { navigateTo } from './Router'; import { ImageWithFallback } from './figma/ImageWithFallback'; import { BrandedTag } from './about/BrandedTag'; import { PrimaryCTAButton } from './PrimaryCTAButton'; -import { toast } from 'sonner@2.0.3'; +import { toast } from 'sonner'; import { getWebinarBySlug, sharedWebinarsData, type WebinarData } from '../data/webinarsData'; interface WebinarDetailProps { diff --git a/src/components/Webinars.tsx b/src/components/Webinars.tsx index e30eeed..e723506 100644 --- a/src/components/Webinars.tsx +++ b/src/components/Webinars.tsx @@ -1,151 +1,100 @@ -import React, { useState, useRef, useEffect } from 'react'; -import { Button } from './ui/button'; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './ui/card'; -import { Badge } from './ui/badge'; -import { Input } from './ui/input'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select'; -import { Slider } from './ui/slider'; -import { ImageWithFallback } from './figma/ImageWithFallback'; -import { PrimaryCTAButton } from './PrimaryCTAButton'; -import { navigateTo } from './Router'; -import { sharedWebinarsData, type WebinarData } from '../data/webinarsData'; -import { WebcastCTABanner } from './WebcastCTABanner'; import { - Search, - Calendar, - Clock, - Users, - Play, ArrowRight, + ChevronLeft, + ChevronRight, + Clock, + Eye, Filter, Grid, List, - SortAsc, - Eye, + Play, + Search, Star, - ChevronLeft, - ChevronRight, + Users, X } from 'lucide-react'; +import { useEffect, useRef, useState } from 'react'; +import { useWebinarListQuery, type WebinarItem } from '../redux/services/webinarApi'; +import { ImageWithFallback } from './figma/ImageWithFallback'; +import { FullScreenLoader } from './FullScreenLoader'; +import { navigateTo } from './Router'; +import { Badge } from './ui/badge'; +import { Button } from './ui/button'; +import { Card, CardContent } from './ui/card'; +import { Input } from './ui/input'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select'; +import { Slider } from './ui/slider'; +import { WebcastCTABanner } from './WebcastCTABanner'; + +// Status options with proper mapping to API values +const statusOptions = [ + { value: 'scheduled', label: '📅 Scheduled', color: 'bg-blue-100 text-blue-800 border-blue-200' }, + { value: 'live', label: '🔴 Live', color: 'bg-red-100 text-red-800 border-red-200' }, + { value: 'ended', label: '✅ Ended', color: 'bg-gray-100 text-gray-800 border-gray-200' }, + { value: 'cancelled', label: '❌ Cancelled', color: 'bg-red-50 text-red-600 border-red-200' } +]; + +const sortOptions = [ + { value: 'most_popular', label: 'Most Popular' }, + { value: 'newest', label: 'Newest First' }, + { value: 'oldest', label: 'Oldest First' }, + { value: 'title', label: 'Title A-Z' }, + { value: 'duration', label: 'Duration' } +]; + +// Static tags for all webinars +const staticTags = ['Leadership', 'Executive Development', 'Strategy', 'Innovation', 'Change Management', 'Business Growth', 'Team Building', 'Digital Transformation']; export function Webinars() { const [searchTerm, setSearchTerm] = useState(''); const [selectedCategory, setSelectedCategory] = useState('All Categories'); - const [selectedFormat, setSelectedFormat] = useState('All Formats'); - const [selectedLevel, setSelectedLevel] = useState('All Levels'); - - // Updated state for multi-select status pills const [selectedStatuses, setSelectedStatuses] = useState([]); - - // Updated state for duration slider (min, max in minutes) const [durationRange, setDurationRange] = useState([0, 120]); - - // Attendee range slider state const [attendeeRange, setAttendeeRange] = useState([0, 5000]); - - const [sortBy, setSortBy] = useState('Most Popular'); + const [sortBy, setSortBy] = useState('most_popular'); const [viewType, setViewType] = useState<'grid' | 'list'>('grid'); const [currentPage, setCurrentPage] = useState(1); const webinarsPerPage = 6; const containerRef = useRef(null); - // Use shared webinars data instead of local mock data - const webinars = sharedWebinarsData; - - // Get unique values for filters from shared data - const categories = ['All Categories', ...Array.from(new Set(webinars.map(webinar => webinar.category)))]; - const formats = ['All Formats', ...Array.from(new Set(webinars.map(webinar => webinar.format)))]; - const levels = ['All Levels', ...Array.from(new Set(webinars.map(webinar => webinar.level)))]; - - // Status options for pills - updated to match shared data structure - const statusOptions = [ - { value: 'upcoming', label: '📅 Upcoming', color: 'bg-blue-100 text-blue-800 border-blue-200' }, - { value: 'live', label: '🔴 Live', color: 'bg-red-100 text-red-800 border-red-200' }, - { value: 'recorded', label: '▶️ Recorded', color: 'bg-green-100 text-green-800 border-green-200' }, - { value: 'featured', label: '⭐ Featured', color: 'bg-yellow-100 text-yellow-800 border-yellow-200' } - ]; - - const sortOptions = [ - { value: 'Most Popular', label: 'Most Popular' }, - { value: 'newest', label: 'Newest First' }, - { value: 'oldest', label: 'Oldest First' }, - { value: 'title', label: 'Title A-Z' }, - { value: 'duration', label: 'Duration' } - ]; - - // Helper function to convert attendees string to number - const parseAttendees = (attendeesStr: string): number => { - const numStr = attendeesStr.replace(/[^\d]/g, ''); - return parseInt(numStr) || 0; - }; - - // Helper function to convert duration string to minutes - const parseDuration = (durationStr: string): number => { - const numStr = durationStr.replace(/[^\d]/g, ''); - return parseInt(numStr) || 0; - }; - - // Filter and sort webinars - const filteredWebinars = webinars.filter(webinar => { - const matchesSearch = webinar.title.toLowerCase().includes(searchTerm.toLowerCase()) || - webinar.description.toLowerCase().includes(searchTerm.toLowerCase()) || - webinar.presenter.toLowerCase().includes(searchTerm.toLowerCase()) || - webinar.tags.some(tag => tag.toLowerCase().includes(searchTerm.toLowerCase())); - const matchesCategory = selectedCategory === 'All Categories' || webinar.category === selectedCategory; - const matchesFormat = selectedFormat === 'All Formats' || webinar.format === selectedFormat; - const matchesLevel = selectedLevel === 'All Levels' || webinar.level === selectedLevel; - - const matchesStatus = selectedStatuses.length === 0 || - selectedStatuses.some(status => { - if (status === 'featured') return webinar.featured; - return webinar.status === status; - }); - - const durationMinutes = parseDuration(webinar.duration); - const matchesDuration = durationMinutes >= durationRange[0] && durationMinutes <= durationRange[1]; - - const attendeeCount = parseAttendees(webinar.attendees); - const matchesAttendees = attendeeCount >= attendeeRange[0] && attendeeCount <= attendeeRange[1]; - - return matchesSearch && matchesCategory && matchesFormat && matchesLevel && matchesStatus && matchesDuration && matchesAttendees; - }).sort((a, b) => { - switch (sortBy) { - case 'Most Popular': - // Add logic for "Most Popular" - you might want to use views, attendees, or featured status - return (b.featured ? 1 : 0) - (a.featured ? 1 : 0) || - parseAttendees(b.attendees) - parseAttendees(a.attendees); - case 'newest': - return new Date(b.date).getTime() - new Date(a.date).getTime(); - case 'oldest': - return new Date(a.date).getTime() - new Date(b.date).getTime(); - case 'title': - return a.title.localeCompare(b.title); - case 'duration': - return parseDuration(b.duration) - parseDuration(a.duration); - default: - return 0; - } + // Fetch webinars from API + const { + data: webinarResponse, + isLoading, + isError, + } = useWebinarListQuery({ + limit: 100, + offset: 0, + search: searchTerm || undefined, + status: selectedStatuses.length > 0 ? selectedStatuses : undefined, + minDuration: durationRange[0] > 0 ? durationRange[0] : undefined, + maxDuration: durationRange[1] < 120 ? durationRange[1] : undefined, + minAttendees: attendeeRange[0] > 0 ? attendeeRange[0] : undefined, + maxAttendees: attendeeRange[1] < 5000 ? attendeeRange[1] : undefined, + sortBy: sortBy as any, }); - // Statistics - const stats = { - total: webinars.length, - upcoming: webinars.filter(w => w.status === 'upcoming').length, - live: webinars.filter(w => w.status === 'live').length, - recorded: webinars.filter(w => w.status === 'recorded').length, - featured: webinars.filter(w => w.featured).length, - categories: new Set(webinars.map(w => w.category)).size + const webinars = webinarResponse?.data?.items || []; + + // Get random tags for each webinar (3 random tags from staticTags) + const getRandomTags = (seed: string) => { + // Use the webinar ID as seed to get consistent tags for each webinar + const hash = seed.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0); + const shuffled = [...staticTags]; + for (let i = shuffled.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; + } + return shuffled.slice(0, 3); }; - // Paginate results - const totalPages = Math.ceil(filteredWebinars.length / webinarsPerPage); - const currentWebinars = filteredWebinars.slice((currentPage - 1) * webinarsPerPage, currentPage * webinarsPerPage); - - console.log('Filtered webinars:', filteredWebinars.length); -console.log('Total pages:', totalPages); -console.log('Current page:', currentPage); -console.log('Current webinars count:', currentWebinars.length); + // Get unique categories from API data + const categories = [ + 'All Categories', + ...Array.from(new Set(webinars.map(webinar => webinar.session_title?.split(' ')[0] || 'General'))) + ]; + // Helper functions const formatDate = (dateString: string) => { return new Date(dateString).toLocaleDateString('en-US', { year: 'numeric', @@ -154,26 +103,81 @@ console.log('Current webinars count:', currentWebinars.length); }); }; + 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 LIVE NOW; + case 'scheduled': + return SCHEDULED; + case 'ended': + return ENDED; + case 'cancelled': + return CANCELLED; + default: + return null; + } + }; + + const getActionText = (status: string) => { + switch (status) { + case 'live': + return 'Join Now'; + case 'scheduled': + return 'Register'; + case 'ended': + return 'Watch Recording'; + case 'cancelled': + return 'Cancelled'; + default: + return 'Learn More'; + } + }; + + // Statistics + const stats = { + total: webinars.length, + scheduled: webinars.filter(w => w.webinar_status === 'scheduled').length, + live: webinars.filter(w => w.webinar_status === 'live').length, + ended: webinars.filter(w => w.webinar_status === 'ended').length, + cancelled: webinars.filter(w => w.webinar_status === 'cancelled').length, + categories: categories.length - 1 + }; + + // Filter webinars + const filteredWebinars = webinars.filter(webinar => { + const matchesCategory = selectedCategory === 'All Categories' || + (webinar.session_title && webinar.session_title.toLowerCase().includes(selectedCategory.toLowerCase())); + return matchesCategory; + }); + + // Paginate results + const totalPages = Math.ceil(filteredWebinars.length / webinarsPerPage); + const currentWebinars = filteredWebinars.slice((currentPage - 1) * webinarsPerPage, currentPage * webinarsPerPage); + const clearAllFilters = () => { setSearchTerm(''); setSelectedCategory('All Categories'); - setSelectedFormat('All Formats'); - setSelectedLevel('All Levels'); setSelectedStatuses([]); setDurationRange([0, 120]); setAttendeeRange([0, 5000]); - setSortBy('Most Popular'); + setSortBy('most_popular'); }; const hasActiveFilters = searchTerm || selectedCategory !== 'All Categories' || - selectedFormat !== 'All Formats' || - selectedLevel !== 'All Levels' || selectedStatuses.length > 0 || durationRange[0] !== 0 || durationRange[1] !== 120 || attendeeRange[0] !== 0 || attendeeRange[1] !== 5000; - // Status pill toggle function const toggleStatus = (status: string) => { setSelectedStatuses(prev => prev.includes(status) @@ -182,95 +186,78 @@ console.log('Current webinars count:', currentWebinars.length); ); }; - // Reset to page 1 when filters change useEffect(() => { setCurrentPage(1); - }, [searchTerm, selectedCategory, selectedFormat, selectedLevel, selectedStatuses, durationRange, attendeeRange, sortBy]); + }, [searchTerm, selectedCategory, selectedStatuses, durationRange, attendeeRange, sortBy]); - // Updated WebinarCard component that navigates to consistent route - const WebinarCard = ({ webinar }: { webinar: WebinarData }) => { + const WebinarCard = ({ webinar }: { webinar: WebinarItem }) => { const handleCardClick = () => { - // Navigate to consistent webinar detail route - navigateTo(`/webinar/${webinar.slug}`); - }; - - const getStatusBadge = () => { - switch (webinar.status) { - case 'live': - return LIVE; - case 'upcoming': - return UPCOMING; - case 'recorded': - return RECORDED; - default: - return null; + if (webinar.webinar_status !== 'cancelled') { + navigateTo(`/webinar/${webinar.id}`); } }; - const getActionText = () => { - switch (webinar.status) { - case 'live': - return 'Join Now'; - case 'upcoming': - return 'Register'; - case 'recorded': - return 'Watch Recording'; - default: - return 'Learn More'; - } - }; + const isCancelled = webinar.webinar_status === 'cancelled'; + const webinarTags = getRandomTags(webinar.id); if (viewType === 'list') { return (
{/* Thumbnail */} -
- +
+
{/* Content */}
- {getStatusBadge()} - {webinar.featured && ( - Featured - )} + {getStatusBadge(webinar.webinar_status)}
- {formatDate(webinar.date)} + {formatDate(webinar.session_datetime)}
-

{webinar.title}

-

{webinar.description}

+

{webinar.session_title}

+

+ {webinar.description || 'No description available'} +

+ + {/* Tags */} +
+ {webinarTags.map((tag, idx) => ( + + {tag} + + ))} +
- {webinar.presenter} + {webinar.owner || 'Kautilya Leadership'} - {webinar.duration} + {formatDuration(webinar.duration_minutes)} - {webinar.attendees} + Max {webinar.max_attendee.toLocaleString()}
-
- {getActionText()} - -
+ {!isCancelled && ( +
+ {getActionText(webinar.webinar_status)} + +
+ )}
@@ -279,100 +266,122 @@ console.log('Current webinars count:', currentWebinars.length); ); } + // Grid View return ( {/* Image */} -
- +
+
+ +
{/* Status Badge */}
- {getStatusBadge()} + {getStatusBadge(webinar.webinar_status)}
- {/* Featured Badge */} - {webinar.featured && ( -
- - - Featured - -
- )} - {/* Play Icon Overlay */} -
-
- + {!isCancelled && ( +
+
+ +
-
+ )}
{/* Content */}
- {webinar.category} + {webinar.recurring_webinar ? 'Recurring' : 'One-time'} - {formatDate(webinar.date)} + {formatDate(webinar.session_datetime)}

- {webinar.title} + {webinar.session_title}

- {webinar.description} + {webinar.description || 'No description available'}

+ {/* Tags */} +
+ {webinarTags.slice(0, 2).map((tag, idx) => ( + + {tag} + + ))} +
+
- {webinar.presenter} + {webinar.owner || 'Kautilya Leadership'}
- {webinar.duration} + {formatDuration(webinar.duration_minutes)}
- {webinar.attendees} + Max {webinar.max_attendee.toLocaleString()}
-
-
- {webinar.tags.slice(0, 2).map((tag, index) => ( - - {tag} - - ))} + {!isCancelled && ( +
+
+ {webinar.require_registration && ( + <> + + Registration Required + + )} +
+
+ {getActionText(webinar.webinar_status)} + +
-
- {getActionText()} - -
-
+ )} ); }; + if (isLoading) { + return ( +
+ +
+ ); + } + + if (isError) { + return ( +
+
+

Error loading webinars. Please try again later.

+ +
+
+ ); + } + return (
- {/* Hero Section with Background Image */} + {/* Hero Section */}
- {/* Background Image */}
- {/* Hero Content */}

Leadership Webcasts &
Expert Insights

-

Explore our comprehensive collection of expert insights, research, and practical guidance to elevate your leadership journey and drive organizational excellence. @@ -397,8 +404,7 @@ console.log('Current webinars count:', currentWebinars.length);

- {/* Statistics Strip at Bottom */} -
+
@@ -424,12 +430,11 @@ console.log('Current webinars count:', currentWebinars.length);
- {/* Search Bar */}
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" @@ -441,7 +446,6 @@ console.log('Current webinars count:', currentWebinars.length); />
- {/* View Toggle and Sort */}
- {/* Main Content Section with Sidebar */} + {/* Main Content Section */}
- {/* Left Sidebar - Sticky Filters */} + {/* Left Sidebar Filters */}
- {/* Filter Header */}
- +
-

- Filters -

+

Filters

{hasActiveFilters && (
- {/* Filter Content */}
- {/* Category Filter */} -
- - -
- - {/* Format Filter */} -
- - -
- - {/* Level Filter */} -
- - -
- - {/* Status Filter - Multi-select Pills */} + {/* Status Filter */}
- {/* Duration Filter - Slider */} + {/* Duration Filter */}
{durationRange[0]} min {durationRange[1]} min
-
- {durationRange[0] === 0 && durationRange[1] === 120 - ? 'All durations' - : `${durationRange[0]}-${durationRange[1]} minutes` - } -
- {/* Attendee Count Filter - Slider */} + {/* Attendee Filter */}
{attendeeRange[0].toLocaleString()} {attendeeRange[1].toLocaleString()}+
-
- {attendeeRange[0] === 0 && attendeeRange[1] === 5000 - ? 'Any size' - : `${attendeeRange[0].toLocaleString()}-${attendeeRange[1].toLocaleString()}+` - } -
@@ -672,24 +603,22 @@ console.log('Current webinars count:', currentWebinars.length); {/* Right Main Content */}
- {/* Results Header */}
- Showing {currentWebinars.length} of {filteredWebinars.length} webcasts + Showing {currentWebinars.length} of {filteredWebinars.length} webinars
Page {currentPage} of {totalPages}
- {/* Content Area */}
{currentWebinars.length === 0 ? (
-

No webcasts found

+

No webinars found

Try adjusting your filters or search terms

@@ -701,7 +630,6 @@ console.log('Current webinars count:', currentWebinars.length);
) : ( <> - {/* Grid View */} {viewType === 'grid' ? (
{currentWebinars.map((webinar) => ( @@ -709,7 +637,6 @@ console.log('Current webinars count:', currentWebinars.length); ))}
) : ( - /* List View */
{currentWebinars.map((webinar) => ( @@ -723,64 +650,46 @@ console.log('Current webinars count:', currentWebinars.length); -
- {Array.from({ length: totalPages }, (_, i) => { + {Array.from({ length: Math.min(totalPages, 5) }, (_, i) => { const page = i + 1; - // Show limited pages for better UX - if (totalPages > 7) { - const showPage = - page === 1 || - page === totalPages || - (page >= currentPage - 1 && page <= currentPage + 1); - - if (!showPage) { - if (page === currentPage - 2 || page === currentPage + 2) { - return ...; - } - return null; - } - } - return ( ); })} + {totalPages > 5 && ...} + {totalPages > 5 && ( + + )}
-
- {/* Webcast CTA Banner */}
); diff --git a/src/components/services/CultureCompetence.tsx b/src/components/services/CultureCompetence.tsx index 08f678b..2188be5 100644 --- a/src/components/services/CultureCompetence.tsx +++ b/src/components/services/CultureCompetence.tsx @@ -166,6 +166,16 @@ interface ServicePageData { 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 @@ -189,17 +199,17 @@ const getIconComponent = (iconUrl: string) => { '/icons/coaching.svg': MessageCircle, '/icons/compass.svg': Compass, }; - + return iconMap[iconUrl] || Target; }; export function CultureCompetence() { const [expandedPhase, setExpandedPhase] = useState(0); - - const { data: apiResponse, isLoading, error } = useGetServiceListQuery({ - service_type: 'culture_and_competence_consulting' + + const { data: apiResponse, isLoading, error } = useGetServiceListQuery({ + service_type: 'culture_and_competence_consulting' }); - + const apiData = apiResponse?.data as ServicePageData | undefined; useEffect(() => { @@ -207,13 +217,13 @@ export function CultureCompetence() { }, []); // Loading state - if (isLoading) { - return ( -
- -
- ); - } + if (isLoading) { + return ( +
+ +
+ ); + } // Error state if (error) { @@ -266,7 +276,7 @@ export function CultureCompetence() { const designationParts = testimonial.designation?.split(',') || []; const role = designationParts[0]?.trim() || ''; const company = designationParts[1]?.trim() || ''; - + return { id: parseInt(testimonial.id) || 0, name: testimonial.name || '', @@ -473,254 +483,254 @@ export function CultureCompetence() { {/* 4. Our Approach */}
-
-
-
-
- -

- {apiData?.approach_section?.title || "Our Approach to Leadership Development"} -

-

- {apiData?.approach_section?.description || "A systematic, research-backed methodology that transforms leadership potential into measurable business impact."} -

-
+
+
+
+
+ +

+ {apiData?.approach_section?.title || "Our Approach to Leadership Development"} +

+

+ {apiData?.approach_section?.description || "A systematic, research-backed methodology that transforms leadership potential into measurable business impact."} +

+
- {/* Flowchart Container with Connecting Lines */} -
- {/* Only render if approach_cards exist and have items */} - {apiData?.approach_section?.approach_cards && apiData.approach_section.approach_cards.length > 0 && ( - <> - {/* Desktop: Horizontal Flowchart */} -
-
- {/* Row 1: First 3 approach cards from API */} -
- {apiData.approach_section.approach_cards.slice(0, 3).map((card, idx) => { - const IconComponent = getIconComponent(card.icon_url); - return ( -
-
- {IconComponent ? : } -
-

{card.title}

-

{card.description}

- {card.bullets && card.bullets.length > 0 && ( -
- {card.bullets.slice(0, 3).map((bullet, bulletIdx) => ( -
- {bullet} + {/* Flowchart Container with Connecting Lines */} +
+ {/* Only render if approach_cards exist and have items */} + {apiData?.approach_section?.approach_cards && apiData.approach_section.approach_cards.length > 0 && ( + <> + {/* Desktop: Horizontal Flowchart */} +
+
+ {/* Row 1: First 3 approach cards from API */} +
+ {apiData.approach_section.approach_cards.slice(0, 3).map((card, idx) => { + const IconComponent = getIconComponent(card.icon_url); + return ( +
+
+ {IconComponent ? : }
- ))} +

{card.title}

+

{card.description}

+ {card.bullets && card.bullets.length > 0 && ( +
+ {card.bullets.slice(0, 3).map((bullet, bulletIdx) => ( +
+ {bullet} +
+ ))} +
+ )} +
+ ); + })} + + {/* Arrows between first 3 cards */} + {apiData.approach_section.approach_cards.length >= 2 && ( +
+
+ +
+ )} + {apiData.approach_section.approach_cards.length >= 3 && ( +
+
+
)}
- ); - })} - {/* Arrows between first 3 cards */} - {apiData.approach_section.approach_cards.length >= 2 && ( -
-
- -
- )} - {apiData.approach_section.approach_cards.length >= 3 && ( -
-
- -
- )} -
+ {/* Vertical Connector - Center Flow Down */} + {apiData.approach_section.approach_cards.length > 3 && ( +
+
+
+ +
+
+ )} - {/* Vertical Connector - Center Flow Down */} - {apiData.approach_section.approach_cards.length > 3 && ( -
-
-
- + {/* Row 2: Next 2 approach cards (if available) */} + {apiData.approach_section.approach_cards.length >= 4 && ( +
+ {apiData.approach_section.approach_cards.slice(3, 5).map((card, idx) => { + const IconComponent = getIconComponent(card.icon_url); + const isFirstOfPair = idx === 0; + return ( +
+
+ {IconComponent ? : } +
+

{card.title}

+

{card.description}

+ {card.bullets && card.bullets.length > 0 && ( +
+ {card.bullets.slice(0, 3).map((bullet, bulletIdx) => ( +
+ {bullet} +
+ ))} +
+ )} +
+ ); + })} + + {/* Arrow between the two cards */} + {apiData.approach_section.approach_cards.length >= 5 && ( +
+
+ +
+ )} +
+ )} + + {/* Final Vertical Connector - Center Flow Down to Outcome */} +
+
+
+ +
+
+ + {/* Row 3: Expected Outcome - Use API outcomes data */} +
+
+
+ {apiData.approach_section.outcomes && apiData.approach_section.outcomes[0] ? (() => { + const OutcomeIcon = getIconComponent(apiData.approach_section.outcomes[0].icon_url); + return OutcomeIcon ? : ; + })() : } +

+ {apiData.approach_section.outcomes?.[0]?.title || "Expected Outcome"} +

+
+

+ {apiData.approach_section.outcomes?.[0]?.description || "A systematic, measurable leadership pipeline that accelerates talent development and succession readiness."} +

+
+ {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) => ( +
+ + {bullet} +
+ )) + ) : ( +
+ + Proven ROI on Leadership Investment +
+ )} +
+
+
- )} - {/* Row 2: Next 2 approach cards (if available) */} - {apiData.approach_section.approach_cards.length >= 4 && ( -
- {apiData.approach_section.approach_cards.slice(3, 5).map((card, idx) => { + {/* Tablet & Mobile: Vertical Flowchart */} +
+ {/* Map all approach cards vertically */} + {apiData.approach_section.approach_cards.map((card, idx) => { const IconComponent = getIconComponent(card.icon_url); - const isFirstOfPair = idx === 0; + const isEven = idx % 2 === 0; return ( -
-
- {IconComponent ? : } +
+
+
+ {IconComponent ? : } +
+

{card.title}

+

{card.description}

+ {card.bullets && card.bullets.length > 0 && ( +
+ {card.bullets.map((bullet, bulletIdx) => ( +
+ {bullet} +
+ ))} +
+ )}
-

{card.title}

-

{card.description}

- {card.bullets && card.bullets.length > 0 && ( -
- {card.bullets.slice(0, 3).map((bullet, bulletIdx) => ( -
- {bullet} -
- ))} + {/* Connector Arrow */} + {idx < apiData.approach_section.approach_cards.length - 1 && ( +
+
)}
); })} - {/* Arrow between the two cards */} - {apiData.approach_section.approach_cards.length >= 5 && ( -
-
- + {/* Expected Outcome - Use API outcomes data */} +
+
+ {apiData.approach_section.outcomes && apiData.approach_section.outcomes[0] ? (() => { + const OutcomeIcon = getIconComponent(apiData.approach_section.outcomes[0].icon_url); + return OutcomeIcon ? : ; + })() : } +

+ {apiData.approach_section.outcomes?.[0]?.title || "Expected Outcome"} +

- )} -
- )} - - {/* Final Vertical Connector - Center Flow Down to Outcome */} -
-
-
- -
-
- - {/* Row 3: Expected Outcome - Use API outcomes data */} -
-
-
- {apiData.approach_section.outcomes && apiData.approach_section.outcomes[0] ? (() => { - const OutcomeIcon = getIconComponent(apiData.approach_section.outcomes[0].icon_url); - return OutcomeIcon ? : ; - })() : } -

- {apiData.approach_section.outcomes?.[0]?.title || "Expected Outcome"} -

-
-

- {apiData.approach_section.outcomes?.[0]?.description || "A systematic, measurable leadership pipeline that accelerates talent development and succession readiness."} -

-
- {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) => ( -
- - {bullet} -
- )) - ) : ( -
- - Proven ROI on Leadership Investment -
- )} -
-
-
-
-
- - {/* Tablet & Mobile: Vertical Flowchart */} -
- {/* 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 ( -
-
-
- {IconComponent ? : } -
-

{card.title}

-

{card.description}

- {card.bullets && card.bullets.length > 0 && ( -
- {card.bullets.map((bullet, bulletIdx) => ( -
- {bullet} +

+ {apiData.approach_section.outcomes?.[0]?.description || "A systematic, measurable leadership pipeline that accelerates talent development and succession readiness."} +

+
+ {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) => ( +
+ + {bullet}
- ))} -
- )} -
- {/* Connector Arrow */} - {idx < apiData.approach_section.approach_cards.length - 1 && ( -
- + )) + ) : ( +
+ + Proven ROI on Leadership Investment +
+ )}
- )} +
- ); - })} + + )} +
- {/* Expected Outcome - Use API outcomes data */} -
-
- {apiData.approach_section.outcomes && apiData.approach_section.outcomes[0] ? (() => { - const OutcomeIcon = getIconComponent(apiData.approach_section.outcomes[0].icon_url); - return OutcomeIcon ? : ; - })() : } -

- {apiData.approach_section.outcomes?.[0]?.title || "Expected Outcome"} -

+ {/* Framework Effectiveness - Stats Section */} + {apiData?.stats_section && apiData.stats_section.stat_cards && apiData.stats_section.stat_cards.length > 0 && ( +
+
+

{apiData.stats_section.title || "Framework Effectiveness"}

+

+ {apiData.stats_section.description || "Measurable results that demonstrate the impact of our leadership development approach."} +

-

- {apiData.approach_section.outcomes?.[0]?.description || "A systematic, measurable leadership pipeline that accelerates talent development and succession readiness."} -

-
- {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) => ( -
- - {bullet} + +
+ {apiData.stats_section.stat_cards.map((stat) => { + const IconComponent = getIconComponent(stat.icon_url); + return ( +
+
+ {IconComponent ? : } +
+
{stat.value || "0"}
+

{stat.label || ""}

- )) - ) : ( -
- - Proven ROI on Leadership Investment -
- )} + ); + })}
-
- - )} -
- - {/* Framework Effectiveness - Stats Section */} - {apiData?.stats_section && apiData.stats_section.stat_cards && apiData.stats_section.stat_cards.length > 0 && ( -
-
-

{apiData.stats_section.title || "Framework Effectiveness"}

-

- {apiData.stats_section.description || "Measurable results that demonstrate the impact of our leadership development approach."} -

-
- -
- {apiData.stats_section.stat_cards.map((stat) => { - const IconComponent = getIconComponent(stat.icon_url); - return ( -
-
- {IconComponent ? : } -
-
{stat.value || "0"}
-

{stat.label || ""}

-
- ); - })} + )}
- )} -
-
-
-
+
+
{/* 5. Sample Program Format */}
@@ -870,41 +880,45 @@ export function CultureCompetence() { /> {/* 8. CTA Section */} -
-
- -
-
-
- -
-
- -

- Ready to transform your culture? - - {" "}Get in touch{" "} - - to align strategy and performance. -

- navigateTo('/contact?topic=culture-competence')} - ariaLabel="Schedule a culture and competence consultation" + {apiData.cta_section && ( +
+
+ -

- Connect with our culture transformation experts to discuss building a high-performance culture aligned with your business strategy. -

+
+
-
-
+ +
+
+ +

+ {apiData.cta_section.text} + + {" "}Get in touch{" "} + + to align strategy and performance. +

+ navigateTo(apiData.cta_section.cta_destination)} + ariaLabel={apiData.cta_section.cta_text} + /> + {apiData.cta_section.description && ( +

+ {apiData.cta_section.description} +

+ )} +
+
+
+ )}
); } \ No newline at end of file diff --git a/src/components/services/ExecutiveCoaching.tsx b/src/components/services/ExecutiveCoaching.tsx index c48845e..17c5917 100644 --- a/src/components/services/ExecutiveCoaching.tsx +++ b/src/components/services/ExecutiveCoaching.tsx @@ -169,6 +169,16 @@ interface ServicePageData { 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 @@ -191,18 +201,18 @@ const getIconComponent = (iconUrl: string) => { '/icons/network.svg': Network, '/icons/user.svg': User, }; - + return iconMap[iconUrl] || MessageCircle; }; export function ExecutiveCoaching() { const [expandedUseCase, setExpandedUseCase] = useState(null); const [expandedPhase, setExpandedPhase] = useState(0); - - const { data: apiResponse, isLoading, error } = useGetServiceListQuery({ - service_type: 'coaching_and_mentoring' + + const { data: apiResponse, isLoading, error } = useGetServiceListQuery({ + service_type: 'coaching_and_mentoring' }); - + const apiData = apiResponse?.data as ServicePageData | undefined; useEffect(() => { @@ -210,13 +220,13 @@ export function ExecutiveCoaching() { }, []); // Loading state - if (isLoading) { - return ( -
- -
- ); - } + if (isLoading) { + return ( +
+ +
+ ); + } // Error state if (error) { @@ -269,7 +279,7 @@ export function ExecutiveCoaching() { const designationParts = testimonial.designation?.split(',') || []; const role = designationParts[0]?.trim() || ''; const company = designationParts[1]?.trim() || ''; - + return { id: parseInt(testimonial.id) || 0, name: testimonial.name || '', @@ -482,254 +492,254 @@ export function ExecutiveCoaching() { {/* 4. Our Approach */}
-
-
-
-
- -

- {apiData?.approach_section?.title || "Our Approach to Leadership Development"} -

-

- {apiData?.approach_section?.description || "A systematic, research-backed methodology that transforms leadership potential into measurable business impact."} -

-
+
+
+
+
+ +

+ {apiData?.approach_section?.title || "Our Approach to Leadership Development"} +

+

+ {apiData?.approach_section?.description || "A systematic, research-backed methodology that transforms leadership potential into measurable business impact."} +

+
- {/* Flowchart Container with Connecting Lines */} -
- {/* Only render if approach_cards exist and have items */} - {apiData?.approach_section?.approach_cards && apiData.approach_section.approach_cards.length > 0 && ( - <> - {/* Desktop: Horizontal Flowchart */} -
-
- {/* Row 1: First 3 approach cards from API */} -
- {apiData.approach_section.approach_cards.slice(0, 3).map((card, idx) => { - const IconComponent = getIconComponent(card.icon_url); - return ( -
-
- {IconComponent ? : } -
-

{card.title}

-

{card.description}

- {card.bullets && card.bullets.length > 0 && ( -
- {card.bullets.slice(0, 3).map((bullet, bulletIdx) => ( -
- {bullet} + {/* Flowchart Container with Connecting Lines */} +
+ {/* Only render if approach_cards exist and have items */} + {apiData?.approach_section?.approach_cards && apiData.approach_section.approach_cards.length > 0 && ( + <> + {/* Desktop: Horizontal Flowchart */} +
+
+ {/* Row 1: First 3 approach cards from API */} +
+ {apiData.approach_section.approach_cards.slice(0, 3).map((card, idx) => { + const IconComponent = getIconComponent(card.icon_url); + return ( +
+
+ {IconComponent ? : }
- ))} +

{card.title}

+

{card.description}

+ {card.bullets && card.bullets.length > 0 && ( +
+ {card.bullets.slice(0, 3).map((bullet, bulletIdx) => ( +
+ {bullet} +
+ ))} +
+ )} +
+ ); + })} + + {/* Arrows between first 3 cards */} + {apiData.approach_section.approach_cards.length >= 2 && ( +
+
+ +
+ )} + {apiData.approach_section.approach_cards.length >= 3 && ( +
+
+
)}
- ); - })} - {/* Arrows between first 3 cards */} - {apiData.approach_section.approach_cards.length >= 2 && ( -
-
- -
- )} - {apiData.approach_section.approach_cards.length >= 3 && ( -
-
- -
- )} -
+ {/* Vertical Connector - Center Flow Down */} + {apiData.approach_section.approach_cards.length > 3 && ( +
+
+
+ +
+
+ )} - {/* Vertical Connector - Center Flow Down */} - {apiData.approach_section.approach_cards.length > 3 && ( -
-
-
- + {/* Row 2: Next 2 approach cards (if available) */} + {apiData.approach_section.approach_cards.length >= 4 && ( +
+ {apiData.approach_section.approach_cards.slice(3, 5).map((card, idx) => { + const IconComponent = getIconComponent(card.icon_url); + const isFirstOfPair = idx === 0; + return ( +
+
+ {IconComponent ? : } +
+

{card.title}

+

{card.description}

+ {card.bullets && card.bullets.length > 0 && ( +
+ {card.bullets.slice(0, 3).map((bullet, bulletIdx) => ( +
+ {bullet} +
+ ))} +
+ )} +
+ ); + })} + + {/* Arrow between the two cards */} + {apiData.approach_section.approach_cards.length >= 5 && ( +
+
+ +
+ )} +
+ )} + + {/* Final Vertical Connector - Center Flow Down to Outcome */} +
+
+
+ +
+
+ + {/* Row 3: Expected Outcome - Use API outcomes data */} +
+
+
+ {apiData.approach_section.outcomes && apiData.approach_section.outcomes[0] ? (() => { + const OutcomeIcon = getIconComponent(apiData.approach_section.outcomes[0].icon_url); + return OutcomeIcon ? : ; + })() : } +

+ {apiData.approach_section.outcomes?.[0]?.title || "Expected Outcome"} +

+
+

+ {apiData.approach_section.outcomes?.[0]?.description || "A systematic, measurable leadership pipeline that accelerates talent development and succession readiness."} +

+
+ {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) => ( +
+ + {bullet} +
+ )) + ) : ( +
+ + Proven ROI on Leadership Investment +
+ )} +
+
+
- )} - {/* Row 2: Next 2 approach cards (if available) */} - {apiData.approach_section.approach_cards.length >= 4 && ( -
- {apiData.approach_section.approach_cards.slice(3, 5).map((card, idx) => { + {/* Tablet & Mobile: Vertical Flowchart */} +
+ {/* Map all approach cards vertically */} + {apiData.approach_section.approach_cards.map((card, idx) => { const IconComponent = getIconComponent(card.icon_url); - const isFirstOfPair = idx === 0; + const isEven = idx % 2 === 0; return ( -
-
- {IconComponent ? : } +
+
+
+ {IconComponent ? : } +
+

{card.title}

+

{card.description}

+ {card.bullets && card.bullets.length > 0 && ( +
+ {card.bullets.map((bullet, bulletIdx) => ( +
+ {bullet} +
+ ))} +
+ )}
-

{card.title}

-

{card.description}

- {card.bullets && card.bullets.length > 0 && ( -
- {card.bullets.slice(0, 3).map((bullet, bulletIdx) => ( -
- {bullet} -
- ))} + {/* Connector Arrow */} + {idx < apiData.approach_section.approach_cards.length - 1 && ( +
+
)}
); })} - {/* Arrow between the two cards */} - {apiData.approach_section.approach_cards.length >= 5 && ( -
-
- + {/* Expected Outcome - Use API outcomes data */} +
+
+ {apiData.approach_section.outcomes && apiData.approach_section.outcomes[0] ? (() => { + const OutcomeIcon = getIconComponent(apiData.approach_section.outcomes[0].icon_url); + return OutcomeIcon ? : ; + })() : } +

+ {apiData.approach_section.outcomes?.[0]?.title || "Expected Outcome"} +

- )} -
- )} - - {/* Final Vertical Connector - Center Flow Down to Outcome */} -
-
-
- -
-
- - {/* Row 3: Expected Outcome - Use API outcomes data */} -
-
-
- {apiData.approach_section.outcomes && apiData.approach_section.outcomes[0] ? (() => { - const OutcomeIcon = getIconComponent(apiData.approach_section.outcomes[0].icon_url); - return OutcomeIcon ? : ; - })() : } -

- {apiData.approach_section.outcomes?.[0]?.title || "Expected Outcome"} -

-
-

- {apiData.approach_section.outcomes?.[0]?.description || "A systematic, measurable leadership pipeline that accelerates talent development and succession readiness."} -

-
- {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) => ( -
- - {bullet} -
- )) - ) : ( -
- - Proven ROI on Leadership Investment -
- )} -
-
-
-
-
- - {/* Tablet & Mobile: Vertical Flowchart */} -
- {/* 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 ( -
-
-
- {IconComponent ? : } -
-

{card.title}

-

{card.description}

- {card.bullets && card.bullets.length > 0 && ( -
- {card.bullets.map((bullet, bulletIdx) => ( -
- {bullet} +

+ {apiData.approach_section.outcomes?.[0]?.description || "A systematic, measurable leadership pipeline that accelerates talent development and succession readiness."} +

+
+ {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) => ( +
+ + {bullet}
- ))} -
- )} -
- {/* Connector Arrow */} - {idx < apiData.approach_section.approach_cards.length - 1 && ( -
- + )) + ) : ( +
+ + Proven ROI on Leadership Investment +
+ )}
- )} +
- ); - })} + + )} +
- {/* Expected Outcome - Use API outcomes data */} -
-
- {apiData.approach_section.outcomes && apiData.approach_section.outcomes[0] ? (() => { - const OutcomeIcon = getIconComponent(apiData.approach_section.outcomes[0].icon_url); - return OutcomeIcon ? : ; - })() : } -

- {apiData.approach_section.outcomes?.[0]?.title || "Expected Outcome"} -

+ {/* Framework Effectiveness - Stats Section */} + {apiData?.stats_section && apiData.stats_section.stat_cards && apiData.stats_section.stat_cards.length > 0 && ( +
+
+

{apiData.stats_section.title || "Framework Effectiveness"}

+

+ {apiData.stats_section.description || "Measurable results that demonstrate the impact of our leadership development approach."} +

-

- {apiData.approach_section.outcomes?.[0]?.description || "A systematic, measurable leadership pipeline that accelerates talent development and succession readiness."} -

-
- {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) => ( -
- - {bullet} + +
+ {apiData.stats_section.stat_cards.map((stat) => { + const IconComponent = getIconComponent(stat.icon_url); + return ( +
+
+ {IconComponent ? : } +
+
{stat.value || "0"}
+

{stat.label || ""}

- )) - ) : ( -
- - Proven ROI on Leadership Investment -
- )} + ); + })}
-
- - )} -
- - {/* Framework Effectiveness - Stats Section */} - {apiData?.stats_section && apiData.stats_section.stat_cards && apiData.stats_section.stat_cards.length > 0 && ( -
-
-

{apiData.stats_section.title || "Framework Effectiveness"}

-

- {apiData.stats_section.description || "Measurable results that demonstrate the impact of our leadership development approach."} -

-
- -
- {apiData.stats_section.stat_cards.map((stat) => { - const IconComponent = getIconComponent(stat.icon_url); - return ( -
-
- {IconComponent ? : } -
-
{stat.value || "0"}
-

{stat.label || ""}

-
- ); - })} + )}
- )} -
-
-
-
+
+
{/* 5. Sample Program Format */}
@@ -879,41 +889,45 @@ export function ExecutiveCoaching() { /> {/* 8. CTA Section */} -
-
- -
-
-
- -
-
- -

- Ready to accelerate your leadership? - - {" "}Get in touch{" "} - - for personalized coaching now. -

- navigateTo('/contact?topic=coaching-mentoring')} - ariaLabel="Schedule a coaching and mentoring consultation" + {apiData.cta_section && ( +
+
+ -

- Connect with our executive coaching experts to discuss personalized leadership development that transforms your effectiveness and career trajectory. -

+
+
-
-
+ +
+
+ +

+ {apiData.cta_section.text} + + {" "}Get in touch{" "} + + for personalized coaching now. +

+ navigateTo(apiData.cta_section.cta_destination)} + ariaLabel={apiData.cta_section.cta_text} + /> + {apiData.cta_section.description && ( +

+ {apiData.cta_section.description} +

+ )} +
+
+
+ )}
); } \ No newline at end of file diff --git a/src/components/services/LeadershipDevelopment.tsx b/src/components/services/LeadershipDevelopment.tsx index b7e87e2..b634a16 100644 --- a/src/components/services/LeadershipDevelopment.tsx +++ b/src/components/services/LeadershipDevelopment.tsx @@ -178,6 +178,16 @@ interface ServicePageData { 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 @@ -217,13 +227,13 @@ export function LeadershipDevelopment() { window.scrollTo(0, 0); }, []); - if (isLoading) { - return ( -
- -
- ); - } + if (isLoading) { + return ( +
+ +
+ ); + } if (error || !apiData) { return ( @@ -858,41 +868,45 @@ export function LeadershipDevelopment() { /> {/* 8. CTA Section */} -
-
- -
-
-
- -
-
- -

- Ready to transform your leadership? - - {" "}Get in touch{" "} - - to drive exceptional strategic outcomes. -

- navigateTo('/contact?topic=leadership-development')} - ariaLabel="Schedule a leadership development consultation" + {apiData.cta_section && ( +
+
+ -

- Connect with our leadership development experts to discuss building visionary leadership capability in your organization. -

+
+
-
-
+ +
+
+ +

+ {apiData.cta_section.text} + + {" "}Get in touch{" "} + + to drive exceptional strategic outcomes. +

+ navigateTo(apiData.cta_section.cta_destination)} + ariaLabel={apiData.cta_section.cta_text} + /> + {apiData.cta_section.description && ( +

+ {apiData.cta_section.description} +

+ )} +
+
+
+ )}
); } \ No newline at end of file diff --git a/src/components/services/LeadershipPipelineDevelopment.tsx b/src/components/services/LeadershipPipelineDevelopment.tsx index 039f0fb..ebaf2b3 100644 --- a/src/components/services/LeadershipPipelineDevelopment.tsx +++ b/src/components/services/LeadershipPipelineDevelopment.tsx @@ -178,6 +178,16 @@ interface ServicePageData { 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 @@ -882,41 +892,45 @@ export function LeadershipPipelineDevelopment() { /> {/* 8. CTA Section */} -
-
- -
-
-
- -
-
- -

- Ready to build your leadership pipeline? - - {" "}Get in touch{" "} - - to eliminate succession gaps now. -

- navigateTo('/contact?topic=leadership-pipeline')} - ariaLabel="Schedule a leadership pipeline consultation" + {apiData.cta_section && ( +
+
+ -

- Connect with our pipeline development experts to discuss building sustainable leadership capability across your organization. -

+
+
-
-
+ +
+
+ +

+ {apiData.cta_section.text} + + {" "}Get in touch{" "} + + to eliminate succession gaps now. +

+ navigateTo(apiData.cta_section.cta_destination)} + ariaLabel={apiData.cta_section.cta_text} + /> + {apiData.cta_section.description && ( +

+ {apiData.cta_section.description} +

+ )} +
+
+
+ )}
); } \ No newline at end of file diff --git a/src/components/services/ManagementDevelopment.tsx b/src/components/services/ManagementDevelopment.tsx index 3b55592..c5bb963 100644 --- a/src/components/services/ManagementDevelopment.tsx +++ b/src/components/services/ManagementDevelopment.tsx @@ -165,6 +165,16 @@ interface ServicePageData { 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 @@ -868,41 +878,45 @@ export function ManagementDevelopment() { /> {/* 8. CTA Section */} -
-
- -
-
-
- -
-
- -

- Ready to build exceptional managers? - - {" "}Get in touch{" "} - - to drive team performance now. -

- navigateTo('/contact?topic=management-development')} - ariaLabel="Schedule a management development consultation" + {apiData.cta_section && ( +
+
+ -

- Connect with our management development experts to discuss building exceptional managers who drive team engagement and results. -

+
+
-
-
+ +
+
+ +

+ {apiData.cta_section.text} + + {" "}Get in touch{" "} + + to drive team performance now. +

+ navigateTo(apiData.cta_section.cta_destination)} + ariaLabel={apiData.cta_section.cta_text} + /> + {apiData.cta_section.description && ( +

+ {apiData.cta_section.description} +

+ )} +
+
+
+ )} ); } \ No newline at end of file diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx index 903d7cc..220677e 100644 --- a/src/components/ui/dialog.tsx +++ b/src/components/ui/dialog.tsx @@ -1,8 +1,8 @@ "use client"; import * as React from "react"; -import * as DialogPrimitive from "@radix-ui/react-dialog@1.1.6"; -import { XIcon } from "lucide-react@0.487.0"; +import * as DialogPrimitive from "@radix-ui/react-dialog"; +import { XIcon } from "lucide-react"; import { cn } from "./utils"; diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx index 109c214..2ac17f1 100644 --- a/src/pages/HomePage.tsx +++ b/src/pages/HomePage.tsx @@ -25,6 +25,19 @@ const HomePage: React.FC = () => { 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 ( @@ -101,9 +114,15 @@ const HomePage: React.FC = () => { - + {/* Pass testimonial data to the TestimonialsSection */} + - + ); }; diff --git a/src/redux/services/aboutUsApi.ts b/src/redux/services/aboutUsApi.ts index 965c225..93a0b18 100644 --- a/src/redux/services/aboutUsApi.ts +++ b/src/redux/services/aboutUsApi.ts @@ -47,6 +47,16 @@ export interface TeamMember { alt_text: string; bio: string; } +export interface CtaData { + id: string; + background_image_url: string; + text: string; + cta_text: string; + cta_destination: string; + description: string; + landing_page_type: string; + service_type: string | null; +} export interface AboutUsData { hero_section: HeroSection; @@ -61,6 +71,7 @@ export interface AboutUsData { methodology: Methodology; philosophy: Philosophy; testimonials: Testimonial[]; + cta_section: CtaData; } export interface Methodology { diff --git a/src/redux/services/courseApi.ts b/src/redux/services/courseApi.ts index c3b260e..490fac0 100644 --- a/src/redux/services/courseApi.ts +++ b/src/redux/services/courseApi.ts @@ -67,6 +67,196 @@ export interface CourseListResponse { }; } +export interface CourseReview { + id: string; + rating: number; + comment: string; + video_url: string | null; + reviewer_name: string; + profile_image: string | null; + bio: string | null; + created_at: string; +} + +export interface CourseFaq { + id: string; + question: string; + answer: string; +} + +export interface CourseTargetAudience { + id: string; + course_xid: string; + course_icon_xid: string; + title: string; + description: string; + display_order: number; +} + +export interface CourseLearningOutcome { + id: string; + course_xid: string; + title: string; + description: string; + display_order: number; +} + +export interface CourseLearningStructure { + id: string; + course_xid: string; + title: string; + description: string; + display_order: number; +} + +export interface CourseFacultyCredential { + id: string; + course_xid: string; + course_faculty_xid: string; + credential_name: string; + display_order: number; +} + +export interface CourseFaculty { + id: string; + course_xid: string; + faculty_name: string; + faculty_title: string; + faculty_organization_name: string; + faculty_biography: string; + display_order: number; + expertises: string[] | null; + credentials: CourseFacultyCredential[]; +} + +export interface CourseLessonResource { + id: string; + course_xid: string; + module_xid: string; + lesson_xid: string; + content_xid: string; + content_type_xid: string; + content_title: string; + total_duration: number | null; + content_type_name: string; + is_active: boolean; +} + +export interface CourseLesson { + id: string; + course_xid: string; + module_xid: string; + lesson_title: string; + lesson_description: string; + is_lock_lesson: boolean; + display_order: number; + lesson_resources: CourseLessonResource[]; +} + +export interface CourseModule { + id: string; + course_xid: string; + module_name: string; + display_order: number; + lessons: CourseLesson[]; +} + +export interface CourseResource { + id: string; + course_xid: string; + content_xid: string; + content_type_xid: string; + display_order: number | null; +} + +export interface CourseCertificateTemplate { + id: string; + template_name: string; + template_code: string; + display_order: number; + is_active: boolean; +} + +export interface CourseCertificate { + id: string; + course_xid: string; + certificate_template_xid: string; + company_logo_url: string | null; + institution_name: string; + program_title: string; + signatory_name: string; + signatory_title: string; + digital_signature_url: string | null; + minimum_pass_percentage: number; + complete_all_lesson_required: boolean; + certificate_template: CourseCertificateTemplate; +} + +export interface RecommendedCourse { + id: string; + course_name: string; + course_desc: string; + thumbnail_img: string; + price: string; + best_value: number; + duration: number; + recommended_course_reviews: { + id: string; + rating: number; + comment: string; + reviewer_name: string; + }[]; +} + +export interface CourseDetail { + id: string; + course_name: string; + course_desc: string; + thumbnail_img: string; + course_category_xid: string; + duration: number; + retail_type: string; + price: string | number; + best_value: number; + target_audience_desc: string | null; + learning_outcomes_desc: string | null; + learning_structure_desc: string | null; + our_methodology_desc: string | null; + is_certificate_available: boolean; + course_status: CourseStatus; + benefit_section: string | null; + learning_section: string | null; + structure_section: string | null; + approach_section: string | null; + faculty_section: string | null; + avg_rating: number; + total_reviews: number; + reviews: CourseReview[]; + course_faqs: CourseFaq[]; + total_modules: number; + total_lessons: number; + formatted_duration: string; + course_category_name: string; + course_language_xids: string[]; + course_target_audiences: CourseTargetAudience[]; + course_learning_outcomes: CourseLearningOutcome[]; + course_learning_structures: CourseLearningStructure[]; + course_facilities: CourseFaculty[]; + modules: CourseModule[]; + course_resources: CourseResource[]; + course_certificate: CourseCertificate | null; + recommended_courses: RecommendedCourse[]; +} + +export interface CourseDetailResponse { + success: boolean; + status: number; + message: string; + data: CourseDetail; + errors: unknown; + correlation_id: string; +} + /* ================= PREPOPULATE TYPES ================= */ export interface CourseCategory { @@ -163,7 +353,19 @@ export const courseApi = createApi({ }, providesTags: ["CourseCategories"], }), + + // GET Course By Id + getcoursebyid: builder.query({ + query: (course_id) => `admin/course/${course_id}`, + providesTags: (_result, _error, course_id) => [{ type: "Course", id: course_id }], + }), + + }), }); -export const { useGetCoursesQuery, useGetCourseCategoriesQuery } = courseApi; \ No newline at end of file +export const { + useGetCoursesQuery, + useGetCourseCategoriesQuery, + useGetcoursebyidQuery, +} = courseApi; diff --git a/src/redux/services/homepageApi.ts b/src/redux/services/homepageApi.ts index 817150e..3bf35b8 100644 --- a/src/redux/services/homepageApi.ts +++ b/src/redux/services/homepageApi.ts @@ -28,6 +28,7 @@ export interface StatItem { /* ================= HIGHLIGHT CARD ================= */ export interface HighlightCard { + id?: string; card_title: string; icon_url: string; accessible_label: string; @@ -46,6 +47,31 @@ export interface CtaBand { cta_destination: string; } +/* ================= TESTIMONIAL TYPES ================= */ + +export interface TestimonialItem { + id: string; + profile_xid: string; + name: string; + designation: string; + content: string; + video_url: string | null; + display_order: number; +} + +/* ================= CTA SECTION TYPES ================= */ + +export interface 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; +} + /* ================= RESPONSE ================= */ export interface HomePageResponse { @@ -57,6 +83,8 @@ export interface HomePageResponse { stats_sections: StatItem[]; highlight_cards: HighlightCard[]; cta_bands: CtaBand[]; + cta_section: CtaSection; + testimonial_section: TestimonialItem[]; }; errors: any; correlation_id: string; diff --git a/src/redux/services/learningFacilityApi.ts b/src/redux/services/learningFacilityApi.ts new file mode 100644 index 0000000..aa9fbd5 --- /dev/null +++ b/src/redux/services/learningFacilityApi.ts @@ -0,0 +1,122 @@ +import { createApi } from "@reduxjs/toolkit/query/react"; +import baseQueryWithReauth from "./baseQuery"; + +export interface KautilyaPageResponse { + success: boolean; + status: number; + message: string; + data: { + hero_sections: { + id: string; + background_image_url: string; + background_image_alt_text: string; + headline: string; + subtext: string; + cta_text: string; + cta_destination: string; + }; + our_story: { + id: string; + tag: string; + title: string; + content: string; + image_url: string; + }; + why_choose_us: { + id: string; + tag: string; + title: string; + description: string; + cards: Array<{ + id: string; + title: string; + description: string; + image_url: string; + icon: string; + display_order: number; + bullets: Array<{ + id: string; + text: string; + }>; + }>; + }; + facility_features: { + id: string; + title: string; + description: string; + features: Array<{ + id: string; + title: string; + description: string; + image_url: string; + sub_title: string; + sub_description: string; + display_order: number; + points: Array<{ + id: string; + text: string; + }>; + }>; + }; + visual_tour: { + id: string; + title: string; + description: string; + categories: Array<{ + id: string; + name: string; + display_order: number; + images: Array<{ + id: string; + image_url: string; + title: string; + subtitle: string; + display_order: number; + }>; + }>; + }; + daily_experience: { + id: string; + title: string; + description: string; + items: Array<{ + id: string; + label: string; + title: string; + description: string; + image_url: string; + display_order: number; + }>; + }; + cta_section: { + id: string; + background_image_url: string; + text: string; + cta_text: string; + cta_destination: string; + description: string; + }; + }; + errors: any; + correlation_id: string; +} + +export const learningFacilityApi = createApi({ + reducerPath: "learningFacilityApi", + baseQuery: baseQueryWithReauth, + tagTypes: ["KautilyaPage"], + endpoints: (builder) => ({ + getKautilyaPage: builder.query< + KautilyaPageResponse["data"], + { } + >({ + query: ({ }) => ({ + url: "/admin/kautilya-page/get", + }), + transformResponse: (response: KautilyaPageResponse) => response.data, + providesTags: [{ type: "KautilyaPage", id: "LIST" }], + }), + }), +}); + +export const { useGetKautilyaPageQuery } = learningFacilityApi; \ No newline at end of file diff --git a/src/redux/services/webinarApi.ts b/src/redux/services/webinarApi.ts new file mode 100644 index 0000000..4aeb897 --- /dev/null +++ b/src/redux/services/webinarApi.ts @@ -0,0 +1,128 @@ +import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"; +import baseQueryWithReauth from "./baseQuery"; + +/* ================= TYPES ================= */ + +export interface WebinarItem { + id: string; + session_title: string; + description: string | null; + session_datetime: string; + duration_minutes: number; + timezone_xid: string; + max_attendee: number; + passcode: string; + require_registration: boolean; + recurring_webinar: boolean; + webinar_status: "scheduled" | "live" | "ended" | "cancelled"; + owner?: string; +} + +export interface WebinarListData { + total: number; + limit: number; + offset: number; + items: WebinarItem[]; +} + +export interface WebinarListResponse { + success: boolean; + status: number; + message: string; + data: WebinarListData; + errors: any; + correlation_id: string; +} + +/* ================= QUERY PARAM TYPE ================= */ + +export interface WebinarListParams { + limit?: number; + offset?: number; + search?: string; + status?: string[]; // ✅ multiple status + fromDate?: string; + toDate?: string; + minDuration?: number; + maxDuration?: number; + minAttendees?: number; // ✅ NEW + maxAttendees?: number; // ✅ NEW + sortBy?: "most_popular" | "newest" | "oldest" | "title" | "duration"; +} + +/* ================= API ================= */ + +export const webinarApi = createApi({ + reducerPath: "webinarApi", + baseQuery: baseQueryWithReauth, + tagTypes: ["Webinar"], + + endpoints: (builder) => ({ + webinarList: builder.query({ + query: ({ + limit = 10, + offset = 0, + search, + status, + fromDate, + toDate, + minDuration, + maxDuration, + minAttendees, + maxAttendees, + sortBy, + }) => { + const params = new URLSearchParams(); + + params.append("limit", String(limit)); + params.append("offset", String(offset)); + + if (search) { + params.append("search_term", search); // ✅ FIXED NAME + } + + if (status && status.length > 0) { + status.forEach((s) => + params.append("session_status", s) // ✅ array support + ); + } + + if (fromDate) { + params.append("from_date", fromDate); + } + + if (toDate) { + params.append("to_date", toDate); + } + + if (minDuration !== undefined) { + params.append("min_duration", String(minDuration)); + } + + if (maxDuration !== undefined) { + params.append("max_duration", String(maxDuration)); + } + + if (minAttendees !== undefined) { + params.append("min_attendee", String(minAttendees)); // ✅ NEW + } + + if (maxAttendees !== undefined) { + params.append("max_attendee", String(maxAttendees)); // ✅ NEW + } + + if (sortBy) { + params.append("sort_by", sortBy); + } + + return `/admin/webinars/list?${params.toString()}`; + }, + + providesTags: ["Webinar"], + }), + }), +}); + +/* ================= EXPORT HOOK ================= */ + +export const { useWebinarListQuery } = webinarApi; \ No newline at end of file diff --git a/src/redux/store/Store.tsx b/src/redux/store/Store.tsx index 5478372..42dbeaf 100644 --- a/src/redux/store/Store.tsx +++ b/src/redux/store/Store.tsx @@ -6,6 +6,8 @@ import { blogApi } from "../services/blogApi"; import { aboutUsApi } from "../services/aboutUsApi"; import { sercicesApi } from "../services/sercicesApi"; import { courseApi } from "../services/courseApi"; +import { learningFacilityApi } from "../services/learningFacilityApi"; +import { webinarApi } from "../services/webinarApi"; export const store = configureStore({ reducer: { @@ -16,6 +18,9 @@ export const store = configureStore({ [aboutUsApi.reducerPath]: aboutUsApi.reducer, [sercicesApi.reducerPath]: sercicesApi.reducer, [courseApi.reducerPath]: courseApi.reducer, + [learningFacilityApi.reducerPath]: learningFacilityApi.reducer, + [webinarApi.reducerPath]: webinarApi.reducer, + }, middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat( @@ -26,6 +31,8 @@ export const store = configureStore({ aboutUsApi.middleware, sercicesApi.middleware, courseApi.middleware, + learningFacilityApi.middleware, + webinarApi.middleware, ), }); diff --git a/src/styles/globals.css b/src/styles/globals.css index 57e4aee..0fa769a 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -167,6 +167,9 @@ /* Small text */ --font-small: 0.875rem; /* 14px */ + /* Extra small text */ + --font-extra-small: 0.75rem; + /* 12px */ --line-height-small: 1.5; --font-weight-small: 400; @@ -601,6 +604,14 @@ html { color: var(--color-black); } + .text-small-extra { + font-size: var(--font-extra-small); + line-height: var(--line-height-small); + font-weight: var(--font-weight-small); + font-family: var(--font-family-base); + color: var(--color-black); + } + .text-eyebrow { font-size: var(--font-eyebrow); line-height: var(--line-height-eyebrow); @@ -5294,4 +5305,31 @@ html { font-size: calc(var(--font-h2) * 0.8); /* reduce by 20% */ line-height: calc(var(--line-height-h2) * 0.9); } -} \ No newline at end of file +} + + .custom-scrollbar { + scrollbar-width: thin; + scrollbar-color: transparent transparent; + } + + .custom-scrollbar::-webkit-scrollbar { + width: 6px; + } + + .custom-scrollbar::-webkit-scrollbar-track { + background: transparent; + } + + .custom-scrollbar::-webkit-scrollbar-thumb { + background-color: transparent; + border-radius: 20px; + } + + /* Optional: Show scrollbar only on hover */ + .custom-scrollbar:hover::-webkit-scrollbar-thumb { + background-color: rgba(0, 0, 0, 0.2); + } + + .custom-scrollbar:hover { + scrollbar-color: rgba(0, 0, 0, 0.2) transparent; + } \ No newline at end of file