Show the course curriculum to users who haven't purchased - #449
Merged
Conversation
The course dashboard rendered only a marketing page for non-purchasers, so free lessons existed but could not be discovered. Add a curriculum outline listing every published module and lesson, with free lessons linked and the rest locked behind a tooltip. Replace the bare 403 on paid lessons with a redirect to the course page plus an explanation, and make locked lessons in the lesson sidebar clickable so they route through it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
simonhamp
marked this pull request as ready for review
July 30, 2026 02:51
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Free course modules were never visible on the course dashboard.
customer/course.blade.phpgated its whole body on@if ($this->hasPurchased || $this->isAdmin), so non-purchasers got only the marketing page — no module listing existed in that branch, andCourse\Indexexposed nothing to render one. Free lessons were already reachable (LessonShow::mount()allowsis_freewithout purchase), so the access plumbing worked; there was just no way to discover them.Visiting a paid lesson without purchase was also a dead end:
abort(403, 'You need Pro access...')with noresources/views/errors/403.blade.phpin the project, so it fell through to Laravel's bareerrors::minimalpage — no layout, no branding, no link anywhere. The copy was misleading too: "Pro" means a subscription tier here (policy_namein['max','pro','mini']), but the masterclass is a one-time product purchase.What changed
Curriculum outline for non-purchasers.
Course\Index::outlineModules()returns published modules with at least one published lesson. Every lesson is listed — free ones link out, the rest render a lock with a "Buy the course to unlock all lessons" tooltip. Collection keys are deliberately preserved so modules are numbered by real course position, not position within the filtered subset.Paid lessons redirect instead of 403ing.
LessonShow::mount()flashes an explanation and redirects to the course page, which already renders the pricing and CTA — turning a dead end into the page most likely to convert. The course page rendered no flash messages at all, so that markup was added.Locked sidebar lessons are clickable. In
lesson-show.blade.php, anything published is now a link and routes through the redirect above; drafts stay unclickable with "Coming Soon".wire:navigateis omitted on locked links so the 302-and-flash is a plain navigation.Layout. The not-purchased page is ordered hero → feature cards → locked-lesson notice → curriculum. The notice sits inside the outline's
max-w-7xlcontainer:app.cssstrips the main container's padding via[data-flux-main]:has(.course-fullbleed), so anything outside an inner container renders edge-to-edge. Bottom padding moved onto the.course-fullbleedwrapper so spacing holds when the curriculum block is absent.Copy. Hero pill "New Course" → "Masterclass" (both the early-bird and post-deadline variants).
Notes for review
is_freeremains unused for access control — gating is entirely lesson-levelis_free. A module flagged free whose lessons aren't will show locked lessons. Left as-is; worth deciding whether that flag should drive access or be removed.session('message')no longer renders for purchasers/admins on this page. Harmless today (only the locked-lesson redirect sets it, which can't fire for them), but a constraint to be aware of.403.blade.phpwas added — otherabort(403)calls elsewhere still hit the bare vendor page.Tests
tests/Feature/CourseContentTest.php: 50 passing, up from 35. Covers the curriculum listing, both exclusion paths (unpublished lesson, unpublished module), paid-module inclusion, mixed free/paid modules, module numbering, the redirect and its flash end-to-end over HTTP, the redirect underapp.debug => false, the 404 path for unpublished lessons, sidebar link behaviour for locked vs draft lessons, and section ordering.79 passing across
CourseContentTest,CoursePageTest,HomeCourseCardTest,DashboardLayoutTest. Pint clean.🤖 Generated with Claude Code