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:
- 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.
- Learner with 8 active subjects loads
/assignmentsand 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
/assignmentsthe 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.
- Parents —
ParentAssignmentsPageis 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/uionly 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¶
- AG Grid / react-data-grid. Heavy, opinionated markup, paid tiers, fights the design system. Skipped.
- Material/Mantine React Table. Wrappers on TanStack with foreign UI libs. Same DS clash. Skipped.
- react-table v7. Predecessor to TanStack v8 by the same author. Deprecated direction. Skipped.
- 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. - 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.
- 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-locateduseTeacherColumns.tsxfactory hook sot()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 TanStackTable<T>instance). Stays local; extract to@kwilo/uionce a second page wants it.index.tsx— swapTeacherAssignmentList/StudentAssignmentListpanel renders for the new tables; route row click to modal state instead ofnavigate('/assignments/:id').- Mobile fallback: keep existing
AssignmentCardrendering inside<div className="md:hidden">; render the table inside<div className="hidden md:block">. - All strings via
t(). New keys inapps/web/src/i18n/locales/en/assignments.json. - New components ship with stories only if extracted to
@kwilo/uilater — 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)shippedafter one college pilot loads ≥20 rows per trainer- Promote table shell to
@kwilo/uiwhen 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.