Skip to content

Wgu jesse stewart/instructor dashboard certificates bulk#190

Open
wgu-jesse-stewart wants to merge 24 commits intoopenedx:mainfrom
WGU-Open-edX:wgu-jesse-stewart/instructor_dashboard_certificates_bulk
Open

Wgu jesse stewart/instructor dashboard certificates bulk#190
wgu-jesse-stewart wants to merge 24 commits intoopenedx:mainfrom
WGU-Open-edX:wgu-jesse-stewart/instructor_dashboard_certificates_bulk

Conversation

@wgu-jesse-stewart
Copy link
Copy Markdown
Contributor

@wgu-jesse-stewart wgu-jesse-stewart commented Apr 28, 2026

feat: bulk certificate exceptions CSV upload + certificates modal redesign

Description

Frontend counterpart to the new BulkCertificateExceptionsView endpoint. Adds CSV upload to the Grant Exceptions flow and redesigns the three certificate-management modals to match the latest design.

What changes for users (course staff / instructors)

Grant Exceptions modal is now a tabbed dialog:

  • Individual tab — one learner (username or email) plus optional notes. Replaces the previous comma-separated multi-learner field; staff who want to add several learners at once should use the Bulk tab.
  • Bulk tab — drag-and-drop CSV upload (.csv only, 5 MB max). The selected filename is shown with a confirmation banner. A "CSV Instructions" tooltip near the bottom of the panel explains the expected format (username/email in column 1, optional note in column 2).
  • Title copy updated to "Add to Exceptions List" with a fuller explanation that mentions the follow-up "Generate Exception Certificates" step.
  • Submit button is labeled Save and is disabled until either a learner is entered (Individual) or a CSV is selected (Bulk).
  • Cancel resets all form state (learner, notes, file, active tab) before closing.

Partial-failure handling: when the CSV endpoint returns a mix of successes and errors, the modal closes, a toast reports the success count, and a warning modal lists each failed row as learner: message. A pure error response shows a danger modal with the API's error message.

Invalidate Certificate modal is now single-learner only. The label changes from "Learners" to "Username or Email", and the input from a comma-separated list to a single field. Title copy is singularized ("Invalidate Certificate"). Submit is Save and is disabled until a learner is entered.

Student Generated Certificates modal (formerly "Disable Certificates"):

  • Renamed throughout — the dropdown menu item, modal title, header alt text, and aria labels all use "Student Generated Certificates" / "More actions" instead of "Disable Certificates".
  • Single modal regardless of current state. A checkbox labeled "Enable Student Generated Certificates" reflects the current value; the description text reads "This functionality is enabled by default."
  • Save only fires the toggle mutation when the checkbox state actually changed; clicking Save with no change just closes. Close resets the checkbox to its original value.
  • Modal grew from sm to md and now has the standard header close (X). Footer buttons are Save / Close (replacing Confirm / Cancel).

The invalidate button on the page header switched its iconBefore from Close to Cancel to match the new icon set.

What changes for developers

  • New API function uploadBulkExceptionsCsv(courseId, file) in data/api.ts — posts a multipart/form-data request with a file field to /api/instructor/v2/courses/{courseId}/certificates/exceptions/bulk.
  • New React Query hook useUploadBulkExceptionsCsv(courseId) in data/apiHook.ts — invalidates the per-course query key on success so the certificate tables refresh.
  • CertificatesPageHeader's onDisableCertificates prop is renamed and made optional: onStudentGeneratedCertificates?: () => void. The dropdown item is conditionally rendered when the handler is provided.
  • GrantExceptionsModal and InvalidateCertificateModal no longer wrap LearnerActionModal; they each own their ModalDialog markup. LearnerActionModal no longer has any consumers in this PR — leaving it in place to bound the diff, but it can be deleted in a follow-up if there are no other planned uses.
  • Existing tests were updated to query the modal by getByRole('dialog') rather than matching the title string, and to use the new "Save" button label.

