all changes
This commit is contained in:
@@ -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}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user