Tasks 8–10: student-facing questions UI — list page with unread badges,
new question form with client-side submission, and thread page with
QuestionThread component for real-time reply + file attachment flow.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add CLOSED status guard in messages POST (returns 409)
- Add extension allowlist check in upload route + text/x-markdown MIME type
- Sanitize files JSON array before DB write
- Add sendQuestionFollowUpEmail helper and use it for student follow-up replies
- Scope email field to staff only in questions list query
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements GET/POST /api/questions, GET /api/questions/[id] with read tracking, POST /api/questions/[id]/messages with email notifications, PATCH /api/questions/[id]/close for staff, and POST /api/student/question-upload for file attachments.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Переопределены токены шкалы Tailwind (--text-xs…--text-5xl) на +2px,
базовый размер body 18px, размеры компонентных классов (.btn-aubade,
.tag-aubade, .admin-sidebar-nav-link) и инлайновые fontSize приведены
к канону дизайн-системы ДС-2. Rem-база (html 16px) не тронута —
спейсинг и сетка не затронуты, растёт только текст.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Hamburger button (top-left, lg:hidden), dark overlay, slide-in animation.
Sidebar closes on lesson link click. Spacer added to prevent content overlap.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces client-side fetch with a proper Server Action + useActionState.
Uncontrolled inputs fix agent-browser testing and improve reliability.
Server action verifies bcrypt hash directly via Prisma.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
authClient.changePassword does not exist in better-auth v1.6 client bundle.
Use direct POST to /api/auth/change-password endpoint instead.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New page /profile: shows name/email and password change form
- Uses authClient.changePassword (current + new + confirm)
- Student name in header is now a link to /profile
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
tw-animate-css, shadcn, @tailwindcss/typography were removed in F008
but their @import/@plugin lines remained in globals.css, breaking the build.
CSS variables are defined inline so none of these imports are needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Users can now request a password reset link via email. Better Auth
sendResetPassword callback sends a branded email via Resend. Login
page shows success notice after password is set.
Co-Authored-By: Claude Sonnet 4.6 <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 coverImage field to Lesson model (prisma)
- Pass coverImage as poster prop to KinescopePlayer
- Show quiz in read-only preview mode for admin on lesson page
- Fix TipTap v3 editor reset on save: pass [lesson.id] as deps to useEditor
to prevent setOptions() from reinitializing content on every re-render
- Replace saveLesson Server Action call with fetch PATCH /api/admin/lessons/[id]
to avoid Next.js 16 automatic RSC refresh after Server Actions
- Simplify revalidatePath: only revalidate module page, not lesson editor page
- 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>
POST /api/admin/lesson-files now checks for an existing record with the
same (lessonId, name) before uploading — replaces it (old S3 object
deleted) instead of always creating a new one. Previously every save
cycle accumulated an extra copy; 1183 duplicates occupying 6.5 GiB were
found and cleaned up.
DELETE now receives the file URL and extracts the S3 key from it, so
manual deletion actually removes the object from storage.
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>
- Wire settings to actual platform behavior: maintenance mode, registration toggle,
notification emails, curator feedback emails, email verification flag
- Add logo (logoUrl, showLogo) and social network links (YouTube, VK, Telegram) settings
- Show logo + school name dynamically in student layout header
- Add footer to student layout with org requisites and social links
- Register page: read settings server-side, validate terms checkbox with legal links
- Login page: show notice when redirected from closed registration
- Settings form: add Logo and Social Networks sections
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- /admin/quizzes: list all quizzes with question and attempt counts
- /admin/quizzes/[quizId]: view all student attempts with answers per question
- Add "Тесты" link to admin sidebar navigation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Show "Отметить как пройденный" button only on lessons without homework
- Show static "Пройдено" badge on homework lessons completed via approval
- Auto-create LessonProgress when curator/admin approves homework submission
- Revalidate student lesson, course, and dashboard pages on approval
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Store human-readable label in LessonFile.name via optional label field on upload
- Add PATCH endpoint to rename existing files inline
- Admin: label input before upload, click-to-edit inline rename
- Student: colored format badge (PDF/DOCX/XLSX/ZIP/etc) replaces paperclip emoji
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prisma 7 proxy objects (DateTime, _count, relations) cannot be
serialized by React RSC. Convert all course page data to plain
JSON objects with JSON.parse/stringify before passing as props.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prisma 7 wraps Json fields in proxy objects that RSC cannot serialize.
Fix: select specific columns (exclude content) in module page,
and JSON.parse/stringify lesson content before passing to client.
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>
Homework (/curator/homework):
- Search by student name/email
- Filter by status (pending/reviewed) and course
- Server-side pagination (20 per page) with URL params
Users (/admin/users):
- Search by name/email, filter by role
- Hover popup on each row: enrolled courses + expiry dates + email
- Pagination (20 per page) with URL params
Comments (/admin/comments):
- New admin page with all active comments
- Search by author or text content
- One-click delete (soft-delete) from the table
- Pagination (30 per page)
- Added "Комментарии" link to admin nav
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Server action createUser() with bcrypt password hash + Account record
- Form with name, email, password (show/hide + generate), role, emailVerified toggle
- Optional welcome email toggle (bypasses auto-hook for admin-created users)
- /admin/users/new page with breadcrumb navigation
- After creation, redirects to the new user's profile page
- "Добавить пользователя" button on the users list page
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>
- 4 stat cards: students (+monthly), courses (published), active enrollments (expiring alert), homework pending
- Recent enrollments list (last 8)
- Top courses by enrollment count
- Activity counters: total lessons completed, total homework submitted
- All cards link to relevant admin pages
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- src/lib/email.ts: HTML templates for 4 email types (Second Brain design)
- Welcome email on user registration (Better Auth databaseHooks)
- Course access email when admin grants enrollment
- Homework submitted email to all admins/curators (first submission only)
- Feedback received email to student with feedback text and lesson link
- Update TECHNICAL.md: Resend domain, from-address, email vars, Stage 3 summary
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>