User roles impacted

  • Course staff / instructors: see the new modal designs and gain CSV upload for exceptions; lose the ability to invalidate multiple certificates in one submission (must invalidate one at a time).
  • Developers: new hook + API function for bulk CSV upload; renamed prop on the page header.
  • Operators: no config changes.

Supporting information

  • Backend endpoint: companion PR adding BulkCertificateExceptionsView at POST /api/instructor/v2/courses/{course_id}/certificates/exceptions/bulk. This frontend PR depends on that endpoint being available.
  • Designs: link the Figma here.
  • Fixes #{issue number if applicable}.

Testing instructions

Backend dependency: bring up an LMS that includes the BulkCertificateExceptionsView endpoint. Without it the Bulk tab will surface a 404 from the API.

Bulk CSV upload (happy path)

  1. Open a course with several enrolled learners.
  2. Create a CSV with username,notes rows, e.g.:
    user1,Manual review approved
    user2,
    
  3. Click Grant Exceptions → switch to the Bulk tab.
  4. Drag the CSV into the dropzone (or click and select).
  5. Confirm the green "File selected: …" banner appears with the filename.
  6. Click Save.
  7. Expect the modal to close and a success toast to report the count of granted exceptions. The certificate exceptions table refreshes (React Query invalidation).
  8. Verify the granted exceptions appear in the table for the rows in the CSV that resolved to real users.

Bulk CSV upload (partial failure)

  1. Edit the CSV to include one valid username and one nonexistent email.
  2. Repeat the upload flow.
  3. Expect the modal to close, a toast for the successful row, and a warning modal listing the failed row as learner: message.

Bulk CSV upload (full failure)

  1. Upload an empty CSV, or a .csv-extension file with malformed content.
  2. Expect a danger modal showing the API error message; the certificates table is unchanged.

Individual exception (regression)

  1. Open Grant Exceptions, stay on the Individual tab.
  2. Type leading/trailing whitespace around a username (e.g., user1 ).
  3. Click Save.
  4. Expect the request to be sent with the trimmed value and an empty notes string.
  5. Confirm Save is disabled when the learner field is empty / whitespace-only.

Invalidate Certificate

  1. Click Invalidate Certificate in the page header.
  2. Verify the title is singular ("Invalidate Certificate") and the field accepts only a single learner.
  3. Submit; confirm Save is disabled until the field has a non-empty value.
  4. Confirm the prior comma-separated multi-learner UX is gone.

Student Generated Certificates

  1. Open the "More actions" dropdown (kebab menu in the page header) → Student Generated Certificates.
  2. Verify the modal opens at md size with a header close (X) and a checkbox reflecting the current enabled state.
  3. Click Save without toggling the checkbox — modal should close, no mutation fired.
  4. Re-open, toggle the checkbox, click Save — mutation fires and table refreshes.
  5. Re-open, toggle the checkbox, click Close (or the header X) — modal closes and the next open shows the original (unchanged) checkbox state.

Run unit tests

npm test -- src/certificates

Other information

  • Depends on the backend BulkCertificateExceptionsView PR.
  • The "CSV Instructions" link in the Bulk tab still has destination="#" and a TODO comment — the documentation URL has not been finalized. The tooltip carries the actual format guidance for now, so the link is informational only.
  • messages.ts includes a csvUploadPending key that is no longer referenced after the real backend endpoint landed; can be removed in a follow-up i18n cleanup.
  • LearnerActionModal is now unused by either consumer — kept in place to bound the diff; safe to delete in a follow-up.
  • The Dropzone onProcessUpload callback walks fileData.values() because Paragon's typings here are loose; this matches the pattern used elsewhere in the codebase but is worth a second look from a reviewer familiar with that component.
  • Accessibility: the modals now expose a real dialog role (the tests assert this), and the form controls are labeled. The "More actions" dropdown toggle uses an explicit alt label.

