Skip to content

Roadmap

Wouter Meetsma edited this page Jun 9, 2026 · 49 revisions

RubricMaker — Feature Roadmap

Items 1–14, 16, 19, and 24–30 are complete. This file tracks the remaining work.


Tier 4 — Testing & Quality

13. Playwright End-to-End Smoke Tests ✅

Status: Complete (PRs #93, #94)

Core smoke tests covering the offline teacher workflow are live. playwright.config.ts and e2e/ are in the repository.


14. Playwright E2E — Supabase Integration Coverage ✅

Status: Complete (PR #104)
Tier: Testing & Quality

Magic-link sign-in via the admin generate_link API (no email/inbucket required), cloud persistence, offline-queue-and-flush, and pending-queue checks are all covered in a dedicated supabase Playwright project (e2e/specs/14-supabase-sync.spec.ts).

What was built:

  • e2e/fixtures/supabase.fixture.ts — creates a fresh test user + school per test, signs in via magic link, tears down after
  • npm run e2e:supabase — runs the Supabase suite (--project=supabase --workers=1) against a local stack
  • CI job E2E — Supabase Integration runs after the standard offline suite; spins up supabase start, exports credentials, uploads separate artifacts
  • Five test scenarios: magic-link auth, cloud persistence after localStorage wipe, deletion sync, offline-queue flush on reconnect, queue empty while online

What to keep building (see CLAUDE.md):

  • Shared rubric flow — teacher A shares, teacher B sees it (not yet covered)
  • Grading-while-offline → sync on reconnect
  • Student portal sign-in via share code
  • Broader coverage of the reconnect-hydration path and multi-device data merging

Tier 5 — Architecture

15. Supabase-First Architecture (localStorage as Offline Cache)

Status: Planned
Tier: Architecture
Effort: 8–12 days

Currently localStorage is the source of truth and Supabase is a write-through cache. This causes Supabase data to lag behind, making cross-device access unreliable. Flipping the model — Supabase primary, localStorage offline cache — makes cloud sync reliable and opens the door to real-time collaboration between colleagues.

Scope:

  • Audit the read path in src/store/storage.ts and src/context/AppContext.tsx
  • AppContext hydrates from Supabase on load (when configured); localStorage is populated as a cache
  • Offline detection via navigator.onLine + reconnect events; queue writes and flush on reconnect
  • Conflict resolution: last-write-wins on updated_at timestamp (simplest safe default)
  • Auth-gated: the Supabase-first path only activates when VITE_SUPABASE_URL is set; full offline-only mode is unchanged for users without Supabase

Architecture impact: High — core data layer (storage.ts, AppContext, StorageSync). Requires a migration path for existing localStorage-only users and thorough E2E coverage before ship (see item 14).


Tier 6 — Reach & Localisation

16. Spanish (ES) i18n + FR/DE Completion ✅

Status: Complete
Tier: Reach & Localisation

All five locale files (en, nl, fr, de, es) are fully in sync at 852 keys each. Spanish is registered in src/i18n.ts and exposed in the Settings language selector. CLAUDE.md documents that all five files must be updated together.


Tier 7 — Content & Templates

17. IB (International Baccalaureate) Rubric Templates

Status: Planned
Tier: Content & Templates
Effort: 1–2 days

IB is used in 5,000+ schools globally. MYP Language Acquisition has 4 criteria × 8 performance levels (0–8), a clean fit for the existing rubric model. DP Language A Literature and Language B criteria can also be pre-built.

What to build:

  • IB MYP Language Acquisition criteria as a built-in rubric template (JSON in src/data/)
  • IB DP Language A: Literature criteria
  • IB DP Language B criteria
  • Surface these in the template picker in the rubric builder

Architecture impact: Minimal — static data files + template picker extension.
Research: IB criteria are publicly documented; no API needed.


Tier 8 — EFL Core

18. Cambridge English Framework Integration

Status: Planned
Tier: EFL Core
Effort: 2–3 days (static) or 5–7 days (live API)

Cambridge levels (KET/PET/FCE/CAE/CPE mapping to A2–C2) are the dominant assessment framework for EFL teachers. The API key field exists in Admin → Integrations and src/services/cambridgeApi.ts contains a working lookupWord() implementation, but the service is not yet imported or called by any feature.

What to build:

  • Investigate Cambridge Assessment API availability and terms; if accessible, integrate via a new src/services/cambridgeApi.ts modelled on src/services/standardsApi.ts
  • If a live API requires a partnership agreement, ship as static CEFR-mapped Cambridge descriptor data (aligned to the existing CEFR infrastructure)
  • Surface Cambridge exam labels (e.g., "B2 First") alongside CEFR levels in the grading and analytics views

Architecture impact: Medium — new service module; CEFR level mapping already exists.


Tier 9 — UX

19. Print CSS — Remaining Views ✅

Status: Complete
Tier: UX

Print buttons added to StudentProfilePage, StatisticsPage, and ComparativeGrading. Centralized @media print block in src/index.css hides nav, sidebar, and interactive controls; forces white background; handles view-specific classes (.statistics-controls, .comparative-actions).


23. Full Themes — Font Families + Named Color Presets

Status: Planned
Tier: UX / Personalisation
Effort: 2–3 days

The app UI is visually tied to a single accent color and the Inter font. Full theme support lets schools and individual teachers express their identity and makes the app more accessible for neurodiverse students who prefer certain typefaces.

Phase 1 (in progress): Foundation already shipped:

  • AppSettings.uiFontFamily field + AppContext applies it as --font CSS variable with dynamic Google Fonts loading
  • 8 named accent-color preset swatches in Settings → General (Ocean, Forest, Indigo, Sunset, Rose, Slate, Teal, Gold)
  • UI font picker in Settings → General (Inter, Nunito, Source Sans 3, Lato, Roboto)

Phase 2 (planned): Full theme bundles:

  • Define 4–6 named themes (e.g. "Academy", "Nature", "Midnight", "Warm") — each theme sets accentColor, uiFontFamily, defaultFormat.fontFamily, defaultFormat.headerColor
  • Add a colorPreset?: string field to AppSettings to track the active preset name
  • Implement tonal accent-color scales (50–900 steps via color-mix() or LCH interpolation) so --accent-50 through --accent-900 are available for richer component styling in charts and rubric exports
  • Export font library for rubric DOCX/PDF: include decorative options: Playfair Display (elegant serif headings), Oswald (condensed bold), Bebas Neue (display caps), Special Elite (distressed typewriter), Courier Prime (clean terminal)
  • Rubric title "ASCII / terminal art" styling: monospace fonts + optional border decorations around rubric headings in DOCX export

What to build:

  • Theme definition file: src/data/themes.ts — array of { id, label, accent, font, headerColor, exportFont } objects
  • Theme picker in Settings → General (visual card swatches with preview)
  • Update src/context/AppContext.tsx to apply theme bundles atomically (single updateSettings call)
  • Extend src/utils/docxExport.ts and src/utils/pdfExport.ts to read settings.defaultFormat.fontFamily for decorative heading fonts
  • CSS custom properties audit: ensure --accent-{50..900} scale is wired consistently to charts, badges, and rubric cell highlights

Architecture impact: Low-medium — theme data file + Settings UI + CSS variable additions. No data model migration needed.


20. Student Portfolio View

Status: Planned
Tier: UX / Features
Effort: 3–4 days

All underlying data exists (grades, CEFR sessions, essays, feedback history) but is spread across separate pages. A portfolio view aggregates a student's full year into a single scrollable report — useful for parent-teacher conferences, student self-reflection, and end-of-year reporting.

What to build:

  • New route /students/:id/portfolio (or a tab on StudentProfilePage)
  • Chronological timeline: rubric grades → essays submitted → CEFR assessments → speaking sessions
  • Print/export to PDF from the portfolio view
  • No new data fetching; purely a UI aggregation of existing AppContext state

Architecture impact: Low — new page on existing data model.


Tier 10 — Compliance

21. Accessibility (WCAG 2.1 AA Audit + Priority Fixes)

Status: Planned
Tier: Compliance
Effort: 3–5 days

WCAG 2.1 AA compliance is required by EU law (EN 301 549) for software used in schools. The app is currently untested for accessibility. Key risk areas: icon-only buttons without aria labels, keyboard navigation in the grading interface, color contrast of CSS custom properties in light and dark mode.

What to build:

  • Run axe-core or Lighthouse accessibility audit; document the top 10 violations
  • Fix: add aria labels to all icon-only buttons (lucide-react icons)
  • Fix: ensure the rubric level selector in GradeStudent is keyboard-navigable (arrow keys, Enter)
  • Fix: verify color contrast ratios for --accent, --text, --bg-elevated in both themes
  • Add axe-core to the Vitest/Playwright suite so regressions are caught automatically

Architecture impact: Low — HTML attributes and CSS only.


Tier 11 — Integrations (Community Contribution)

22. Magister / SOMtoday Roster Import

Status: Planned (Community Contribution)
Tier: Integrations
Effort: 4–6 days per platform

Magister and SOMtoday are the dominant student information systems (SIS) in Dutch schools. Auto-importing class rosters would eliminate the largest manual onboarding step for NL teachers. Full integration documentation is in /docs/MAGISTER_INTEGRATION.md.

What to build:

  • src/services/magisterApi.ts — Magister REST + OAuth flow
  • src/services/somtodayApi.ts — SOMtoday OpenID Connect flow
  • OAuth token exchange must go through a Supabase edge function (client secret cannot be exposed in the SPA)
  • Student data model already exists; the import maps to existing types

Architecture impact: Medium — new service files; edge function for token exchange.
Note: This item is open for community contribution. Core team will review and merge a well-tested PR.


Tier 12 — Bug Fixes & Quick Wins

Found during multi-role UX testing. Ordered by impact × effort ratio — do these first.

24. Comment Bank Inserts Plain Text into TipTap HTML (Bug) ✅

Status: Complete
Tier: Bug Fixes & Quick Wins
Effort: 0.5 days
Impact: High — affects every teacher who combines comment bank with rich-text feedback
Found by: Teacher tester

Comment bank insertion now uses TipTap's insertContent API (commentEditorRef.current.insertContent(text)) instead of string concatenation. The text lands as a proper document node and the resulting HTML remains valid regardless of cursor position.

Architecture impact: Minimal — single component change.


25. Duplicate Criterion Icons Are Identical (UX Bug) ✅

Status: Complete
Tier: Bug Fixes & Quick Wins
Effort: < 0.5 days
Impact: Medium — confuses every teacher using the rubric builder
Found by: Teacher tester

Both the classic and WYSIWYG rubric builder modes now use distinct icons: Copy for "Copy criterion to clipboard" and Files for "Duplicate criterion." The WYSIWYG duplicate button also received title and aria-label attributes that were previously missing.

Architecture impact: Trivial — icon import swap.


26. Missing i18n Coverage Across Three Views ✅

Status: Complete
Tier: Bug Fixes & Quick Wins
Effort: 1–1.5 days
Impact: Medium — affects all non-English users
Found by: Teacher tester, Student tester

All hardcoded English strings in RubricBuilder.tsx, GradeStudent.tsx, ComparativeGrading.tsx, and StudentEssayPage.tsx have been replaced with t() calls. 21 new keys were added to all five locale files (en, nl, fr, de, es), covering form labels, button titles, aria-labels, sub-item tooltips, score panel headers, comparative grading buttons, and email gate error messages. The tier12.test.tsx suite verifies that all new keys are present in every locale file.

Architecture impact: Low — string replacements + locale file additions.


27. Essay Draft Uses sessionStorage — Lost on Browser Close ✅

Status: Complete
Tier: Bug Fixes & Quick Wins
Effort: 0.5 days
Impact: High — a student who accidentally closes the browser tab loses their entire essay draft
Found by: Student tester

Essay drafts are now persisted to localStorage under the rm_essay_draft_<code> key. A "Draft restored" banner appears on page load when a saved draft exists. The draft is cleared on successful submission. Only the countdown timer continues to use sessionStorage (intentional — timer resets on reload are correct behaviour).

Architecture impact: Trivial — one-line API swap + banner component.


28. Voice Grading Language Hardcoded to NL/EN Only ✅

Status: Complete
Tier: Bug Fixes & Quick Wins
Effort: < 0.5 days
Impact: Medium — FR, DE, ES teachers get English speech recognition regardless of UI language
Found by: Teacher tester

GradeStudent.tsx now maps all five app languages to their BCP-47 counterparts: nl → nl-NL, fr → fr-FR, de → de-DE, es → es-ES, with en-US as the default. Speech recognition now respects the teacher's chosen UI language.

Architecture impact: Trivial — object lookup swap.


29. Admin Panel — No Offline-Mode Guidance ✅

Status: Complete
Tier: Bug Fixes & Quick Wins
Effort: 0.5 days
Impact: Low-Medium — confuses admins deploying in offline-only mode
Found by: Administrator tester

UsersTab and SchoolsTab in AdminPage.tsx now check dbStatus.isConnected and render a clear offline-state message instead of an infinite spinner. Translation keys (admin.users_offline_title, admin.users_offline_body, admin.schools_offline_title, admin.schools_offline_body) are present in all five locale files.

Architecture impact: Minimal — guard clause + EmptyState component.


30. Anchor Paper Feature Has No Explanation ✅

Status: Complete
Tier: Bug Fixes & Quick Wins
Effort: 0.5 days
Impact: Medium — teachers new to standards-based grading don't know what "Mark as anchor" means
Found by: Teacher tester

A help icon (?) next to the "Mark as anchor" checkbox now shows a tooltip explaining the concept. The help text is stored in the gradeStudent.anchor_help_text key and translated into all five locale files.

Architecture impact: Trivial — tooltip component.


Tier 13 — Essay & Writing Workflow

31. Class-Level Essay Assignment Entry Point

Status: Planned
Tier: Essay & Writing Workflow
Effort: 2–3 days
Impact: High — currently the only way to create an essay is buried inside a single student's grading view
Found by: Teacher tester

The essay assignment workflow lives exclusively in GradeStudent (via the "Essay" button in the topbar). To assign an essay to an entire class, the teacher must:

  1. Open any one student's grading view
  2. Click "Essay" and configure the assignment
  3. Click "Show class link" to open the EssaySlipSheet

This sequence is non-obvious. Many teachers would not discover the slip-sheet path. There is also no way to see all active or past essay assignments from a dashboard.

What to build:

  • New "Essays" tab on the RubricList page (or a dedicated /essays route): list all essay assignments per rubric with status (draft / active / past deadline)
  • "New essay assignment" action from the rubric card that opens EssayAssignmentModal pre-populated with the rubric, targeting the whole class (not a single student)
  • Link the slip-sheet directly from this new entry point
  • "My Essays" widget on the Dashboard showing assignments with unreviewed submissions

Architecture impact: Low-medium — new route/tab + minor changes to EssayAssignmentModal to accept an optional class ID instead of requiring a student ID.


32. Essay Text Visible During Comparative Grading

Status: Planned
Tier: Essay & Writing Workflow
Effort: 2–3 days
Impact: High — makes the comparative grading view useful for writing assignments
Found by: Teacher tester

ComparativeGrading shows two students' rubric scores side-by-side but has no way to display their submitted essay text. For writing assignments the teacher needs to read both texts while grading — currently they would need to switch back and forth between the individual grading views.

What to build:

  • Add a collapsible "Essay" panel above (or inline with) each student column in the comparative grading grid
  • Fetch essay submission HTML for each student via the existing EssayImportModal / EssayAdapter flow
  • Render both essays in read-only TipTap instances with synchronized scroll
  • Add an "essay" mode toggle to the comparative session header (hidden when no essay submissions exist)

Architecture impact: Medium — new fetch + read-only TipTap render in ComparativeGrading.tsx.


33. Essay Page — Theme Support and i18n

Status: Planned
Tier: Essay & Writing Workflow
Effort: 1.5–2 days
Impact: Medium — affects every student taking a timed essay
Found by: Student tester

StudentEssayPage (including EmailGate) hardcodes a light-mode color palette (background: '#f8fafc', color: '#1e293b') and uses no useTranslation calls — all visible strings are hardcoded English. This is inconsistent with the rest of the app where the student may have selected Dutch, French, German, or Spanish.

What to build:

  • Replace all hardcoded hex colors with CSS custom properties (var(--bg), var(--text), etc.) so the page inherits the user's theme
  • Add useTranslation to StudentEssayPage and EmailGate; add translation keys to all five locale files for: email gate copy, SEB blocking banner, submission confirmation, deadline-passed screen, and all button labels
  • Confirm dark mode works end-to-end on the essay page

Architecture impact: Low — CSS variable substitution + i18n wrapping; no logic changes.


Tier 14 — Student Experience

34. Self-Assessment — Graduated Confidence Scale

Status: Planned
Tier: Student Experience
Effort: 1–2 days
Impact: Medium — current binary rating loses nuance that matters for CEFR profiling
Found by: Student tester

SelfAssessPage presents each Can-Do descriptor with a single "confident" checkbox. A student who partially understands a descriptor has no way to express that; they must choose between fully claiming it and fully rejecting it. This reduces the diagnostic value of self-assessments.

What to build:

  • Replace the boolean confident field with a 1–4 scale (e.g., 1 = "Not yet", 2 = "Sometimes", 3 = "Usually", 4 = "Always / Confident")
  • Update SelfAssessmentRating type: add optional confidenceLevel?: 1 | 2 | 3 | 4 alongside the legacy confident: boolean for backwards compatibility
  • Render as a small segmented button or emoji-scale row per descriptor
  • Aggregate the new scale into the CEFR overview charts (e.g., average confidence level per skill per student)
  • Add translation keys for the four labels in all five locale files

Architecture impact: Low — type extension + UI change in SelfAssessPage; CEFR aggregator needs a minor update.


35. Peer Review Visibility in Student Portal

Status: Planned
Tier: Student Experience
Effort: 1–2 days
Impact: Medium — students don't know they have a peer review to complete
Found by: Student tester

The student portal (StudentPortalPage) shows rubric grades, CEFR progress, and essay assignments — but has no section for pending peer reviews. Students learn about peer review assignments only if the teacher explicitly shares a separate link with them. Most students in testing never discovered they had a peer review to complete.

What to build:

  • Add a "Peer Reviews" section to the student portal below the essay assignments section
  • Query peerReviews from AppContext for the current studentId (as reviewer) to find incomplete ones
  • Show: rubric name, assigned submission (student being reviewed, if visible), due date if set, link to the peer review URL
  • Highlight incomplete peer reviews with a badge

Architecture impact: Low — UI addition to StudentPortalPage; no new data fetching.


36. Grade-Published Notification (Supabase Mode)

Status: Planned
Tier: Student Experience
Effort: 3–4 days
Impact: High — without notifications, students must proactively check their portal
Found by: Student tester

Students have no way to know when a teacher has published feedback. In Supabase mode, the grading data is stored in the database, making email triggers feasible via Supabase Edge Functions.

What to build:

  • Supabase Edge Function: notify-student-graded — triggered by an insert/update on the student_rubrics table; looks up the student's email (if stored) and sends a templated notification email via the configured SMTP provider
  • Teacher setting (in SettingsPage): "Notify students when graded" toggle (default off until teachers opt in)
  • Email template: "Your feedback for [Rubric Name] is ready — view it at [portal link]"
  • Gracefully skip notification if student has no email or SMTP is not configured

Architecture impact: Medium — new Edge Function; optional SMTP dependency already configured for auth emails.


Tier 15 — Rubric Builder Polish

37. Criterion Weight Validation

Status: Planned
Tier: Rubric Builder Polish
Effort: 0.5 days
Impact: Medium — teachers can accidentally submit rubrics where weights sum to 110% or 80% with no warning
Found by: Teacher tester

In weighted-percentage scoring mode, each criterion has a weight field but no running total is shown and no warning fires when weights don't add to 100%. The grade calculation engine normalises the weights at runtime, but the rubric still looks misconfigured to the teacher.

What to build:

  • Compute totalWeight = criteria.reduce((sum, c) => sum + c.weight, 0) in RubricBuilder
  • Display the running total near the criteria header: "Total weight: 95% ⚠️ (should be 100%)"
  • Color the indicator amber when outside 98–102%, red when outside 90–110%
  • Optionally add a "Distribute evenly" one-click button that sets all weights to Math.round(100 / criteria.length)
  • Add a non-blocking warning (not a hard block) on save if total weight is outside 90–110%

Architecture impact: Trivial — derived value computation + UI display.


38. Level Reordering Within a Criterion

Status: Planned
Tier: Rubric Builder Polish
Effort: 1–2 days
Impact: Medium — teachers currently cannot reorder levels without deleting and recreating them
Found by: Teacher tester

Criteria support drag-and-drop reordering via @hello-pangea/dnd. Levels within a criterion do not — there are no move handles, and the rubric builder renders levels in a horizontal scroll without any reorder affordance. A teacher who wants to reverse the order of levels (e.g., to match the rubric format setting levelOrder) must delete and recreate each level.

What to build:

  • Add a GripHorizontal drag handle to each level card in the horizontal level list
  • Wire a nested Droppable/Draggable within the criterion's level row using @hello-pangea/dnd (already installed)
  • Update the updateCriterion call to reorder the levels array on drop

Architecture impact: Low — nested DnD within existing DnD context; the library supports this.


39. "Save as Template" from Existing Rubric

Status: Planned
Tier: Rubric Builder Polish
Effort: 1–2 days
Impact: Medium — teachers who build a rubric want to reuse it without importing/exporting JSON
Found by: Teacher tester

Teachers can create rubrics from built-in templates or import a JSON file, but there is no way to save an existing rubric as a named personal template. The workaround is to export JSON and re-import — a multi-step friction point.

What to build:

  • Add a "Save as template" option to the rubric builder export menu (alongside PDF, DOCX, JSON, Print)
  • Store user-defined templates in localStorage under a dedicated key (e.g., rm_user_templates)
  • Surface them alongside the built-in templates in the rubric builder's "New from template" picker
  • Allow deleting user-defined templates from the picker

Architecture impact: Low — new localStorage key + template picker UI extension; no changes to the rubric data model.


40. Rubric Sharing UI Between Teachers

Status: Planned
Tier: Rubric Builder Polish
Effort: 2–3 days (Supabase mode only)
Impact: Medium — the RubricShare type and database schema already exist; the UI is missing
Found by: Administrator tester

The RubricShare type (src/types/index.ts) and the Supabase schema have support for teacher-to-teacher rubric sharing, but there is no UI to trigger or view shares. Teachers in the same school cannot collaborate on or reuse each other's rubrics without the JSON export/import workaround.

What to build:

  • "Share with colleague" button in the rubric builder topbar (visible only in Supabase mode)
  • Modal: enter colleague's email → creates a rubric_shares row in the database
  • Recipient sees shared rubrics in a "Shared with me" section on the rubric list
  • Recipient can clone a shared rubric into their own account

Architecture impact: Medium — new Supabase write path + rubric list UI section.


Items marked with (Supabase mode only) require an active database connection. All items marked (offline) work without Supabase.

Tiers 12–15 were added following a structured three-role UX testing session (teacher, administrator, student) conducted on the main branch. Items are ordered within each tier by impact × effort ratio.

Clone this wiki locally