Skip to content

feat(gradebook): weighted view with per-tab weights and exclude flag#8419

Draft
LWS49 wants to merge 19 commits into
lws49/feat-gradebook-weights-pr2-apifrom
lws49/feat-gradebook-weights-pr3-fe
Draft

feat(gradebook): weighted view with per-tab weights and exclude flag#8419
LWS49 wants to merge 19 commits into
lws49/feat-gradebook-weights-pr2-apifrom
lws49/feat-gradebook-weights-pr3-fe

Conversation

@LWS49

@LWS49 LWS49 commented May 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements the gradebook weighted view feature end-to-end. Adds a new staff-only gradebook page with two views: a flat "All assessments" table showing every published assessment, and a "By weight" view showing per-tab weighted totals (Canvas-style: Σ(tab% × weight), bonus allowed). The backend adds a gradebook_weight column on assessment tabs, grade summary queries, max-grade aggregation, and ability checks for staff read access and manager-only weight editing. The frontend adds a configurable per-tab weights dialog with an exclude-from-weighted-total flag, a course-admin toggle to enable the weighted view, and a one-time dismissable hint that surfaces the setting to managers before they've turned it on.

Design decisions

  • Weighted view is off by default - most courses won't use per-tab weights, so the majority gets no extra tab; managers who want it opt in via Course Settings → Gradebook. Always-on would impose a permanent extra tab and an opt-out chore on the majority.
  • Discoverability via a one-time dismissable Alert, not a hover Tooltip - mirrors the existing CourseSuspendedAlert pattern (MUI Alert + Link into admin settings). Tooltips are wrong for one-time announcements. Hint is manager-gated (canManageWeights) and only shows while the setting is off, so it disappears once it's done its job.
  • localStorage dismissal scoped to ${userId}:${key} - matches the pattern already used in useTanStackTableBuilder; per-device behaviour is an accepted limitation for an onboarding nudge. Backend-persisted dismissal is the upgrade path if ever needed, deliberately deferred.
  • Tabs can be excluded from the weighted total - the configure-weights dialog exposes an exclude toggle per tab; excluded tabs render greyed cells and are skipped in the weighted total and denominator, rather than counting as zero.

Regression prevention

Automated tests cover: weighted total computation including excluded tabs (computeWeighted.test.ts), ConfigureWeightsPrompt rendering and exclude toggle, GradebookTable and GradebookWeightedTable cell rendering, GradebookColumnTree, GradebookIndex routing and role gating, WeightedViewHint visibility and dismissal, useDismissibleOnce hook, GradebookSettings admin toggle, backend controller specs for index and update_weights, and model specs for Tab#gradebook_weight, Assessment.max_grades, Submission.grade_summary, and ability checks.

Manual testing confirmed: manager sees hint when weighted view is off; hint absent when weighted view is on; TA/staff sees no hint; dismiss persists across page reload; dismissed state is per-user (second manager sees it fresh); settings link navigates correctly to Course Settings → Gradebook.

Backward compat preserved - weighted view is additive; existing gradebook pages and assessment flows are unchanged.

@LWS49 LWS49 changed the title Lws49/feat gradebook weights pr3 fe feat(gradebook): add weighted view with per-tab grade weights May 29, 2026
@LWS49 LWS49 changed the title feat(gradebook): add weighted view with per-tab grade weights feat(gradebook): add weighted view with per-tab grade weights (pr3 of 3) May 29, 2026
@LWS49 LWS49 changed the base branch from master to lws49/feat-gradebook-weights-pr2-api May 29, 2026 06:29
@LWS49 LWS49 force-pushed the lws49/feat-gradebook-weights-pr2-api branch 5 times, most recently from 333c45a to 8b79b30 Compare June 4, 2026 06:07
@LWS49 LWS49 force-pushed the lws49/feat-gradebook-weights-pr3-fe branch 4 times, most recently from b37937a to d4661f1 Compare June 8, 2026 11:35
@LWS49 LWS49 changed the title feat(gradebook): add weighted view with per-tab grade weights (pr3 of 3) feat(gradebook): weighted view with per-tab weights and exclude flag (pr3 of 3) Jun 8, 2026
@LWS49 LWS49 changed the title feat(gradebook): weighted view with per-tab weights and exclude flag (pr3 of 3) feat(gradebook): weighted view with per-tab weights and exclude flag Jun 9, 2026
LWS49 added 15 commits June 9, 2026 10:16
Update the search placeholder and filter logic in ManageUsersTable, StudentStatisticsTable and UserInvitationsTable to match against external ID in addition to name and email. The existing shouldInclude in ManageUsersTable was refactored to pre-compute the query string and add the externalId check. UserInvitationsTable gains a search prop with a localised placeholder, plus sets searchable: true on the conditionally-rendered externalId column so TanStack's column-level filter also covers it.

