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

06 — Screen Specifications (Homework Module)

Production-grade specs for each Homework screen: chrome, states, components, micro-interactions, a11y, responsive, dark/light, keyboard, motion. Tokens: Sp = 00-shared/02 §3, Ty = typography §2, El = elevation §5, R = radius §4, Mo = motion §6. All components reference 00-shared/03 and 07 (module).


1. Homework List /homework

Chrome: AppBar "Homework" (titleLarge, El-e1); class filter AppDropdown under AppBar; body = RefreshIndicator + grouped ListView.builder (group "Due soon" / "Past due" computed client-side vs dueDate); horizontal padding Sp.16 (phone), Sp.24 (tablet+). Teacher FAB AppFAB.extended "New homework" (Icons.add, heroTag unique).

Row (HomeworkCard, teacher & student):

[Subject chip]              [status badge: active/closed]   [my-state chip (student)]
Title                    titleMedium, 2 lines max
Grade 8 - C · Assigned 3d ago   bodySmall onSurfaceVariant
Due 15 Aug · in 5d               bodyMedium; overdue → tertiary + icon schedule
[attachment icon ×N]            bodySmall
  • Meta from doc: title, subjectId (resolved), classId (resolved), dueDate, status (homework.schema.ts:18-34), assignedDate, attachments.length.
  • Late semantics (OQ-1): server never flags late. Client computes isLate = dueDate.isBefore(now) — card shows tertiary badge "Overdue · 2d" + schedule icon (color + icon + text, never color-only — 09 §9). Once the server adds enforcement, render server truth only.
  • States: skeleton (AppSkeleton(list)); empty AppEmptyState "No homework yet" + FAB; error AppErrorState (404 "Class not found" if class invalid, 5xx + requestId); offline AppOfflineBanner + cached list (TTL 5 min, 06 §3.3).
  • Pull-to-refresh: re-fetch + WS resubscribe (planned).
  • Row menu (teacher): Edit, Delete; delete → AppDialog confirm ("Delete this homework? Submissions are kept on the server." — soft delete, no cascade, homework.service.ts:70-81) → DELETE /homework/:id server-confirmed, row fade-out Mo.m-base, snackbar "Deleted".
  • A11y: card = one Semantics(button) label "{title}, {subject}, due {date}"; status badges appended; focus order filter → list → FAB.
  • Dark/light: token-only; overdue badge tertiaryContainer (light) / tertiary-tinted container (dark) with icon+label.
  • Tablet/desktop: master-detail at ≥ 840 dp (list left 360–400 dp, detail right); hover tint on cards; N shortcut = new homework.

2. Homework Detail /homework/:id

Chrome: AppBar title "Homework" + trailing AppMenu (teacher: Edit / Delete); scrollable SingleChildScrollView, sections spaced Sp.24.

Header card (surfaceContainerLow, r-lg, e-1)
  title headlineMedium (Ty)
  chips: subject · class   [status badge]   [my-state chip (student)]
Info card
  AppInfoRow: Teacher (resolved), Assigned (assignedDate), Due (dueDate; overdue styled)
  AppInfoRow: Attachments (N)  → tap opens attachment sheet
Description  AppMarkdownViewer (data = description; sanitized links)
Actions (role-gated):
  student, no submission → FilledButton "Submit" (fullWidth 48h)
  student, submitted     → SubmissionStatusChip + submittedAt + (graded? marks card)
  teacher                → OutlinedButton "Submissions (N)" → /submissions
  parent                 → read-only, no actions

States: loading skeleton; 404 → AppEmptyState "This homework was removed or is not available." (matches server NotFoundException, homework.service.ts:47); offline → banner + cached detail (00-shared/06 §3.3 — detail views cached last-good).

Grade feedback block (student, when my submission status='graded'):

Card (secondaryContainer tint)  "Grade"
  marks  displaySmall mono tabular  ·  badge "Graded"
  remarks → AppMarkdownViewer
  gradedAt bodySmall  ("Graded 2d ago")

marks is Number? (homework-submission.schema.ts:30-31) — absent until graded; render "Pending grade" AppBadge when status='submitted' (schema enum ['submitted','graded'], homework-submission.schema.ts:24).

Deep link studylyon://homework/:id → this screen; notification tap → same.

3. Create Homework Form /homework/new

Chrome: full-screen page (> 3 fields rule, 00-shared/05 §5); AppBar "New homework"

  • Save TextButton; body form list Sp.16 gaps, resizeToAvoidBottomInset: true.
1 class      AppDropdown (Academics classes; required; onChanged → subject options filter)
2 subject    AppDropdown (Subjects; required)
3 title      AppTextField (required; maxLength 200 client; autofocus)
4 description AppTextField multiline 4-6 lines; note "Markdown supported" (AppMarkdownViewer
              renders it — 00-shared/03 E)
5 dueDate    AppDatePicker (required; client min = today; locale-aware; time optional 23:59)
6 attachments AppAttachmentUploader (multi; upload via POST /files/upload per file)

Submit flow: button "Assign homework" → loading spinner; server 201 doc returned (homework.service.ts:27-43); success → detail + snackbar "Homework assigned". Validation errors (400 VALIDATION_ERROR): field-level inline, focus first invalid (submission.dto/homework.dto decorators: IsMongoId, IsString, IsDateString, IsOptionalhomework.dto.ts:4-33). Attachment UX: each file uploads immediately (id → chip); failures retryable per file; homework is only created after all attachments uploaded (separate requests — create must not fire with pending uploads).

