@@ -31,7 +31,6 @@ import { WebinarsPage } from "./components/WebinarsPage";
|
||||
import HomePage from './pages/HomePage';
|
||||
import { AboutUs } from './components/AboutUs';
|
||||
import { Services } from './components/Services';
|
||||
import { LearningFacilityNew } from './components/LearningFacilityNew';
|
||||
import { FooterNew } from './components/FooterNew';
|
||||
import { Privacy } from "./pages/Privacy";
|
||||
import { TermsCondition } from "./pages/TermsCondition";
|
||||
@@ -39,6 +38,8 @@ import { FAQ } from "./pages/FAQ";
|
||||
import { LeadershipPipelineDevelopment } from "./components/services/LeadershipPipelineDevelopment";
|
||||
import { LeadershipDevelopment } from "./components/services/LeadershipDevelopment";
|
||||
import { KautilyaFacility } from "./components/KautilyaFacility";
|
||||
import { LearningFacilityPage } from "./components/LearningFacilityPage";
|
||||
import WebinarDetail from "./components/WebinarDetail";
|
||||
// import EnrollPlaceholder from "./components/EnrollPlaceholder";
|
||||
// import ForgotPasswordPlaceholder from "./components/ForgotPasswordPlaceholder";
|
||||
// import DashboardPlaceholder from "./components/DashboardPlaceholder";
|
||||
@@ -85,6 +86,7 @@ export default function App() {
|
||||
|
||||
{/* Webinars Pages */}
|
||||
<Route path="/webinars" element={<WebinarsPage />} />
|
||||
<Route path="/webinars/:webinar_id" element={<WebinarDetail />} />
|
||||
<Route path="/webinars-legacy" element={<WebinarsListing />} />
|
||||
|
||||
{/* Learning Online */}
|
||||
@@ -115,7 +117,7 @@ export default function App() {
|
||||
<Route path="/programme/:slug" element={<ProgrammeDetail />} />
|
||||
|
||||
{/* Learning Facility */}
|
||||
<Route path="/learning-facility" element={<LearningFacilityNew />} />
|
||||
<Route path="/learning-facility" element={<LearningFacilityPage />} />
|
||||
{/* Privacy policy */}
|
||||
<Route path="/privacy-policy" element={<Privacy />} />
|
||||
<Route path="/term-condition" element={<TermsCondition />} />
|
||||
|
||||
@@ -12,8 +12,8 @@ interface CTABannerSectionProps {
|
||||
cta_text: string;
|
||||
cta_destination: string;
|
||||
description: string;
|
||||
landing_page_type: string;
|
||||
service_type: string | null;
|
||||
landing_page_type?: string;
|
||||
service_type?: string | null;
|
||||
};
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
@@ -104,7 +104,10 @@ export function CourseCard({ course, onClick, className, onAddToCart }: CourseCa
|
||||
fontFamily: 'var(--font-family-base)'
|
||||
}}
|
||||
>
|
||||
{course.title}
|
||||
{course.title
|
||||
?.split(' ')
|
||||
.slice(0, 3)
|
||||
.join(' ') + (course.title.split(' ').length > 3 ? '...' : '')}
|
||||
</h3>
|
||||
|
||||
{/* Course Description - Limited to 2 lines with ellipsis */}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
2106
src/components/LearningFacilityPage.tsx
Normal file
2106
src/components/LearningFacilityPage.tsx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -445,13 +445,13 @@ export function ProgrammeDetail({ slug }: ProgrammeDetailProps) {
|
||||
courseDetail.is_certificate_available ? 'Certified' : 'Non-Certified'
|
||||
].filter(Boolean),
|
||||
previewVideoUrl:
|
||||
courseDetail.reviews.find((review) => review.video_url)?.video_url ||
|
||||
(courseDetail.reviews ?? []).find((review) => review.video_url)?.video_url ||
|
||||
mockProgrammeData.previewVideoUrl,
|
||||
highlights:
|
||||
courseDetail.course_learning_outcomes.slice(0, 5).map((item) => item.title) ||
|
||||
(courseDetail.course_learning_outcomes ?? []).slice(0, 5).map((item) => item.title) ||
|
||||
mockProgrammeData.highlights,
|
||||
deliveryMethods: courseDetail.modules.length
|
||||
? courseDetail.modules.map((module) => module.module_name)
|
||||
deliveryMethods: (courseDetail.modules ?? []).length
|
||||
? (courseDetail.modules ?? []).map((module) => module.module_name)
|
||||
: mockProgrammeData.deliveryMethods,
|
||||
credentials:
|
||||
courseDetail.course_certificate?.program_title ||
|
||||
@@ -476,9 +476,9 @@ export function ProgrammeDetail({ slug }: ProgrammeDetailProps) {
|
||||
return courseDetail.modules.map((module, index) => ({
|
||||
moduleNumber: index + 1,
|
||||
title: module.module_name,
|
||||
duration: `${module.lessons.length} lesson${module.lessons.length === 1 ? '' : 's'}`,
|
||||
deliveryStyle: module.lessons.some((lesson) => !lesson.is_lock_lesson) ? 'Self-Paced' : 'Locked',
|
||||
topics: module.lessons.map((lesson) => lesson.lesson_title)
|
||||
duration: `${(module.lessons ?? []).length} lesson${(module.lessons ?? []).length === 1 ? '' : 's'}`,
|
||||
deliveryStyle: (module.lessons ?? []).some((lesson) => !lesson.is_lock_lesson) ? 'Self-Paced' : 'Locked',
|
||||
topics: (module.lessons ?? []).map((lesson) => lesson.lesson_title)
|
||||
}));
|
||||
}, [courseDetail]);
|
||||
|
||||
@@ -493,7 +493,7 @@ export function ProgrammeDetail({ slug }: ProgrammeDetailProps) {
|
||||
bio: member.faculty_biography,
|
||||
image: '',
|
||||
linkedinUrl: '',
|
||||
credentials: member.credentials.map((credential) => credential.credential_name),
|
||||
credentials: (member.credentials ?? []).map((credential) => credential.credential_name),
|
||||
expertise: member.expertises || []
|
||||
}));
|
||||
}, [courseDetail]);
|
||||
@@ -536,7 +536,9 @@ export function ProgrammeDetail({ slug }: ProgrammeDetailProps) {
|
||||
|
||||
const useCases = useMemo(() => {
|
||||
if (!courseDetail?.modules?.length) return mockUseCases;
|
||||
return courseDetail.modules.flatMap((module) => module.lessons.map((lesson) => lesson.lesson_title)).slice(0, 6);
|
||||
return courseDetail.modules
|
||||
.flatMap((module) => (module.lessons ?? []).map((lesson) => lesson.lesson_title))
|
||||
.slice(0, 6);
|
||||
}, [courseDetail]);
|
||||
|
||||
const relatedProgrammes = mockRelatedProgrammes;
|
||||
@@ -1006,16 +1008,8 @@ export function ProgrammeDetail({ slug }: ProgrammeDetailProps) {
|
||||
<Dialog open={showCertificatePreview} onOpenChange={setShowCertificatePreview}>
|
||||
<DialogPortal>
|
||||
<DialogOverlay className="fixed inset-0 bg-black/70 z-[9999]" />
|
||||
<DialogContent className="fixed left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%] w-full max-w-3xl z-[9999] focus:outline-none bg-transparent border-none shadow-none p-0">
|
||||
<DialogContent className="fixed left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%] w-full max-w-4xl z-[9999] focus:outline-none bg-transparent border-none shadow-none p-0">
|
||||
<div className="relative">
|
||||
{/* <DialogClose asChild>
|
||||
<button
|
||||
className="absolute -top-12 right-0 text-white hover:bg-white/20 rounded-full p-1 z-10 focus:outline-none"
|
||||
aria-label="Close"
|
||||
>
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
</DialogClose> */}
|
||||
|
||||
{/* Certificate Card - Authentic Certificate Style */}
|
||||
<div className="bg-white rounded-lg shadow-2xl overflow-hidden max-h-[90vh] overflow-y-auto custom-scrollbar">
|
||||
|
||||
@@ -41,19 +41,53 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'motion/react';
|
||||
import { navigateTo } from './Router';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { ImageWithFallback } from './figma/ImageWithFallback';
|
||||
import { BrandedTag } from './about/BrandedTag';
|
||||
import { PrimaryCTAButton } from './PrimaryCTAButton';
|
||||
import { toast } from 'sonner';
|
||||
import { getWebinarBySlug, sharedWebinarsData, type WebinarData } from '../data/webinarsData';
|
||||
import { useGetWebinarByIdQuery } from '../redux/services/webinarApi';
|
||||
|
||||
interface WebinarDetailProps {
|
||||
params: { slug: string };
|
||||
params?: { webinar_id: string };
|
||||
}
|
||||
|
||||
export default function WebinarDetail({ params }: WebinarDetailProps) {
|
||||
// Get webinar data from shared data source
|
||||
const [webinar, setWebinar] = useState<WebinarData | null>(null);
|
||||
// Helper function to format date
|
||||
const formatDate = (dateTimeString: string) => {
|
||||
const date = new Date(dateTimeString);
|
||||
return {
|
||||
date: date.toLocaleDateString('en-US', {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
}),
|
||||
time: date.toLocaleTimeString('en-US', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: true
|
||||
}),
|
||||
full: date
|
||||
};
|
||||
};
|
||||
|
||||
// Helper to get webinar status based on date and API status
|
||||
const getWebinarStatus = (sessionDatetime: string, webinarStatus: string): 'upcoming' | 'live' | 'recorded' => {
|
||||
const now = new Date();
|
||||
const sessionDate = new Date(sessionDatetime);
|
||||
|
||||
if (webinarStatus === 'cancelled') return 'recorded';
|
||||
if (webinarStatus === 'live') return 'live';
|
||||
if (sessionDate > now) return 'upcoming';
|
||||
return 'recorded';
|
||||
};
|
||||
|
||||
export default function WebinarDetail({ params }: WebinarDetailProps = {}) {
|
||||
const routeParams = useParams<{ webinar_id: string }>();
|
||||
const webinarId = routeParams.webinar_id || params?.webinar_id;
|
||||
const { data: webinarData, isLoading, error } = useGetWebinarByIdQuery(webinarId as string, {
|
||||
skip: !webinarId
|
||||
});
|
||||
const [timeLeft, setTimeLeft] = useState({ days: 0, hours: 0, minutes: 0, seconds: 0 });
|
||||
const [isRegistered, setIsRegistered] = useState(false);
|
||||
const [showRegistrationForm, setShowRegistrationForm] = useState(false);
|
||||
@@ -75,22 +109,76 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
||||
const videoRef = useRef<HTMLVideoElement>(null);
|
||||
const shareMenuRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Load webinar data on component mount
|
||||
useEffect(() => {
|
||||
const webinarData = getWebinarBySlug(params.slug);
|
||||
if (webinarData) {
|
||||
setWebinar(webinarData);
|
||||
} else {
|
||||
// Fallback: redirect to webinars page if webinar not found
|
||||
navigateTo('/webinars');
|
||||
}
|
||||
}, [params.slug]);
|
||||
// Transform API data to match your component's expected format
|
||||
const webinar = webinarData?.data ? {
|
||||
id: webinarData.data.id,
|
||||
title: webinarData.data.session_title,
|
||||
description: webinarData.data.description || '',
|
||||
thumbnail: webinarData.data.media?.file_name
|
||||
? `/api/media/${webinarData.data.media.id}`
|
||||
: '/images/default-webinar.jpg',
|
||||
theme: webinarData.data.webinar_category_id || 'Webinar',
|
||||
date: formatDate(webinarData.data.session_datetime).date,
|
||||
time: formatDate(webinarData.data.session_datetime).time,
|
||||
timezone: 'IST', // You might need to fetch timezone name from timezone_xid
|
||||
duration: `${webinarData.data.duration_minutes} minutes`,
|
||||
status: getWebinarStatus(webinarData.data.session_datetime, webinarData.data.webinar_status),
|
||||
attendees: `${webinarData.data.max_attendee}`, // Max capacity or registered count
|
||||
maxAttendees: webinarData.data.max_attendee,
|
||||
registrationOpen: webinarData.data.require_registration && webinarData.data.webinar_status === 'scheduled',
|
||||
recordingReady: webinarData.data.webinar_status === 'ended',
|
||||
zoomUrl: '#', // Not provided in API, might need additional endpoint
|
||||
recordingUrl: '#', // Not provided in API
|
||||
price: 'Free', // Not in API, adjust as needed
|
||||
format: 'Virtual Event',
|
||||
host: {
|
||||
name: webinarData.data.speakers?.[0]?.name || 'Host Name',
|
||||
title: webinarData.data.speakers?.[0]?.designation || 'Host',
|
||||
company: webinarData.data.speakers?.[0]?.company || '',
|
||||
bio: webinarData.data.speakers?.[0]?.bio || '',
|
||||
avatar: webinarData.data.speakers?.[0]?.image_url || '/images/default-avatar.jpg',
|
||||
linkedin: '#'
|
||||
},
|
||||
panelists: webinarData.data.speakers?.filter(s => !s.is_host).map(speaker => ({
|
||||
id: speaker.id,
|
||||
name: speaker.name,
|
||||
title: speaker.designation,
|
||||
company: speaker.company,
|
||||
bio: speaker.bio,
|
||||
avatar: speaker.image_url || '/images/default-avatar.jpg',
|
||||
linkedin: '#'
|
||||
})) || [],
|
||||
abstract: webinarData.data.about_this_session?.description || webinarData.data.description || '',
|
||||
keyTakeaways: webinarData.data.about_this_session?.points?.map(p => p.point) || [],
|
||||
agenda: webinarData.data.agenda_items?.map(item => ({
|
||||
time: formatDate(item.start_time).time,
|
||||
title: item.title,
|
||||
description: item.description
|
||||
})) || [],
|
||||
faqs: [] as Array<{ question: string; answer: string }>, // Not in API response
|
||||
tags: [] as string[], // Not in API response, derive from category or other fields
|
||||
relatedProgrammes: webinarData.data.course_links?.map(courseLink => ({
|
||||
id: courseLink.course.id,
|
||||
slug: courseLink.course.course_name.toLowerCase().replace(/\s+/g, '-'),
|
||||
title: courseLink.course.course_name,
|
||||
description: courseLink.course.course_desc,
|
||||
category: courseLink.course.course_category_name,
|
||||
level: courseLink.course.retail_type === 'private' ? 'Private' : 'Public',
|
||||
duration: courseLink.course.total_duration ? `${courseLink.course.total_duration} mins` : 'Self-paced',
|
||||
participants: courseLink.course.total_reviews || 0,
|
||||
rating: courseLink.course.avg_rating || 0,
|
||||
price: courseLink.course.price ? `₹${courseLink.course.price}` : 'Free',
|
||||
originalPrice: courseLink.course.best_value ? `₹${courseLink.course.best_value}` : `₹${courseLink.course.price}`,
|
||||
image: courseLink.course.thumbnail_img || '/images/default-course.jpg',
|
||||
bestValue: courseLink.course.best_value
|
||||
})) || []
|
||||
} : null;
|
||||
|
||||
// Countdown timer
|
||||
useEffect(() => {
|
||||
if (!webinar || webinar.status !== 'upcoming') return;
|
||||
if (!webinar || webinar.status !== 'upcoming' || !webinarData?.data.session_datetime) return;
|
||||
|
||||
const targetDate = new Date(`${webinar.date}T${webinar.time}:00`);
|
||||
const targetDate = new Date(webinarData.data.session_datetime);
|
||||
|
||||
const updateCountdown = () => {
|
||||
const now = new Date().getTime();
|
||||
@@ -110,7 +198,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
||||
const interval = setInterval(updateCountdown, 1000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [webinar?.date, webinar?.time, webinar?.status]);
|
||||
}, [webinar, webinarData?.data.session_datetime]);
|
||||
|
||||
// Close share menu when clicking outside
|
||||
useEffect(() => {
|
||||
@@ -129,7 +217,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
||||
e.preventDefault();
|
||||
setIsSubmittingRegistration(true);
|
||||
|
||||
// Simulate API call
|
||||
// TODO: Integrate with your registration API endpoint
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
setIsSubmittingRegistration(false);
|
||||
@@ -163,7 +251,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
||||
if (!webinar) return { text: 'Loading...', onClick: () => {}, disabled: true };
|
||||
|
||||
const now = new Date();
|
||||
const webinarDate = new Date(`${webinar.date}T${webinar.time}:00`);
|
||||
const webinarDate = webinarData?.data.session_datetime ? new Date(webinarData.data.session_datetime) : new Date();
|
||||
const tenMinutesBefore = new Date(webinarDate.getTime() - 10 * 60 * 1000);
|
||||
|
||||
switch (webinar.status) {
|
||||
@@ -177,7 +265,10 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
||||
if (now >= tenMinutesBefore) {
|
||||
return {
|
||||
text: 'Launch in Zoom',
|
||||
onClick: () => window.open(webinar.zoomUrl, '_blank'),
|
||||
onClick: () => {
|
||||
// You'll need to get the Zoom URL from a separate endpoint
|
||||
toast.info('Zoom link would open here');
|
||||
},
|
||||
disabled: false
|
||||
};
|
||||
} else {
|
||||
@@ -256,7 +347,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
||||
};
|
||||
|
||||
// Show loading state while webinar data is loading
|
||||
if (!webinar) {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="text-center">
|
||||
@@ -267,7 +358,26 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
||||
);
|
||||
}
|
||||
|
||||
// Show error state
|
||||
if (error || !webinar) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
<div className="text-center">
|
||||
<AlertCircle className="w-12 h-12 text-red-500 mx-auto mb-4" />
|
||||
<h2 className="text-h2 mb-2">Webinar Not Found</h2>
|
||||
<p className="text-body text-gray-600 mb-6">The webinar you're looking for doesn't exist or has been removed.</p>
|
||||
<PrimaryCTAButton
|
||||
text="Browse Webinars"
|
||||
onClick={() => navigateTo('/webinars')}
|
||||
className="cta-text-black"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const ctaProps = getCTAProps();
|
||||
const sessionDateTime = webinarData?.data.session_datetime ? new Date(webinarData.data.session_datetime) : null;
|
||||
|
||||
return (
|
||||
<div className="min-h-screen" style={{ backgroundColor: '#FFFFFF' }}>
|
||||
@@ -298,7 +408,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
||||
</p>
|
||||
|
||||
{/* Countdown Timer for Upcoming Webinars */}
|
||||
{webinar.status === 'upcoming' && (
|
||||
{webinar.status === 'upcoming' && sessionDateTime && (
|
||||
<div className="mb-8">
|
||||
<div className="grid grid-cols-4 gap-4 max-w-md mb-4">
|
||||
{[
|
||||
@@ -318,12 +428,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
||||
))}
|
||||
</div>
|
||||
<div className="text-small-white opacity-80">
|
||||
Starts {new Date(`${webinar.date}T${webinar.time}:00`).toLocaleDateString('en-US', {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})} at {webinar.time} {webinar.timezone}
|
||||
Starts {webinar.date} at {webinar.time}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -337,11 +442,11 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
||||
<span className="text-small font-medium">LIVE NOW</span>
|
||||
</div>
|
||||
<div className="text-small-white">
|
||||
{webinar.attendees} people watching
|
||||
Max capacity: {webinar.maxAttendees} attendees
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-body-lg-white">
|
||||
Session in progress • Ends at {webinar.endTime} {webinar.timezone}
|
||||
Session in progress
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -356,7 +461,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
||||
)}
|
||||
</div>
|
||||
<div className="text-body-lg-white">
|
||||
Session held on {new Date(`${webinar.date}T${webinar.time}:00`).toLocaleDateString('en-US', {
|
||||
Session held on {sessionDateTime?.toLocaleDateString('en-US', {
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
year: 'numeric'
|
||||
@@ -425,7 +530,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
||||
<div className="flex items-center gap-2 text-muted">
|
||||
<Users className="w-4 h-4" />
|
||||
<span className="text-small">
|
||||
{webinar.attendees} {webinar.status === 'live' ? 'watching' : 'registered'}
|
||||
{webinar.attendees} {webinar.status === 'live' ? 'watching' : 'capacity'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -445,45 +550,49 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
||||
{webinar.abstract}
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<h3 className="text-h3 mb-4">What You'll Learn</h3>
|
||||
<ul className="space-y-3">
|
||||
{webinar.keyTakeaways.map((takeaway, index) => (
|
||||
<li key={index} className="flex items-start gap-3">
|
||||
<CheckCircle className="w-5 h-5 text-[#04045B] mt-0.5 flex-shrink-0" />
|
||||
<span className="text-body">{takeaway}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
{webinar.keyTakeaways.length > 0 && (
|
||||
<div>
|
||||
<h3 className="text-h3 mb-4">What You'll Learn</h3>
|
||||
<ul className="space-y-3">
|
||||
{webinar.keyTakeaways.map((takeaway, index) => (
|
||||
<li key={index} className="flex items-start gap-3">
|
||||
<CheckCircle className="w-5 h-5 text-[#04045B] mt-0.5 flex-shrink-0" />
|
||||
<span className="text-body">{takeaway}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Agenda Timeline */}
|
||||
<section>
|
||||
<h2 className="text-h2 mb-6">Session Agenda</h2>
|
||||
<div className="space-y-6">
|
||||
{webinar.agenda.map((item, index) => (
|
||||
<div key={index} className="flex gap-6">
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="w-4 h-4 bg-[#04045B] rounded-full" />
|
||||
{index < webinar.agenda.length - 1 && (
|
||||
<div className="w-0.5 h-16 bg-gray-200 mt-2" />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1 pb-8">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center gap-2 mb-2">
|
||||
<Badge variant="outline" className="w-fit text-[#04045B] border-[#04045B]">
|
||||
{item.time}
|
||||
</Badge>
|
||||
<h3 className="text-h4">{item.title}</h3>
|
||||
{webinar.agenda.length > 0 && (
|
||||
<section>
|
||||
<h2 className="text-h2 mb-6">Session Agenda</h2>
|
||||
<div className="space-y-6">
|
||||
{webinar.agenda.map((item, index) => (
|
||||
<div key={index} className="flex gap-6">
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="w-4 h-4 bg-[#04045B] rounded-full" />
|
||||
{index < webinar.agenda.length - 1 && (
|
||||
<div className="w-0.5 h-16 bg-gray-200 mt-2" />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1 pb-8">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center gap-2 mb-2">
|
||||
<Badge variant="outline" className="w-fit text-[#04045B] border-[#04045B]">
|
||||
{item.time}
|
||||
</Badge>
|
||||
<h3 className="text-h4">{item.title}</h3>
|
||||
</div>
|
||||
<p className="text-body text-muted">{item.description}</p>
|
||||
</div>
|
||||
<p className="text-body text-muted">{item.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Host & Panelists */}
|
||||
<section>
|
||||
@@ -503,14 +612,16 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
||||
<h3 className="text-h4 mb-1">{webinar.host.name}</h3>
|
||||
<p className="text-small text-[#04045B] font-medium">HOST</p>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => window.open(webinar.host.linkedin, '_blank')}
|
||||
className="text-[#04045B] hover:bg-[#04045B] hover:text-white"
|
||||
>
|
||||
<ExternalLink className="w-4 h-4" />
|
||||
</Button>
|
||||
{webinar.host.linkedin !== '#' && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => window.open(webinar.host.linkedin, '_blank')}
|
||||
className="text-[#04045B] hover:bg-[#04045B] hover:text-white"
|
||||
>
|
||||
<ExternalLink className="w-4 h-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-small text-muted mb-1">{webinar.host.title}</p>
|
||||
<p className="text-small text-muted">{webinar.host.company}</p>
|
||||
@@ -535,14 +646,16 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
||||
<h3 className="text-h4 mb-1">{panelist.name}</h3>
|
||||
<p className="text-small text-[#F8C301] font-medium">SPEAKER</p>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => window.open(panelist.linkedin, '_blank')}
|
||||
className="text-[#04045B] hover:bg-[#04045B] hover:text-white"
|
||||
>
|
||||
<ExternalLink className="w-4 h-4" />
|
||||
</Button>
|
||||
{panelist.linkedin !== '#' && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => window.open(panelist.linkedin, '_blank')}
|
||||
className="text-[#04045B] hover:bg-[#04045B] hover:text-white"
|
||||
>
|
||||
<ExternalLink className="w-4 h-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-small text-muted mb-1">{panelist.title}</p>
|
||||
<p className="text-small text-muted">{panelist.company}</p>
|
||||
@@ -604,7 +717,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
||||
<div className="text-center mb-6">
|
||||
<div className="text-h3 mb-2">{webinar.price}</div>
|
||||
<div className="text-small text-muted">
|
||||
{webinar.maxAttendees - parseInt(webinar.attendees.replace(/\D/g, '')) || 0} spots remaining
|
||||
{webinar.maxAttendees} spots available
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -612,15 +725,8 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
||||
<div className="flex items-center gap-3">
|
||||
<Calendar className="w-5 h-5 text-[#04045B]" />
|
||||
<div>
|
||||
<div className="text-body font-medium">
|
||||
{new Date(`${webinar.date}T${webinar.time}:00`).toLocaleDateString('en-US', {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})}
|
||||
</div>
|
||||
<div className="text-small text-muted">{webinar.time} {webinar.timezone}</div>
|
||||
<div className="text-body font-medium">{webinar.date}</div>
|
||||
<div className="text-small text-muted">{webinar.time}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -636,7 +742,7 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<Users className="w-5 h-5 text-[#04045B]" />
|
||||
<div className="text-body">{webinar.attendees} registered</div>
|
||||
<div className="text-body">Capacity: {webinar.attendees} attendees</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -863,9 +969,9 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
||||
{/* Add to Cart Button - Outline Blue */}
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={(e) => {
|
||||
onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.stopPropagation();
|
||||
// Add to cart functionality would go here
|
||||
toast.info('Add to cart functionality would go here');
|
||||
}}
|
||||
className="flex-1 flex items-center justify-center gap-2 h-11 rounded-lg transition-all duration-200 font-medium"
|
||||
style={{
|
||||
@@ -877,11 +983,11 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
||||
fontWeight: '500',
|
||||
borderWidth: '2px'
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.currentTarget.style.backgroundColor = '#04045B';
|
||||
e.currentTarget.style.color = 'white';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.currentTarget.style.backgroundColor = 'transparent';
|
||||
e.currentTarget.style.color = '#04045B';
|
||||
}}
|
||||
@@ -901,10 +1007,10 @@ export default function WebinarDetail({ params }: WebinarDetailProps) {
|
||||
fontWeight: '500',
|
||||
border: 'none'
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.currentTarget.style.backgroundColor = '#030359';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.currentTarget.style.backgroundColor = '#04045B';
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -193,7 +193,7 @@ export function Webinars() {
|
||||
const WebinarCard = ({ webinar }: { webinar: WebinarItem }) => {
|
||||
const handleCardClick = () => {
|
||||
if (webinar.webinar_status !== 'cancelled') {
|
||||
navigateTo(`/webinar/${webinar.id}`);
|
||||
navigateTo(`/webinars/${webinar.id}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ export const aboutUsApi = createApi({
|
||||
// ✅ GET About Us
|
||||
getAboutUs: builder.query<AboutUsData, void>({
|
||||
query: () => ({
|
||||
url: "/admin/about-us",
|
||||
url: "/guest/about-us",
|
||||
method: "GET",
|
||||
}),
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ export const blogApi = createApi({
|
||||
if (tag_id && tag_id !== "all") params.tag_id = tag_id;
|
||||
|
||||
return {
|
||||
url: "/admin/blogs/list",
|
||||
url: "/guest/blogs/list",
|
||||
method: "GET",
|
||||
params,
|
||||
};
|
||||
@@ -114,7 +114,7 @@ export const blogApi = createApi({
|
||||
|
||||
getBlogByID: builder.query<BlogItem, string>({
|
||||
query: (id) => ({
|
||||
url: `/admin/blogs/list/${id}`,
|
||||
url: `/guest/blogs/list/${id}`,
|
||||
method: "GET",
|
||||
}),
|
||||
transformResponse: (response: BlogByIdResponse) => response.data,
|
||||
|
||||
@@ -10,7 +10,7 @@ export const contactUsApi = createApi({
|
||||
// GET Lead Categories
|
||||
getLeadCategories: builder.query({
|
||||
query: ({ limit = 10, offset = 0, status = "active" }) => ({
|
||||
url: "admin/prepopulate/lead-categories/list",
|
||||
url: "guest/prepopulate/lead-categories/list",
|
||||
params: {
|
||||
limit,
|
||||
offset,
|
||||
@@ -23,7 +23,7 @@ export const contactUsApi = createApi({
|
||||
// CREATE Lead
|
||||
createLead: builder.mutation({
|
||||
query: (body) => ({
|
||||
url: "admin/leads/create",
|
||||
url: "guest/leads/create",
|
||||
method: "POST",
|
||||
body,
|
||||
}),
|
||||
|
||||
@@ -321,8 +321,8 @@ export const courseApi = createApi({
|
||||
const queryString = searchParams.toString();
|
||||
|
||||
return queryString
|
||||
? `admin/course/public/list?${queryString}`
|
||||
: `admin/course/public/list`;
|
||||
? `guest/course/public/list?${queryString}`
|
||||
: `guest/course/public/list`;
|
||||
},
|
||||
|
||||
providesTags: (result) =>
|
||||
@@ -348,15 +348,15 @@ export const courseApi = createApi({
|
||||
}
|
||||
const queryString = searchParams.toString();
|
||||
return queryString
|
||||
? `admin/prepopulate/course-categories/list?${queryString}`
|
||||
: `admin/prepopulate/course-categories/list`;
|
||||
? `guest/prepopulate/course-categories/list?${queryString}`
|
||||
: `guest/prepopulate/course-categories/list`;
|
||||
},
|
||||
providesTags: ["CourseCategories"],
|
||||
}),
|
||||
|
||||
// GET Course By Id
|
||||
getcoursebyid: builder.query<CourseDetailResponse, string>({
|
||||
query: (course_id) => `admin/course/${course_id}`,
|
||||
query: (course_id) => `guest/course/${course_id}`,
|
||||
providesTags: (_result, _error, course_id) => [{ type: "Course", id: course_id }],
|
||||
}),
|
||||
|
||||
@@ -368,4 +368,5 @@ export const {
|
||||
useGetCoursesQuery,
|
||||
useGetCourseCategoriesQuery,
|
||||
useGetcoursebyidQuery,
|
||||
|
||||
} = courseApi;
|
||||
|
||||
@@ -10,7 +10,7 @@ export const faqApi = createApi({
|
||||
// GET FAQs LIST
|
||||
getFaqs: builder.query({
|
||||
query: ({ limit = 10, offset = 0, search_term, content_status, content_category_xid }) => ({
|
||||
url: "admin/faq/list",
|
||||
url: "guest/faq/list",
|
||||
params: {
|
||||
limit,
|
||||
offset,
|
||||
@@ -25,7 +25,7 @@ export const faqApi = createApi({
|
||||
// GET category TAGS LIST
|
||||
getFaqCategories: builder.query({
|
||||
query: ({ limit = 10, offset = 0, search_query }) => ({
|
||||
url: "admin/prepopulate/content-categories/list",
|
||||
url: "guest/prepopulate/content-categories/list",
|
||||
params: {
|
||||
limit,
|
||||
offset,
|
||||
|
||||
@@ -103,7 +103,7 @@ export const homepageApi = createApi({
|
||||
{ landing_page_type: "home" | "services" | "about_us" }
|
||||
>({
|
||||
query: ({ landing_page_type }) => ({
|
||||
url: "/admin/home-page/list",
|
||||
url: "/guest/home-page/list",
|
||||
params: { landing_page_type },
|
||||
}),
|
||||
|
||||
@@ -114,7 +114,7 @@ export const homepageApi = createApi({
|
||||
|
||||
getFeaturedBlogs: builder.query({
|
||||
query: ({ limit = 3 }) => ({
|
||||
url: `/admin/blogs/featured?limit=${limit}`,
|
||||
url: `/guest/blogs/featured?limit=${limit}`,
|
||||
method: 'GET',
|
||||
}),
|
||||
transformResponse: (response: any) => {
|
||||
|
||||
@@ -14,7 +14,15 @@ export interface KautilyaPageResponse {
|
||||
subtext: string;
|
||||
cta_text: string;
|
||||
cta_destination: string;
|
||||
images: Array<{ // ✅ FIX ADDED
|
||||
id: string;
|
||||
hero_section_xid: string;
|
||||
image_url: string;
|
||||
alt_text: string;
|
||||
display_order: number;
|
||||
}>;
|
||||
};
|
||||
|
||||
our_story: {
|
||||
id: string;
|
||||
tag: string;
|
||||
@@ -22,6 +30,7 @@ export interface KautilyaPageResponse {
|
||||
content: string;
|
||||
image_url: string;
|
||||
};
|
||||
|
||||
why_choose_us: {
|
||||
id: string;
|
||||
tag: string;
|
||||
@@ -40,6 +49,7 @@ export interface KautilyaPageResponse {
|
||||
}>;
|
||||
}>;
|
||||
};
|
||||
|
||||
facility_features: {
|
||||
id: string;
|
||||
title: string;
|
||||
@@ -58,6 +68,7 @@ export interface KautilyaPageResponse {
|
||||
}>;
|
||||
}>;
|
||||
};
|
||||
|
||||
visual_tour: {
|
||||
id: string;
|
||||
title: string;
|
||||
@@ -75,6 +86,7 @@ export interface KautilyaPageResponse {
|
||||
}>;
|
||||
}>;
|
||||
};
|
||||
|
||||
daily_experience: {
|
||||
id: string;
|
||||
title: string;
|
||||
@@ -88,6 +100,7 @@ export interface KautilyaPageResponse {
|
||||
display_order: number;
|
||||
}>;
|
||||
};
|
||||
|
||||
cta_section: {
|
||||
id: string;
|
||||
background_image_url: string;
|
||||
@@ -97,6 +110,7 @@ export interface KautilyaPageResponse {
|
||||
description: string;
|
||||
};
|
||||
};
|
||||
|
||||
errors: any;
|
||||
correlation_id: string;
|
||||
}
|
||||
@@ -111,7 +125,7 @@ export const learningFacilityApi = createApi({
|
||||
{ }
|
||||
>({
|
||||
query: ({ }) => ({
|
||||
url: "/admin/kautilya-page/get",
|
||||
url: "/guest/kautilya-page/get",
|
||||
}),
|
||||
transformResponse: (response: KautilyaPageResponse) => response.data,
|
||||
providesTags: [{ type: "KautilyaPage", id: "LIST" }],
|
||||
|
||||
@@ -10,7 +10,7 @@ export const sercicesApi = createApi({
|
||||
|
||||
getServiceList: builder.query<any, { service_type: string }>({
|
||||
query: ({ service_type }) => ({
|
||||
url: `/admin/service-page/list`,
|
||||
url: `/guest/service-page/list`,
|
||||
params: { service_type },
|
||||
}),
|
||||
}),
|
||||
|
||||
@@ -50,6 +50,92 @@ export interface WebinarListParams {
|
||||
sortBy?: "most_popular" | "newest" | "oldest" | "title" | "duration";
|
||||
}
|
||||
|
||||
export interface WebinarDetailResponse {
|
||||
success: boolean;
|
||||
status: number;
|
||||
message: string;
|
||||
data: {
|
||||
id: string;
|
||||
session_title: string;
|
||||
description: string | null;
|
||||
session_datetime: string;
|
||||
duration_minutes: number;
|
||||
timezone_xid: string;
|
||||
webinar_category_id: string;
|
||||
max_attendee: number;
|
||||
passcode: string;
|
||||
require_registration: boolean;
|
||||
recurring_webinar: boolean;
|
||||
webinar_status: "scheduled" | "live" | "ended" | "cancelled";
|
||||
type: string;
|
||||
media: {
|
||||
id: string;
|
||||
file_name: string;
|
||||
file_type: string;
|
||||
file_extension: string;
|
||||
} | null;
|
||||
about_this_session: {
|
||||
description: string;
|
||||
points: Array<{
|
||||
id: string;
|
||||
about_id: string;
|
||||
point: string;
|
||||
sort_order: number;
|
||||
}>;
|
||||
} | null;
|
||||
agenda_items: Array<{
|
||||
id: string;
|
||||
webinar_xid: string;
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
title: string;
|
||||
description: string;
|
||||
}>;
|
||||
speakers: Array<{
|
||||
id: string;
|
||||
webinar_xid: string;
|
||||
name: string;
|
||||
designation: string;
|
||||
company: string;
|
||||
bio: string;
|
||||
image_url: string;
|
||||
is_host: boolean;
|
||||
}>;
|
||||
faqs: any;
|
||||
course_links: Array<{
|
||||
id: string;
|
||||
webinar_xid: string;
|
||||
course_xid: string;
|
||||
display_order: number;
|
||||
course: {
|
||||
id: string;
|
||||
course_name: string;
|
||||
course_desc: string;
|
||||
thumbnail_img: string;
|
||||
course_category_xid: string;
|
||||
course_category_name: string;
|
||||
best_value: number;
|
||||
avg_rating: number;
|
||||
total_reviews: number;
|
||||
retail_type: string;
|
||||
price: number;
|
||||
is_certificate_available: boolean;
|
||||
course_status: string;
|
||||
updated_at: string;
|
||||
total_duration: number | null;
|
||||
no_of_modules: number;
|
||||
media_id: string | null;
|
||||
media_file_type: string | null;
|
||||
media_file_extension: string | null;
|
||||
media_file_name: string | null;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
errors: any;
|
||||
correlation_id: string;
|
||||
}
|
||||
|
||||
|
||||
/* ================= API ================= */
|
||||
|
||||
export const webinarApi = createApi({
|
||||
@@ -115,14 +201,18 @@ export const webinarApi = createApi({
|
||||
params.append("sort_by", sortBy);
|
||||
}
|
||||
|
||||
return `/admin/webinars/list?${params.toString()}`;
|
||||
return `/guest/webinars/list?${params.toString()}`;
|
||||
},
|
||||
|
||||
providesTags: ["Webinar"],
|
||||
}),
|
||||
getWebinarById: builder.query<WebinarDetailResponse, string>({
|
||||
query: (webinarId) => `/guest/webinars/${webinarId}`,
|
||||
providesTags: (result, error, id) => [{ type: "Webinar", id }],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
/* ================= EXPORT HOOK ================= */
|
||||
|
||||
export const { useWebinarListQuery } = webinarApi;
|
||||
export const { useWebinarListQuery, useGetWebinarByIdQuery } = webinarApi;
|
||||
Reference in New Issue
Block a user