Add a searchText translation key to user-invitations/translations.ts mirroring the wording used in the users bundle.
Introduces a course-wide gradebook showing per-student grades across all assessments. Instructors can toggle which assessment columns are visible via a hierarchical column picker (grouped by category/tab), then export the current view to CSV.

Backend adds GradebookController#index (JSON), ability guard, and model methods on Assessment and Submission for fetching grade data.

Table lib gains reusable ColumnPickerTemplate, MuiColumnPickerPrompt, ColumnPickerTreeGroup, and toolbar integration used by the gradebook.
- Add gradebook_weight (0-100 integer) column to course_assessment_tabs
- Add manage_gradebook_weights/settings abilities (manager/owner only)
- Add Course Admin -> Gradebook settings page to toggle the setting
…alog, toolbar buttons, visibility state, and tests

    - Add MuiColumnPickerDialog with Apply/Export actions, locked-column enforcement, and optional dataColumnIds hint when no data columns are selected
    - Update MuiTableToolbar to render a trigger button (opens dialog) and a direct export button alongside the existing CSV download icon
    - Extend useTanStackTableBuilder with column visibility state (localStorage persistence, dynamic reconciliation), onExportFromPicker, onDirectExport, and cross-page selection helpers (selectedCount, toggleAllFiltered, etc.)
    - Add ColumnPickerTemplate interface and Body.ts selection fields
    - Add full test coverage for dialog, toolbar, and hook behaviour
    - Add lib.components.table.* locale keys (en/ko/zh)
- add Tab.update_gradebook_weights: transactional bulk update scoped to
  course tabs, with pre-flight ID validation and single pre-fetch query
- extend GET /gradebook index JSON with weightedViewEnabled,
  canManageWeights, and per-tab gradebookWeight (gated by setting)
- add PATCH /gradebook/weights: manager-only, returns 422 on validation
  failure or foreign tab, transactionally rolls back on any error
…alog, toolbar buttons, visibility state, and tests

    - Add MuiColumnPickerDialog with Apply/Export actions, locked-column enforcement, and optional dataColumnIds hint when no data columns are selected
    - Update MuiTableToolbar to render a trigger button (opens dialog) and a direct export button alongside the existing CSV download icon
    - Extend useTanStackTableBuilder with column visibility state (localStorage persistence, dynamic reconciliation), onExportFromPicker, onDirectExport, and cross-page selection helpers (selectedCount, toggleAllFiltered, etc.)
    - Add ColumnPickerTemplate interface and Body.ts selection fields
    - Add full test coverage for dialog, toolbar, and hook behaviour
    - Add lib.components.table.* locale keys (en/ko/zh)
- Add gradebook_weight (0-100 integer) column to course_assessment_tabs
- Add Course::Settings::GradebookComponent with weighted_view_enabled
- Add manage_gradebook_weights/settings abilities (manager/owner only)
- Add Course Admin -> Gradebook settings page to toggle the setting
- add Tab.update_gradebook_weights: transactional bulk update scoped to
  course tabs, with pre-flight ID validation and single pre-fetch query
- extend GET /gradebook index JSON with weightedViewEnabled,
  canManageWeights, and per-tab gradebookWeight (gated by setting)
- add PATCH /gradebook/weights: manager-only, returns 422 on validation
  failure or foreign tab, transactionally rolls back on any error
… toggle

- add GradebookWeightedTable with 3-row sticky header showing per-tab
  weighted subtotals and overall weighted totals per student
- add ConfigureWeightsDialog for managers to edit tab weights (0–100),
  with sum-to-100 warning and integer/NaN validation
- add All vs By-weight view toggle in GradebookIndex (role-aware)
- add computeWeighted helpers for tab subtotal and student total
- store weightedViewEnabled, canManageWeights, and per-tab gradebookWeight
  from API; wire updateWeights thunk for PATCH /gradebook/weights
- add i18n keys and full test coverage for table, dialog, and helpers
@LWS49 LWS49 force-pushed the lws49/feat-gradebook-weights-pr3-fe branch from 5508fd2 to 2f958d0 Compare June 9, 2026 03:20
feat(gradebook): additive points total — drop normalize division and exclude filter

feat(gradebook): remove gradebookExcluded from types and store

feat(gradebook): display points (subtotal×weight) in weighted table cells

feat(gradebook): remove Exclude control from ConfigureWeightsPrompt

test(gradebook): update tests
@LWS49 LWS49 force-pushed the lws49/feat-gradebook-weights-pr3-fe branch from 2f958d0 to bfab06e Compare June 9, 2026 06:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant