import React, { useEffect, useMemo, useState } from 'react'
import { Link, usePage } from '@inertiajs/react'
import SeoHead from '../../components/seo/SeoHead'
function CourseBreadcrumbs({ items = [] }) {
if (!items.length) return null
return (
{items.map((item, index) => {
const isLast = index === items.length - 1
return (
{index > 0 ? / : null}
{isLast ? (
{item.label}
) : (
{item.label}
)}
)
})}
)
}
function ProgressMeter({ progress }) {
const percent = Math.max(0, Math.min(100, Number(progress?.percent || 0)))
return (
{progress ? 'In progress' : 'Not started'}
{progress
? `${progress.completedRequired}/${progress.totalRequired} required lessons completed`
: 'Start the course to begin tracking progress through required lessons.'}
)
}
function LessonChip({ lesson }) {
const thumbnail = lesson?.cover_image_url || lesson?.article_cover_image_url || lesson?.cover_image || lesson?.article_cover_image || ''
const stepLabel = lesson?.course_step_label || null
const stepNumber = Number(lesson?.course_step_number || 0)
const isCompleted = Boolean(lesson?.completed)
const readingMinutes = Number(lesson?.reading_minutes || 0)
const ctaLabel = isCompleted ? 'Review lesson' : 'Open lesson'
return (
{thumbnail ? (
) : (
)}
{lesson.is_required ? (
Required
) : (
Optional
)}
{isCompleted ? (
Done
) : null}
{stepLabel ?
{stepLabel}
: null}
{stepNumber > 0 ?
{String(stepNumber).padStart(2, '0')}
: null}
{stepLabel ?
{stepLabel}
: null}
{lesson.formatted_lesson_number ?
{lesson.formatted_lesson_number} : null}
{lesson.difficulty || 'lesson'}
{lesson.access_level || 'free'}
{readingMinutes > 0 ?
{readingMinutes} min : null}
{lesson.title}
{isCompleted ? 'You already finished this lesson.' : 'Follow this step next in the course path.'}
{lesson.excerpt || lesson.content_preview || 'Open this lesson inside the course.'}
{lesson.lesson_type || 'article'}
{lesson.category_name ? {lesson.category_name} : null}
Status
{isCompleted ? 'Completed' : 'Up next'}
Access
{lesson.access_level || 'Free'}
Read time
{readingMinutes > 0 ? `${readingMinutes} min` : 'Quick read'}
)
}
function SectionBlock({ section, isActive = false }) {
if (!section?.is_visible) return null
return (
Course section
{section.order_num + 1}
{section.title}
{section.description ?
{section.description}
: null}
{section.lessons?.length || 0} lessons
{isActive ? Reading now : null}
{(section.lessons || []).map((lesson) => (
))}
)
}
export default function AcademyCoursesShow({ seo, course, sections = [], unsectionedLessons = [], pricingUrl }) {
const flash = usePage().props.flash || {}
const cover = course?.cover_image_url || course?.cover_image || course?.teaser_image_url || course?.teaser_image || ''
const progress = course?.progress || null
const sectionJumpItems = useMemo(
() => [
...(unsectionedLessons.length ? [{ id: 'course-outline-core', label: 'Core lessons', count: unsectionedLessons.length }] : []),
...sections
.filter((section) => section?.is_visible)
.map((section) => ({ id: `section-${section.id}`, label: section.title, count: (section.lessons || []).length })),
],
[sections, unsectionedLessons],
)
const [activeJumpId, setActiveJumpId] = useState(sectionJumpItems[0]?.id || null)
const breadcrumbs = [
{ label: 'Academy', href: '/academy' },
{ label: 'Courses', href: '/academy/courses' },
{ label: course?.title || 'Course', href: course?.public_url || '#' },
]
useEffect(() => {
if (!sectionJumpItems.length || typeof window === 'undefined' || typeof IntersectionObserver === 'undefined') {
return undefined
}
const observer = new IntersectionObserver(
(entries) => {
const visibleEntries = entries.filter((entry) => entry.isIntersecting).sort((left, right) => right.intersectionRatio - left.intersectionRatio)
if (!visibleEntries.length) return
setActiveJumpId(visibleEntries[0].target.id)
},
{
rootMargin: '-20% 0px -55% 0px',
threshold: [0.2, 0.45, 0.7],
},
)
const elements = sectionJumpItems.map((item) => document.getElementById(item.id)).filter(Boolean)
elements.forEach((element) => observer.observe(element))
return () => observer.disconnect()
}, [sectionJumpItems])
return (
{flash.success ?
{flash.success}
: null}
{flash.error ?
{flash.error}
: null}
{cover ?
: null}
Academy course
{course?.difficulty}
{course?.access_level}
{progress?.percent ? {progress.percent}% complete : null}
{course?.title}
{course?.subtitle ?
{course.subtitle}
: null}
{course?.excerpt || course?.description}
{cover ? (
) : (
No course cover image yet
)}
{unsectionedLessons.length ? (
) : null}
{sections.filter((section) => section?.is_visible).map((section) => (
))}
)
}