Adds a Prisma model for per-student Clean PDF API keys plus a
hand-written migration (no local Postgres to run `migrate dev`
against). getOrCreatePdfKey/regenerateKey wrap the model with
lazy-creation and rotation logic on top of generatePdfKey().
Inside-LMS toolbox for registered students: callout-CSS, YAML-frontmatter,
theme CSS-variables, Style Settings generators. Auth inherited from (student)
route group; no public surface, no email gate.
- Pure generators in src/lib/tools/* with Vitest (33 tests)
- Shared YAML-safe scalar quoter (_shared/yaml.ts) used by frontmatter +
style-settings: unquoted user input was silently breaking YAML (tags '#x'
-> null, color default '#7C3AED' -> null, 'a: b' -> parse error)
- hex validation in callout (no NaN), date input constrained, clipboard +
analytics calls guarded
- Prisma ToolUsage model + migration; logToolUsage Server Action (auth-first,
10-min dedup window per user/tool)
- Tool index /tools + ToolCard (explicit lucide map, no import *) + header link
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- /api/internal/grant: secret-auth endpoint for payment-router to
provision access on a paid order — find-or-create user (emailVerified,
temp password, mustChangePassword), enroll by course slug list,
AccessLog, delivery email. Idempotent by order_id.
- User.mustChangePassword flag (+ migration); (student) layout redirects
flagged users to /change-password (forced first-login change).
- email.ts: sendCourseGrantEmail (temp password for new buyers).
- middleware: /api/internal is public (own secret auth).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduces BalanceTransaction model to track per-user balance history
(prepayments, refunds, partner credits). Admin can add/delete transactions;
current balance is computed as the running sum.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add parentId field to LessonComment (self-referential FK, SetNull on delete)
- Show replies indented under parent comment with left border visual
- Add reply button (visible to admin/curator only) with inline textarea
- Only root comments shown in main list; replies nested below their parent
- Update comment count to include replies
- Server-side validation: only admin/curator can reply, parent must belong to same lesson
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add phone/birthday columns via Prisma migration
- Admin user page shows phone and birthday with inline edit UI
- UserContactEditor client component for editing contact info
- updateUserContact server action with admin-only guard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Course: add allowAudio toggle (per-course setting, off by default)
- HomeworkSubmission: add audioUrl field
- Student: AudioRecorder in homework form when allowAudio is enabled
- Student: show audio player in submission view and curator feedback view
- Curator: show student audio on submission detail page
- AudioRecorder: accept uploadUrl prop (reused for student/curator)
- API: /api/student/audio-upload route for S3 upload
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CourseTree: expandable module/lesson overview with Eye/Video icons
- SortableLessons: Kinescope ID in create form, published toggle, move-to-module dropdown
- Actions: toggleLessonPublished, moveLessonToModule, updateModule with description
- Schema: add description field to Module model + migration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Settings key-value table in Prisma with migration
- getSettings() / getSetting() helpers in lib/settings.ts
- Admin UI at /admin/settings with 6 sections: General, Notifications,
Student profile, Legal docs, Curator permissions, Code injection
- saveSettings() server action with admin-only guard
- Maintenance mode: non-admin users redirected to /maintenance page
- schoolName propagated to page metadata and all email templates
- headCode / bodyCode injected into root layout <head> and <body>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>