Skip to content

Assignments Table View + Quick-Look Modal

Status: in-progress LNO: N — Neutral (scaling fix, not needle-moving on its own; unblocks usability at 50+ assignment scale). Owners: @bhanu49 Last updated: 2026-05-16 Roles affected: Trainer · Learner Primary routes: /assignments (HomeworkPage), modal overlay on same route Source of the ask: self-initiated (Bhanu, 2026-05-16) — see below

TL;DR

The assignments list (/assignments) currently renders each item as a full-width panel. Past ~10–15 rows the page becomes a scroll trap with no sorting and no way to scan due dates at a glance. We're replacing the panel list with a sortable table on desktop (cards stay on mobile), and lifting the assignment detail surface from a separate route into a quick-look modal launched from the row. Deep workflows (grading, taking an exam, full edit) still navigate to dedicated pages — the modal is for triage, not work.

Why this exists

Who asked for it

Self-initiated. Triggered by reviewing the page during the PostHog-proxy work — the panel layout looked fine with the 3-5 test assignments in staging but will break the moment a college trainer loads a real semester.

No user interview. No support ticket. Honest framing: this is a structural fix shipped ahead of the pain, sized off forward-looking math, not user reports.

What user pain does it solve

Two anticipated pains, both density-driven:

  1. Trainer with 30+ assignments across a semester has no way to answer "which of these need grading right now?" without scrolling and eyeballing chips. There is no sort, no column scan, no batch view.
  2. Learner with 8 active subjects loads /assignments and gets a wall of panels. The "what's due tomorrow" answer is buried in card decorations.

The reference shape (OverviewTab.tsx:222–271, faculty summary table) already exists in the codebase for the analytics dashboards and reads cleanly at 50+ rows. Same pattern, applied to the user-facing assignments page.

Cost of not doing it

  • Near-term: low. Staging has tiny datasets; current users don't feel the pain yet.
  • Medium-term: a single college pilot with one engineering branch (60 learners × 6 subjects × 4 assignments/sem ≈ 1.4k learner rows; trainers see ~24 assignments each) breaks the panel view.
  • If we don't fix this now, the next bug bash on the page will land 4 "make this scrollable / sortable / faster" tickets we'll fix piecemeal.

Validation

Weak. No user has complained. Hypothesis-driven, not interview-driven.

Counter-evidence considered: - Mobile users may prefer the card layout; we keep cards on <md. - Younger learners may scan cards faster than dense rows; we keep grouped view (Today / Tomorrow / Overdue / Upcoming) as the default learner tab so casual users aren't dropped into a spreadsheet.

If post-launch usage shows the modal is unused or rows feel cramped, we revisit. Sean Ellis counterfactual: if removed a week post-ship, trainers with >15 assignments would notice; learners with grouped tabs would not.

Personas

  • Prof. Ravi, CSE-A Sem-5 OS lecturer at BTA Engineering College. Has 24 active assignments across two sections. Wants to open /assignments, sort by "needs grading," and clear the queue.
  • Rahul, 2nd-year CSE learner. Opens /assignments the day before a quiz. Already lands on the "Upcoming" grouped tab — that view stays card-shaped, not tabular.

ICP exclusions

  • K-12 trainers — affected but not target persona for this round. The same components serve them; we don't re-validate copy.
  • Mobile users — get the card fallback. The table is a desktop pattern.
  • ParentsParentAssignmentsPage is a separate file and out of scope.

Outcomes for the user

  • Prof. Ravi sorts by "grading pending" and gets every actionable row in two clicks.
  • Prof. Ravi sees a row, clicks it, gets a modal with submission counts and one-button paths to Grade / Edit / Delete without losing his scroll position.
  • Rahul's "Upcoming" tab is unchanged (grouped cards). His "All" tab switches to table — fine; it was always a power-user view.
  • Mobile users see the same cards they see today, unchanged.

