@openedx/frontend-app-gradebook 2.0.0-alpha.4 → 2.0.0-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.rst +1 -5
- package/dist/Gradebook.d.ts +7 -0
- package/dist/Gradebook.js +12 -0
- package/dist/Gradebook.js.map +1 -0
- package/dist/Main.d.ts +0 -1
- package/dist/Main.js +7 -9
- package/dist/Main.js.map +1 -1
- package/dist/app.js +2 -2
- package/dist/app.js.map +1 -1
- package/dist/components/BulkManagementHistoryView/ResultsSummary.d.ts +2 -3
- package/dist/components/BulkManagementHistoryView/ResultsSummary.js +6 -3
- package/dist/components/BulkManagementHistoryView/ResultsSummary.js.map +1 -1
- package/dist/components/BulkManagementHistoryView/data/api.d.ts +2 -2
- package/dist/components/BulkManagementHistoryView/data/api.js +4 -2
- package/dist/components/BulkManagementHistoryView/data/api.js.map +1 -1
- package/dist/components/BulkManagementHistoryView/data/apiHook.js +3 -3
- package/dist/components/BulkManagementHistoryView/data/apiHook.js.map +1 -1
- package/dist/components/GradebookFilters/CourseGradeFilter/hooks.js +3 -1
- package/dist/components/GradebookFilters/CourseGradeFilter/hooks.js.map +1 -1
- package/dist/components/GradebookFilters/data/api.d.ts +4 -4
- package/dist/components/GradebookFilters/data/api.js +6 -6
- package/dist/components/GradebookFilters/data/api.js.map +1 -1
- package/dist/components/GradebookFilters/data/apiHook.js +5 -5
- package/dist/components/GradebookFilters/data/apiHook.js.map +1 -1
- package/dist/components/GradebookHeader/index.js +21 -6
- package/dist/components/GradebookHeader/index.js.map +1 -1
- package/dist/components/GradesView/BulkManagementControls/index.js +3 -1
- package/dist/components/GradesView/BulkManagementControls/index.js.map +1 -1
- package/dist/components/GradesView/InterventionsReport/index.js +3 -1
- package/dist/components/GradesView/InterventionsReport/index.js.map +1 -1
- package/dist/components/GradesView/data/api.d.ts +2 -1
- package/dist/components/GradesView/data/api.js +2 -2
- package/dist/components/GradesView/data/api.js.map +1 -1
- package/dist/components/GradesView/data/apiHook.js +18 -15
- package/dist/components/GradesView/data/apiHook.js.map +1 -1
- package/dist/components/GradesView/data/hooks.js +14 -6
- package/dist/components/GradesView/data/hooks.js.map +1 -1
- package/dist/components/GradesView/data/queryKeys.d.ts +4 -0
- package/dist/components/GradesView/data/queryKeys.js +4 -0
- package/dist/components/GradesView/data/queryKeys.js.map +1 -1
- package/dist/data/api.d.ts +2 -2
- package/dist/data/api.js +4 -4
- package/dist/data/api.js.map +1 -1
- package/dist/data/apiHook.js +7 -7
- package/dist/data/apiHook.js.map +1 -1
- package/dist/data/courseIdContext.d.ts +2 -0
- package/dist/data/courseIdContext.js +6 -0
- package/dist/data/courseIdContext.js.map +1 -0
- package/dist/data/gradebookNavigationContext.d.ts +6 -0
- package/dist/data/gradebookNavigationContext.js +9 -0
- package/dist/data/gradebookNavigationContext.js.map +1 -0
- package/dist/data/query.d.ts +14 -0
- package/dist/data/query.js +27 -0
- package/dist/data/query.js.map +1 -0
- package/dist/data/queryKeys.js +3 -0
- package/dist/data/queryKeys.js.map +1 -1
- package/dist/data/services/lms/api.js +17 -13
- package/dist/data/services/lms/api.js.map +1 -1
- package/dist/data/services/lms/urls.d.ts +13 -13
- package/dist/data/services/lms/urls.js +15 -17
- package/dist/data/services/lms/urls.js.map +1 -1
- package/dist/data/services/segment/constants.d.ts +0 -5
- package/dist/data/services/segment/constants.js +0 -2
- package/dist/data/services/segment/constants.js.map +1 -1
- package/dist/data/services/segment/events.d.ts +8 -8
- package/dist/data/services/segment/events.js +10 -10
- package/dist/data/services/segment/events.js.map +1 -1
- package/dist/i18n/utils.d.ts +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/slots.d.ts +3 -0
- package/dist/slots.js +22 -0
- package/dist/slots.js.map +1 -0
- package/package.json +2 -2
- package/dist/data/queryClient.d.ts +0 -10
- package/dist/data/queryClient.js +0 -34
- package/dist/data/queryClient.js.map +0 -1
- package/dist/providers.d.ts +0 -3
- package/dist/providers.js +0 -13
- package/dist/providers.js.map +0 -1
|
@@ -8,19 +8,20 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import { useMemo } from 'react';
|
|
11
|
-
import { useMutation,
|
|
12
|
-
import { useParams } from 'react-router-dom';
|
|
11
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
13
12
|
import lms from '../../../data/services/lms';
|
|
14
13
|
import { sortAlphaAsc } from '../../../data/formatUtils';
|
|
15
14
|
import { filtersSnapshot } from '../../../data/filtersSnapshot';
|
|
16
15
|
import { useGradebookUi } from '../../../data/gradebookUiContext';
|
|
17
16
|
import { useCourseIdWithGate } from '../../../data/apiHook';
|
|
17
|
+
import { useCourseId } from '../../../data/courseIdContext';
|
|
18
|
+
import { useGradebookQuery } from '../../../data/query';
|
|
18
19
|
import { trackGradesDisplayed, trackGradeOverrideSucceeded, trackGradeOverrideFailed, trackUploadOverrideSucceeded, trackUploadOverrideFailed, } from '../../../data/services/segment/events';
|
|
19
20
|
// Cross-feature invalidation: a successful CSV upload refreshes the bulk-operation
|
|
20
21
|
// history owned by the BulkManagementHistoryView feature.
|
|
21
22
|
import { bulkOperationHistoryQueryKeys } from '../../BulkManagementHistoryView/data/queryKeys';
|
|
22
23
|
import { getGradeOverrideHistory, getGrades, getGradesPage } from './api';
|
|
23
|
-
import { gradeOverrideHistoryQueryKeys, gradesQueryKeys } from './queryKeys';
|
|
24
|
+
import { gradeMutationKeys, gradeOverrideHistoryQueryKeys, gradesQueryKeys } from './queryKeys';
|
|
24
25
|
import { buildGradesFetchParams } from './utils';
|
|
25
26
|
/**
|
|
26
27
|
* Builds the `getGrades` params from the live `filtersSnapshot`. Reads at fetch
|
|
@@ -40,15 +41,15 @@ const buildGradebookDataParams = () => buildGradesFetchParams(filtersSnapshot);
|
|
|
40
41
|
* latest values); a non-null cursor fetches that opaque prev/next URL —
|
|
41
42
|
* replacing the legacy `fetchPrevNextGrades` thunk.
|
|
42
43
|
*/
|
|
43
|
-
export const useGrades = (courseId, gradesPageEndpoint, { enabled = true } = {}) =>
|
|
44
|
+
export const useGrades = (courseId, gradesPageEndpoint, { enabled = true } = {}) => useGradebookQuery({
|
|
44
45
|
queryKey: [...gradesQueryKeys.byCourse(courseId), gradesPageEndpoint !== null && gradesPageEndpoint !== void 0 ? gradesPageEndpoint : 'base'],
|
|
45
46
|
queryFn: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
46
47
|
const data = gradesPageEndpoint
|
|
47
48
|
? yield getGradesPage(gradesPageEndpoint)
|
|
48
|
-
: yield getGrades(buildGradebookDataParams());
|
|
49
|
+
: yield getGrades(Object.assign({ courseId }, buildGradebookDataParams()));
|
|
49
50
|
// Mirrors the legacy `receivedGrades` redux-beacon trigger: one event per
|
|
50
51
|
// actual fetch, with the filters it ran under and the resulting cursors.
|
|
51
|
-
trackGradesDisplayed({
|
|
52
|
+
trackGradesDisplayed(courseId, {
|
|
52
53
|
assignmentType: filtersSnapshot.assignmentType,
|
|
53
54
|
cohort: filtersSnapshot.cohort,
|
|
54
55
|
track: filtersSnapshot.track,
|
|
@@ -91,7 +92,7 @@ export const useGradesData = () => {
|
|
|
91
92
|
* for a specific cell). `retry: false` because a `success: false` business error
|
|
92
93
|
* is not worth retrying.
|
|
93
94
|
*/
|
|
94
|
-
export const useGradeOverrideHistory = (subsectionId, userId) => (
|
|
95
|
+
export const useGradeOverrideHistory = (subsectionId, userId) => (useGradebookQuery({
|
|
95
96
|
queryKey: gradeOverrideHistoryQueryKeys.byCell(subsectionId !== null && subsectionId !== void 0 ? subsectionId : null, userId !== null && userId !== void 0 ? userId : null),
|
|
96
97
|
queryFn: () => getGradeOverrideHistory(subsectionId, userId),
|
|
97
98
|
enabled: !!subsectionId && userId !== undefined && userId !== null,
|
|
@@ -114,12 +115,13 @@ export const useGradeOverrideHistory = (subsectionId, userId) => (useQuery({
|
|
|
114
115
|
*/
|
|
115
116
|
export const useUpdateGrades = () => {
|
|
116
117
|
const queryClient = useQueryClient();
|
|
117
|
-
const
|
|
118
|
+
const courseId = useCourseId();
|
|
118
119
|
const { setShowSuccess, setGradesPageEndpoint, modalState } = useGradebookUi();
|
|
119
120
|
const mutation = useMutation({
|
|
120
|
-
|
|
121
|
+
mutationKey: gradeMutationKeys.updateGrades,
|
|
122
|
+
mutationFn: (updateData) => lms.api.updateGradebookData(courseId, updateData),
|
|
121
123
|
onSuccess: ({ data }, updateData) => {
|
|
122
|
-
trackGradeOverrideSucceeded(data);
|
|
124
|
+
trackGradeOverrideSucceeded(courseId, data);
|
|
123
125
|
setShowSuccess(true);
|
|
124
126
|
setGradesPageEndpoint(null);
|
|
125
127
|
queryClient.invalidateQueries({
|
|
@@ -132,7 +134,7 @@ export const useUpdateGrades = () => {
|
|
|
132
134
|
});
|
|
133
135
|
},
|
|
134
136
|
onError: (error) => {
|
|
135
|
-
trackGradeOverrideFailed(error);
|
|
137
|
+
trackGradeOverrideFailed(courseId, error);
|
|
136
138
|
},
|
|
137
139
|
});
|
|
138
140
|
return () => {
|
|
@@ -159,24 +161,25 @@ export const useUpdateGrades = () => {
|
|
|
159
161
|
*/
|
|
160
162
|
export const useSubmitImportGradesButtonData = () => {
|
|
161
163
|
const queryClient = useQueryClient();
|
|
162
|
-
const
|
|
164
|
+
const courseId = useCourseId();
|
|
163
165
|
const { setShowImportSuccessToast, resetCsvUpload, markCsvUploadSuccess, setCsvUploadErrors, } = useGradebookUi();
|
|
164
166
|
const mutation = useMutation({
|
|
165
|
-
|
|
167
|
+
mutationKey: gradeMutationKeys.uploadGradeCsv,
|
|
168
|
+
mutationFn: (formData) => lms.api.uploadGradeCsv(courseId, formData),
|
|
166
169
|
onMutate: () => {
|
|
167
170
|
resetCsvUpload();
|
|
168
171
|
},
|
|
169
172
|
onSuccess: () => {
|
|
170
173
|
markCsvUploadSuccess();
|
|
171
174
|
setShowImportSuccessToast(true);
|
|
172
|
-
trackUploadOverrideSucceeded();
|
|
175
|
+
trackUploadOverrideSucceeded(courseId);
|
|
173
176
|
queryClient.invalidateQueries({
|
|
174
177
|
queryKey: bulkOperationHistoryQueryKeys.byCourse(courseId),
|
|
175
178
|
});
|
|
176
179
|
},
|
|
177
180
|
onError: (error) => {
|
|
178
181
|
var _a, _b;
|
|
179
|
-
trackUploadOverrideFailed(error);
|
|
182
|
+
trackUploadOverrideFailed(courseId, error);
|
|
180
183
|
if ((error === null || error === void 0 ? void 0 : error.status) === 200 && ((_b = (_a = error === null || error === void 0 ? void 0 : error.data) === null || _a === void 0 ? void 0 : _a.error_messages) === null || _b === void 0 ? void 0 : _b.length)) {
|
|
181
184
|
const { error_messages: errorMessages } = error.data;
|
|
182
185
|
setCsvUploadErrors(errorMessages);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiHook.js","sourceRoot":"","sources":["../../../../src/components/GradesView/data/apiHook.ts"],"names":[],"mappings":"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","sourcesContent":["import { useMemo } from 'react';\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\nimport { useParams } from 'react-router-dom';\n\nimport lms from '@src/data/services/lms';\nimport { sortAlphaAsc } from '@src/data/formatUtils';\nimport { filtersSnapshot } from '@src/data/filtersSnapshot';\nimport { useGradebookUi } from '@src/data/gradebookUiContext';\nimport { useCourseIdWithGate } from '@src/data/apiHook';\nimport {\n trackGradesDisplayed,\n trackGradeOverrideSucceeded,\n trackGradeOverrideFailed,\n trackUploadOverrideSucceeded,\n trackUploadOverrideFailed,\n} from '@src/data/services/segment/events';\n// Cross-feature invalidation: a successful CSV upload refreshes the bulk-operation\n// history owned by the BulkManagementHistoryView feature.\nimport { bulkOperationHistoryQueryKeys } from '@src/components/BulkManagementHistoryView/data/queryKeys';\n\nimport { getGradeOverrideHistory, getGrades, getGradesPage } from './api';\nimport { gradeOverrideHistoryQueryKeys, gradesQueryKeys } from './queryKeys';\nimport { buildGradesFetchParams } from './utils';\n\n/**\n * Builds the `getGrades` params from the live `filtersSnapshot`. Reads at fetch\n * time (not from the query key) so imperative refetches pick up the latest\n * filter values — the FiltersProvider keeps `filtersSnapshot` in sync.\n */\nconst buildGradebookDataParams = () => buildGradesFetchParams(filtersSnapshot);\n\n/**\n * useGrades()\n * Single-responsibility query for the main gradebook rows. Caller supplies\n * `courseId`, the current pagination cursor (`gradesPageEndpoint` from\n * `GradebookUi`, or `null` for the base/unpaged page), and decides when it may\n * run via `enabled`. The base grades key is a prefix of the paged key, so\n * invalidating `byCourse(courseId)` still refetches any page. The first\n * (unpaged) page reads the current filter params at fetch time (so an\n * imperative `invalidateQueries` on filter apply / search refetches with the\n * latest values); a non-null cursor fetches that opaque prev/next URL —\n * replacing the legacy `fetchPrevNextGrades` thunk.\n */\nexport const useGrades = (\n courseId: string,\n gradesPageEndpoint: string | null,\n { enabled = true }: { enabled?: boolean } = {},\n) => useQuery({\n queryKey: [...gradesQueryKeys.byCourse(courseId), gradesPageEndpoint ?? 'base'],\n queryFn: async () => {\n const data = gradesPageEndpoint\n ? await getGradesPage(gradesPageEndpoint)\n : await getGrades(buildGradebookDataParams());\n // Mirrors the legacy `receivedGrades` redux-beacon trigger: one event per\n // actual fetch, with the filters it ran under and the resulting cursors.\n trackGradesDisplayed({\n assignmentType: filtersSnapshot.assignmentType,\n cohort: filtersSnapshot.cohort,\n track: filtersSnapshot.track,\n prev: data.previous,\n next: data.next,\n });\n return data;\n },\n enabled: !!courseId && enabled,\n});\n\n/** One subsection cell within a learner's grade row. */\nexport interface SubsectionBreakdown {\n label: string;\n category?: string;\n subsection_name?: string;\n module_id?: string;\n score_possible?: number;\n score_earned?: number;\n percent?: number;\n attempted?: boolean;\n [key: string]: unknown;\n}\n\n/** One learner's grade row from the gradebook response. */\nexport interface GradeEntry {\n user_id?: number;\n username?: string;\n external_user_key?: string;\n email?: string;\n percent?: number;\n section_breakdown: SubsectionBreakdown[];\n [key: string]: unknown;\n}\n\n/** Shaped grades read-model derived from the `useGrades` query. */\nexport interface GradesData {\n results: GradeEntry[];\n prevPage: string | null;\n nextPage: string | null;\n totalUsersCount: number;\n filteredUsersCount: number;\n isFetching: boolean;\n isError: boolean;\n isSuccess: boolean;\n}\n\n/**\n * useGradesData()\n * Shapes the `useGrades` query into the read-model the grades table / counts /\n * pagination consume — the React Query equivalent of the Redux `grades` slice\n * fields that `useGradebookData` used to mirror. Results are sorted by username\n * (matching the legacy `fetching.received` handling).\n */\nexport const useGradesData = (): GradesData => {\n const { courseId, enabled } = useCourseIdWithGate();\n const { gradesPageEndpoint } = useGradebookUi();\n const query = useGrades(courseId, gradesPageEndpoint, { enabled });\n const data = query.data as {\n results?: GradeEntry[];\n previous?: string | null;\n next?: string | null;\n total_users_count?: number;\n filtered_users_count?: number;\n } | undefined;\n const results = useMemo(\n () => [...(data?.results ?? [])].sort(sortAlphaAsc),\n [data],\n );\n return {\n results,\n prevPage: data?.previous ?? null,\n nextPage: data?.next ?? null,\n totalUsersCount: data?.total_users_count ?? 0,\n filteredUsersCount: data?.filtered_users_count ?? 0,\n isFetching: query.isFetching,\n isError: query.isError,\n isSuccess: query.isSuccess,\n };\n};\n\n/**\n * useGradeOverrideHistory(subsectionId, userId)\n * Grade-override history for a single learner/subsection, used by the edit modal.\n * Enabled only once both identifiers are present (i.e. the modal has been opened\n * for a specific cell). `retry: false` because a `success: false` business error\n * is not worth retrying.\n */\nexport const useGradeOverrideHistory = (subsectionId?: string, userId?: string | number) => (\n useQuery({\n queryKey: gradeOverrideHistoryQueryKeys.byCell(subsectionId ?? null, userId ?? null),\n queryFn: () => getGradeOverrideHistory(subsectionId as string, userId as string | number),\n enabled: !!subsectionId && userId !== undefined && userId !== null,\n retry: false,\n })\n);\n\n/** Shape of the rejection thrown by `uploadGradeCsv` (an HTTP response, not an Error). */\ninterface CsvUploadError {\n status?: number;\n data?: { error_messages?: string[]; saved?: number; total?: number };\n}\n\n/** One grade-override entry sent to the gradebook bulk-update endpoint. */\ninterface GradeOverrideUpdate {\n grade: {\n comment: string;\n earned_graded_override: string | number;\n };\n usage_id: string | null;\n user_id: number | null;\n}\n\n/**\n * useUpdateGrades()\n * Grade-override save from the edit modal. The HTTP call runs through React Query;\n * success emits the Segment `update.*` event, shows the success banner, resets to\n * the first grades page, and invalidates the grades query (so the table reflects\n * the override) and the edited cell's override history (so reopening the modal\n * shows the new entry).\n *\n * The payload is captured as mutation variables when the trigger is called (same\n * shape as the old `selectors.app.editUpdateData`): the caller closes the modal\n * right after triggering the save, and a pending mutation picks up re-rendered\n * options, so a closure over `modalState` would read the reset (empty) state.\n *\n * Returns a no-arg trigger to match the legacy `useUpdateGrades()` call site.\n */\nexport const useUpdateGrades = () => {\n const queryClient = useQueryClient();\n const { courseId = '' } = useParams();\n const { setShowSuccess, setGradesPageEndpoint, modalState } = useGradebookUi();\n const mutation = useMutation({\n mutationFn: (updateData: GradeOverrideUpdate[]) => lms.api.updateGradebookData(updateData),\n onSuccess: ({ data }, updateData) => {\n trackGradeOverrideSucceeded(data);\n setShowSuccess(true);\n setGradesPageEndpoint(null);\n queryClient.invalidateQueries({\n queryKey: gradesQueryKeys.byCourse(courseId),\n });\n updateData.forEach(({ usage_id: usageId, user_id: userId }) => {\n queryClient.invalidateQueries({\n queryKey: gradeOverrideHistoryQueryKeys.byCell(usageId, userId),\n });\n });\n },\n onError: (error) => {\n trackGradeOverrideFailed(error);\n },\n });\n return () => {\n mutation.mutate([{\n grade: {\n comment: modalState.reasonForChange,\n earned_graded_override: modalState.adjustedGradeValue,\n },\n usage_id: modalState.updateModuleId,\n user_id: modalState.updateUserId,\n }]);\n };\n};\n\n/**\n * useSubmitImportGradesButtonData()\n * Bulk grade-override CSV upload. The upload runs through React Query and, on\n * success, invalidates the bulk-operation-history query so the new upload shows up.\n * CSV progress/results/toast are React UI state (GradebookUi context) and success/\n * failure emit the Segment `uploadOverride.*` events.\n *\n * Returns `(formData) => Promise` whose promise always resolves (errors are handled\n * inside `onError`), matching the legacy thunk so the caller's `.then(clearInput)`\n * still runs on both success and failure.\n */\nexport const useSubmitImportGradesButtonData = () => {\n const queryClient = useQueryClient();\n const { courseId = '' } = useParams();\n const {\n setShowImportSuccessToast,\n resetCsvUpload,\n markCsvUploadSuccess,\n setCsvUploadErrors,\n } = useGradebookUi();\n const mutation = useMutation<unknown, CsvUploadError, FormData>({\n mutationFn: (formData) => lms.api.uploadGradeCsv(formData),\n onMutate: () => {\n resetCsvUpload();\n },\n onSuccess: () => {\n markCsvUploadSuccess();\n setShowImportSuccessToast(true);\n trackUploadOverrideSucceeded();\n queryClient.invalidateQueries({\n queryKey: bulkOperationHistoryQueryKeys.byCourse(courseId),\n });\n },\n onError: (error) => {\n trackUploadOverrideFailed(error);\n if (error?.status === 200 && error?.data?.error_messages?.length) {\n const { error_messages: errorMessages } = error.data;\n setCsvUploadErrors(errorMessages);\n } else {\n setCsvUploadErrors(['Unknown error.']);\n }\n },\n });\n return (formData: FormData) => mutation.mutateAsync(formData).catch(() => {});\n};\n"]}
|
|
1
|
+
{"version":3,"file":"apiHook.js","sourceRoot":"","sources":["../../../../src/components/GradesView/data/apiHook.ts"],"names":[],"mappings":"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","sourcesContent":["import { useMemo } from 'react';\nimport { useMutation, useQueryClient } from '@tanstack/react-query';\n\nimport lms from '@src/data/services/lms';\nimport { sortAlphaAsc } from '@src/data/formatUtils';\nimport { filtersSnapshot } from '@src/data/filtersSnapshot';\nimport { useGradebookUi } from '@src/data/gradebookUiContext';\nimport { useCourseIdWithGate } from '@src/data/apiHook';\nimport { useCourseId } from '@src/data/courseIdContext';\nimport { useGradebookQuery } from '@src/data/query';\nimport {\n trackGradesDisplayed,\n trackGradeOverrideSucceeded,\n trackGradeOverrideFailed,\n trackUploadOverrideSucceeded,\n trackUploadOverrideFailed,\n} from '@src/data/services/segment/events';\n// Cross-feature invalidation: a successful CSV upload refreshes the bulk-operation\n// history owned by the BulkManagementHistoryView feature.\nimport { bulkOperationHistoryQueryKeys } from '@src/components/BulkManagementHistoryView/data/queryKeys';\n\nimport { getGradeOverrideHistory, getGrades, getGradesPage } from './api';\nimport { gradeMutationKeys, gradeOverrideHistoryQueryKeys, gradesQueryKeys } from './queryKeys';\nimport { buildGradesFetchParams } from './utils';\n\n/**\n * Builds the `getGrades` params from the live `filtersSnapshot`. Reads at fetch\n * time (not from the query key) so imperative refetches pick up the latest\n * filter values — the FiltersProvider keeps `filtersSnapshot` in sync.\n */\nconst buildGradebookDataParams = () => buildGradesFetchParams(filtersSnapshot);\n\n/**\n * useGrades()\n * Single-responsibility query for the main gradebook rows. Caller supplies\n * `courseId`, the current pagination cursor (`gradesPageEndpoint` from\n * `GradebookUi`, or `null` for the base/unpaged page), and decides when it may\n * run via `enabled`. The base grades key is a prefix of the paged key, so\n * invalidating `byCourse(courseId)` still refetches any page. The first\n * (unpaged) page reads the current filter params at fetch time (so an\n * imperative `invalidateQueries` on filter apply / search refetches with the\n * latest values); a non-null cursor fetches that opaque prev/next URL —\n * replacing the legacy `fetchPrevNextGrades` thunk.\n */\nexport const useGrades = (\n courseId: string,\n gradesPageEndpoint: string | null,\n { enabled = true }: { enabled?: boolean } = {},\n) => useGradebookQuery({\n queryKey: [...gradesQueryKeys.byCourse(courseId), gradesPageEndpoint ?? 'base'],\n queryFn: async () => {\n const data = gradesPageEndpoint\n ? await getGradesPage(gradesPageEndpoint)\n : await getGrades({ courseId, ...buildGradebookDataParams() });\n // Mirrors the legacy `receivedGrades` redux-beacon trigger: one event per\n // actual fetch, with the filters it ran under and the resulting cursors.\n trackGradesDisplayed(courseId, {\n assignmentType: filtersSnapshot.assignmentType,\n cohort: filtersSnapshot.cohort,\n track: filtersSnapshot.track,\n prev: data.previous,\n next: data.next,\n });\n return data;\n },\n enabled: !!courseId && enabled,\n});\n\n/** One subsection cell within a learner's grade row. */\nexport interface SubsectionBreakdown {\n label: string;\n category?: string;\n subsection_name?: string;\n module_id?: string;\n score_possible?: number;\n score_earned?: number;\n percent?: number;\n attempted?: boolean;\n [key: string]: unknown;\n}\n\n/** One learner's grade row from the gradebook response. */\nexport interface GradeEntry {\n user_id?: number;\n username?: string;\n external_user_key?: string;\n email?: string;\n percent?: number;\n section_breakdown: SubsectionBreakdown[];\n [key: string]: unknown;\n}\n\n/** Shaped grades read-model derived from the `useGrades` query. */\nexport interface GradesData {\n results: GradeEntry[];\n prevPage: string | null;\n nextPage: string | null;\n totalUsersCount: number;\n filteredUsersCount: number;\n isFetching: boolean;\n isError: boolean;\n isSuccess: boolean;\n}\n\n/**\n * useGradesData()\n * Shapes the `useGrades` query into the read-model the grades table / counts /\n * pagination consume — the React Query equivalent of the Redux `grades` slice\n * fields that `useGradebookData` used to mirror. Results are sorted by username\n * (matching the legacy `fetching.received` handling).\n */\nexport const useGradesData = (): GradesData => {\n const { courseId, enabled } = useCourseIdWithGate();\n const { gradesPageEndpoint } = useGradebookUi();\n const query = useGrades(courseId, gradesPageEndpoint, { enabled });\n const data = query.data as {\n results?: GradeEntry[];\n previous?: string | null;\n next?: string | null;\n total_users_count?: number;\n filtered_users_count?: number;\n } | undefined;\n const results = useMemo(\n () => [...(data?.results ?? [])].sort(sortAlphaAsc),\n [data],\n );\n return {\n results,\n prevPage: data?.previous ?? null,\n nextPage: data?.next ?? null,\n totalUsersCount: data?.total_users_count ?? 0,\n filteredUsersCount: data?.filtered_users_count ?? 0,\n isFetching: query.isFetching,\n isError: query.isError,\n isSuccess: query.isSuccess,\n };\n};\n\n/**\n * useGradeOverrideHistory(subsectionId, userId)\n * Grade-override history for a single learner/subsection, used by the edit modal.\n * Enabled only once both identifiers are present (i.e. the modal has been opened\n * for a specific cell). `retry: false` because a `success: false` business error\n * is not worth retrying.\n */\nexport const useGradeOverrideHistory = (subsectionId?: string, userId?: string | number) => (\n useGradebookQuery({\n queryKey: gradeOverrideHistoryQueryKeys.byCell(subsectionId ?? null, userId ?? null),\n queryFn: () => getGradeOverrideHistory(subsectionId as string, userId as string | number),\n enabled: !!subsectionId && userId !== undefined && userId !== null,\n retry: false,\n })\n);\n\n/** Shape of the rejection thrown by `uploadGradeCsv` (an HTTP response, not an Error). */\ninterface CsvUploadError {\n status?: number;\n data?: { error_messages?: string[]; saved?: number; total?: number };\n}\n\n/** One grade-override entry sent to the gradebook bulk-update endpoint. */\ninterface GradeOverrideUpdate {\n grade: {\n comment: string;\n earned_graded_override: string | number;\n };\n usage_id: string | null;\n user_id: number | null;\n}\n\n/**\n * useUpdateGrades()\n * Grade-override save from the edit modal. The HTTP call runs through React Query;\n * success emits the Segment `update.*` event, shows the success banner, resets to\n * the first grades page, and invalidates the grades query (so the table reflects\n * the override) and the edited cell's override history (so reopening the modal\n * shows the new entry).\n *\n * The payload is captured as mutation variables when the trigger is called (same\n * shape as the old `selectors.app.editUpdateData`): the caller closes the modal\n * right after triggering the save, and a pending mutation picks up re-rendered\n * options, so a closure over `modalState` would read the reset (empty) state.\n *\n * Returns a no-arg trigger to match the legacy `useUpdateGrades()` call site.\n */\nexport const useUpdateGrades = () => {\n const queryClient = useQueryClient();\n const courseId = useCourseId();\n const { setShowSuccess, setGradesPageEndpoint, modalState } = useGradebookUi();\n const mutation = useMutation({\n mutationKey: gradeMutationKeys.updateGrades,\n mutationFn: (updateData: GradeOverrideUpdate[]) => lms.api.updateGradebookData(courseId, updateData),\n onSuccess: ({ data }, updateData) => {\n trackGradeOverrideSucceeded(courseId, data);\n setShowSuccess(true);\n setGradesPageEndpoint(null);\n queryClient.invalidateQueries({\n queryKey: gradesQueryKeys.byCourse(courseId),\n });\n updateData.forEach(({ usage_id: usageId, user_id: userId }) => {\n queryClient.invalidateQueries({\n queryKey: gradeOverrideHistoryQueryKeys.byCell(usageId, userId),\n });\n });\n },\n onError: (error) => {\n trackGradeOverrideFailed(courseId, error);\n },\n });\n return () => {\n mutation.mutate([{\n grade: {\n comment: modalState.reasonForChange,\n earned_graded_override: modalState.adjustedGradeValue,\n },\n usage_id: modalState.updateModuleId,\n user_id: modalState.updateUserId,\n }]);\n };\n};\n\n/**\n * useSubmitImportGradesButtonData()\n * Bulk grade-override CSV upload. The upload runs through React Query and, on\n * success, invalidates the bulk-operation-history query so the new upload shows up.\n * CSV progress/results/toast are React UI state (GradebookUi context) and success/\n * failure emit the Segment `uploadOverride.*` events.\n *\n * Returns `(formData) => Promise` whose promise always resolves (errors are handled\n * inside `onError`), matching the legacy thunk so the caller's `.then(clearInput)`\n * still runs on both success and failure.\n */\nexport const useSubmitImportGradesButtonData = () => {\n const queryClient = useQueryClient();\n const courseId = useCourseId();\n const {\n setShowImportSuccessToast,\n resetCsvUpload,\n markCsvUploadSuccess,\n setCsvUploadErrors,\n } = useGradebookUi();\n const mutation = useMutation<unknown, CsvUploadError, FormData>({\n mutationKey: gradeMutationKeys.uploadGradeCsv,\n mutationFn: (formData) => lms.api.uploadGradeCsv(courseId, formData),\n onMutate: () => {\n resetCsvUpload();\n },\n onSuccess: () => {\n markCsvUploadSuccess();\n setShowImportSuccessToast(true);\n trackUploadOverrideSucceeded(courseId);\n queryClient.invalidateQueries({\n queryKey: bulkOperationHistoryQueryKeys.byCourse(courseId),\n });\n },\n onError: (error) => {\n trackUploadOverrideFailed(courseId, error);\n if (error?.status === 200 && error?.data?.error_messages?.length) {\n const { error_messages: errorMessages } = error.data;\n setCsvUploadErrors(errorMessages);\n } else {\n setCsvUploadErrors(['Unknown error.']);\n }\n },\n });\n return (formData: FormData) => mutation.mutateAsync(formData).catch(() => {});\n};\n"]}
|
|
@@ -11,12 +11,13 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
};
|
|
12
12
|
import { useMemo } from 'react';
|
|
13
13
|
import { useIsMutating, useQueryClient } from '@tanstack/react-query';
|
|
14
|
-
import { useParams } from 'react-router-dom';
|
|
15
14
|
import lms from '../../../data/services/lms';
|
|
16
15
|
import initialFilters, { filterConfig, filters as filterNames } from '../../../data/constants/filters';
|
|
17
16
|
import { useFilters } from '../../../data/filtersContext';
|
|
18
17
|
import { useGradebookUi } from '../../../data/gradebookUiContext';
|
|
19
18
|
import { useCanViewGradebook } from '../../../data/apiHook';
|
|
19
|
+
import { useCourseId } from '../../../data/courseIdContext';
|
|
20
|
+
import { BASE_KEY } from '../../../data/queryKeys';
|
|
20
21
|
import { useSelectedCohortEntry, useSelectedTrackEntry, } from '../../GradebookFilters/data/hooks';
|
|
21
22
|
import { useGradesData, useGradeOverrideHistory } from './apiHook';
|
|
22
23
|
import { gradesQueryKeys } from './queryKeys';
|
|
@@ -72,12 +73,13 @@ export const useSelectedAssignmentLabel = () => { var _a; return (_a = useSelect
|
|
|
72
73
|
/**
|
|
73
74
|
* useShouldShowSpinner()
|
|
74
75
|
* The busy indicator: the roles gate combined with the grades query fetching or
|
|
75
|
-
*
|
|
76
|
+
* one of the gradebook's own mutations in flight (grade override save / CSV
|
|
77
|
+
* upload).
|
|
76
78
|
*/
|
|
77
79
|
export const useShouldShowSpinner = () => {
|
|
78
80
|
const canViewGradebook = useCanViewGradebook();
|
|
79
81
|
const { isFetching } = useGradesData();
|
|
80
|
-
const mutatingCount = useIsMutating();
|
|
82
|
+
const mutatingCount = useIsMutating({ mutationKey: BASE_KEY });
|
|
81
83
|
return canViewGradebook && (isFetching || mutatingCount > 0);
|
|
82
84
|
};
|
|
83
85
|
/**
|
|
@@ -125,8 +127,14 @@ const useLmsApiServiceArgs = () => {
|
|
|
125
127
|
excludedCourseRoles: includeCourseRoleMembers ? '' : 'all',
|
|
126
128
|
};
|
|
127
129
|
};
|
|
128
|
-
export const useGradeExportUrl = () =>
|
|
129
|
-
|
|
130
|
+
export const useGradeExportUrl = () => {
|
|
131
|
+
const courseId = useCourseId();
|
|
132
|
+
return lms.urls.gradeCsvUrl(courseId, useLmsApiServiceArgs());
|
|
133
|
+
};
|
|
134
|
+
export const useInterventionExportUrl = () => {
|
|
135
|
+
const courseId = useCourseId();
|
|
136
|
+
return lms.urls.interventionExportCsvUrl(courseId, useLmsApiServiceArgs());
|
|
137
|
+
};
|
|
130
138
|
/**
|
|
131
139
|
* useFilterBadgeConfig(filterName)
|
|
132
140
|
* The badge config (value + isDefault) for a given filter, built from the
|
|
@@ -192,7 +200,7 @@ export const useEditModalPossibleGrade = () => {
|
|
|
192
200
|
// refetch with the current filters.
|
|
193
201
|
const useInvalidateGrades = () => {
|
|
194
202
|
const queryClient = useQueryClient();
|
|
195
|
-
const
|
|
203
|
+
const courseId = useCourseId();
|
|
196
204
|
return () => queryClient.invalidateQueries({ queryKey: gradesQueryKeys.byCourse(courseId) });
|
|
197
205
|
};
|
|
198
206
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../../src/components/GradesView/data/hooks.js"],"names":[],"mappings":"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","sourcesContent":["import { useMemo } from 'react';\nimport { useIsMutating, useQueryClient } from '@tanstack/react-query';\nimport { useParams } from 'react-router-dom';\n\nimport lms from '@src/data/services/lms';\nimport initialFilters, { filterConfig, filters as filterNames } from '@src/data/constants/filters';\nimport { useFilters } from '@src/data/filtersContext';\nimport { useGradebookUi } from '@src/data/gradebookUiContext';\nimport { useCanViewGradebook } from '@src/data/apiHook';\nimport {\n useSelectedCohortEntry,\n useSelectedTrackEntry,\n} from '@src/components/GradebookFilters/data/hooks';\n\nimport { useGradesData, useGradeOverrideHistory } from './apiHook';\nimport { gradesQueryKeys } from './queryKeys';\nimport {\n chooseRelevantAssignmentData,\n formatMaxAssignmentGrade,\n formatMaxCourseGrade,\n formatMinAssignmentGrade,\n formatMinCourseGrade,\n getAssignmentsFromResultsSubstate,\n headingMapper,\n isDefault,\n relevantAssignmentDataFromResults,\n} from './utils';\n\n// Stable empty reference so read hooks don't return a fresh array each render.\nconst EMPTY_ARRAY = [];\n\n/**\n * useGradesHeadings()\n * The gradebook table headings for the current assignment-type / assignment\n * filters, derived from the grades query's section breakdown.\n */\nexport const useGradesHeadings = () => {\n const { results } = useGradesData();\n const { assignmentType, assignment: assignmentId } = useFilters();\n return useMemo(() => {\n const selectedAssignmentLabel = relevantAssignmentDataFromResults(results, assignmentId)?.label;\n const sectionBreakdown = getAssignmentsFromResultsSubstate(results);\n return headingMapper(\n assignmentType || 'All',\n selectedAssignmentLabel || 'All',\n )(sectionBreakdown);\n }, [results, assignmentType, assignmentId]);\n};\n\n/**\n * useSelectableAssignmentLabels()\n * The assignment options for the assignment dropdown, derived from the grades\n * results and filtered by the current assignment-type.\n */\nexport const useSelectableAssignmentLabels = () => {\n const { results } = useGradesData();\n const { assignmentType } = useFilters();\n return useMemo(() => {\n const all = getAssignmentsFromResultsSubstate(results)\n .map(chooseRelevantAssignmentData);\n return (assignmentType && assignmentType !== 'All')\n ? all.filter((assignment) => assignment.type === assignmentType)\n : all;\n }, [results, assignmentType]);\n};\n\n/**\n * useSelectedAssignmentData()\n * The currently-selected assignment's data (label/type/id, from the grades\n * results), or undefined when no assignment is selected.\n */\nexport const useSelectedAssignmentData = () => {\n const { results } = useGradesData();\n const { assignment: assignmentId } = useFilters();\n return useMemo(\n () => relevantAssignmentDataFromResults(results, assignmentId),\n [results, assignmentId],\n );\n};\n\n/**\n * useSelectedAssignmentLabel()\n * The label of the currently-selected assignment (from the grades results).\n */\nexport const useSelectedAssignmentLabel = () => useSelectedAssignmentData()?.label;\n\n/**\n * useShouldShowSpinner()\n * The busy indicator: the roles gate combined with the grades query fetching or\n * any in-flight mutation (grade override save / CSV upload).\n */\nexport const useShouldShowSpinner = () => {\n const canViewGradebook = useCanViewGradebook();\n const { isFetching } = useGradesData();\n const mutatingCount = useIsMutating();\n return canViewGradebook && (isFetching || mutatingCount > 0);\n};\n\n/**\n * useAllGrades()\n * The (sorted) grades rows for the table.\n */\nexport const useAllGrades = () => useGradesData().results;\n\n/**\n * useUserCounts()\n * Filtered/total user counts from the grades query.\n */\nexport const useUserCounts = () => {\n const { filteredUsersCount, totalUsersCount } = useGradesData();\n return { filteredUsersCount, totalUsersCount };\n};\n\n/**\n * useGradeData()\n * The pagination cursors + counts (from the grades query) plus the current\n * `gradeFormat` (from the GradebookUi context).\n */\nexport const useGradeData = () => {\n const {\n nextPage, prevPage, totalUsersCount, filteredUsersCount,\n } = useGradesData();\n const { gradeFormat } = useGradebookUi();\n return {\n nextPage, prevPage, totalUsersCount, filteredUsersCount, gradeFormat,\n };\n};\n\n// Export URL args, built from the FiltersProvider (applied grade limits + the\n// immediate filters) and the selected cohort name. Reuses the pure\n// `format*` helpers so the CSV/intervention URL formatting is unchanged.\nconst useLmsApiServiceArgs = () => {\n const {\n assignment, assignmentType, track, includeCourseRoleMembers,\n appliedAssignmentGradeMin, appliedAssignmentGradeMax,\n appliedCourseGradeMin, appliedCourseGradeMax,\n } = useFilters();\n const selectedCohort = useSelectedCohortEntry();\n const assignmentId = assignment || undefined;\n const opts = { assignmentId };\n return {\n cohort: selectedCohort ? selectedCohort.name : undefined,\n track,\n assignment: assignmentId,\n assignmentType,\n assignmentGradeMin: formatMinAssignmentGrade(appliedAssignmentGradeMin, opts),\n assignmentGradeMax: formatMaxAssignmentGrade(appliedAssignmentGradeMax, opts),\n courseGradeMin: formatMinCourseGrade(appliedCourseGradeMin),\n courseGradeMax: formatMaxCourseGrade(appliedCourseGradeMax),\n excludedCourseRoles: includeCourseRoleMembers ? '' : 'all',\n };\n};\n\nexport const useGradeExportUrl = () => lms.urls.gradeCsvUrl(useLmsApiServiceArgs());\nexport const useInterventionExportUrl = () => lms.urls.interventionExportCsvUrl(useLmsApiServiceArgs());\n\n/**\n * useFilterBadgeConfig(filterName)\n * The badge config (value + isDefault) for a given filter, built from the\n * FiltersProvider (immediate filters + applied grade limits + query-derived\n * assignment label) and the selected cohort/track names. Reuses `filterConfig` +\n * the pure `selectors.filters.isDefault` so formatting is unchanged.\n */\nexport const useFilterBadgeConfig = (filterName) => {\n const selectedCohort = useSelectedCohortEntry();\n const selectedTrack = useSelectedTrackEntry();\n const selectedAssignmentLabel = useSelectedAssignmentLabel();\n const {\n assignmentType, includeCourseRoleMembers,\n appliedAssignmentGradeMin, appliedAssignmentGradeMax,\n appliedCourseGradeMin, appliedCourseGradeMax,\n } = useFilters();\n\n if (filterName === filterNames.cohort || filterName === filterNames.track) {\n const entry = filterName === filterNames.cohort ? selectedCohort : selectedTrack;\n const value = entry ? entry.name : '';\n return {\n ...filterConfig[filterName],\n value,\n isDefault: value === initialFilters[filterName],\n };\n }\n\n const { filterOrder, ...config } = filterConfig[filterName];\n\n if (filterOrder) {\n const [min, max] = filterName === filterNames.assignmentGrade\n ? [appliedAssignmentGradeMin, appliedAssignmentGradeMax]\n : [appliedCourseGradeMin, appliedCourseGradeMax];\n return {\n ...config,\n value: `${min} - ${max}`,\n isDefault: isDefault(filterOrder[0], min) && isDefault(filterOrder[1], max),\n };\n }\n\n const singleValues = {\n [filterNames.assignmentType]: assignmentType,\n [filterNames.assignment]: selectedAssignmentLabel || '',\n [filterNames.includeCourseRoleMembers]: includeCourseRoleMembers,\n };\n const value = singleValues[filterName];\n return { ...config, value, isDefault: isDefault(filterName, value) };\n};\n\n/**\n * useGradeOverrideData()\n * The edit-modal override read-model, sourced from the override-history query\n * gated on the modal's subsection/user ids (from the GradebookUi context).\n */\nexport const useGradeOverrideData = () => {\n const { modalState } = useGradebookUi();\n const query = useGradeOverrideHistory(modalState.updateModuleId, modalState.updateUserId);\n const { data } = query;\n return {\n gradeOverrideHistoryResults: data?.overrideHistory ?? EMPTY_ARRAY,\n gradeOverrideCurrentEarnedGradedOverride: data?.currentEarnedGradedOverride ?? null,\n gradeOriginalEarnedGraded: data?.originalGradeEarnedGraded ?? null,\n gradeOriginalPossibleGraded: data?.originalGradePossibleGraded ?? null,\n gradeOverrideHistoryError: query.isError ? (query.error?.message || '') : '',\n hasOverrideErrors: query.isError,\n };\n};\n\n/**\n * useEditModalPossibleGrade()\n * The edit-modal \"possible\" grade: the context `adjustedGradePossible` or, as a\n * fallback, the override query's original possible-graded value.\n */\nexport const useEditModalPossibleGrade = () => {\n const { modalState } = useGradebookUi();\n const { gradeOriginalPossibleGraded } = useGradeOverrideData();\n return modalState.adjustedGradePossible || gradeOriginalPossibleGraded;\n};\n\n// Invalidate the main grades query, causing the mounted `useGrades` observer to\n// refetch with the current filters.\nconst useInvalidateGrades = () => {\n const queryClient = useQueryClient();\n const { courseId = '' } = useParams();\n return () => queryClient.invalidateQueries({ queryKey: gradesQueryKeys.byCourse(courseId) });\n};\n\n/**\n * useRefetchGrades()\n * Refetches the grades query from the first (unpaged) page with the latest\n * filters. Always invalidates (the base-page cache entry may still be fresh, and\n * a fresh entry is served as-is when the key flips back to it); when on a\n * prev/next page, also clears the pagination cursor.\n */\nexport const useRefetchGrades = () => {\n const invalidateGrades = useInvalidateGrades();\n const { gradesPageEndpoint, setGradesPageEndpoint } = useGradebookUi();\n return () => {\n if (gradesPageEndpoint !== null) {\n setGradesPageEndpoint(null);\n }\n invalidateGrades();\n };\n};\n\n/**\n * useFetchGradesIfAssignmentGradeFiltersSet()\n * Refetches from the start only when a non-default applied assignment-grade limit\n * is set (mirrors the legacy `areAssignmentGradeFiltersSet` gate).\n */\nexport const useFetchGradesIfAssignmentGradeFiltersSet = () => {\n const refetchFromStart = useRefetchGrades();\n const { appliedAssignmentGradeMin, appliedAssignmentGradeMax } = useFilters();\n return () => {\n const isSet = appliedAssignmentGradeMin !== initialFilters.assignmentGradeMin\n || appliedAssignmentGradeMax !== initialFilters.assignmentGradeMax;\n if (isSet) {\n refetchFromStart();\n }\n };\n};\n\n/**\n * useFetchPrevNextGrades()\n * Pagination trigger: sets the page cursor in the GradebookUi context (the\n * `useGrades` query keys off it and fetches that opaque prev/next URL).\n */\nexport const useFetchPrevNextGrades = () => {\n const { setGradesPageEndpoint } = useGradebookUi();\n return (endpoint) => {\n setGradesPageEndpoint(endpoint);\n };\n};\n"]}
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../../src/components/GradesView/data/hooks.js"],"names":[],"mappings":"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","sourcesContent":["import { useMemo } from 'react';\nimport { useIsMutating, useQueryClient } from '@tanstack/react-query';\n\nimport lms from '@src/data/services/lms';\nimport initialFilters, { filterConfig, filters as filterNames } from '@src/data/constants/filters';\nimport { useFilters } from '@src/data/filtersContext';\nimport { useGradebookUi } from '@src/data/gradebookUiContext';\nimport { useCanViewGradebook } from '@src/data/apiHook';\nimport { useCourseId } from '@src/data/courseIdContext';\nimport { BASE_KEY } from '@src/data/queryKeys';\nimport {\n useSelectedCohortEntry,\n useSelectedTrackEntry,\n} from '@src/components/GradebookFilters/data/hooks';\n\nimport { useGradesData, useGradeOverrideHistory } from './apiHook';\nimport { gradesQueryKeys } from './queryKeys';\nimport {\n chooseRelevantAssignmentData,\n formatMaxAssignmentGrade,\n formatMaxCourseGrade,\n formatMinAssignmentGrade,\n formatMinCourseGrade,\n getAssignmentsFromResultsSubstate,\n headingMapper,\n isDefault,\n relevantAssignmentDataFromResults,\n} from './utils';\n\n// Stable empty reference so read hooks don't return a fresh array each render.\nconst EMPTY_ARRAY = [];\n\n/**\n * useGradesHeadings()\n * The gradebook table headings for the current assignment-type / assignment\n * filters, derived from the grades query's section breakdown.\n */\nexport const useGradesHeadings = () => {\n const { results } = useGradesData();\n const { assignmentType, assignment: assignmentId } = useFilters();\n return useMemo(() => {\n const selectedAssignmentLabel = relevantAssignmentDataFromResults(results, assignmentId)?.label;\n const sectionBreakdown = getAssignmentsFromResultsSubstate(results);\n return headingMapper(\n assignmentType || 'All',\n selectedAssignmentLabel || 'All',\n )(sectionBreakdown);\n }, [results, assignmentType, assignmentId]);\n};\n\n/**\n * useSelectableAssignmentLabels()\n * The assignment options for the assignment dropdown, derived from the grades\n * results and filtered by the current assignment-type.\n */\nexport const useSelectableAssignmentLabels = () => {\n const { results } = useGradesData();\n const { assignmentType } = useFilters();\n return useMemo(() => {\n const all = getAssignmentsFromResultsSubstate(results)\n .map(chooseRelevantAssignmentData);\n return (assignmentType && assignmentType !== 'All')\n ? all.filter((assignment) => assignment.type === assignmentType)\n : all;\n }, [results, assignmentType]);\n};\n\n/**\n * useSelectedAssignmentData()\n * The currently-selected assignment's data (label/type/id, from the grades\n * results), or undefined when no assignment is selected.\n */\nexport const useSelectedAssignmentData = () => {\n const { results } = useGradesData();\n const { assignment: assignmentId } = useFilters();\n return useMemo(\n () => relevantAssignmentDataFromResults(results, assignmentId),\n [results, assignmentId],\n );\n};\n\n/**\n * useSelectedAssignmentLabel()\n * The label of the currently-selected assignment (from the grades results).\n */\nexport const useSelectedAssignmentLabel = () => useSelectedAssignmentData()?.label;\n\n/**\n * useShouldShowSpinner()\n * The busy indicator: the roles gate combined with the grades query fetching or\n * one of the gradebook's own mutations in flight (grade override save / CSV\n * upload).\n */\nexport const useShouldShowSpinner = () => {\n const canViewGradebook = useCanViewGradebook();\n const { isFetching } = useGradesData();\n const mutatingCount = useIsMutating({ mutationKey: BASE_KEY });\n return canViewGradebook && (isFetching || mutatingCount > 0);\n};\n\n/**\n * useAllGrades()\n * The (sorted) grades rows for the table.\n */\nexport const useAllGrades = () => useGradesData().results;\n\n/**\n * useUserCounts()\n * Filtered/total user counts from the grades query.\n */\nexport const useUserCounts = () => {\n const { filteredUsersCount, totalUsersCount } = useGradesData();\n return { filteredUsersCount, totalUsersCount };\n};\n\n/**\n * useGradeData()\n * The pagination cursors + counts (from the grades query) plus the current\n * `gradeFormat` (from the GradebookUi context).\n */\nexport const useGradeData = () => {\n const {\n nextPage, prevPage, totalUsersCount, filteredUsersCount,\n } = useGradesData();\n const { gradeFormat } = useGradebookUi();\n return {\n nextPage, prevPage, totalUsersCount, filteredUsersCount, gradeFormat,\n };\n};\n\n// Export URL args, built from the FiltersProvider (applied grade limits + the\n// immediate filters) and the selected cohort name. Reuses the pure\n// `format*` helpers so the CSV/intervention URL formatting is unchanged.\nconst useLmsApiServiceArgs = () => {\n const {\n assignment, assignmentType, track, includeCourseRoleMembers,\n appliedAssignmentGradeMin, appliedAssignmentGradeMax,\n appliedCourseGradeMin, appliedCourseGradeMax,\n } = useFilters();\n const selectedCohort = useSelectedCohortEntry();\n const assignmentId = assignment || undefined;\n const opts = { assignmentId };\n return {\n cohort: selectedCohort ? selectedCohort.name : undefined,\n track,\n assignment: assignmentId,\n assignmentType,\n assignmentGradeMin: formatMinAssignmentGrade(appliedAssignmentGradeMin, opts),\n assignmentGradeMax: formatMaxAssignmentGrade(appliedAssignmentGradeMax, opts),\n courseGradeMin: formatMinCourseGrade(appliedCourseGradeMin),\n courseGradeMax: formatMaxCourseGrade(appliedCourseGradeMax),\n excludedCourseRoles: includeCourseRoleMembers ? '' : 'all',\n };\n};\n\nexport const useGradeExportUrl = () => {\n const courseId = useCourseId();\n return lms.urls.gradeCsvUrl(courseId, useLmsApiServiceArgs());\n};\nexport const useInterventionExportUrl = () => {\n const courseId = useCourseId();\n return lms.urls.interventionExportCsvUrl(courseId, useLmsApiServiceArgs());\n};\n\n/**\n * useFilterBadgeConfig(filterName)\n * The badge config (value + isDefault) for a given filter, built from the\n * FiltersProvider (immediate filters + applied grade limits + query-derived\n * assignment label) and the selected cohort/track names. Reuses `filterConfig` +\n * the pure `selectors.filters.isDefault` so formatting is unchanged.\n */\nexport const useFilterBadgeConfig = (filterName) => {\n const selectedCohort = useSelectedCohortEntry();\n const selectedTrack = useSelectedTrackEntry();\n const selectedAssignmentLabel = useSelectedAssignmentLabel();\n const {\n assignmentType, includeCourseRoleMembers,\n appliedAssignmentGradeMin, appliedAssignmentGradeMax,\n appliedCourseGradeMin, appliedCourseGradeMax,\n } = useFilters();\n\n if (filterName === filterNames.cohort || filterName === filterNames.track) {\n const entry = filterName === filterNames.cohort ? selectedCohort : selectedTrack;\n const value = entry ? entry.name : '';\n return {\n ...filterConfig[filterName],\n value,\n isDefault: value === initialFilters[filterName],\n };\n }\n\n const { filterOrder, ...config } = filterConfig[filterName];\n\n if (filterOrder) {\n const [min, max] = filterName === filterNames.assignmentGrade\n ? [appliedAssignmentGradeMin, appliedAssignmentGradeMax]\n : [appliedCourseGradeMin, appliedCourseGradeMax];\n return {\n ...config,\n value: `${min} - ${max}`,\n isDefault: isDefault(filterOrder[0], min) && isDefault(filterOrder[1], max),\n };\n }\n\n const singleValues = {\n [filterNames.assignmentType]: assignmentType,\n [filterNames.assignment]: selectedAssignmentLabel || '',\n [filterNames.includeCourseRoleMembers]: includeCourseRoleMembers,\n };\n const value = singleValues[filterName];\n return { ...config, value, isDefault: isDefault(filterName, value) };\n};\n\n/**\n * useGradeOverrideData()\n * The edit-modal override read-model, sourced from the override-history query\n * gated on the modal's subsection/user ids (from the GradebookUi context).\n */\nexport const useGradeOverrideData = () => {\n const { modalState } = useGradebookUi();\n const query = useGradeOverrideHistory(modalState.updateModuleId, modalState.updateUserId);\n const { data } = query;\n return {\n gradeOverrideHistoryResults: data?.overrideHistory ?? EMPTY_ARRAY,\n gradeOverrideCurrentEarnedGradedOverride: data?.currentEarnedGradedOverride ?? null,\n gradeOriginalEarnedGraded: data?.originalGradeEarnedGraded ?? null,\n gradeOriginalPossibleGraded: data?.originalGradePossibleGraded ?? null,\n gradeOverrideHistoryError: query.isError ? (query.error?.message || '') : '',\n hasOverrideErrors: query.isError,\n };\n};\n\n/**\n * useEditModalPossibleGrade()\n * The edit-modal \"possible\" grade: the context `adjustedGradePossible` or, as a\n * fallback, the override query's original possible-graded value.\n */\nexport const useEditModalPossibleGrade = () => {\n const { modalState } = useGradebookUi();\n const { gradeOriginalPossibleGraded } = useGradeOverrideData();\n return modalState.adjustedGradePossible || gradeOriginalPossibleGraded;\n};\n\n// Invalidate the main grades query, causing the mounted `useGrades` observer to\n// refetch with the current filters.\nconst useInvalidateGrades = () => {\n const queryClient = useQueryClient();\n const courseId = useCourseId();\n return () => queryClient.invalidateQueries({ queryKey: gradesQueryKeys.byCourse(courseId) });\n};\n\n/**\n * useRefetchGrades()\n * Refetches the grades query from the first (unpaged) page with the latest\n * filters. Always invalidates (the base-page cache entry may still be fresh, and\n * a fresh entry is served as-is when the key flips back to it); when on a\n * prev/next page, also clears the pagination cursor.\n */\nexport const useRefetchGrades = () => {\n const invalidateGrades = useInvalidateGrades();\n const { gradesPageEndpoint, setGradesPageEndpoint } = useGradebookUi();\n return () => {\n if (gradesPageEndpoint !== null) {\n setGradesPageEndpoint(null);\n }\n invalidateGrades();\n };\n};\n\n/**\n * useFetchGradesIfAssignmentGradeFiltersSet()\n * Refetches from the start only when a non-default applied assignment-grade limit\n * is set (mirrors the legacy `areAssignmentGradeFiltersSet` gate).\n */\nexport const useFetchGradesIfAssignmentGradeFiltersSet = () => {\n const refetchFromStart = useRefetchGrades();\n const { appliedAssignmentGradeMin, appliedAssignmentGradeMax } = useFilters();\n return () => {\n const isSet = appliedAssignmentGradeMin !== initialFilters.assignmentGradeMin\n || appliedAssignmentGradeMax !== initialFilters.assignmentGradeMax;\n if (isSet) {\n refetchFromStart();\n }\n };\n};\n\n/**\n * useFetchPrevNextGrades()\n * Pagination trigger: sets the page cursor in the GradebookUi context (the\n * `useGrades` query keys off it and fetches that opaque prev/next URL).\n */\nexport const useFetchPrevNextGrades = () => {\n const { setGradesPageEndpoint } = useGradebookUi();\n return (endpoint) => {\n setGradesPageEndpoint(endpoint);\n };\n};\n"]}
|
|
@@ -6,6 +6,10 @@ export declare const gradesQueryKeys: {
|
|
|
6
6
|
all: readonly ["gradebook"];
|
|
7
7
|
byCourse: (courseId: string) => readonly ["gradebook", string, "grades"];
|
|
8
8
|
};
|
|
9
|
+
export declare const gradeMutationKeys: {
|
|
10
|
+
updateGrades: readonly ["gradebook", "updateGrades"];
|
|
11
|
+
uploadGradeCsv: readonly ["gradebook", "uploadGradeCsv"];
|
|
12
|
+
};
|
|
9
13
|
/** Grade-override history for one learner on one subsection (edit modal). */
|
|
10
14
|
export declare const gradeOverrideHistoryQueryKeys: {
|
|
11
15
|
all: readonly ["gradebook", "gradeOverrideHistory"];
|
|
@@ -7,6 +7,10 @@ export const gradesQueryKeys = {
|
|
|
7
7
|
all: BASE_KEY,
|
|
8
8
|
byCourse: (courseId) => [...BASE_KEY, courseId, 'grades'],
|
|
9
9
|
};
|
|
10
|
+
export const gradeMutationKeys = {
|
|
11
|
+
updateGrades: [...BASE_KEY, 'updateGrades'],
|
|
12
|
+
uploadGradeCsv: [...BASE_KEY, 'uploadGradeCsv'],
|
|
13
|
+
};
|
|
10
14
|
/** Grade-override history for one learner on one subsection (edit modal). */
|
|
11
15
|
export const gradeOverrideHistoryQueryKeys = {
|
|
12
16
|
all: [...BASE_KEY, 'gradeOverrideHistory'],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queryKeys.js","sourceRoot":"","sources":["../../../../src/components/GradesView/data/queryKeys.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE/C;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,GAAG,EAAE,QAAQ;IACb,QAAQ,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,CAAC,GAAG,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAU;CAC3E,CAAC;AAEF,6EAA6E;AAC7E,MAAM,CAAC,MAAM,6BAA6B,GAAG;IAC3C,GAAG,EAAE,CAAC,GAAG,QAAQ,EAAE,sBAAsB,CAAU;IACnD,MAAM,EAAE,CACN,YAA2B,EAC3B,MAA8B,EAC9B,EAAE,CAAC,CAAC,GAAG,QAAQ,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,CAAU;CAC1E,CAAC","sourcesContent":["import { BASE_KEY } from '@src/data/queryKeys';\n\n/**\n * The main gradebook rows. The base key is a prefix of each paged key (see\n * `useGrades`), so invalidating `byCourse(courseId)` refetches any page.\n */\nexport const gradesQueryKeys = {\n all: BASE_KEY,\n byCourse: (courseId: string) => [...BASE_KEY, courseId, 'grades'] as const,\n};\n\n/** Grade-override history for one learner on one subsection (edit modal). */\nexport const gradeOverrideHistoryQueryKeys = {\n all: [...BASE_KEY, 'gradeOverrideHistory'] as const,\n byCell: (\n subsectionId: string | null,\n userId: string | number | null,\n ) => [...BASE_KEY, 'gradeOverrideHistory', subsectionId, userId] as const,\n};\n"]}
|
|
1
|
+
{"version":3,"file":"queryKeys.js","sourceRoot":"","sources":["../../../../src/components/GradesView/data/queryKeys.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE/C;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,GAAG,EAAE,QAAQ;IACb,QAAQ,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,CAAC,GAAG,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAU;CAC3E,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,YAAY,EAAE,CAAC,GAAG,QAAQ,EAAE,cAAc,CAAU;IACpD,cAAc,EAAE,CAAC,GAAG,QAAQ,EAAE,gBAAgB,CAAU;CACzD,CAAC;AAEF,6EAA6E;AAC7E,MAAM,CAAC,MAAM,6BAA6B,GAAG;IAC3C,GAAG,EAAE,CAAC,GAAG,QAAQ,EAAE,sBAAsB,CAAU;IACnD,MAAM,EAAE,CACN,YAA2B,EAC3B,MAA8B,EAC9B,EAAE,CAAC,CAAC,GAAG,QAAQ,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,CAAU;CAC1E,CAAC","sourcesContent":["import { BASE_KEY } from '@src/data/queryKeys';\n\n/**\n * The main gradebook rows. The base key is a prefix of each paged key (see\n * `useGrades`), so invalidating `byCourse(courseId)` refetches any page.\n */\nexport const gradesQueryKeys = {\n all: BASE_KEY,\n byCourse: (courseId: string) => [...BASE_KEY, courseId, 'grades'] as const,\n};\n\nexport const gradeMutationKeys = {\n updateGrades: [...BASE_KEY, 'updateGrades'] as const,\n uploadGradeCsv: [...BASE_KEY, 'uploadGradeCsv'] as const,\n};\n\n/** Grade-override history for one learner on one subsection (edit modal). */\nexport const gradeOverrideHistoryQueryKeys = {\n all: [...BASE_KEY, 'gradeOverrideHistory'] as const,\n byCell: (\n subsectionId: string | null,\n userId: string | number | null,\n ) => [...BASE_KEY, 'gradeOverrideHistory', subsectionId, userId] as const,\n};\n"]}
|
package/dist/data/api.d.ts
CHANGED
|
@@ -13,8 +13,8 @@ export interface AssignmentTypesData {
|
|
|
13
13
|
*/
|
|
14
14
|
export declare const getCanUserViewGradebook: (courseId: string) => Promise<boolean>;
|
|
15
15
|
/**
|
|
16
|
-
* getAssignmentTypes()
|
|
16
|
+
* getAssignmentTypes(courseId)
|
|
17
17
|
* Fetches grading info and derives the assignment-type list plus the
|
|
18
18
|
* grades-frozen and bulk-management flags that ride along with it.
|
|
19
19
|
*/
|
|
20
|
-
export declare const getAssignmentTypes: () => Promise<AssignmentTypesData>;
|
|
20
|
+
export declare const getAssignmentTypes: (courseId: string) => Promise<AssignmentTypesData>;
|
package/dist/data/api.js
CHANGED
|
@@ -16,17 +16,17 @@ export const allowedRoles = ['staff', 'limited_staff', 'instructor', 'support'];
|
|
|
16
16
|
* gradebook for this course (staff, or holding an allowed role in the course).
|
|
17
17
|
*/
|
|
18
18
|
export const getCanUserViewGradebook = (courseId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
19
|
-
const { data } = yield lms.api.fetch.roles();
|
|
19
|
+
const { data } = yield lms.api.fetch.roles(courseId);
|
|
20
20
|
const isAllowedRole = (role) => (role.course_id === courseId && allowedRoles.includes(role.role));
|
|
21
21
|
return Boolean(data.is_staff || (data.roles || []).some(isAllowedRole));
|
|
22
22
|
});
|
|
23
23
|
/**
|
|
24
|
-
* getAssignmentTypes()
|
|
24
|
+
* getAssignmentTypes(courseId)
|
|
25
25
|
* Fetches grading info and derives the assignment-type list plus the
|
|
26
26
|
* grades-frozen and bulk-management flags that ride along with it.
|
|
27
27
|
*/
|
|
28
|
-
export const getAssignmentTypes = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
29
|
-
const { data } = yield lms.api.fetch.assignmentTypes();
|
|
28
|
+
export const getAssignmentTypes = (courseId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
29
|
+
const { data } = yield lms.api.fetch.assignmentTypes(courseId);
|
|
30
30
|
return {
|
|
31
31
|
assignmentTypes: Object.keys(data.assignment_types || {}),
|
|
32
32
|
areGradesFrozen: Boolean(data.grades_frozen),
|
package/dist/data/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/data/api.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,GAAG,MAAM,wBAAwB,CAAC;AAEzC,oEAAoE;AACpE,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;AAchF;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAO,QAAgB,EAAoB,EAAE;IAClF,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/data/api.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,GAAG,MAAM,wBAAwB,CAAC;AAEzC,oEAAoE;AACpE,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;AAchF;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAO,QAAgB,EAAoB,EAAE;IAClF,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACrD,MAAM,aAAa,GAAG,CAAC,IAAgB,EAAE,EAAE,CAAC,CAC1C,IAAI,CAAC,SAAS,KAAK,QAAQ,IAAI,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAChE,CAAC;IACF,OAAO,OAAO,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;AAC1E,CAAC,CAAA,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAO,QAAgB,EAAgC,EAAE;IACzF,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC/D,OAAO;QACL,eAAe,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,IAAI,EAAE,CAAC;QACzD,eAAe,EAAE,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC;QAC5C,uBAAuB,EAAE,OAAO,CAAC,IAAI,CAAC,uBAAuB,CAAC;KAC/D,CAAC;AACJ,CAAC,CAAA,CAAC","sourcesContent":["import lms from '@src/data/services/lms';\n\n/** LMS course roles that grant permission to view the gradebook. */\nexport const allowedRoles = ['staff', 'limited_staff', 'instructor', 'support'];\n\ninterface CourseRole {\n course_id: string;\n role: string;\n}\n\n/** Derived assignment-type data consumed by filters and the header. */\nexport interface AssignmentTypesData {\n assignmentTypes: string[];\n areGradesFrozen: boolean;\n bulkManagementAvailable: boolean;\n}\n\n/**\n * getCanUserViewGradebook(courseId)\n * Fetches the current user's roles and derives whether they may view the\n * gradebook for this course (staff, or holding an allowed role in the course).\n */\nexport const getCanUserViewGradebook = async (courseId: string): Promise<boolean> => {\n const { data } = await lms.api.fetch.roles(courseId);\n const isAllowedRole = (role: CourseRole) => (\n role.course_id === courseId && allowedRoles.includes(role.role)\n );\n return Boolean(data.is_staff || (data.roles || []).some(isAllowedRole));\n};\n\n/**\n * getAssignmentTypes(courseId)\n * Fetches grading info and derives the assignment-type list plus the\n * grades-frozen and bulk-management flags that ride along with it.\n */\nexport const getAssignmentTypes = async (courseId: string): Promise<AssignmentTypesData> => {\n const { data } = await lms.api.fetch.assignmentTypes(courseId);\n return {\n assignmentTypes: Object.keys(data.assignment_types || {}),\n areGradesFrozen: Boolean(data.grades_frozen),\n bulkManagementAvailable: Boolean(data.can_see_bulk_management),\n };\n};\n"]}
|
package/dist/data/apiHook.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { useQuery } from '@tanstack/react-query';
|
|
2
|
-
import { useParams } from 'react-router-dom';
|
|
3
1
|
import { getAssignmentTypes, getCanUserViewGradebook } from './api';
|
|
2
|
+
import { useCourseId } from './courseIdContext';
|
|
3
|
+
import { useGradebookQuery } from './query';
|
|
4
4
|
import { assignmentTypesQueryKeys, rolesQueryKeys } from './queryKeys';
|
|
5
5
|
/**
|
|
6
6
|
* useCanUserViewGradebook()
|
|
@@ -9,8 +9,8 @@ import { assignmentTypesQueryKeys, rolesQueryKeys } from './queryKeys';
|
|
|
9
9
|
* mirroring the legacy `fetchRoles` -> fan-out behavior.
|
|
10
10
|
*/
|
|
11
11
|
export const useCanUserViewGradebook = () => {
|
|
12
|
-
const
|
|
13
|
-
return
|
|
12
|
+
const courseId = useCourseId();
|
|
13
|
+
return useGradebookQuery({
|
|
14
14
|
queryKey: rolesQueryKeys.byCourse(courseId),
|
|
15
15
|
queryFn: () => getCanUserViewGradebook(courseId),
|
|
16
16
|
enabled: !!courseId,
|
|
@@ -22,9 +22,9 @@ export const useCanUserViewGradebook = () => {
|
|
|
22
22
|
* bulk-management flags). Caller supplies `courseId` and decides when it may
|
|
23
23
|
* run via `enabled`.
|
|
24
24
|
*/
|
|
25
|
-
export const useAssignmentTypes = (courseId, { enabled = true } = {}) =>
|
|
25
|
+
export const useAssignmentTypes = (courseId, { enabled = true } = {}) => useGradebookQuery({
|
|
26
26
|
queryKey: assignmentTypesQueryKeys.byCourse(courseId),
|
|
27
|
-
queryFn: getAssignmentTypes,
|
|
27
|
+
queryFn: () => getAssignmentTypes(courseId),
|
|
28
28
|
enabled: !!courseId && enabled,
|
|
29
29
|
});
|
|
30
30
|
/**
|
|
@@ -48,7 +48,7 @@ export const useCanViewGradebook = () => {
|
|
|
48
48
|
* gate rule lives in one place.
|
|
49
49
|
*/
|
|
50
50
|
export const useCourseIdWithGate = () => {
|
|
51
|
-
const
|
|
51
|
+
const courseId = useCourseId();
|
|
52
52
|
const { data: canViewGradebook } = useCanUserViewGradebook();
|
|
53
53
|
return { courseId, enabled: !!canViewGradebook };
|
|
54
54
|
};
|
package/dist/data/apiHook.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiHook.js","sourceRoot":"","sources":["../../src/data/apiHook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"apiHook.js","sourceRoot":"","sources":["../../src/data/apiHook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,OAAO,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,wBAAwB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAEvE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAG,EAAE;IAC1C,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,OAAO,iBAAiB,CAAC;QACvB,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAC3C,OAAO,EAAE,GAAG,EAAE,CAAC,uBAAuB,CAAC,QAAQ,CAAC;QAChD,OAAO,EAAE,CAAC,CAAC,QAAQ;KACpB,CAAC,CAAC;AACL,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,QAAgB,EAChB,EAAE,OAAO,GAAG,IAAI,KAA4B,EAAE,EAC9C,EAAE,CAAC,iBAAiB,CAAC;IACrB,QAAQ,EAAE,wBAAwB,CAAC,QAAQ,CAAC,QAAQ,CAAC;IACrD,OAAO,EAAE,GAAG,EAAE,CAAC,kBAAkB,CAAC,QAAQ,CAAC;IAC3C,OAAO,EAAE,CAAC,CAAC,QAAQ,IAAI,OAAO;CAC/B,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAY,EAAE;IAC/C,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,uBAAuB,EAAE,CAAC;IACpD,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5C,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAA2C,EAAE;IAC9E,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,GAAG,uBAAuB,EAAE,CAAC;IAC7D,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,gBAAgB,EAAE,CAAC;AACnD,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAY,EAAE;;IACjD,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,mBAAmB,EAAE,CAAC;IACpD,OAAO,CAAC,CAAC,CAAA,MAAA,kBAAkB,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,0CAAE,uBAAuB,CAAA,CAAC;AACnF,CAAC,CAAC","sourcesContent":["import { getAssignmentTypes, getCanUserViewGradebook } from './api';\nimport { useCourseId } from './courseIdContext';\nimport { useGradebookQuery } from './query';\nimport { assignmentTypesQueryKeys, rolesQueryKeys } from './queryKeys';\n\n/**\n * useCanUserViewGradebook()\n * Query for the roles-based permission gate. This is the root of the fetch\n * cascade: the other server queries stay disabled until this resolves truthy,\n * mirroring the legacy `fetchRoles` -> fan-out behavior.\n */\nexport const useCanUserViewGradebook = () => {\n const courseId = useCourseId();\n return useGradebookQuery({\n queryKey: rolesQueryKeys.byCourse(courseId),\n queryFn: () => getCanUserViewGradebook(courseId),\n enabled: !!courseId,\n });\n};\n\n/**\n * useAssignmentTypes()\n * Single-responsibility query for assignment types (types + grades-frozen +\n * bulk-management flags). Caller supplies `courseId` and decides when it may\n * run via `enabled`.\n */\nexport const useAssignmentTypes = (\n courseId: string,\n { enabled = true }: { enabled?: boolean } = {},\n) => useGradebookQuery({\n queryKey: assignmentTypesQueryKeys.byCourse(courseId),\n queryFn: () => getAssignmentTypes(courseId),\n enabled: !!courseId && enabled,\n});\n\n/**\n * useCanViewGradebook()\n * The optimistic boolean read of the roles gate used by UI (header, spinner):\n * `true` while the roles query is still loading (the legacy Redux initial state\n * was `true`), the resolved value on success, `false` on error.\n */\nexport const useCanViewGradebook = (): boolean => {\n const { data, isError } = useCanUserViewGradebook();\n if (isError) {\n return false;\n }\n return data === undefined ? true : !!data;\n};\n\n/**\n * useCourseIdWithGate()\n * Shared gate for the downstream server queries: the routed `courseId` plus an\n * `enabled` flag that resolves truthy once `useCanUserViewGradebook` does.\n * Callers pass the returned pair into single-responsibility query hooks so the\n * gate rule lives in one place.\n */\nexport const useCourseIdWithGate = (): { courseId: string; enabled: boolean } => {\n const courseId = useCourseId();\n const { data: canViewGradebook } = useCanUserViewGradebook();\n return { courseId, enabled: !!canViewGradebook };\n};\n\n/**\n * useShowBulkManagement()\n * Whether bulk management is available for the course (derived from the\n * assignment-types query). Gates the header toggle + bulk-management controls.\n */\nexport const useShowBulkManagement = (): boolean => {\n const { courseId, enabled } = useCourseIdWithGate();\n return !!useAssignmentTypes(courseId, { enabled }).data?.bulkManagementAvailable;\n};\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
// Widget contract: hosts (route or dashboard slot) supply courseId here so the
|
|
3
|
+
// data hooks don't depend on the enclosing router's `:courseId` param.
|
|
4
|
+
export const CourseIdContext = createContext('');
|
|
5
|
+
export const useCourseId = () => useContext(CourseIdContext);
|
|
6
|
+
//# sourceMappingURL=courseIdContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"courseIdContext.js","sourceRoot":"","sources":["../../src/data/courseIdContext.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAElD,+EAA+E;AAC/E,uEAAuE;AACvE,MAAM,CAAC,MAAM,eAAe,GAAG,aAAa,CAAS,EAAE,CAAC,CAAC;AACzD,MAAM,CAAC,MAAM,WAAW,GAAG,GAAW,EAAE,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC","sourcesContent":["import { createContext, useContext } from 'react';\n\n// Widget contract: hosts (route or dashboard slot) supply courseId here so the\n// data hooks don't depend on the enclosing router's `:courseId` param.\nexport const CourseIdContext = createContext<string>('');\nexport const useCourseId = (): string => useContext(CourseIdContext);\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
// Optional host-provided handler for the built-in "Back to Dashboard" link in
|
|
3
|
+
// GradebookHeader. When set, it wins over URL navigation, letting slot hosts
|
|
4
|
+
// return to their own local view (e.g. the CCX Coach Student Grades tab).
|
|
5
|
+
export const GradebookNavigationContext = createContext({
|
|
6
|
+
onBack: undefined,
|
|
7
|
+
});
|
|
8
|
+
export const useGradebookNavigation = () => useContext(GradebookNavigationContext);
|
|
9
|
+
//# sourceMappingURL=gradebookNavigationContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gradebookNavigationContext.js","sourceRoot":"","sources":["../../src/data/gradebookNavigationContext.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAElD,8EAA8E;AAC9E,6EAA6E;AAC7E,0EAA0E;AAC1E,MAAM,CAAC,MAAM,0BAA0B,GAAG,aAAa,CAA0B;IAC/E,MAAM,EAAE,SAAS;CAClB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,0BAA0B,CAAC,CAAC","sourcesContent":["import { createContext, useContext } from 'react';\n\n// Optional host-provided handler for the built-in \"Back to Dashboard\" link in\n// GradebookHeader. When set, it wins over URL navigation, letting slot hosts\n// return to their own local view (e.g. the CCX Coach Student Grades tab).\nexport const GradebookNavigationContext = createContext<{ onBack?: () => void }>({\n onBack: undefined,\n});\n\nexport const useGradebookNavigation = () => useContext(GradebookNavigationContext);\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { DefaultError, UseQueryOptions, UseQueryResult } from '@tanstack/react-query';
|
|
2
|
+
export declare const retryUnlessClientError: (failureCount: number, error: unknown) => boolean;
|
|
3
|
+
export declare const gradebookQueryOptions: {
|
|
4
|
+
readonly staleTime: number;
|
|
5
|
+
readonly refetchOnWindowFocus: false;
|
|
6
|
+
readonly retry: (failureCount: number, error: unknown) => boolean;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* These can't be defaults on a QueryClient: app providers wrap the whole site,
|
|
10
|
+
* so an app-owned client would impose them on every other app sharing it. The
|
|
11
|
+
* shell's client sets no defaults, so a query that skips this wrapper silently
|
|
12
|
+
* falls back to stock React Query behavior.
|
|
13
|
+
*/
|
|
14
|
+
export declare function useGradebookQuery<TQueryFnData, TError = DefaultError, TData = TQueryFnData>(options: UseQueryOptions<TQueryFnData, TError, TData>): UseQueryResult<TData, TError>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useQuery, } from '@tanstack/react-query';
|
|
2
|
+
const isClientError = (error) => {
|
|
3
|
+
var _a;
|
|
4
|
+
const status = (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status;
|
|
5
|
+
return typeof status === 'number' && status >= 400 && status < 500;
|
|
6
|
+
};
|
|
7
|
+
export const retryUnlessClientError = (failureCount, error) => {
|
|
8
|
+
if (isClientError(error)) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
return failureCount < 3;
|
|
12
|
+
};
|
|
13
|
+
export const gradebookQueryOptions = {
|
|
14
|
+
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
15
|
+
refetchOnWindowFocus: false,
|
|
16
|
+
retry: retryUnlessClientError,
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* These can't be defaults on a QueryClient: app providers wrap the whole site,
|
|
20
|
+
* so an app-owned client would impose them on every other app sharing it. The
|
|
21
|
+
* shell's client sets no defaults, so a query that skips this wrapper silently
|
|
22
|
+
* falls back to stock React Query behavior.
|
|
23
|
+
*/
|
|
24
|
+
export function useGradebookQuery(options) {
|
|
25
|
+
return useQuery(Object.assign(Object.assign({}, gradebookQueryOptions), options));
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/data/query.ts"],"names":[],"mappings":"AAAA,OAAO,EACS,QAAQ,GACvB,MAAM,uBAAuB,CAAC;AAO/B,MAAM,aAAa,GAAG,CAAC,KAAc,EAAW,EAAE;;IAChD,MAAM,MAAM,GAAG,MAAC,KAAuB,aAAvB,KAAK,uBAAL,KAAK,CAAoB,QAAQ,0CAAE,MAAM,CAAC;IAC1D,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,GAAG,GAAG,CAAC;AACrE,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,YAAoB,EAAE,KAAc,EAAW,EAAE;IACtF,IAAI,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,YAAY,GAAG,CAAC,CAAC;AAC1B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE,YAAY;IACtC,oBAAoB,EAAE,KAAK;IAC3B,KAAK,EAAE,sBAAsB;CACrB,CAAC;AAEX;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAAqD;IAErD,OAAO,QAAQ,iCAAmC,qBAAqB,GAAK,OAAO,EAAG,CAAC;AACzF,CAAC","sourcesContent":["import {\n DefaultError, useQuery, UseQueryOptions, UseQueryResult,\n} from '@tanstack/react-query';\n\n/** Subset of an Axios-style error we inspect, to avoid depending on axios types. */\ninterface HttpErrorLike {\n response?: { status?: number };\n}\n\nconst isClientError = (error: unknown): boolean => {\n const status = (error as HttpErrorLike)?.response?.status;\n return typeof status === 'number' && status >= 400 && status < 500;\n};\n\nexport const retryUnlessClientError = (failureCount: number, error: unknown): boolean => {\n if (isClientError(error)) {\n return false;\n }\n return failureCount < 3;\n};\n\nexport const gradebookQueryOptions = {\n staleTime: 5 * 60 * 1000, // 5 minutes\n refetchOnWindowFocus: false,\n retry: retryUnlessClientError,\n} as const;\n\n/**\n * These can't be defaults on a QueryClient: app providers wrap the whole site,\n * so an app-owned client would impose them on every other app sharing it. The\n * shell's client sets no defaults, so a query that skips this wrapper silently\n * falls back to stock React Query behavior.\n */\nexport function useGradebookQuery<TQueryFnData, TError = DefaultError, TData = TQueryFnData>(\n options: UseQueryOptions<TQueryFnData, TError, TData>,\n): UseQueryResult<TData, TError> {\n return useQuery<TQueryFnData, TError, TData>({ ...gradebookQueryOptions, ...options });\n}\n"]}
|
package/dist/data/queryKeys.js
CHANGED
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
// caches independently per course and mutations can invalidate a whole course
|
|
8
8
|
// subtree via a broad prefix. The array shapes match the legacy
|
|
9
9
|
// `gradebookQueryKeys` so cache identity / invalidation is preserved.
|
|
10
|
+
//
|
|
11
|
+
// Mutations are keyed under `BASE_KEY` too: the client is shared site-wide, so
|
|
12
|
+
// `useShouldShowSpinner` counts by this prefix and a keyless mutation is missed.
|
|
10
13
|
export const BASE_KEY = ['gradebook'];
|
|
11
14
|
/** Roles-based "can view the gradebook" permission gate (the fetch-cascade root). */
|
|
12
15
|
export const rolesQueryKeys = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queryKeys.js","sourceRoot":"","sources":["../../src/data/queryKeys.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,8EAA8E;AAC9E,kFAAkF;AAClF,sDAAsD;AACtD,EAAE;AACF,+EAA+E;AAC/E,8EAA8E;AAC9E,gEAAgE;AAChE,sEAAsE;AACtE,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,WAAW,CAAU,CAAC;AAE/C,qFAAqF;AACrF,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,GAAG,EAAE,QAAQ;IACb,QAAQ,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,CAAC,GAAG,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAU;CAC1E,CAAC;AAEF,kFAAkF;AAClF,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,GAAG,EAAE,QAAQ;IACb,QAAQ,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,CAAC,GAAG,QAAQ,EAAE,QAAQ,EAAE,iBAAiB,CAAU;CACpF,CAAC","sourcesContent":["// Top-level (app-wide) query-key factories, mirroring the instructor dashboard's\n// `src/data/queryKeys.ts`: shared server-state that multiple features gate on\n// lives here (the roles permission gate + the assignment-types/config bundle used\n// by the header, filters, and bulk-management views).\n//\n// Keys are namespaced under `BASE_KEY` and scoped by `courseId` so React Query\n// caches independently per course and mutations can invalidate a whole course\n// subtree via a broad prefix. The array shapes match the legacy\n// `gradebookQueryKeys` so cache identity / invalidation is preserved.\nexport const BASE_KEY = ['gradebook'] as const;\n\n/** Roles-based \"can view the gradebook\" permission gate (the fetch-cascade root). */\nexport const rolesQueryKeys = {\n all: BASE_KEY,\n byCourse: (courseId: string) => [...BASE_KEY, courseId, 'roles'] as const,\n};\n\n/** Assignment types + grades-frozen / bulk-management flags (app-wide config). */\nexport const assignmentTypesQueryKeys = {\n all: BASE_KEY,\n byCourse: (courseId: string) => [...BASE_KEY, courseId, 'assignmentTypes'] as const,\n};\n"]}
|
|
1
|
+
{"version":3,"file":"queryKeys.js","sourceRoot":"","sources":["../../src/data/queryKeys.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,8EAA8E;AAC9E,kFAAkF;AAClF,sDAAsD;AACtD,EAAE;AACF,+EAA+E;AAC/E,8EAA8E;AAC9E,gEAAgE;AAChE,sEAAsE;AACtE,EAAE;AACF,+EAA+E;AAC/E,iFAAiF;AACjF,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,WAAW,CAAU,CAAC;AAE/C,qFAAqF;AACrF,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,GAAG,EAAE,QAAQ;IACb,QAAQ,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,CAAC,GAAG,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAU;CAC1E,CAAC;AAEF,kFAAkF;AAClF,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,GAAG,EAAE,QAAQ;IACb,QAAQ,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,CAAC,GAAG,QAAQ,EAAE,QAAQ,EAAE,iBAAiB,CAAU;CACpF,CAAC","sourcesContent":["// Top-level (app-wide) query-key factories, mirroring the instructor dashboard's\n// `src/data/queryKeys.ts`: shared server-state that multiple features gate on\n// lives here (the roles permission gate + the assignment-types/config bundle used\n// by the header, filters, and bulk-management views).\n//\n// Keys are namespaced under `BASE_KEY` and scoped by `courseId` so React Query\n// caches independently per course and mutations can invalidate a whole course\n// subtree via a broad prefix. The array shapes match the legacy\n// `gradebookQueryKeys` so cache identity / invalidation is preserved.\n//\n// Mutations are keyed under `BASE_KEY` too: the client is shared site-wide, so\n// `useShouldShowSpinner` counts by this prefix and a keyless mutation is missed.\nexport const BASE_KEY = ['gradebook'] as const;\n\n/** Roles-based \"can view the gradebook\" permission gate (the fetch-cascade root). */\nexport const rolesQueryKeys = {\n all: BASE_KEY,\n byCourse: (courseId: string) => [...BASE_KEY, courseId, 'roles'] as const,\n};\n\n/** Assignment types + grades-frozen / bulk-management flags (app-wide config). */\nexport const assignmentTypesQueryKeys = {\n all: BASE_KEY,\n byCourse: (courseId: string) => [...BASE_KEY, courseId, 'assignmentTypes'] as const,\n};\n"]}
|