|
| 1 | +import { BookOpen, Check, CirclePlay, Clock } from 'lucide-react' |
| 2 | +import { cn } from '@/lib/utils' |
| 3 | + |
| 4 | +interface Lesson { |
| 5 | + title: string |
| 6 | + /** e.g. "4:12". Omit to show "View" instead. */ |
| 7 | + duration?: string |
| 8 | + /** Highlights the current lesson. */ |
| 9 | + active?: boolean |
| 10 | + /** Renders a completed checkmark. */ |
| 11 | + done?: boolean |
| 12 | +} |
| 13 | + |
| 14 | +interface CourseProgressProps { |
| 15 | + /** Course name shown as the panel heading. */ |
| 16 | + course: string |
| 17 | + lessons: Lesson[] |
| 18 | + /** e.g. "Approx. 18 min". */ |
| 19 | + durationLabel?: string |
| 20 | + /** Completion percentage 0–100. */ |
| 21 | + progress?: number |
| 22 | + className?: string |
| 23 | +} |
| 24 | + |
| 25 | +/** Right-rail course panel: lesson count, duration, progress bar, and lesson list. */ |
| 26 | +export function CourseProgress({ |
| 27 | + course, |
| 28 | + lessons, |
| 29 | + durationLabel, |
| 30 | + progress = 0, |
| 31 | + className, |
| 32 | +}: CourseProgressProps) { |
| 33 | + return ( |
| 34 | + <aside className={cn('rounded-xl border border-fd-border bg-fd-card/40 p-5', className)}> |
| 35 | + <h2 className='mt-0 mb-3 font-semibold text-fd-foreground text-lg'>{course}</h2> |
| 36 | + |
| 37 | + <div className='flex flex-wrap items-center gap-2 border-fd-border border-b pb-4 text-fd-muted-foreground text-xs'> |
| 38 | + <span className='inline-flex items-center gap-1.5 rounded-md border border-fd-border px-2 py-1'> |
| 39 | + <BookOpen className='size-3.5' /> |
| 40 | + {lessons.length} lessons |
| 41 | + </span> |
| 42 | + {durationLabel && ( |
| 43 | + <span className='inline-flex items-center gap-1.5 rounded-md border border-fd-border px-2 py-1'> |
| 44 | + <Clock className='size-3.5' /> |
| 45 | + {durationLabel} |
| 46 | + </span> |
| 47 | + )} |
| 48 | + </div> |
| 49 | + |
| 50 | + <div className='py-4'> |
| 51 | + <div className='mb-2 flex items-center justify-between text-sm'> |
| 52 | + <span className='text-fd-foreground'>Your progress</span> |
| 53 | + <span className='text-fd-muted-foreground'>{Math.min(100, Math.max(0, progress))}%</span> |
| 54 | + </div> |
| 55 | + <div className='h-1.5 w-full overflow-hidden rounded-full bg-fd-muted'> |
| 56 | + <div |
| 57 | + className='h-full rounded-full bg-[#33c482] transition-all' |
| 58 | + style={{ width: `${Math.min(100, Math.max(0, progress))}%` }} |
| 59 | + /> |
| 60 | + </div> |
| 61 | + </div> |
| 62 | + |
| 63 | + <ul className='m-0 flex list-none flex-col gap-0.5 p-0'> |
| 64 | + {lessons.map((lesson) => ( |
| 65 | + <li |
| 66 | + key={lesson.title} |
| 67 | + className={cn( |
| 68 | + 'flex items-center gap-2.5 rounded-lg px-2.5 py-2 text-sm', |
| 69 | + lesson.active |
| 70 | + ? 'bg-fd-accent text-fd-foreground' |
| 71 | + : 'text-fd-muted-foreground hover:bg-fd-accent/50' |
| 72 | + )} |
| 73 | + > |
| 74 | + {lesson.done ? ( |
| 75 | + <Check className='size-4 shrink-0 text-[#33c482]' /> |
| 76 | + ) : ( |
| 77 | + <CirclePlay |
| 78 | + className={cn('size-4 shrink-0', lesson.active && 'text-fd-foreground')} |
| 79 | + /> |
| 80 | + )} |
| 81 | + <span className={cn('flex-1 truncate', lesson.active && 'font-medium')}> |
| 82 | + {lesson.title} |
| 83 | + </span> |
| 84 | + <span className='shrink-0 text-fd-muted-foreground text-xs tabular-nums'> |
| 85 | + {lesson.duration ?? 'View'} |
| 86 | + </span> |
| 87 | + </li> |
| 88 | + ))} |
| 89 | + </ul> |
| 90 | + </aside> |
| 91 | + ) |
| 92 | +} |
0 commit comments