forgia · dev log
Engineering log for the forgia LMS platform.
- 27 Jun 26One page, three screens: the tabbed Course studio
Managing a Course used to mean bouncing between three separate screens — curriculum, students, settings — each with its own URL, its own header, its own back button. This slice folds them into a single tabbed page with a persistent header and the Publish button re-homed where you actually look for it. No new features inside the tabs: every tab is the old screen, relocated unchanged. The interesting decisions are structural — one LiveView for every tab, deep-linkable routes switched with push_patch, legacy URLs that redirect so nobody's bookmarks break, and a breadcrumb deliberately deleted.
sliceliveviewroutingdesign-systemrefactor - 25 Jun 26A Certificate is a promise you can't take back
Students now earn a publicly verifiable Certificate the moment they complete every Lesson in a Course. The feature is small; the decision underneath it isn't. A credential that can be revoked retroactively — because the Instructor added a Lesson, or the Student renamed their Profile — is worthless. So the Certificate is a permanent, immutable snapshot. Plus the name gate that defers issuance, why issuance lives in the domain and not the LiveView, and a 320px overflow the design mockup didn't warn us about.
slicecertificateliveviewdddadrdesign-system - 22 Jun 26A profile page, two navbars, and a Profile that doesn't exist yet
Users can now set their own name and avatar on a /profile page. The decision worth writing down isn't the form — it's that a Profile is created lazily, on the first Save, because self-registered magic-link users never had one. Plus the Ecto trap that silently skips the write, why the dropdown got built twice, and the value of reading the design system before inventing CSS.
sliceprofileliveviewectodesign-systemddd - 21 Jun 26Selling a paid course: Paddle, a webhook, and the art of waiting
A Student can now buy a paid Course. The interesting parts aren't the Buy button — they're the decisions underneath: the price you see is Paddle's, not ours (localized by IP); access is granted only by a signature-verified webhook, never by the browser saying "I paid"; and because that webhook is asynchronous, the panel shows a "Finalizing…" state that polls itself into "Continue Learning" with no refresh. Plus the idempotency that makes a double-delivered webhook a no-op.
slicepaymentspaddlewebhookenrollmentliveviewddd - 20 Jun 26Student dashboard: a home that answers "where was I?"
The Student now logs in to a "My learning" dashboard instead of the public catalog. One card per Enrollment, each with a progress bar, the lessons still remaining, and a one-click "Continue" straight to the next incomplete Lesson. No new progress math — the screen is a read model over Completions we already record — and the ordering, the edge cases, and what we deliberately left out are where the design actually lives.
slicedashboardprogressenrollmentstudentliveviewddd - 20 Jun 26Invite Students: granting a course before the account exists
The Instructor can now enroll people who have no account yet. Instead of pre-creating shell accounts and mailing magic-link accept tokens, an invite is a standing Invitation bound to an email — claimed into a real Enrollment and Profile only when that exact address confirms an account. No tokens, no expiry, no resend machinery.
sliceenrollmentinvitationinstructorliveviewdddgdpr - 19 Jun 26Manual Enrollment: granting access by email
The Instructor can now manually enroll existing students into a Published course by email — with a live preview that flags valid, already-enrolled, and invalid rows, idempotent enrollment, and a notification email. The first bite of a deliberately sliced epic.
sliceenrollmentinstructorliveviewstudentsddd - 17 Jun 26Studio Usability Polish
A batch of usability fixes across the instructor studio: a styled confirmation dialog before destructive deletes, a reorder bug fix for tied positions, names-as-links for cleaner grids, auto-focus on the first field, and a dev-only home page that maps both perspectives.
polishusabilityliveviewdesign-systemstudio - 17 Jun 26Student Takes a Quiz
The student-facing side of Quiz Lessons: one question at a time, immediate per-question feedback, a resumable in-progress attempt, a stale-reset when the instructor edits mid-attempt, and a Completion recorded on the final correct answer.
liveviewquizectodesign-systemddd - 17 Jun 26Slice 7: Quiz Lessons
Instructors can now add Quiz Lessons with single-choice Questions — a three-panel editor for writing questions, typing answer options, marking the correct one, and reordering. Students taking the quiz is a separate slice.
slicequizquestionsliveviewectodesign-system - 02 Jun 26Slice 6: Video Lessons
Instructors can now attach a Bunny Stream video to any Lesson. The video renders above the body content in the student view — paste the URL from the browser address bar and the system handles the rest.
slicevideobunny-streamlessonsliveviewcomponent - 31 May 26Slice 5: Track Lesson Progress
Students can now mark Lessons as complete, track Progress as a percentage on the Course page, and see per-Lesson checkmarks in the syllabus — all in real time, with no page reload.
sliceprogresscompletionliveviewstudents - 31 May 26Slice 4: Enroll in a Free Course
Students can now browse the course catalog, create an account, enroll in a free course in one flow, and read lessons in a clean sidebar layout — all without touching a line of JavaScript.
sliceenrollmentauthcatalogliveviewstudents - 31 May 26Code Review: 10 Security and Quality Fixes
A code review pass found 10 issues across the codebase — from a cross-course lesson access hole to a TOCTOU race in enrollment. Here's what was fixed and why.
securitycode-reviewhardeningliveviewecto - 30 May 26Slice 3: Image Upload in Lesson Editor
Course authors can now upload images directly into their Markdown lessons. Drag a file, watch the live upload feedback, and the image URL is inserted right at the cursor.
sliceuploadimageslessonsb2liveviewcomponent - 29 May 26Slice 2: Build and Publish a Course
The Instructor can now structure a Course into Chapters and Text Lessons, reorder them, write content in Markdown with a live preview, and publish the Course in one click.
slicecourseschapterslessonsmarkdownliveview - 29 May 26Slice 1: Create and Edit a Course
First working slice: the Instructor can create a Course with title, description, price, and language — and edit it after creation.
slicecoursesphoenixliveview