worked on the course modal
This commit is contained in:
@@ -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">
|
||||
📅
|
||||
|
||||
Reference in New Issue
Block a user