Spec 08 Screens and Routes Teacher - SekiroKenjii/Quizzivy GitHub Wiki

Section of Quizzivy — Frontend Portal & Data Model Specification · Previous · Next

8. Screens & routes — teacher (/teacher/*)

TeacherLayout (R4, layouts/TeacherLayout.tsx and layouts/shell/): a sidebar, a top bar with the breadcrumb trail, the search, the bell and the theme toggle, and the page. It follows the theme. A route declares handle: { crumb, width?, sidebar? }: the whole breadcrumb trail, whose last crumb titles the document; width: "full", the page frame's one exception (the import review); and a collapsed start for the visit (the import review asks for one). Every other route renders in the one page frame of §12.

  • The sidebar groups the destinations as the deck does (Home; Teaching: Assignments, Grading, Classes, Students; Content: Tests, Question bank, Media), each shown only to a user with the permission its list operation declares. From 768px it is in the page's flow, 248px expanded or 60px collapsed (icons, the label as the name, no badge), as the user last chose; the choice is kept in localStorage under quizzivy.sidebar and the sidebar never collapses by itself. Below 768px it is a 272px drawer over the page that traps focus and closes on every navigation.
  • Layout by content width, not viewport. <main> is the scroller; a page lays itself out by the width of its content area (useContentWidthAtLeast, useContentBand), so collapsing the sidebar can widen a page into its next layout. The only viewport breakpoint is the shell's 768.
  • One outlet at every width, so a page keeps its state across 768. A route change returns <main> to the top and moves focus to it; a change of a parameter alone does neither.
Route Screen Key behaviour
/teacher Dashboard Figures, submissions over 7, 14 or 30 days, today's openings and closings, recent activity with the Flagged marker, live taking counts; the teacher home API below. Polls while the user is active.
/teacher/tests Tests list Cards: title, status, skills, #questions, total points, updated. Filter by status. Create / duplicate / archive; permanent delete for unreferenced archived tests. A new test opens in the builder.
/teacher/tests/:id/edit Test builder Left: outline with drag-to-reorder. A question also drags into a shared-context group, out of one and between two (§7.3): each member row has a grip, the group's header takes a drop while it is tinted, and every move has a menu entry and a keyboard path. Right: question editor incl. question media (§11.1). Autosave debounced 1.5s. Publish validates: points > 0; choice questions have ≥1 correct option; fill_blank has ≥1 accepted answer per blank; audio questions have a processed asset; no empty sections. Publishing takes an optional change note.
/teacher/tests/:id Test detail Student-eye preview of any version, with its history, the unpublished changes and the diff between versions. Restore a snapshot into a draft, select the default for future assignments, or delete an unused non-default version.
/teacher/imports, /teacher/imports/new, /teacher/imports/:id, …/review, …/confirm Word import History; one workspace per import for upload or paste and processing, its Sources pane beside its status pane (DG-207); the review workspace; preview and create (§16, docs/plan/18-word-import-ux.md). Shown only while GET /teacher/imports/capabilities reports intake enabled. The Sources pane says what leaves Quizzivy when an import is read with AI (§16).
/teacher/question-bank, /teacher/question-bank/new, /teacher/question-bank/:id Question bank and editor Type, level, skill and tag filters + full-text search. CRUD. Media badge + inline preview.
/teacher/question-bank/groups, /teacher/question-bank/groups/:id Group bank/editor Independent complete-group copies, materials and member editing, shared recording policies, revision-safe autosave and account-scoped local recovery.
/teacher/media Media library Uploaded audio/images: name, duration, size, where used. Rename, replace with the same kind, set an audio file's default play limit (§11.1). Delete blocked if referenced by any published version.
/teacher/assignments Assignments list Test, targets, window, status, submitted/total, flagged count.
/teacher/assignments/new, /teacher/assignments/:id/edit Assignment wizard Test, students, schedule, rules: duration, attempts, shuffle, review options, integrity policy (§10.3) and the note to students. Starts from the teacher's assignment defaults.
/teacher/assignments/:id Assignment detail Stats and the Students, Questions and Settings tabs. The roster is per student: not started / in progress (live remaining, live focus-loss) / submitted / graded; it polls every 15s while the assignment is open and the user is active. Extend, per-student overrides, reset, void, duplicate, reopen and close early, each with confirm. The v0.9.x …/attempts address opens the Students tab.
/teacher/attempts/:id Review & grading Per question; auto-graded shown; short_answer gets points input + comment + sample answer panel. Audio questions show plays used vs allowed. Integrity tab (§10.4). "Finish grading" → graded.
/teacher/grading Grading queue Unmarked manual answers across the caller's assignments, by student or by question (below). teaching.grading.
/teacher/students Students Table with search, class and account (Google or password) filters; create into one's own classes; edit; reset one password or up to 40 (§5.4).
/teacher/classes, /teacher/classes/:id Classes Cards with their codes; CRUD, schedule and room, members, join-code card (§6.4).
/teacher/settings/:section? Settings Profile, Sign-in & security (password, Google, signed-in devices), Notifications, Assignment defaults, Appearance, AI assistant (ai.use) and the API reference for its holders.

The AI assistant's surfaces (D19 to D22; V-06 to V-14 of the v2 deck, docs/plan/74d-d4-deck-update.md section G). Every row is built: the first three in T-D4.34, the question editor's in T-D4.35, the grading card in T-D4.36, Assignment detail › Questions in T-D4.37 and the import workspace's in T-D4.39; Settings › AI assistant is T-D4.38's. The operations behind every one are built (§15). A panel is a dialog, so none adds a route.

Every entry point is drawn only while GET /public/status says ai and the caller holds ai.use. For a teacher who is paused, or whose allowance or the centre's pool is used up, it is drawn disabled with the reason, not hidden (§12).

Where Entry point Operation
Test builder, title bar "AI check" and "Generate" before Preview; both flush pending edits first, as Preview does checkTestWithAi, generateAiQuestions
Question bank, header "Clean up" and "Generate" cleanUpQuestionBank, generateAiQuestions
Test detail "Make a variant", disabled without a published version makeTestVariant, then duplicateTest, duplicateQuestion and updateQuestion
Question editor, in the bank and in the builder "Write with AI" on the Explanation; "Suggest wrong options" on a single or multiple choice question writeAiExplanation, suggestAiDistractors
Grading card "Suggest a score" on a short answer while "Let AI read student answers" is on; with it off, one line that says so and links to Settings › AI assistant suggestAiScore
Assignment detail › Questions "Explain results with AI", under the same switch (with it off, the same kind of line); "Make a practice set" opens Generate and saves a new draft test "{title} · Practice" explainAssignmentResults, then createQuestion, createTest and updateTest
Import workspace, Sources pane the "Read with AI" card, which mirrors the preference "Read imports with AI" createWordImport with readWithAi

The builder's Generate adds the kept questions where its source belongs: from a passage that a shared-context group holds, to that group, as new members after its existing ones, through the group's own update under its revision ("Add {n} to {group}"); from pasted text, a topic or nothing, to the open question's section as new questions ("Add {n} to {section}"). Either way the first new question opens.

A panel is a session that belongs to its page, not to the drawer: it ends with Done, a primary's write or Start over, and with the page, never with the drawer closing (the deck's prototype resets on every open, and ours does not, because each run spends the teacher's credits). In the builder, "Open question" on a finding closes the drawer and opens the question beside it; the same "AI check" button brings the findings back.

A variant is a new draft test named "{title} · Variant", never a version of the open one. A copied draft names the same bank questions as the test it copies, so each changed question is duplicated and the change is written to the duplicate: the test and its questions are untouched. A variant covers the standalone questions; the members of a group are not varied and are copied as they are. The bank clean-up merges nothing: a near-duplicate offers "Open" for each question and "Keep both", and tag suggestions apply through the bulk tag write (DG-197).

Settings › AI assistant (/teacher/settings/ai, ai.use) holds the month's usage from getAiUsage (used of the allowance, what is left, a bar and the five rows generation, grading, explanations, imports and checks, with the date the allowance resets, the first of the next month in APP_TIMEZONE: "It resets on 1 November"), the preferences (UserPreferences.ai: the language the assistant writes in, tagging what it drafts as "AI draft", letting it read student answers, reading imports with it), and "Ask for more credits" (1,000 to 100,000 and a reason of at most 500 characters) while the centre's rule is to take requests and none is waiting, and the request waiting, with its date, until an Admin decides it (DG-200). With the assistant off the section says "AI is not turned on for this school." (DG-201).

Admin › AI credits is the pool, the requests waiting, the teachers with their month and the rules. Availability: R5 (T-R5.35, in the Admin console R5 builds; none exists before). D4 ships its operations (§15) and cmd/maintenance ai-credits (docs/setup/operations.md), which the owner uses until then. The rule for a pool that runs out has one value, pause everyone; "Add credits" is the Admin's approval, and the product bills nobody (DG-196).

No student screen mentions the assistant. The Test intro's sentence about cloud processing waits for Thuong's wording (§17).

The teacher home API (getDashboard) accepts range=7d|14d|30d, defaulting to 14d. Only submissions depends on this range: one zero-padded calendar day per entry in the actor's zone, oldest first with today last, counting submitted, timed_out and graded papers by submitted_at. The total is their sum. The integer mean percentage rounds half up over graded papers with a valid score; graded papers without scores still count, and the mean is null when none has a score. The existing nullable score columns and historical rows are unchanged.

takingNow counts distinct students and assignments with an in_progress paper whose deadline is strictly in the future. today holds up to 20 published assignment openings and effective closings in the actor's calendar day, earliest first; an early close uses the earlier of closed_at and closes_at. notSubmitted counts distinct enabled targets the caller reaches without a handed-in paper; a former target's submission does not subtract from that roster. recentActivity holds the ten newest events: one state per non-voided attempt (started while in progress, submitted once handed in), and student-like joins through a taught class's code. Attempt flags carry through; joins are unflagged. These new readings contain no score, band or answer.

getTeacherSummary supplies live assignments matching the caller's open list, unmarked manual answers in reachable handed-in papers, and the caller's unread notifications. The grading count is null and its query is not run without teaching.grading; live assignments is currently always a number. Both home operations use .Own() even for an Admin, while listAttempts keeps its wider scope. The application supplies the new queries' clock and resolves the IANA calendar zone through ports.Zones; T-R4.7 wires the identity effective-zone query. Only a stored NULL defaults to Asia/Ho_Chi_Minh; an invalid stored zone or ineligible account propagates an error. A nil port retains the default for existing callers. Frontend date displays react to the caller's accepted display zone; existing local datetime inputs and their rules preview remain fixed-HCM until T-R4.43. Legacy readings retain their SQL clocks. An absent notifications summary port returns 501.

The grading queue requires teaching.grading and preserves the existing assignment and paper scope. It includes saved, unmarked manual answers in submitted or timed-out papers; a manual score of zero is marked. Student groups use student IDs; question groups use the assignment and frozen question IDs together. Counts cover the complete filtered set before the deterministic first200 items, with oldest submissions first and null times last. Optional assignment and student filters apply to counts and items. Group labels use current names or the global one-based frozen question number; assignment titles are current, while question keys, rich content and group context come from the published version. The aggregate, groups and item prefix share one SQL statement snapshot; later immutable enrichment does not claim a whole-request repeatable-read snapshot. Reading or exhausting the queue never finishes grading; the teacher still invokes Finish explicitly. The rebuilt web queue is T-R4.28.

When nothing is left to mark (T-D4.21b) the header reads "Không còn câu trả lời nào cần chấm." and the page checks for papers that are marked but not finished: every submitted and timed-out paper the caller reaches with nothing waiting for a mark (listAttempts, pendingGrading=false, read to the end), under the queue's filters. "Đã chấm xong tất cả" shows only after that check completes and finds none; while it runs, or after it fails, the page says only that no answers are left to mark. The papers it finds are listed under "Sẵn sàng hoàn tất", below the panes, grouped by assignment with the oldest hand-in first, five to a group before "Show more". A row's Review opens its check, read fresh from getAttemptForReview: the score, the answers given, whether the teacher marked it, it was marked automatically or it was left blank, and any flag. Finish grading there is enabled only while that read's pendingManual is 0, and the paper is read again before the request; after a Finish the next paper's check opens with focus on its Finish. While answers wait the list folds to one line and the header links to it. Nothing finishes on its own, and there is no Finish for a whole assignment.

Teacher list behaviour (approved change request, 2026-09-22):

  • Tests, questions, media, classes, students and assignments support explicit multi-selection and confirmed bulk removal. Class rosters support bulk membership removal. Failed items remain selected with an individual explanation; successful items are not retried. Attempt history, grading records and integrity/audit events remain retained.
  • Tests and questions expose duplication beside the row menu and inside it. A duplicate leaves the teacher on the list and marks the source and newly created row until navigation.
  • List search, filters and pagination survive navigation to a child and back for the signed-in session. Explicit shared URLs take precedence. The question bank shows updated time and orders newest-created questions first.
  • The question bank's usage count expands a nested table of currently attached test outlines, fetched on demand, with links to their builders. Frozen published snapshots are excluded from this count and remain unaffected by bank edits.
  • Archive tests/classes or disable student accounts before permanent deletion. Assignment deletion requires a draft or closed assignment without attempts. Foreign-key references to assigned work block deletion. Student actor references in retained audit history also block deletion; the existing manual anonymization policy remains separate.
  • Published snapshots are never edited in place. Editing an old version copies its frozen content into new bank questions and replaces the editable draft after confirmation. Existing assignments retain their version. Switching the default does not reset the monotonically increasing publication counter. Only unused, non-default versions can be deleted.
  • Empty builder groups accept question drops; double-clicking a group title opens rename. Switching questions flushes the current editor and uses the returned saved record. Fresh starter text disappears on focus without scheduling an invalid empty save. Tags suggest recent and matching existing names.
  • Assignment duration offers 30, 45 and 60 minutes plus a custom minute input. Attempt count is a number input defaulting to 1. Focus loss offers unlimited, none allowed, or a custom positive count.

Section of Quizzivy — Frontend Portal & Data Model Specification · Previous · Next


Generated from docs/quizzivy-spec-v0.3.md. Edit that file in the repository; changes made on this page are overwritten.

⚠️ **GitHub.com Fallback** ⚠️