v1.0.42: Closing the Gap on the Learner Course Player
v1.0.42: Closing the Gap on the Learner Course Player
Overview
With v1.0.42 we've identified and formally specified a critical missing piece in the learner journey: the course player page. The backend infrastructure to support a full lesson-viewing and progress-tracking experience has been in place for some time, but no frontend route exists to connect learners to that functionality.
This post explains the gap, what already works, and what needs to be built.
What Already Works
SSO Provisioning & Onboarding
Learners are onboarded through their organization's identity provider. The onboarding flow (src/app/onboarding/onboarding-client.tsx) and the org-creation path both call trpc.org.create successfully. Once a learner authenticates via SSO, they are provisioned into the platform and enrolled in the appropriate courses.
Backend Progress Router
The learner router (src/lib/routers/progress.ts) already implements everything needed to power a fully functional course player:
| Procedure | Purpose |
|---|---|
getCoursePlayer | Returns course structure, lesson list, and content for a given course |
getEnrolledCourses | Returns all courses a learner is enrolled in |
markLessonViewed | Records when a learner opens a lesson |
markLessonComplete | Marks a lesson as finished |
updateVideoPosition | Saves the learner's playback position for mid-video resume |
The Gap
Despite the backend being ready, there is no page route for the course player. A learner who successfully authenticates and is enrolled in a course has nowhere to navigate to view that content.
The expected route — something like /dashboard/courses/[courseId]/player or /dashboard/courses/[courseId]/learn — does not exist. This means:
- Course content imported from Teachable (or created natively) cannot be viewed by learners.
- Progress tracking procedures go uncalled.
- The SSO enrollment flow completes successfully but leads to a dead end.
The Fix: learn/page.tsx
The specified fix is to create the following file:
src/app/dashboard/courses/[courseId]/learn/page.tsx
This page is responsible for the complete lesson-viewing experience. Here is what it must implement:
1. Fetch Course Data
On load, the page calls learner.getCoursePlayer with the courseId from the route params. This returns the full course structure — sections, lessons, content type, and any video URLs or document attachments.
2. Render the Lesson Player
The page renders the active lesson's content. Depending on lesson type this may include:
- Video player — for video lessons imported or linked from Teachable
- Text/rich content — for lecture pages with embedded images or documents
3. Progress Tracking
The page integrates with the three progress-tracking procedures:
// When a learner opens a lesson
progress.markLessonViewed({ lessonId })
// When a learner completes a lesson
progress.markLessonComplete({ lessonId })
// Periodically during video playback (e.g. every 10s or on pause)
progress.updateVideoPosition({ lessonId, positionSeconds })
Calling updateVideoPosition regularly ensures learners can resume a video from where they left off, even across sessions or devices.
Why This Matters
The course player page is the core value-delivery surface of the platform. Everything upstream — Teachable import, SSO provisioning, enrollment — exists to get a learner to this page. Until it exists, the end-to-end learner flow is incomplete regardless of how well the rest of the system functions.