4. Submission Form /homework/:id/submit (sheet or page)

Chrome: page (or full-height sheet on phone) with AppBar "Submit homework".

Student identity   (from profile; sent as studentId in body — client fills, hidden)
remarks            AppTextField multiline "Add a note to your teacher" (optional; server
                   @IsOptional @IsString — submission.dto.ts:13-16)
attachments        AppAttachmentUploader (optional; same file.upload flow)
[Submit button]    FilledButton fullWidth "Submit homework"

States:

  • submitting → button spinner, inputs disabled, anti-double-submit (00-shared/08 §6).
  • Success 201 → replace with success view (check icon success, "Submitted on {submittedAt}") → button "View homework" → detail. submittedAt comes from the server doc (homework.service.ts:96).
  • 409 DUPLICATE_RESOURCE "Already submitted." (homework.service.ts:92) → NOT an error: navigate to detail with "Already submitted" AppBanner(info) — the student may have submitted from another device.
  • 404 → detail empty state ("removed").
  • 400 → field errors. 5xx → AppErrorState + form state preserved.

Late-submit moment (OQ-1): when now > dueDate, show inline AppBanner(warning) "Due date has passed — your teacher may accept it" but do not block (server accepts; blocking client-side would contradict the server contract).

Double-submit safety: submit is not optimistic (00-shared/07 §9 write-once op); the button locks after first tap; the 409 handler covers retried requests (Idempotency-Key header supported pattern — 00-shared/07 §9).

5. Submissions List (teacher) /homework/:id/submissions

Chrome: AppBar "Submissions · {title}"; summary line bodySmall "N submitted · M graded"; RefreshIndicator + ListView.builder.

Row (SubmissionRow):

[avatar initial]  Student name            titleMedium
                  Submitted 3d ago · Late badge?   bodySmall
[status chip]     submitted → tertiary-tinted "Pending"; graded → success "Graded"
[marks badge]     mono "17"  (graded only)
[attachment icons ×N]
  • Late badge: client-computed submittedAt > dueDateAppBadge(tertiary, icon schedule) "Late · 2d" (AppBadge + icon + text — a11y 09 §9). Semantics: "Submitted late by 2 days".
  • Grouping: "Ungraded" first (status='submitted'), then "Graded" (status='graded') — no server sort (findByHomework returns insertion order, homework-submission.repository.ts:20-24); client groups. Proposed: server sort by submittedAt.
  • States: empty AppEmptyState "No submissions yet" + "Share the homework link" (proposed); 404 → detail-removed empty state.
  • Row tap: ungraded → grading sheet; graded → read-only feedback w/ "Regrade" action.

6. Grading Sheet /homework/:id/submissions/:submissionId

Chrome: page; AppBar "Grade · {student}"; body scrollable.

Student header   avatar + name + submittedAt (+ late badge)
Attachment preview  AppAttachmentList (download via GET /files/:id/download)
marks            AppTextField numeric (label "Marks"; hint "e.g. 20")
                 client rules: required, ≥ 0, ≤ 100 default (configurable per org —
                 server has NO bounds, OQ-4: submission.dto.ts:20-21)
remarks          AppTextField multiline "Feedback for the student" (optional,
                 @IsOptional @IsString — submission.dto.ts:24-26)
[Grade button]   FilledButton fullWidth "Save grade"

Regrade mode (existing status='graded'): show current marks + remarks; button "Save new grade" + AppBanner(warning) "This overwrites the previous grade" (server overwrites unconditionally and emits HomeworkGraded again — homework.service.ts:127-143, OQ-2).

States: grading (button spinner, no double-submit); success → row state updates via pop result + snackbar "Grade saved"; 404 "Submission not found." (homework.service.ts:126) → back + refresh; 400 → field errors.

Optimistic vs server-confirmed (see 13): grading is server-confirmed — the updated doc (marks, remarks, status:'graded', gradedAt) is written only on 200. No local paint-before-write (side effects: event → notifications).

Batch flow (proposed): after saving a grade, "Next ungraded" chip appears → advances to the next status='submitted' row (client-side; list already loaded).

7. Grade Feedback View (student) — detail block (spec in §2)

Reuses GradeFeedbackCard component; read-only; marks in mono tabular figures.

8. Attachment Preview / Share

  • PDF/image preview in app (PageView + zoom, 00-shared/08 §1 double-tap zoom); other types → share sheet / external viewer.
  • Download: GET /files/:id/download → buffer → save/share; error 404 → snackbar "File no longer available".
  • Upload (from create/submit forms): AppAttachmentUploader — per-file states {idle, uploading(progress), uploaded(fileId), failed(retry), removed}; retry = re-POST same file (new upload each attempt — no resume; 00-shared/12 B7).

9. Shared micro-interaction & motion notes (all screens)

  • Route transitions Mo.m-base; cards El-e1; FAB Mo.m-entrance scale, hide on scroll-down (00-shared/03 D).
  • Snackbars: success < 3 s, error ≥ 4 s.
  • Status chip change (submittedgraded) animates AnimatedSwitcher Mo.m-fast.
  • Keyboard: numeric TextInputType.number for marks; multiline for remarks; Ctrl+Enter submits on desktop (00-shared/08 §2).
  • Safe areas: bottom padding under FAB/lists; sheets respect MediaQuery.
  • Reduced motion: fades + instant only, no shake/stagger.