07 — Component Library (Homework Module)
- A. Homework representation
- B. Attachments
- C. Grading surface
- D. Status & feedback
- E. Only elements the API supports
- F. Flutter widget map (module)
- G. Golden tests (base 00-shared/03 G)
Module-specific components built from shared
App*primitives (00-shared/03). Props, states, animation, a11y, tokens, Flutter mapping. Reuse over new — only components that don't exist in the shared library are defined here.
A. Homework representation
HomeworkCard
- Purpose: one homework row in the list (teacher + student variants).
- Props:
title,subjectLabel,classLabel,dueDate,assignedDate,status(active/closed,homework.schema.ts:33-34),attachmentCount,myState?(none|submitted|graded|overdue),overdue,onTap,menu?(teacher: edit/delete). - Rendering:
AppCardflat (e-0) +ListTile-like layout; subjectAppChip(readonly); statusAppBadge(active →secondaryContainer, closed →outline); my-stateSubmissionStatusChip; due linebodyMedium(onSurfaceVariant), overdue →tertiary+scheduleicon; attachment count iconattachment. - States: default / selected (master-detail) / disabled (deleted in flight).
- A11y: single
Semantics(button)— "Algebra Worksheet, Mathematics, due 15 Aug, not submitted"; menu separately labeled. - Tokens:
surfaceContainerLow,R-md,El-e0/e1. - Flutter:
Card+InkWell+Row/Column; date viaDateTime.toDisplayDate(locale)(00-shared/11 §8).
SubmissionRow
- Purpose: one submission row in the teacher's submission list.
- Props:
studentName,submittedAt,status(submitted/graded),marks?,remarks?,attachmentCount,isLate(client-computed),onTap,onGrade?. - Rendering:
AppAvatar(initials) leading; title = student name; subtitle = "Submitted {relative} · {attachmentCount} attachments"; trailing =AppBadgestatus (submitted →tertiaryContainer"Pending"; graded →secondaryContainer"Graded") + marksAppBadge(mono); late →LateBadge. - A11y: label "Student name, submitted 3 days ago, pending, late by 2 days".
- Flutter:
ListTileinAppCarde-0.
LateBadge (AppBadge variant)
- Purpose:
submittedAt > dueDateindicator (OQ-1 — server has no flag; client-derived). - Props:
lateBy(Duration),compact?. - Rendering:
tertiarytext +scheduleicon 16, chiptertiaryContainer; text "Late · 2d". - A11y: always icon + text (never color-only, 09 §9); semantics "submitted late by 2 days".
- Flutter:
Badge/customContainer+Icon.
SubmissionStatusChip
- Purpose: student-facing submission state on homework cards/detail.
- Props:
state(none|submitted|graded|overdue),marks?. - Mapping: none →
AppBadge(outline)"Not submitted"; submitted →AppBadge(tertiaryContainer)"Submitted"; graded →AppBadge(secondaryContainer)"Graded · 17/20"; overdue →AppBadge(tertiary)"Overdue". - Flutter:
Chipwithavataricon.
GradeFeedbackCard
- Purpose: student sees marks + remarks after grading.
- Props:
marks?,remarks?,gradedAt?,submittedAt,late. - Rendering:
AppCardtonal (secondaryContainer); marksdisplaySmallmonotabular figures + "/{max}" config; remarksAppMarkdownViewer; footer "Graded {relative} · Submitted {relative}". - A11y: label "Grade 17 out of 20. Feedback: {remarks}".
- Flutter:
Card.filled+Row.
B. Attachments
AppAttachmentUploader
- Purpose: multi-file upload inside create/submit forms; drives
POST /files/upload(files.controller.ts:29-41). - Props:
files[],onAdd,onRetry,onRemove,maxCount?(client default 5),maxSizeMB?(client default 25 — no server limit exists, OQ-4/01; enforce client-side),acceptTypes?(client hint; server accepts anything). - Per-file state machine:
idle → uploading(progress 0..1) → uploaded(fileId) | failed(reason); removal allowed in idle/uploaded/failed. - Rendering:
AppAttachmentTile(name, size, type, uploading %, retry, remove) + "Add attachment"OutlinedButton. - A11y: progress announced (
liveRegion"Uploading {name}, 45%"); remove labeled. - Flutter:
ListTilerows +LinearProgressIndicatorper file;file_picker-style picker (verify license; wrap for swappability).
AppAttachmentList
- Purpose: read-only attachment display (homework detail, submission detail).
- Props:
items[{id, name, size, mimeType}],onPreview,onDownload. - Behavior: tap → preview (PDF/image in-app) or share sheet; long-press menu (Save, Share).
- Source: file metadata comes from the
filescollection (file.schema.ts:8-43), fetched by id; download viaGET /files/:id/download(files.controller.ts:55-64). - Flutter:
AppAttachmentTilelist.
C. Grading surface
GradeSheetBody
- Purpose: the marks+remarks form body of the grading sheet (reused for regrade).
- Props:
initialMarks?,initialRemarks?,maxMarks(client default 100),regrade(bool),onSubmit(marks, remarks),submitting. - Validation: required, numeric,
0 ≤ marks ≤ maxMarks(server has no bounds —submission.dto.ts:20-21, OQ-4); remarks ≤ 2000 chars (client). - Rendering:
AppTextFieldnumeric +AppTextFieldmultiline + regradeAppBanner. - Flutter:
Form+TextFormFieldvalidators.
D. Status & feedback
- Reuses
AppBanner(info: "Already submitted"; warning: late, regrade),AppSnackbar,AppErrorState,AppEmptyState,AppOfflineBanner,AppSkeletonfrom 00-shared/03 with no module changes.
E. Only elements the API supports
- Homework/submission lists are non-paginated arrays (
findByClass,findByHomeworkreturn full arrays —homework.repository.ts:17-21,homework-submission.repository.ts:20-24) → no infinite scroll; render all. Pagination(planned)when endpoints growpage/limit. - No "my submissions" bulk endpoint (OQ-6) → detail screen fetches
GET /homework/:id/submissionsfor the single homework and filters bystudentId.
F. Flutter widget map (module)
| Component | Flutter widgets |
|---|---|
| HomeworkCard | Card + InkWell + Chips + Badge |
| SubmissionRow | ListTile + Badge + MenuAnchor |
| LateBadge | Container + Icon + Text |
| SubmissionStatusChip | Chip |
| GradeFeedbackCard | Card.filled + AppMarkdownViewer |
| AppAttachmentUploader | Row of ListTile + LinearProgressIndicator |
| AppAttachmentList | ListView of AppAttachmentTile |
| GradeSheetBody | Form + TextFormField |
G. Golden tests (base 00-shared/03 G)
Every module component golden at phone/tablet/desktop + dark; required state goldens:
HomeworkCard (submitted/graded/overdue/closed), LateBadge, AppAttachmentUploader
(uploading/error/complete), GradeSheetBody (empty/invalid/regrade).