Add student questions feature design spec
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user