Non-goals

  • No virtualization. Page sizes are small enough that TanStack Table's default render is fine. Revisit at 200+ rows.
  • No server-side sorting / pagination. Both endpoints (listForTeacher, listForStudent) already return the full set; sort happens client-side.
  • No row-selection / bulk actions. Out of scope for v1. Architecture supports it (TanStack Table has selection built-in).
  • No replacing AssignmentDetailPage. The page still exists for deep linking, SSR-friendly URLs, and full-context workflows. Modal is the row-click default; the page is the long-form surface.
  • No new DS component. The <DataTable> shell lives locally in HomeworkPage for v1. We extract it to @kwilo/ui only when a second consumer needs it.
  • No grading inside the modal. Modal is read + light actions. Grading flow keeps its own page.
  • No new icons or color tokens. All existing.

Rejected alternatives

  1. AG Grid / react-data-grid. Heavy, opinionated markup, paid tiers, fights the design system. Skipped.
  2. Material/Mantine React Table. Wrappers on TanStack with foreign UI libs. Same DS clash. Skipped.
  3. react-table v7. Predecessor to TanStack v8 by the same author. Deprecated direction. Skipped.
  4. Plain <table> with manual sort state. Considered. Would ship faster, but adds tech debt the moment we want column sort, row selection, or column visibility. TanStack v8 gives all of those for ~10kb gzipped and zero markup.
  5. Drawer instead of modal for detail. Considered. Modal wins for "quick triage of one item." Drawer suggests a list-builder workflow which we don't have.
  6. Inline-expand row instead of modal. Considered. Breaks the scan rhythm of the table and pushes other rows offscreen.

Implementation outline

(Detail belongs in code review, not the doc — sketch only.)

  • apps/web/package.json — add @tanstack/react-table@^8.
  • apps/web/src/pages/shared/HomeworkPage/components/:
  • AssignmentTable.tsx — trainer view. Column defs in a co-located useTeacherColumns.tsx factory hook so t() is in scope.
  • StudentAssignmentTable.tsx — learner view (used on the "All" tab and "Completed" tab; "Upcoming" stays grouped).
  • AssignmentDetailModal.tsx — quick-look modal. Renders title, type, due, status, target groups, submission stats (trainer) or submission status + grade (learner). Footer buttons link out to deep pages.
  • DataTable.tsx — local table shell (<table> + thead/tbody + th/td primitives, styled with DS tokens, accepts a TanStack Table<T> instance). Stays local; extract to @kwilo/ui once a second page wants it.
  • index.tsx — swap TeacherAssignmentList / StudentAssignmentList panel renders for the new tables; route row click to modal state instead of navigate('/assignments/:id').
  • Mobile fallback: keep existing AssignmentCard rendering inside <div className="md:hidden">; render the table inside <div className="hidden md:block">.
  • All strings via t(). New keys in apps/web/src/i18n/locales/en/assignments.json.
  • New components ship with stories only if extracted to @kwilo/ui later — local components per repo convention.

Trainer columns (proposed)

Column Source Notes
Title title Sortable, clickable (opens modal). Type badge inline-left.
Status status Chip: draft / active / grading / complete. Color by status.
Due date due_date Sortable. Red label if past + status === 'active'.
Submissions submission_stats.submitted_count / expected_count Plain text fraction.
Grading pending submission_stats.pending_grading_count Accent color when > 0.
Created created_at Sortable.
Actions Edit + Delete icon buttons.

Learner columns (proposed)

Column Source Notes
Title title Sortable, clickable. Type badge inline.
Course course_title
Due date due_date Sortable. "Today" / "Tomorrow" / "Overdue" chip on hot rows.
Status submission_status Chip.
Score grade_percentage Only when submission_status === 'graded'.
Action "Start" / "Continue" / "View result" depending on status. Navigates out.

Status lifecycle

  • draft (this PR)
  • in-progress (post-merge, gather telemetry on modal open rate + sort usage via PostHog)
  • shipped after one college pilot loads ≥20 rows per trainer
  • Promote table shell to @kwilo/ui when a second consumer needs it

Open questions

  • Should the modal preserve scroll position on close? Default yes — TanStack Table doesn't re-render rows on a parent state change as long as columns are memoized.
  • Do we want a "Saved views" feature later (filter + sort presets per user)? Not in scope; mention only as a future plug.