Add student questions feature design spec

This commit is contained in:
2026-05-19 12:52:57 +05:00
parent a27089bc0c
commit ec128f670a
@@ -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