This commit is contained in:
polwex 2026-03-24 16:12:30 +07:00
commit d42e47b15b
31 changed files with 3045 additions and 0 deletions

View file

@ -0,0 +1,52 @@
import { LESSONS } from '../data/lessons'
import type { UserProgress } from '../types'
import styles from '../styles/typing.module.css'
type Props = {
progress: UserProgress
onSelect: (lessonId: number) => void
}
export function LessonSelect({ progress, onSelect }: Props) {
const isUnlocked = (lessonId: number) => {
const lesson = LESSONS.find(l => l.id === lessonId)
if (!lesson?.unlockAfter) return true
return progress.completedLessons.includes(lesson.unlockAfter)
}
return (
<div>
<div className={styles.modeHeader}>
<h2 className={styles.modeTitle}>Lessons</h2>
<p className={styles.modeSubtitle}>
Master each lesson to unlock the next ({progress.completedLessons.length}/{LESSONS.length} completed)
</p>
</div>
<div className={styles.lessonSelect}>
{LESSONS.map(lesson => {
const unlocked = isUnlocked(lesson.id)
const completed = progress.completedLessons.includes(lesson.id)
return (
<div
key={lesson.id}
className={`${styles.lessonCard} ${!unlocked ? styles.locked : ''} ${completed ? styles.completed : ''}`}
onClick={() => unlocked && onSelect(lesson.id)}
>
<div className={styles.lessonId}>Lesson {lesson.id}</div>
<div className={styles.lessonName}>{lesson.name}</div>
{lesson.newKeys.length > 0 && (
<div className={styles.lessonKeys}>
New: {lesson.newKeys.join(' ')}
</div>
)}
<div className={styles.lessonStatus}>
{completed ? 'Completed' : unlocked ? 'Ready' : 'Locked'}
</div>
</div>
)
})}
</div>
</div>
)
}