worked on the course modal

This commit is contained in:
2025-09-09 17:38:12 +05:30
parent 31a068ae4e
commit f9016fd732

View File

@@ -9,7 +9,7 @@ import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '..
import { Avatar, AvatarFallback, AvatarImage } from '../../components/ui/avatar';
import { Separator } from '../../components/ui/separator';
import { Alert, AlertDescription } from '../../components/ui/alert';
import {
import {
Play,
Pause,
CheckCircle,
@@ -385,12 +385,12 @@ function VideoPlayer({ course, isPlaying, onPlayPause }: {
return (
<div className="relative bg-black rounded-lg overflow-hidden">
<div className="aspect-video relative">
<img
<img
src={course.thumbnail}
alt={course.title}
className="w-full h-full object-cover"
/>
{/* Play button overlay */}
<div className="absolute inset-0 flex items-center justify-center">
<Button
@@ -417,18 +417,18 @@ function VideoPlayer({ course, isPlaying, onPlayPause }: {
>
{isPlaying ? <Pause className="h-4 w-4" /> : <Play className="h-4 w-4" />}
</Button>
<div className="flex-1">
<Progress
value={(currentTime / duration) * 100}
<Progress
value={(currentTime / duration) * 100}
className="h-1 bg-white/20"
/>
</div>
<span className="text-sm">
{formatTime(currentTime)} / {formatTime(duration)}
</span>
<Button
variant="ghost"
size="icon"
@@ -437,7 +437,7 @@ function VideoPlayer({ course, isPlaying, onPlayPause }: {
>
{isMuted ? <VolumeX className="h-4 w-4" /> : <Volume2 className="h-4 w-4" />}
</Button>
<Button
variant="ghost"
size="icon"
@@ -454,11 +454,12 @@ function VideoPlayer({ course, isPlaying, onPlayPause }: {
// Course Header Component
function CourseHeader({ course }: { course: Course }) {
const navigate = useNavigate()
return (
<div className="space-y-4">
<div className="space-y-0">
{/* Breadcrumb Navigation */}
<nav className="flex items-center gap-2 text-base text-muted-foreground">
<button
<button
onClick={() => navigate('/dashboard')}
className="hover:text-foreground transition-colors active:bg-gray-100 focus:outline-none px-2 py-1 rounded"
style={{ outline: 'none' }}
@@ -466,7 +467,7 @@ function CourseHeader({ course }: { course: Course }) {
Overview
</button>
<ChevronRight className="h-4 w-4" />
<button
<button
onClick={() => navigate('/library')}
className="hover:text-foreground transition-colors active:bg-gray-100 focus:outline-none px-2 py-1 rounded"
style={{ outline: 'none' }}
@@ -531,12 +532,12 @@ function ActionButtons() {
<BookmarkPlus className="h-4 w-4 mr-2" />
Save Note
</Button>
<Button variant="outline" className="text-base min-h-[44px]">
<Download className="h-4 w-4 mr-2" />
Download
</Button>
<Button variant="outline" className="text-base min-h-[44px]">
<Share2 className="h-4 w-4 mr-2" />
Share
@@ -565,7 +566,7 @@ function FooterAwareSidebar({ children }: { children: React.ReactNode }) {
// Get footer position
const footerTop = footer.getBoundingClientRect().top + scrollTop;
// Calculate when sidebar would start overlapping footer
// Account for sidebar height and sticky top offset (24 * 14px = 336px for top-24)
const sidebarHeight = sidebarRef.current.offsetHeight;
@@ -607,11 +608,10 @@ function FooterAwareSidebar({ children }: { children: React.ReactNode }) {
return (
<div
ref={sidebarRef}
className={`transition-all duration-200 ${
shouldStick
? 'sticky top-24 z-40'
: 'relative'
}`}
className={`transition-all duration-200 ${shouldStick
? 'sticky top-24 z-40'
: 'relative'
}`}
style={{
// Ensure sidebar doesn't exceed viewport height when sticky
maxHeight: shouldStick ? 'calc(100vh - 6rem)' : 'none'
@@ -647,7 +647,7 @@ function CourseContentSidebar({ course }: { course: Course }) {
<ChevronDown className="h-5 w-5 text-muted-foreground" />
</CardTitle>
</CardHeader>
<CardContent className="p-0">
<div className="px-6 pb-4">
{/* Course Intro Progress */}
@@ -663,19 +663,18 @@ function CourseContentSidebar({ course }: { course: Course }) {
<div className="space-y-3 max-h-[50vh] overflow-y-auto scrollbar-minimal">
{/* Current course sections from first module */}
{course.modules[0]?.sections.map((section, index) => {
const isCurrentSection = !section.completed &&
const isCurrentSection = !section.completed &&
course.modules[0].sections.slice(0, index).every(s => s.completed);
return (
<div
<div
key={section.id}
className={`flex items-center justify-between p-3 rounded-lg transition-colors cursor-pointer active:bg-gray-100 ${
section.completed
? 'hover:bg-muted/50'
: isCurrentSection
? 'bg-primary/5 border border-primary/20'
: 'opacity-60 hover:bg-muted/30'
}`}
className={`flex items-center justify-between p-3 rounded-lg transition-colors cursor-pointer active:bg-gray-100 ${section.completed
? 'hover:bg-muted/50'
: isCurrentSection
? 'bg-primary/5 border border-primary/20'
: 'opacity-60 hover:bg-muted/30'
}`}
style={{ outline: 'none' }}
>
<div className="flex items-center gap-3 flex-1">
@@ -688,26 +687,23 @@ function CourseContentSidebar({ course }: { course: Course }) {
) : (
<div className="w-5 h-5 rounded-full border-2 border-muted-foreground"></div>
)}
<div className="flex-1 min-w-0">
<p className={`font-medium text-base ${
section.completed ? 'text-foreground' :
isCurrentSection ? 'text-primary' :
'text-muted-foreground'
}`}>
<p className={`font-medium text-base ${section.completed ? 'text-foreground' :
isCurrentSection ? 'text-primary' :
'text-muted-foreground'
}`}>
{String(index + 1).padStart(2, '0')}: {section.title}
</p>
<p className={`text-sm ${
section.completed ? 'text-success' : 'text-muted-foreground'
}`}>
<p className={`text-sm ${section.completed ? 'text-success' : 'text-muted-foreground'
}`}>
{section.completed ? 'Completed' : section.duration}
</p>
</div>
</div>
<Play className={`h-5 w-5 ${
isCurrentSection ? 'text-primary' : 'text-muted-foreground'
}`} />
<Play className={`h-5 w-5 ${isCurrentSection ? 'text-primary' : 'text-muted-foreground'
}`} />
</div>
);
})}
@@ -735,7 +731,7 @@ function CourseContentSidebar({ course }: { course: Course }) {
<ChevronDown className="h-4 w-4 text-muted-foreground" />
)}
</button>
{expandedModules.has(module.id) && (
<div className="px-4 pb-4">
<div className="text-sm text-muted-foreground">
@@ -923,11 +919,10 @@ function ReviewsContent({ course }: { course: Course }) {
{[...Array(5)].map((_, i) => (
<Star
key={i}
className={`h-4 w-4 ${
i < review.rating
? 'fill-yellow-400 text-yellow-400'
: 'text-muted-foreground'
}`}
className={`h-4 w-4 ${i < review.rating
? 'fill-yellow-400 text-yellow-400'
: 'text-muted-foreground'
}`}
/>
))}
</div>
@@ -937,14 +932,14 @@ function ReviewsContent({ course }: { course: Course }) {
</div>
</div>
<div className="flex items-center gap-4 text-sm text-muted-foreground">
<button
<button
className="flex items-center gap-1 hover:text-foreground active:bg-gray-100 focus:outline-none px-2 py-1 rounded"
style={{ outline: 'none' }}
>
<ThumbsUp className="h-4 w-4" />
Helpful
</button>
<button
<button
className="hover:text-foreground active:bg-gray-100 focus:outline-none px-2 py-1 rounded"
style={{ outline: 'none' }}
>
@@ -963,7 +958,7 @@ export function CourseTimeline({ userType = 'individual' }: CourseTimelineProps)
const [activeTab, setActiveTab] = useState('overview');
const [course, setCourse] = useState<Course | null>(null);
const [isLoading, setIsLoading] = useState(true);
const navigate = useNavigate();
const navigate = useNavigate();
// Mock user data
@@ -977,14 +972,14 @@ export function CourseTimeline({ userType = 'individual' }: CourseTimelineProps)
useEffect(() => {
const courseId = getCourseId();
const selectedCourse = COURSE_DATABASE[courseId];
if (selectedCourse) {
setCourse(selectedCourse);
} else {
// Fallback to Strategic Leadership Excellence
setCourse(COURSE_DATABASE['1']);
}
// Simulate loading time
setTimeout(() => {
setIsLoading(false);
@@ -998,7 +993,7 @@ export function CourseTimeline({ userType = 'individual' }: CourseTimelineProps)
if (isLoading || !course) {
return (
<LearnerLayout currentPage="/course-timeline" userType={userType} user={user}>
<div className="space-y-6">
<div>
<div className="animate-pulse">
<div className="h-8 bg-muted rounded w-48 mb-4"></div>
<div className="h-12 bg-muted rounded w-3/4 mb-4"></div>
@@ -1012,9 +1007,9 @@ export function CourseTimeline({ userType = 'individual' }: CourseTimelineProps)
return (
<LearnerLayout currentPage="/course-timeline" userType={userType} user={user}>
{/* Main Container with proper spacing to account for footer */}
<div className="min-h-screen bg-gray-50">
<div className="min-h-screen bg-gray-50">
{/* Coursera-Style Header with Breadcrumb */}
<div className="bg-white border-b border-gray-200 sticky top-[70px] z-40">
<div className="bg-white border-b border-gray-200 sticky top-[0px] z-40">
<div className="w-full max-w-none px-4 lg:px-6">
<div className="py-4">
{/* Breadcrumb Navigation */}
@@ -1035,7 +1030,7 @@ export function CourseTimeline({ userType = 'individual' }: CourseTimelineProps)
<ChevronRight className="h-4 w-4" />
<span className="text-gray-900 font-medium">{course.title}</span>
</div>
{/* Course Title and Progress */}
<div className="flex items-center justify-between">
<div>
@@ -1063,11 +1058,11 @@ export function CourseTimeline({ userType = 'individual' }: CourseTimelineProps)
{/* Main Layout - 3 Column Coursera Style */}
<div className="flex min-h-screen">
{/* Left Sidebar - Course Navigation */}
<div className="w-80 bg-white border-r border-gray-200 sticky top-[140px] h-[calc(100vh-140px)] overflow-y-auto">
<div className="w-80 bg-white border-r border-gray-200 sticky top-[104px] h-[calc(100vh-104px)] overflow-y-auto" >
<div className="p-6">
<div className="mb-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Course Material</h3>
{/* Course Modules */}
<Accordion type="multiple" defaultValue={["module-1", "module-2"]} className="space-y-2">
{/* Module 1 */}
@@ -1095,7 +1090,7 @@ export function CourseTimeline({ userType = 'individual' }: CourseTimelineProps)
<CheckCircle className="h-3 w-3 text-green-600" />
</div>
</div>
<div className="flex items-center gap-3 p-2 bg-blue-50 border border-blue-200 rounded-lg">
<Video className="h-4 w-4 text-blue-600" />
<div className="flex-1">
@@ -1106,7 +1101,7 @@ export function CourseTimeline({ userType = 'individual' }: CourseTimelineProps)
<Play className="h-2 w-2 text-white fill-white" />
</div>
</div>
<div className="flex items-center gap-3 p-2 hover:bg-gray-50 rounded-lg cursor-pointer opacity-60">
<FileText className="h-4 w-4 text-gray-400" />
<div className="flex-1">
@@ -1142,7 +1137,7 @@ export function CourseTimeline({ userType = 'individual' }: CourseTimelineProps)
</div>
<Lock className="h-4 w-4 text-gray-400" />
</div>
<div className="flex items-center gap-3 p-2 hover:bg-gray-50 rounded-lg cursor-pointer opacity-60">
<FileText className="h-4 w-4 text-gray-400" />
<div className="flex-1">
@@ -1171,7 +1166,7 @@ export function CourseTimeline({ userType = 'individual' }: CourseTimelineProps)
</AccordionItem>
</Accordion>
</div>
{/* Course Info Section */}
<div className="border-t border-gray-200 pt-6 space-y-4">
<div className="flex items-center gap-3 text-sm text-gray-600 hover:text-[#04045B] cursor-pointer">
@@ -1201,12 +1196,12 @@ export function CourseTimeline({ userType = 'individual' }: CourseTimelineProps)
<span>Communication Styles</span>
</div>
<h2 className="text-xl font-semibold text-gray-900 mb-4">Communication Styles</h2>
{/* Video Player */}
<VideoPlayer
course={course}
isPlaying={isPlaying}
onPlayPause={handlePlayPause}
<VideoPlayer
course={course}
isPlaying={isPlaying}
onPlayPause={handlePlayPause}
/>
</div>
@@ -1221,11 +1216,10 @@ export function CourseTimeline({ userType = 'individual' }: CourseTimelineProps)
<button
key={tab.value}
onClick={() => setActiveTab(tab.value)}
className={`py-3 px-1 border-b-2 font-medium text-sm transition-colors ${
activeTab === tab.value
? 'border-[#04045B] text-[#04045B]'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}`}
className={`py-3 px-1 border-b-2 font-medium text-sm transition-colors ${activeTab === tab.value
? 'border-[#04045B] text-[#04045B]'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}`}
>
{tab.label}
</button>
@@ -1285,16 +1279,16 @@ export function CourseTimeline({ userType = 'individual' }: CourseTimelineProps)
</div>
{/* Right Sidebar - Learning Plan */}
<div className="w-80 bg-white border-l border-gray-200 sticky top-[140px] h-[calc(100vh-140px)] overflow-y-auto">
<div className="w-80 bg-white border-l border-gray-200 sticky top-[104px] h-[calc(100vh-104px)] overflow-y-auto">
<div className="p-6">
<div className="mb-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Learning plan</h3>
<div className="space-y-4">
<div className="text-sm text-gray-600">
I'm committed to learning 1 day a week on Coursera.
</div>
{/* Current Week */}
<div className="border border-gray-200 rounded-lg p-4">
<div className="flex items-center justify-between mb-2">
@@ -1320,7 +1314,7 @@ export function CourseTimeline({ userType = 'individual' }: CourseTimelineProps)
{/* Timeline */}
<div className="space-y-3">
<div className="text-sm font-medium text-gray-900">Course timeline</div>
<div className="space-y-3">
<div className="flex items-center gap-3">
<div className="w-8 h-8 rounded-full bg-green-100 flex items-center justify-center text-xs font-semibold text-green-800">
@@ -1331,7 +1325,7 @@ export function CourseTimeline({ userType = 'individual' }: CourseTimelineProps)
<div className="text-xs text-gray-500">Your goal deadline</div>
</div>
</div>
<div className="flex items-center gap-3">
<div className="w-8 h-8 rounded-full bg-blue-100 flex items-center justify-center text-xs font-semibold text-blue-800">
2
@@ -1341,7 +1335,7 @@ export function CourseTimeline({ userType = 'individual' }: CourseTimelineProps)
<div className="text-xs text-gray-500">Due in 3 days</div>
</div>
</div>
<div className="flex items-center gap-3">
<div className="w-8 h-8 rounded-full bg-gray-100 flex items-center justify-center text-xs font-semibold text-gray-400">
📅