-
Notifications
You must be signed in to change notification settings - Fork 1
Roadmap
RubricMaker is an offline-first rubric creation and grading tool for educators. This roadmap outlines completed features, current work in progress, and planned development phases.
- ✅ Rubric builder with multiple scoring modes (Total Points, Weighted, Single-Point)
- ✅ Interactive grading interface with score modifiers
- ✅ CEFR framework integration (Can-Do statements)
- ✅ Student self-assessment & peer review
- ✅ Essay assignments with rich text editor
- ✅ Analytics dashboard with class statistics
- ✅ Multi-format export (PDF, DOCX, CSV)
- ✅ Student portal with shareable links
- ✅ Offline-first architecture with localStorage
- ✅ Basic theme customization
Status: Stable and feature-complete for core education workflows
PR #128 — Final Three Roadmap Items
Supabase-first implementation with intelligent merge strategy
-
src/utils/syncMerge.tsreplaces wholesale spread operations at all three hydration sites- Startup sync
- Network reconnect hydration
- In-page OTP sync
- Strategy: Remote as baseline (preserves deletions); locally queued edits protected from stale data; pending deletes not resurrected
-
Rubrics only: Last-write-wins (LWW) on
updatedAt— other collections carry event timestamps (gradedAt,submittedAt) only - Offline-first preservation: Changes queue locally, sync on reconnection
-
Test coverage: 1263/1263 unit tests passing; e2e spec
17-offline-sync-merge.spec.ts(static validation + CI coverage)
CEFR ↔ Cambridge Exam mapping + vocabulary API integration
- Static CEFR-to-exam mapping (
src/data/cambridgeExams.ts)- A2 Key → C2 Proficiency coverage
- Function:
cambridgeExamForLevel()
-
UI: Opt-in
showCambridgeLabelssetting renders "B2 · FCE" labels alongside CEFR badges- Visible on CEFR overview page
- Shown on student profile pages
-
API Integration: Previously unused
cambridgeApi.lookupWord()now wired to vocabulary editor- Admin-configured API key required
- Enriches vocab items with CEFR level + definition
- Offline-first: fully hidden without key, fills empty fields only
-
i18n: New
cambridge.*namespace added to 5 locales with parity tests
One-click theme presets + tonal accent scales + Google Fonts support
-
5 Theme Bundles (
src/data/themes.ts)- Academy, Nature, Midnight, Warm, Slate
- Visual card picker in Settings → General
- Atomic update: single
updateSettingscall with newcolorPresetfield
-
Tonal Scale Generation (
src/utils/accentScale.ts)-
--accent-50..900scale via CSScolor-mix()in oklab - Applied globally from AppContext
-
-
Export Fonts
- DOCX: headings/body honor rubric format font via document styles
- PDF: 5 new decorative Google Fonts (Playfair Display, Oswald, Bebas Neue, Special Elite, Courier Prime)
- Format picker: live preview included
- Export pathway: fonts injected at generation time
-
i18n: New
themes.*namespace added to 5 locales with parity tests
Test Results: 123 new unit tests + 1 new Playwright spec; full suite: 1263/1263 passing, typecheck and lint clean
- Added
secondSupabasePagefixture toe2e/fixtures/supabase.fixture.ts— a second browser context signed in as the SAME test user (sharestestUserEmailwithsupabasePage), enabling true two-device tests - New
e2e/specs/18-multi-device-sync.spec.ts: basic propagation (device A edits, device B reloads and sees it), simultaneous-edit/LWW race (the device that saves last wins the whole record, persyncMerge.ts), and network-partition resilience (offline edit on A flushes after reconnect without disturbing a concurrent online edit on B) - The old
test.skip"Multi-device propagation" placeholder in17-offline-sync-merge.spec.tsis removed - typecheck/lint/unit suite (1283/1283) clean
-
Outstanding: new specs are statically reviewed and register correctly with Playwright, but have not yet run against a real local Supabase stack — no Docker daemon was available in the dev sandbox. Run
npm run db:start && npm run e2e:supabaseonce Docker (Desktop/colima/orbstack) is available and iterate as needed before merging.
-
StudentRubric.updatedAtadded; set on every grade/peer-review save and self-assessment submit -
updatedAtalso added toClass,Student,GradeScale,CommentSnippet,CommentBankItem,SelfAssessment,SpeakingSession,DocumentAnalysisResult— set on every create/update via theAppContextreducer -
syncMerge.tsnow applies LWW to all 10 of these collections (previously rubrics-only) -
attachments,exportTemplates, andfavoriteStandardsintentionally left out — add/delete-only, no update action, so pending-queue protection already covers their conflicts - 1283/1283 unit tests passing, typecheck and lint clean
- Added a "Cambridge English exam labels" guide to
Features.md - Added a "Themes & Appearance" section to
Features.md(theme bundles, tonal accent scale, export fonts) - Added a "Conflict resolution & sync strategy" section to
Supabase-Sync.mddocumenting LWW, pending-queue protection, deletion handling, and known limits - The previously-noted "#38/#39/#40 marked Planned" cleanup item was checked against the current wiki — no page outside this Roadmap references those numbers as "Planned", so no further action was needed there
- Upgraded
secondSupabasePagefixture (sharestestUserEmailwithsupabasePage) ✅ - Race condition scenarios: simultaneous edits across devices ✅
- Network partition resilience validation ✅
- Sync state consistency under network failures ✅
- Owner: Followed spec-14 pattern from existing e2e suite
- Priority: HIGH — foundational for multi-device feature stability
-
Remaining: run
npm run db:start && npm run e2e:supabaseagainst a real local Supabase stack (no Docker daemon was available when the spec was written) and mergefeat/multi-device-e2e -
Note:
e2e/specs/17-offline-sync-merge.spec.tsnow passes in CI (post PR #128). Two lessons it taught, relevant for the multi-device fixture work:- Sync adapters return
{ success: false }instead of throwing —pushOnemust check theSyncResult, or offline writes are silently dropped instead of queued. - In the Supabase Playwright project, data seeded only into
localStorageis wiped by post-reload hydration — seed matchingrm_pending_syncupsert entries so the startup flush re-pushes it.
- Sync adapters return
- LWW now covers rubrics, grades (
studentRubrics), peer reviews, classes, students, grade scales, comment snippets, comment bank items, self-assessments, speaking sessions, and analysis results — all via anupdatedAtfield set on every save and compared insyncMerge.ts -
attachments,exportTemplates,favoriteStandardsare add/delete-only (no edit action) — pending-queue protection already covers them, LWW would add no value - No DB migrations needed — all
datacolumns arejsonb, so the newupdatedAtfield flows through automatically - Impact: Enables distributed grading sessions, multi-teacher rubric curation, and concurrent class/student roster edits
- Verified items #38, #39, #40 are not referenced as "Planned" anywhere else in the wiki — no further status update needed
- Added visual guides for Cambridge exam framework to
Features.md - Added theme customization tutorial (theme bundles, accent scale, export fonts) to
Features.md - Documented LWW sync strategy & conflict resolution limits in
Supabase-Sync.md - Priority: MEDIUM — UX clarity for adopters
Implemented across four merge waves (branch feature/phase-3-efl-enhancements): Wave 1 — shared infra + test data layer (2ceeeec); Wave 2 — recordings, vocab dashboard, peer analytics, testing UI (45249a7); Wave 3 — test results, class-average adjustment, live monitor (d9f71b9); Wave 4 — e2e test-environment lifecycle + docs (f7e2ae8).
- Audio/video recording via
useMediaRecorder+ IndexedDB blob store (src/services/mediaStore.ts) — blobs never touchlocalStorage/quota - Wired into
SpeakingSession.tsx; read-only playback in the student portfolio (StudentProfilePage.tsx) - Privacy-first: audio allowed local-only (persistent on-device warning banner); video gated behind a configured Supabase database (session-length video doesn't fit a device-only model)
- Optional cloud sync via
RecordingSync.ts(privaterecordingsbucket, 50 MB cap, signed-URL cache); deleting a session cascades to local + cloud blobs - Migration
034_recordings_storage.sql
- New
/vocabularyroute (VocabularyDashboardPage.tsx) with per-student/class CEFR vocabulary distribution charts (VocabCefrDistributionChart.tsx, A1→C2 stacked bars) -
src/utils/vocabProfileAggregator.tsaggregatesDocumentAnalysisResult.levelCounts - Open CEFR-banded word lists (
src/data/openCefrVocabulary.ts) merged intocefrVocabularyProfiler.ts— reduces reliance on the paid Cambridge API (kept as optional definitions-only enrichment) - CSV export of vocab lists filtered by CEFR band
-
StudentRubric.gradedBynow populated on peer-review save — peer feedback is traceable ("Anonymous reviewer" only when absent on older data) - New
/peer-analytics/:rubricIdroute (PeerReviewAnalyticsPage.tsx): consistency scoring (mean abs deviation vs. teacher baseline), per-reviewer leniency bias, inter-rater spread, comment-frequency heatmap (reusingCriterionHeatmap.tsx), round-over-round trends -
src/utils/peerReviewAggregator.ts(+ tests covering missing-baseline / missing-gradedBy)
- Full
Test/StudentTest/TestQuestion/ProctorEventdata model, synced via the standard LWW collection pattern (migrations033–034) - Teacher:
/testslist + builder (TestBuilderPage.tsx) with MC/short-answer/open questions, CEFR/Standards linking,requireSEBtoggle, share-link assignment modal (offline links embed the full test for no-DB use) - Student:
/test/:code(StudentTestPage.tsx) — no-auth, draft autosave, optional timer, SEB advisory gate (SebGate.tsx, UA-based — documented as deterrent only, not enforcement) - Results:
/tests/:testId/results/:studentTestId(TestResultsPage.tsx) — auto-scored MC/short-answer, manual grading for open questions, per-standard/CEFR rollups, grade-scale mapping - Class-average adjustment:
ClassAverageAdjuster.tsx+testCalc.ts— reversible, auditableadjustmentfield, raw scores preserved - Live monitor:
/tests/:testId/monitorand/essays/:assignmentId/monitor(LiveMonitorPage.tsx), Formative-style presence/response-grid/live-draft preview, built on the genericuseLiveSessionTelemetryhook (tab-switch/copy-paste/battery/heartbeat) andproctorAggregator.ts; offline mode falls back to post-hoc event-log review - e2e:
19-test-environment.spec.ts— full offline lifecycle (build → assign → take → submit with proctoring telemetry → import → grade → adjust), green on chromium/firefox/webkit - Docs triple-update (DocsPage
ROUTE_TREE, README routes table, LandingPage feature cards) completed for all of 3.1–3.4
Verification (WP5): supabase/bootstrap.sql regenerated from 34 migrations (no drift); typecheck clean; lint 0 errors (138 pre-existing any warnings); full unit suite 1475/1475 passing across 99 files; coverage 64.0% lines / 62.6% statements / 54.9% branches / 52.7% functions (all above the 50%/37% thresholds); i18n parity green for en/nl/fr/de/es. e2e: 243 passed, 1 skipped; the only failures are pre-existing and unrelated — 12-navigation.spec.ts on mobile-chrome (fails identically on main, tracked separately) and the supabase project specs (14–17, 19-test-environment.spec.ts firefox flake under full-suite load — passes in isolation), which require npm run db:start (no Docker daemon in this dev sandbox, same outstanding item as Phase 2.1).
PR #144 — Admin hardening, audit log, CSV sync, student archive (merged 17 June 2026)
Implemented in Statistics page — new "Compare" view mode + track/year filters.
- ✅ Class-vs-Class analysis — Compare tab: select up to 4 classes, same rubric → grouped avg bar chart + per-criterion gap chart; class names and counts shown per cell
- ✅ Trend-Analysis — Multi-class trend overlay (LineChart, one line per class, by rubric creation date)
- ✅ Tailored recommendations — Collapsible "Insights" panel with rule-based tips: struggling class (<55% avg), weak criterion (≥15 pp below class avg), largest inter-class divergence (≥20 pp gap)
- ✅ Track + year filters — Dropdowns filter the class selector in both Rubric and Compare modes using
Class.voTrack(VMBO-BB/KB/TL, HAVO, VWO) andClass.year(already typed; no migration needed) - New utility:
src/utils/classComparisonAggregator.ts(compareClasses,buildMultiClassTrend,getInsights) - New component:
src/components/Statistics/MultiClassTrendChart.tsx
New page at /activity-dashboard (sidebar: LayoutGrid icon).
- ✅ Activity × Class grid — Horizontally scrollable table; rows = Rubrics / Tests / Essays (section headers); columns = classes (filtered by year + track); sticky activity-name column
- ✅ Assign from view — Rubric cell: Link/Unlink toggles
Class.rubricIds; Essay cell: "Assign All" bulk-createsEssayAssignmentfor unenrolled students using the existing group template; Test cell: "Open" navigates to test builder (tests are URL-based, no pre-assign model) - ✅ Count badges — Each cell shows
submitted/totalstudents; green when > 0 - ✅ School year + track filter — Same filter dropdowns as Statistics; scopes the column set
- New page:
src/pages/ActivityDashboardPage.tsx - New utility:
src/utils/activityDashboardAggregator.ts(getActivityRows,buildDashboardMatrix)
-
User Roles:
user→teacherrename; admin/observer roles in Supabase RLS (migration 036) ✅ - Data Retention: pg_cron retention cleanup (admin 3yr / grade 1yr / export+auth 1mo) ✅
- Student Anonymisation: Soft-delete archive with Restore + Anonymize actions; Admin → Archive tab ✅
-
Audit Log:
audit_logstable (migration 037);AuditLoggerwired to role changes, deletes, grade saves, rubric edits, exports, auth; Admin → Audit tab with category filter + CSV export ✅ - Estimated effort: 3–4 weeks
- ✅ CSV upsert + class sync: re-importing updates existing students by email; optional sync mode removes students not in the CSV (roster sync)
- ⬜ Native Magister API integration: bulk import + dynamic ongoing sync
- ⬜ Mock API testing for common scenarios
- Note: CSV path is production-ready; Magister API requires a dedicated integration effort
- Estimated effort: 2–3 weeks (remaining)
- ✅
docs/SELF_HOSTING_OPS.md: backup/restore, upgrade path, resource sizing, pg_cron enablement, log rotation, troubleshooting - ✅ HestiaCP/Virtualmin deployment guides updated (PR #144)
- ⬜ Monitoring: CPU/memory alerts, Supabase quota tracking
- Estimated effort: 2 weeks
Delivered as five parallel work-package PRs (#147–#151). Two scoping decisions: "video tours" was implemented as the existing interactive Joyride walkthrough pattern (the app is offline-first with no video infrastructure); the AAA audit took the full-push interpretation, forcing every theme×accent to 7:1 where physically possible.
5.1 Mobile Grading Experience ✅ (PR #148)
- ✅ Touch-friendly level input — a stepper (−/+ + numeric readout) is shown alongside the sub-item/point-range sliders under
@media (hover: none), reusing the existing score handlers; the slider stays for pointer devices - ✅ Sticky score summary — already present (
.grade-footer, fixed, safe-area insets) - ✅ Responsive —
ComparativeGradingreflows to a single stacked column below 768px; iPad-portrait CSS overrides; enlarged range thumbs on touch - ✅ Mobile e2e —
04-grading+ new04b-grading-mobilespec added to the Playwrightmobile-chromeproject
- ✅ Color contrast — new
src/utils/contrastCheck.ts(WCAG luminance/ratio, dependency-free) + an enforcement test that audits every theme bundle, accent preset, and CEFR badge colour against AAA thresholds; ~14 hex values nudged inthemes.ts/cefrDescriptors.tsto clear 7:1 (button labels use the 4.5:1 large-text bar, link text uses the darker accent-700 at 7:1) - ✅ Focus management —
GradeStudent/RubricBuildermodals routed through the shared Radix-backedModal(focus trap + restoration to trigger + Escape-to-close) - ✅ Keyboard navigation —
@hello-pangea/dndkeyboard dragging in the rubric builder with translatable drag-handle labels; global:focus-visibleoutline and skip-to-content link (now i18n'd) - ⬜ Follow-up: add
RubricBuilder/GradeStudent/ComparativeGradingto the jest-axe page suite (deferred — heavy mocking for those large pages)
- ✅ Interactive tours — "Tour this page" Joyride walkthroughs added to 8 more pages (ComparativeGrading, Essays, Tests, Activity Dashboard, CEFR Overview, Speaking Session, Student Profile, Students), on top of the RubricBuilder/Export/Statistics/Grading tours from PR #146
- ✅ Tooltips —
HelpPopoverpromoted to a sharedsrc/components/ui/Tooltip.tsx; contextual help added for score modifiers, LWW sync behaviour (Admin → Database), and proctoring flags (Test Results / Live Monitor) - ✅ Multilingual — all new tour/help strings translated in EN, NL, FR, DE, ES (i18n parity green)
- Recommend next rubrics based on CEFR targets & past performance
- Teacher dashboard: cohort analysis, outlier identification, intervention flags
- Community template sharing (CC-BY-SA)
- Voting & discoverability by subject/level
- Minimal backend API for metadata
- LMS: Canvas, Blackboard, Moodle (LTI 1.3)
- Data Feeds: Clever CSV, OneRoster (for large districts)
| Issue | Impact | Owner | Priority |
|---|---|---|---|
-
PR #128 — Implement roadmap items #18 (Cambridge), #23 Phase 2 (themes), #15 (sync)
- Cambridge exam mapping + vocabulary API
- Theme bundles + tonal accent scales + Google Fonts export
- Sync conflict resolution (LWW for rubrics)
-
PR #144 — Phase 4 — admin hardening, audit log, CSV sync, student archive
- Role rename
user→teacher; admin/observer RLS roles - Audit log table +
AuditLoggerservice; Admin → Audit tab - CSV upsert + class sync; roster sync mode
- Student soft-delete archive with restore & anonymize; Admin → Archive tab
- Self-hosting ops guide (
docs/SELF_HOSTING_OPS.md)
- Role rename
-
Phase 5 (UI/UX Polish) — five work-package PRs:
- #147 — AAA contrast util + enforcement test, theme/CEFR colour fixes
- #148 — touch-friendly mobile grading (stepper, reflow, iPad CSS, mobile e2e)
- #149 — page tours for 8 more pages
- #150 — contextual help tooltips (score modifier, LWW, proctoring)
- #151 — accessible modals, focus restoration, translatable a11y labels
- Unit tests: Vitest (target >90% coverage)
- E2E tests: Playwright (spec-14 pattern for async operations)
- i18n: Parity tests across EN, NL, FR, DE, ES locales -> New Locales may be added, make sure to follow standards, and check if locale keys fit within the set UI/UX
- Accessibility: axe-core in CI (WCAG 2.1 AA minimum)
- Performance: Offline-first validation; sync latency benchmarks for > 100 rubrics
Last updated: 20 June 2026 (Phase 5 — UI/UX Polish ✅: 5.1 Mobile Grading ✅, 5.2 WCAG AAA ✅ (axe page-suite expansion deferred), 5.3 Help & Documentation ✅; delivered as PRs #147–#151)