From ec128f670a76abe36834d84596ab2586390b44f5 Mon Sep 17 00:00:00 2001 From: dmitriylaukhin Date: Tue, 19 May 2026 12:52:57 +0500 Subject: [PATCH] Add student questions feature design spec --- .../20260519-student-questions-design.md | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 docs/specs/20260519-student-questions-design.md diff --git a/docs/specs/20260519-student-questions-design.md b/docs/specs/20260519-student-questions-design.md new file mode 100644 index 0000000..6b794ed --- /dev/null +++ b/docs/specs/20260519-student-questions-design.md @@ -0,0 +1,159 @@ +# Student Questions — Design Spec +_Created: 20260519_ + +## Overview + +A support-chat feature inside the LMS. Students ask questions, school staff (admin/curator) answers. Each question is a threaded conversation with open/closed status. Includes file attachments and email notifications for all parties. + +Also in scope: email notifications for homework submissions (new + student updates). + +--- + +## Data Model + +### StudentQuestion +``` +id String @id @default(cuid()) +userId String -- student who created it +courseId String? -- optional course context +title String +status QuestionStatus @default(OPEN) -- OPEN | CLOSED +closedAt DateTime? +closedById String? -- admin/curator who closed +createdAt DateTime @default(now()) +updatedAt DateTime @updatedAt + +user User +course Course? +messages StudentQuestionMessage[] +``` + +### StudentQuestionMessage +``` +id String @id @default(cuid()) +questionId String +authorId String +text String +files String[] -- S3 paths: questions/{questionId}/{messageId}/{filename} +isRead Boolean @default(false) +createdAt DateTime @default(now()) + +question StudentQuestion +author User +``` + +### QuestionStatus (enum) +``` +OPEN +CLOSED +``` + +--- + +## Routes + +### Student +| Route | Description | +|---|---| +| `(student)/questions` | List of own questions with unread indicators | +| `(student)/questions/new` | Form to create a new question | +| `(student)/questions/[id]` | Thread view — read messages + reply | + +### Admin / Curator +| Route | Description | +|---|---| +| `admin/questions` | Split-view: question list left, thread right | +| `curator/questions` | Same split-view (curators have same access as admins here) | + +--- + +## UI Behaviour + +### Student — Questions List (`/questions`) +- Header: "Мои вопросы" + "+ Задать вопрос" button (→ `/questions/new`) +- Each row: title, message count, last activity, status badge (ОТКРЫТ / ЗАКРЫТ) +- Unread indicator: black dot + "Новый ответ от школы" when school replied since last student visit +- Closed questions: dimmed (opacity 0.7), grey badge +- Active (unread) questions: bold border, bold title + +### Student — Thread (`/questions/[id]`) +- Header: question title, created date, status, "← Все вопросы" link +- Message bubbles: student messages left (#E8E8E0), school messages right (#F5F5F0 + green border #E8F0D8) +- New school message: bold label "🔵 новое" in timestamp +- File attachments shown inline under message text (📎 filename · size) +- Reply form at bottom: textarea + attach button + send button +- Attachment types: jpg, png, pdf, md · max 10 MB +- Student CAN reply to closed questions (creates new message, does NOT reopen question) + +### Admin/Curator — Split View (`/admin/questions`, `/curator/questions`) +- Left panel (45%): tab filter "Открытые / Закрытые", question list sorted by last activity +- Unread: red dot, green-tinted background, bold student name +- Right panel (55%): selected thread with full message history + reply form + "✓ Закрыть вопрос" button +- Only admin/curator can close a question +- Closing a question does NOT prevent further messages + +--- + +## Notifications + +### → School (admin + all curators) +| Trigger | Channel | +|---|---| +| New question created | Email + admin sidebar badge (count of unread questions) | +| Student adds message to existing question | Email | + +### → Student +| Trigger | Channel | +|---|---| +| Admin/curator replies to question | Email | + +### Admin Badge +- Sidebar badge shows count of questions with unread messages (school hasn't seen yet) +- Separate from homework badge + +### Email for Homework (added to scope) +| Trigger | Recipient | +|---|---| +| New HomeworkSubmission created | Admin + all curators | +| Student updates existing submission (adds text/file) | Admin + all curators | + +--- + +## File Storage + +- Path pattern: `questions/{questionId}/{messageId}/{filename}` +- Reuse existing S3 upload infrastructure (`src/lib/s3.ts`) +- Allowed: jpg, png, pdf, md +- Max size: 10 MB per file +- No limit on number of files per message (reasonable: 5) + +--- + +## Read Tracking + +- `isRead` flag per message, set to `true` when the OTHER party opens the thread +- Student opens `/questions/[id]` → all school messages in that thread marked `isRead = true` +- Admin/curator opens a question in split-view → all student messages marked `isRead = true` +- Admin badge recalculates on each page load (count questions where latest student message is unread) + +--- + +## API Routes + +``` +POST /api/questions -- create question +GET /api/questions -- list own questions (student) or all (admin/curator) +GET /api/questions/[id] -- get question + messages +POST /api/questions/[id]/messages -- add message + files +PATCH /api/questions/[id]/close -- close question (admin/curator only) +POST /api/upload/question-file -- upload attachment, returns S3 path +``` + +--- + +## Out of Scope (this iteration) +- Question categories / tags +- Assigning question to a specific curator +- Email threading (reply-to email to answer) +- Push/browser notifications +- Question templates