Skip to content

feat(gradebook): external-ID import, manage panel, and drag-and-drop reorder#8457

Open
LWS49 wants to merge 4 commits into
lws49/ext-pr1-foundationfrom
lws49/ext-pr2-manage-reorder
Open

feat(gradebook): external-ID import, manage panel, and drag-and-drop reorder#8457
LWS49 wants to merge 4 commits into
lws49/ext-pr1-foundationfrom
lws49/ext-pr2-manage-reorder

Conversation

@LWS49

@LWS49 LWS49 commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

Gives external assessments a proper management home and adds External-ID-based grade import. A single page-level "Manage external assessments" panel (above the tabs) replaces the two scattered toolbar buttons and the in-column kebab menu; it lists every external (Name / Max / Weight / Actions) and hosts Add, Import, Edit, and Delete. Import matches students by "External ID" (the rest of Coursemology's term) or email, with the identifier keyed by column position. Externals can be reordered by drag-and-drop, persisted via a new position column and a canonical-order endpoint. Includes the External-ID column made sortable in Manage Users, and an N+1 fix on external delete.

Design decisions

  • Management lives in one panel gated on canManageWeights but not on weightedViewEnabled - recording a paper-exam grade is valid even with weighting off, so externals must stay manageable. The Weight column is shown read-only and only when the weighted view is on (weight editing stays in Configure Weights, to avoid two edit paths).
  • All rename/delete moved out of the in-table column kebab into the panel - removes the add-without-delete and toolbar-add-vs-buried-delete asymmetries; the header keeps only an "External" chip for identification.
  • Import weightage is zeroed server-side unless the weighted view is enabled, mirroring the manual-add path - the field being hidden on the frontend was previously the only guard, so a stale or crafted request could write a nonzero weight into a weighting-off course.
  • Adding/editing an external with a non-zero weight (or importing a weighted component) while the table shows the equal-split default first persists that visible split (via the existing updateWeights endpoint, using PR1's materializedDefaultWeights helper) - otherwise setting one real weight would disengage the fallback and collapse every other tab to its stored 0. No-op when weights are already configured or the new weight is 0.

Regression prevention

Covers: manage panel (list, add/edit/delete, empty state), the delete-prompt confirm/failure-toast/cancel paths, reorder dispatch + failure rollback, buildTemplate header generation, all gradebook operation thunks (id-negation, optimistic apply/rollback, create-weight branch, DELETE_EXTERNAL_ASSESSMENT), the default-weight materialization on add/edit/import, and the External-ID sortable users column (frontend suites green). Adds the position migration 20260624000000 (reversible). Backward compatible: existing externals get sequential positions on migrate. External-assessments controller specs run on push/CI.

Includes the second migration of the stack - run against your dev DB before review.

Used-then-replaced note for reviewers: PR1 shipped ImportExternalAssessmentsButton and RenameExternalColumnPrompt; this PR removes both as the single entry point and panel replace them. This is intentional, not a regression.

Screenshots worth adding: the Manage panel, and the drag-and-drop reorder interaction.

LWS49 added 4 commits June 17, 2026 14:43
…links & sorting

Introduce the course gradebook: a frozen-column table of students × assessments
with a column picker, CSV export, and per-grade links into submissions.

Gradebook table
- Page with TanStack-backed table: pinned checkbox + Name columns, sticky
  header and Max Marks rows, frozen-column border seams that survive sticky
  scroll compositing.
- Column picker (assessments grouped by tab/category) and CSV export of the
  selected columns; empty-state hint when no data columns are chosen.
- External ID column, shown when any student has one.
- Grade cells link to the student's submission; a dismissible GradeLinkHint
  banner explains the affordance (persisted per-user via useDismissibleOnce).
- "Search students" global search.
- Default sort with name ascending, null/undefined at bottom of sort regardless of order

Shared table builder
- getColumnCanGlobalFilter is gated on column visibility ("search what you
  see"): hiding a column via the picker removes it from search, and the
  nullable-first-row type sniff is bypassed. Affects all TanStack tables.

Backend
- Gradebook controller, ability and course component; index JSON serializes
  students, assessments, submissions (with submissionId) and gamification.
- Submission grade query also selects the submission id for grade links.

- Remove the redundant ScoreAssessmentSummary download from Statistics.
Add weighted view built on top of gradebook:

- add tables course_gradebook_contributions and course_gradebook_assessment_contributions
- weighted table with equal/custom weight modes and per-assessment
  weight inputs, with a sum gate on custom weights
- points / percentage display toggle
- inline per-student assessment breakdown (row expand)
- projected-total hint
- gradebook_excluded column, serialization, and update-weights API echo
- per-assessment include/exclude in the configure-weights modal,
  seeding custom weights from included assessments only
- excluded assessments shown in the breakdown with no contribution
- add SegmentedSelect component for stylized selection that is not "on-off"
end
end


Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Correctable] Layout/EmptyLines: Extra blank line detected.

@LWS49 LWS49 force-pushed the lws49/ext-pr1-foundation branch 3 times, most recently from 441d043 to aeb0e66 Compare June 26, 2026 10:59
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