feat: add instructor dashboard overrides to replicate canvas and rapid-response-report plugins behaviour in MFE#58
Conversation
…d-response-report plugins behaviour in MFE
There was a problem hiding this comment.
Code Review
This pull request adds custom Canvas Integration and Rapid Response Reports tabs to the OpenEdX Instructor Dashboard across the mitx, mitxonline, and xpro sites. Feedback on the implementation focuses on improving robustness and consistency: in CanvasIntegrationPage.tsx, safely handling potential null or non-object values in renderTable, memoizing columns to prevent unnecessary re-renders, and URL-encoding the assignment ID parameter; in RapidResponseReportsPage.tsx, using optional chaining to safely access response data and specifying a fixed timezone (such as UTC) to ensure consistent date grouping for all users.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| const renderTable = (data: any) => { | ||
| if (!data || (Array.isArray(data) && data.length === 0)) { | ||
| return <p>No results found.</p>; | ||
| } | ||
| const items = Array.isArray(data) ? data : [data]; | ||
| if (items.length === 0) return <p>No results found.</p>; | ||
| const columns = Object.keys(items[0]).map(col => ({ | ||
| Header: col, | ||
| accessor: col, | ||
| })); | ||
| return ( | ||
| <DataTable | ||
| itemCount={items.length} | ||
| data={items} | ||
| columns={columns} | ||
| > | ||
| <DataTable.Table /> | ||
| </DataTable> | ||
| ); | ||
| }; |
There was a problem hiding this comment.
There are two issues here:
- Correctness/Safety: If
items[0]is null, undefined, or not an object (e.g., a string),Object.keys(items[0])will throw aTypeErroror return unexpected numeric keys. We should safely check thatitems[0]is a non-null object before extracting keys. - Performance:
DataTable(built onreact-table) uses reference equality forcolumns. Recreatingcolumnson every render can cause performance issues or infinite render loops. It is highly recommended to liftcolumnsanddatato the component level and memoize them usinguseMemo.
Here is a safer implementation of renderTable that guards against non-object items. Consider also refactoring this to use useMemo at the component level for columns and data.
const renderTable = (data: any) => {
if (!data || (Array.isArray(data) && data.length === 0)) {
return <p>No results found.</p>;
}
const items = Array.isArray(data) ? data : [data];
if (items.length === 0 || !items[0] || typeof items[0] !== 'object') {
return <p>No results found.</p>;
}
const columns = Object.keys(items[0]).map(col => ({
Header: col,
accessor: col,
}));
return (
<DataTable
itemCount={items.length}
data={items}
columns={columns}
>
<DataTable.Table />
</DataTable>
);
};
| try { | ||
| const client = getAuthenticatedHttpClient(); | ||
| const response = await client.get(`${baseUrl}/rapid_response_runs`); | ||
| const runs: ProblemRun[] = Array.isArray(response.data) ? response.data : response.data.problem_runs || []; |
There was a problem hiding this comment.
If response.data is null or undefined (e.g., if the API returns an empty response or a 204 No Content), evaluating response.data.problem_runs will throw a TypeError: Cannot read properties of null. Use optional chaining to safely access problem_runs.
| const runs: ProblemRun[] = Array.isArray(response.data) ? response.data : response.data.problem_runs || []; | |
| const runs: ProblemRun[] = Array.isArray(response.data) ? response.data : response.data?.problem_runs || []; |
| setError('Please select an assignment first.'); | ||
| return; | ||
| } | ||
| const data = await makeRequest(`list_canvas_grades?assignment_id=${selectedAssignment}`); |
There was a problem hiding this comment.
To prevent potential issues with special characters or spaces in assignment IDs, it is safer to URL-encode the selectedAssignment parameter using encodeURIComponent.
| const data = await makeRequest(`list_canvas_grades?assignment_id=${selectedAssignment}`); | |
| const data = await makeRequest('list_canvas_grades?assignment_id=' + encodeURIComponent(selectedAssignment)); |
| const date = new Date(run.created).toLocaleDateString('en-US', { | ||
| year: 'numeric', | ||
| month: '2-digit', | ||
| day: '2-digit', | ||
| }); |
There was a problem hiding this comment.
toLocaleDateString is timezone-dependent and uses the user's local browser timezone by default. This can cause runs created at the same UTC time to be grouped under different dates for different instructors depending on their local timezone. To ensure consistent grouping across all users, specify a fixed timezone (such as 'UTC').
const date = new Date(run.created).toLocaleDateString('en-US', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
timeZone: 'UTC',
});
There was a problem hiding this comment.
Pull request overview
This PR adds two custom instructor dashboard tabs — Canvas Integration (enrollment sync and grade export) and Rapid Response Reports (download links for rapid response runs) — to the MFE-based instructor dashboard across all three MIT ODL deployments (mitxonline, mitx, xpro). It replicates behavior previously provided by Django-template-based plugins (ol_openedx_canvas_integration and ol_openedx_rapid_response_reports) in the new frontend-base slot architecture.
Changes:
- Adds a new
instructor-dashboard/shared module withCanvasIntegrationPage,RapidResponseReportsPage, and acreateInstructorDashboardCustomApp()factory that registers tab navigation entries and routed page content viaWidgetOperationTypes.APPENDslot operations. - Wires the custom app into all 6 site config files (build + dev for each deployment) after the base
instructorDashboardApp.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
frontend/shared/src/instructor-dashboard/index.tsx |
App factory registering 4 slot operations (2 tab entries + 2 route pages) using a PlaceholderSlot pattern |
frontend/shared/src/instructor-dashboard/CanvasIntegrationPage.tsx |
Canvas enrollment list/sync and grade export UI with DataTable display |
frontend/shared/src/instructor-dashboard/RapidResponseReportsPage.tsx |
Rapid response runs fetched on mount, grouped by date, with download links |
frontend/mitxonline/site.config.build.tsx |
Adds createInstructorDashboardCustomApp() import and registration |
frontend/mitxonline/site.config.dev.tsx |
Same for dev config |
frontend/mitx/site.config.build.tsx |
Same for MITx build config |
frontend/mitx/site.config.dev.tsx |
Same for MITx dev config |
frontend/xpro/site.config.build.tsx |
Same for xPro build config |
frontend/xpro/site.config.dev.tsx |
Same for xPro dev config |
| <Button variant="outline-primary" onClick={handleOverloadEnrollments} disabled={loading}> | ||
| Overload Enrollment List Using Canvas | ||
| </Button> |
be40e31 to
0be1299
Compare
|
| GitGuardian id | GitGuardian status | Secret | Commit | Filename | |
|---|---|---|---|---|---|
| 5823683 | Triggered | Zapier Webhook URL | 6a7c467 | deployments/mit-ol/settings/cms/models/aqueduct.py | View secret |
| 33931195 | Triggered | Generic Password | 6a7c467 | local-dev/manifests/infra/mariadb.yaml | View secret |
🛠 Guidelines to remediate hardcoded secrets
- Understand the implications of revoking this secret by investigating where it is used in your code.
- Replace and store your secrets safely. Learn here the best practices.
- Revoke and rotate these secrets.
- If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.
To avoid such incidents in the future consider
- following these best practices for managing and storing secrets including API keys and other credentials
- install secret detection on pre-commit to catch secret before it leaves your machine and ease remediation.
🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
…ared snapshot Commit 6a7c467 ("adjust after rebase") accidentally committed the local-dev snapshot copy (frontend/mitxonline/shared) and dropped the canonical frontend/shared. That broke the @shared/* tsconfig alias used by all site projects and the Dagger --shared-src mount (mitx/xpro builds), and made the PR show shared/src/footer/index.tsx as deleted. Relocate the snapshot content back to the canonical frontend/shared and add an anchored /shared/ entry to each site project's .gitignore so the local-dev copy (cp -R ../shared ./shared for `npm run dev`) can't be committed again. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per review, the MIT OL header/footer customizations (shared/src/footer, shared/src/header, shared/src/styles/mitxonline.scss) are split into their own branch/PR (anas/mfe-header-footer-overrides). This branch keeps only the instructor-dashboard MFE work; the site configs still wire createMITOLFooterApp and createMITxOnlineHeaderApp from main's (now-unmodified) shared components. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ashboard-overrides # Conflicts: # deployments/mit-ol/pip_package_lists/master/mitx-staging.txt # deployments/mit-ol/pip_package_lists/ulmo/mitx-staging.txt # deployments/mit-ol/pip_package_lists/ulmo/mitx.txt # deployments/mit-ol/pip_package_lists/verawood/mitx-staging.txt # deployments/mit-ol/pip_package_lists/verawood/mitx.txt
asadali145
left a comment
There was a problem hiding this comment.
Just a question after code review. I'll test it after that.
| // --------------------------------------------------------------------------- | ||
|
|
||
| const SLOT = { | ||
| tabs: "org.openedx.frontend.slot.instructorDashboard.tabs.v1", |
There was a problem hiding this comment.
This is unused, it can be a single const ROUTES_SLOT_ID="org.openedx.frontend.slot.instructorDashboard.routes.v1".
|
@Anas12091101 are these any docs to run this MFE apart from the testing instructions? Also, what if we want to do some development inside the instructor dashboard? How are we supposed to run the MFE in that case? Can the new slot overrides be used in dev mode? |
asadali145
left a comment
There was a problem hiding this comment.
LGTM! There are some conflicts and I have a question on docs and dev mode.
| // commonAppConfig (mitolHeader / mitolFooter) is loaded at runtime from the LMS | ||
| // frontend_site_config API rather than hardcoded here. The dev server proxies | ||
| // /api/frontend_site_config/v1 to lmsBaseUrl; the response is deep-merged over | ||
| // this static config. Requires ENABLE_MFE_CONFIG_API + FRONTEND_SITE_CONFIG set | ||
| // on the LMS. |
There was a problem hiding this comment.
I added it for anyone who might wonder why runtimeConfigJsonUrl is defined there. Since it's not immediately obvious, I thought a short comment would provide the context and save future readers from having to dig through the implementation.
There are some docs under the mitxonline folder: There's also a shared README, but it doesn't currently include instructions for running frontend-base. We can add those there.
When I started working on this, I ran the Instructor Dashboard MFE directly, just like any other MFE, and it worked fine. We can continue doing local development that way. For slot overrides, we can use the existing site.config.dev.tsx file to override the slots during development. |
…ashboard-overrides # Conflicts: # deployments/mit-ol/pip_package_lists/master/mitx-staging.txt # deployments/mit-ol/pip_package_lists/master/mitx.txt # deployments/mit-ol/pip_package_lists/master/mitxonline.txt # deployments/mit-ol/pip_package_lists/verawood/mitx-staging.txt # deployments/mit-ol/pip_package_lists/verawood/mitx.txt
Can we document both of these? Maybe in a new PR? |
Summary
Adds Canvas Integration and Rapid Response Reports pages to the instructor dashboard MFE across the three MIT ODL deployments (mitxonline, mitx, xpro), replicating the behaviour of the legacy
ol_openedx_canvas_integrationandol_openedx_rapid_response_reportsplugins under the OEP-65 frontend-base shell.What's included
A shared
instructor-dashboard/module underfrontend/shared/src/:CanvasIntegrationPage.tsx— Canvas LMS enrollment sync, assignment grade export/listing, and result renderingCanvasPendingTasks.tsx— polls thelist_canvas_tasksendpoint and renders a "Pending Tasks" table (mirroring the dashboard's Data Downloads pending-tasks UX), auto-stopping polling on terminal task statesRapidResponseReportsPage.tsx— grouped rapid-response run report download linksindex.tsx— thecreateInstructorDashboardCustomApp()factoryTab gating happens in the backend, not the MFE
The factory registers the two pages as route-only widgets (2 slot operations on the
instructorDashboard.routesslot). The nav tabs are injected by the LMS via theInstructorDashboardTabsRequestedfilter in the plugins, so a tab surfaces only where its backend actually applies — matching the legacy server-side gating:canvas_idset)This avoids showing tabs whose backend APIs aren't present. (An earlier iteration registered the tabs in the MFE directly; that was replaced by the backend filter.)
Site config integration
createInstructorDashboardCustomApp()is added to all 6 site configs (build + dev for mitxonline / mitx / xpro), after the baseinstructorDashboardApp.Plugin version pins
The pip package lists pin the plugin versions that provide the backend tab filters and endpoints:
ol-openedx-canvas-integration==0.8.0ol-openedx-rapid-response-reports==0.5.0Other changes
frontend/README.md— documents deployment prerequisites (the pinned plugin versions;FRONTEND_SITE_CONFIG+ENABLE_MFE_CONFIG_APIprovisioned via ol-infrastructure)..gitignore— ignores the generatedshared/snapshot.styles/styleLoader.tsxhelper.Backend dependency
Requires the matching
open-edx-pluginschanges — the Canvas / Rapid Response tab filters and thelist_canvas_tasks/rapid_response_runsendpoints (open-edx-plugins PR #819). No edx-platform cherry-pick is needed for the MFE.Not included
Header/footer overrides were split out into a separate PR (#73).
Related issue: https://github.com/mitodl/hq/issues/11581
Screenshots
Screen.Recording.2026-06-19.at.8.41.00.PM.mov
How can this be tested?
./manage.py lms shell):/apps, the LMS must build the tab links with the same prefix — setINSTRUCTOR_MICROFRONTEND_URLto a value ending in/apps/instructor-dashboard(prod sets this in ol-infrastructure).curl -s -w "\nHTTP %{http_code}\n" http://local.openedx.io:8000/api/frontend_site_config/v1/http://apps.local.openedx.io:8080/apps/instructor-dashboard/<course-key>/course_infoand verify it loads.ol-openedx-canvas-integration) and Rapid Response Reports (ol-openedx-rapid-response-reports) plugins.canvas_idset), and the Rapid Responses tab appears when its plugin is installed.