Best Practices Checklist

  • Any new files are using TypeScript (.ts, .tsx).
  • Deprecated propTypes, defaultProps, and injectIntl patterns are not used in any new or modified code.
  • Tests should use the helpers in src/testUtils.tsx (specifically initializeMocks) — existing tests in this file use a local renderWithAlertAndIntl / renderWithIntl helper; new tests follow the existing pattern. Migrating to initializeMocks can be a separate cleanup.
  • Use React Query to load data from REST APIs. (useUploadBulkExceptionsCsv is a new useMutation.)
  • All new i18n messages in messages.ts files have a description for translators.
  • Imports avoid using ../ (uses @src/...).

@openedx-webhooks openedx-webhooks added open-source-contribution PR author is not from Axim or 2U core contributor PR author is a Core Contributor (who may or may not have write access to this repo). labels Apr 28, 2026
@openedx-webhooks
Copy link
Copy Markdown

openedx-webhooks commented Apr 28, 2026

Thanks for the pull request, @wgu-jesse-stewart!

This repository is currently maintained by @openedx/committers-frontend-app-instructor-dashboard.

Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review.

🔘 Get product approval

If you haven't already, check this list to see if your contribution needs to go through the product review process.

  • If it does, you'll need to submit a product proposal for your contribution, and have it reviewed by the Product Working Group.
    • This process (including the steps you'll need to take) is documented here.
  • If it doesn't, simply proceed with the next step.
🔘 Provide context

To help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:

  • Dependencies

    This PR must be merged before / after / at the same time as ...

  • Blockers

    This PR is waiting for OEP-1234 to be accepted.

  • Timeline information

    This PR must be merged by XX date because ...

  • Partner information

    This is for a course on edx.org.

  • Supporting documentation
  • Relevant Open edX discussion forum threads
🔘 Get a green build

If one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green.

Details
Where can I find more information?

If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources:

When can I expect my changes to be merged?

Our goal is to get community contributions seen and reviewed as efficiently as possible.

However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:

  • The size and impact of the changes that it introduces
  • The need for product review
  • Maintenance status of the parent repository

💡 As a result it may take up to several weeks or months to complete a review and merge your PR.

Comment thread src/certificates/components/CertificatesPageHeader.tsx Outdated
Comment thread src/certificates/components/CertificatesToolbar.tsx Outdated
Comment thread src/certificates/CertificatesPage.tsx Outdated
Comment thread site.config.dev.tsx
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 28, 2026

Codecov Report

❌ Patch coverage is 95.65217% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.15%. Comparing base (8532409) to head (dd5e256).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...c/certificates/components/GrantExceptionsModal.tsx 89.74% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #190      +/-   ##
==========================================
+ Coverage   89.89%   90.15%   +0.25%     
==========================================
  Files         139      139              
  Lines        2277     2376      +99     
  Branches      459      478      +19     
==========================================
+ Hits         2047     2142      +95     
- Misses        224      228       +4     
  Partials        6        6              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@wgu-jesse-stewart wgu-jesse-stewart force-pushed the wgu-jesse-stewart/instructor_dashboard_certificates_bulk branch from 307a2eb to a685422 Compare April 29, 2026 00:37
@mphilbrick211 mphilbrick211 added the mao-onboarding Reviewing this will help onboard devs from an Axim mission-aligned organization (MAO). label Apr 29, 2026
@mphilbrick211 mphilbrick211 moved this from Needs Triage to Waiting on Author in Contributions Apr 29, 2026
@wgu-jesse-stewart wgu-jesse-stewart marked this pull request as ready for review April 29, 2026 14:42
},
onError: (error) => {
showModal({
title: MODAL_TITLES.ERROR,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

maybe im catching this late, but i think this one should be an intl message, if not in other languages you would see Error without translation in the title

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core contributor PR author is a Core Contributor (who may or may not have write access to this repo). mao-onboarding Reviewing this will help onboard devs from an Axim mission-aligned organization (MAO). open-source-contribution PR author is not from Axim or 2U

Projects

Status: Waiting on Author

Development

Successfully merging this pull request may close these issues.

4 participants