Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

07 — Component Library (Homework Module)

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: AppCard flat (e-0) + ListTile-like layout; subject AppChip (readonly); status AppBadge (active → secondaryContainer, closed → outline); my-state SubmissionStatusChip; due line bodyMedium (onSurfaceVariant), overdue → tertiary + schedule icon; attachment count icon attachment.
  • 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 via DateTime.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 = AppBadge status (submitted → tertiaryContainer "Pending"; graded → secondaryContainer "Graded") + marks AppBadge(mono); late → LateBadge.
  • A11y: label "Student name, submitted 3 days ago, pending, late by 2 days".
  • Flutter: ListTile in AppCard e-0.

LateBadge (AppBadge variant)

  • Purpose: submittedAt > dueDate indicator (OQ-1 — server has no flag; client-derived).
  • Props: lateBy (Duration), compact?.
  • Rendering: tertiary text + schedule icon 16, chip tertiaryContainer; text "Late · 2d".
  • A11y: always icon + text (never color-only, 09 §9); semantics "submitted late by 2 days".
  • Flutter: Badge/custom Container + 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: Chip with avatar icon.

GradeFeedbackCard

  • Purpose: student sees marks + remarks after grading.
  • Props: marks?, remarks?, gradedAt?, submittedAt, late.
  • Rendering: AppCard tonal (secondaryContainer); marks displaySmall mono tabular figures + "/{max}" config; remarks AppMarkdownViewer; 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: ListTile rows + LinearProgressIndicator per 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 files collection (file.schema.ts:8-43), fetched by id; download via GET /files/:id/download (files.controller.ts:55-64).
  • Flutter: AppAttachmentTile list.

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: AppTextField numeric + AppTextField multiline + regrade AppBanner.
  • Flutter: Form + TextFormField validators.

D. Status & feedback

  • Reuses AppBanner (info: "Already submitted"; warning: late, regrade), AppSnackbar, AppErrorState, AppEmptyState, AppOfflineBanner, AppSkeleton from 00-shared/03 with no module changes.

E. Only elements the API supports

  • Homework/submission lists are non-paginated arrays (findByClass, findByHomework return full arrays — homework.repository.ts:17-21, homework-submission.repository.ts:20-24) → no infinite scroll; render all. Pagination (planned) when endpoints grow page/limit.
  • No "my submissions" bulk endpoint (OQ-6) → detail screen fetches GET /homework/:id/submissions for the single homework and filters by studentId.

F. Flutter widget map (module)

ComponentFlutter widgets
HomeworkCardCard + InkWell + Chips + Badge
SubmissionRowListTile + Badge + MenuAnchor
LateBadgeContainer + Icon + Text
SubmissionStatusChipChip
GradeFeedbackCardCard.filled + AppMarkdownViewer
AppAttachmentUploaderRow of ListTile + LinearProgressIndicator
AppAttachmentListListView of AppAttachmentTile
GradeSheetBodyForm + 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).