@openedx/frontend-app-gradebook 2.0.0-alpha.7 → 2.0.0-alpha.8

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.
Files changed (29) hide show
  1. package/dist/components/BulkManagementHistoryView/BulkManagementAlerts.js +1 -2
  2. package/dist/components/BulkManagementHistoryView/BulkManagementAlerts.js.map +1 -1
  3. package/dist/components/GradesView/ImportResultToast/hooks.d.ts +15 -0
  4. package/dist/components/GradesView/ImportResultToast/hooks.js +51 -0
  5. package/dist/components/GradesView/ImportResultToast/hooks.js.map +1 -0
  6. package/dist/components/GradesView/ImportResultToast/index.d.ts +5 -0
  7. package/dist/components/GradesView/ImportResultToast/index.js +23 -0
  8. package/dist/components/GradesView/ImportResultToast/index.js.map +1 -0
  9. package/dist/components/GradesView/{ImportSuccessToast → ImportResultToast}/messages.d.ts +13 -6
  10. package/dist/components/GradesView/{ImportSuccessToast → ImportResultToast}/messages.js +8 -1
  11. package/dist/components/GradesView/ImportResultToast/messages.js.map +1 -0
  12. package/dist/components/GradesView/data/apiHook.js +6 -2
  13. package/dist/components/GradesView/data/apiHook.js.map +1 -1
  14. package/dist/components/GradesView/index.js +2 -2
  15. package/dist/components/GradesView/index.js.map +1 -1
  16. package/dist/components/GradesView/messages.d.ts +9 -1
  17. package/dist/components/GradesView/messages.js +5 -0
  18. package/dist/components/GradesView/messages.js.map +1 -1
  19. package/dist/data/gradebookUiContext.d.ts +3 -0
  20. package/dist/data/gradebookUiContext.js +6 -1
  21. package/dist/data/gradebookUiContext.js.map +1 -1
  22. package/package.json +1 -1
  23. package/dist/components/GradesView/ImportSuccessToast/hooks.d.ts +0 -10
  24. package/dist/components/GradesView/ImportSuccessToast/hooks.js +0 -31
  25. package/dist/components/GradesView/ImportSuccessToast/hooks.js.map +0 -1
  26. package/dist/components/GradesView/ImportSuccessToast/index.d.ts +0 -5
  27. package/dist/components/GradesView/ImportSuccessToast/index.js +0 -15
  28. package/dist/components/GradesView/ImportSuccessToast/index.js.map +0 -1
  29. package/dist/components/GradesView/ImportSuccessToast/messages.js.map +0 -1
@@ -9,9 +9,8 @@ import messages from './messages';
9
9
  */
10
10
  export const BulkManagementAlerts = () => {
11
11
  const { csvUploadSuccess, csvUploadErrorMessages } = useGradebookUi();
12
- // Mirrors the old `selectors.grades.bulkImportError` string formatting.
13
12
  const bulkImportError = csvUploadErrorMessages.length
14
- ? `Errors while processing: ${csvUploadErrorMessages.join('; ')};`
13
+ ? `Errors while processing: ${csvUploadErrorMessages.join('; ')}`
15
14
  : '';
16
15
  return (_jsxs(_Fragment, { children: [_jsx(Alert, { variant: "danger", show: !!bulkImportError, dismissible: false, children: bulkImportError }), _jsx(Alert, { variant: "success", show: csvUploadSuccess, dismissible: false, children: _jsx(FormattedMessage, Object.assign({}, messages.successDialog)) })] }));
17
16
  };
@@ -1 +1 @@
1
- {"version":3,"file":"BulkManagementAlerts.js","sourceRoot":"","sources":["../../../src/components/BulkManagementHistoryView/BulkManagementAlerts.jsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEzC,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,QAAQ,MAAM,YAAY,CAAC;AAElC;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,EAAE;IACvC,MAAM,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,GAAG,cAAc,EAAE,CAAC;IACtE,wEAAwE;IACxE,MAAM,eAAe,GAAG,sBAAsB,CAAC,MAAM;QACnD,CAAC,CAAC,4BAA4B,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;QAClE,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO,CACL,8BACE,KAAC,KAAK,IACJ,OAAO,EAAC,QAAQ,EAChB,IAAI,EAAE,CAAC,CAAC,eAAe,EACvB,WAAW,EAAE,KAAK,YAEjB,eAAe,GACV,EACR,KAAC,KAAK,IACJ,OAAO,EAAC,SAAS,EACjB,IAAI,EAAE,gBAAgB,EACtB,WAAW,EAAE,KAAK,YAElB,KAAC,gBAAgB,oBAAK,QAAQ,CAAC,aAAa,EAAI,GAC1C,IACP,CACJ,CAAC;AACJ,CAAC,CAAC;AAEF,oBAAoB,CAAC,SAAS,GAAG,EAAE,CAAC;AAEpC,eAAe,oBAAoB,CAAC","sourcesContent":["import { FormattedMessage } from '@openedx/frontend-base';\n\nimport { Alert } from '@openedx/paragon';\n\nimport { useGradebookUi } from '@src/data/gradebookUiContext';\nimport messages from './messages';\n\n/**\n * <BulkManagementAlerts />\n * Alerts to display at the top of the BulkManagement tab.\n */\nexport const BulkManagementAlerts = () => {\n const { csvUploadSuccess, csvUploadErrorMessages } = useGradebookUi();\n // Mirrors the old `selectors.grades.bulkImportError` string formatting.\n const bulkImportError = csvUploadErrorMessages.length\n ? `Errors while processing: ${csvUploadErrorMessages.join('; ')};`\n : '';\n\n return (\n <>\n <Alert\n variant=\"danger\"\n show={!!bulkImportError}\n dismissible={false}\n >\n {bulkImportError}\n </Alert>\n <Alert\n variant=\"success\"\n show={csvUploadSuccess}\n dismissible={false}\n >\n <FormattedMessage {...messages.successDialog} />\n </Alert>\n </>\n );\n};\n\nBulkManagementAlerts.propTypes = {};\n\nexport default BulkManagementAlerts;\n"]}
1
+ {"version":3,"file":"BulkManagementAlerts.js","sourceRoot":"","sources":["../../../src/components/BulkManagementHistoryView/BulkManagementAlerts.jsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEzC,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,QAAQ,MAAM,YAAY,CAAC;AAElC;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,EAAE;IACvC,MAAM,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,GAAG,cAAc,EAAE,CAAC;IACtE,MAAM,eAAe,GAAG,sBAAsB,CAAC,MAAM;QACnD,CAAC,CAAC,4BAA4B,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QACjE,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO,CACL,8BACE,KAAC,KAAK,IACJ,OAAO,EAAC,QAAQ,EAChB,IAAI,EAAE,CAAC,CAAC,eAAe,EACvB,WAAW,EAAE,KAAK,YAEjB,eAAe,GACV,EACR,KAAC,KAAK,IACJ,OAAO,EAAC,SAAS,EACjB,IAAI,EAAE,gBAAgB,EACtB,WAAW,EAAE,KAAK,YAElB,KAAC,gBAAgB,oBAAK,QAAQ,CAAC,aAAa,EAAI,GAC1C,IACP,CACJ,CAAC;AACJ,CAAC,CAAC;AAEF,oBAAoB,CAAC,SAAS,GAAG,EAAE,CAAC;AAEpC,eAAe,oBAAoB,CAAC","sourcesContent":["import { FormattedMessage } from '@openedx/frontend-base';\n\nimport { Alert } from '@openedx/paragon';\n\nimport { useGradebookUi } from '@src/data/gradebookUiContext';\nimport messages from './messages';\n\n/**\n * <BulkManagementAlerts />\n * Alerts to display at the top of the BulkManagement tab.\n */\nexport const BulkManagementAlerts = () => {\n const { csvUploadSuccess, csvUploadErrorMessages } = useGradebookUi();\n const bulkImportError = csvUploadErrorMessages.length\n ? `Errors while processing: ${csvUploadErrorMessages.join('; ')}`\n : '';\n\n return (\n <>\n <Alert\n variant=\"danger\"\n show={!!bulkImportError}\n dismissible={false}\n >\n {bulkImportError}\n </Alert>\n <Alert\n variant=\"success\"\n show={csvUploadSuccess}\n dismissible={false}\n >\n <FormattedMessage {...messages.successDialog} />\n </Alert>\n </>\n );\n};\n\nBulkManagementAlerts.propTypes = {};\n\nexport default BulkManagementAlerts;\n"]}
@@ -0,0 +1,15 @@
1
+ export function useImportResultToastData(): {
2
+ action: {
3
+ label: string;
4
+ onClick: () => void;
5
+ };
6
+ onClose: () => void;
7
+ show: boolean;
8
+ toastProps: {
9
+ autohide: boolean;
10
+ } | {
11
+ autohide?: undefined;
12
+ };
13
+ description: string;
14
+ };
15
+ export default useImportResultToastData;
@@ -0,0 +1,51 @@
1
+ import { useIntl } from '@openedx/frontend-base';
2
+ import { useGradebookUi } from '../../../data/gradebookUiContext';
3
+ import { views } from '../../../data/constants/app';
4
+ import messages from './messages';
5
+ /**
6
+ * <ImportResultToast />
7
+ * Reports the outcome of a grade upload, whether it succeeded or failed, and links to the
8
+ * Bulk Management History tab.
9
+ */
10
+ export const useImportResultToastData = () => {
11
+ const { formatMessage } = useIntl();
12
+ const { showImportSuccessToast, setShowImportSuccessToast, showImportErrorToast, setShowImportErrorToast, csvUploadErrorMessages, setActiveView, } = useGradebookUi();
13
+ const details = csvUploadErrorMessages.join('; ');
14
+ // A failure needs its message to say anything useful, so it is only reportable once both
15
+ // are present. A flagged failure suppresses the success claim either way: with no message
16
+ // there is nothing to show, but reporting success alongside a known failure is the one
17
+ // outcome never worth risking.
18
+ const isError = !!showImportErrorToast && !!details;
19
+ const isSuccess = !showImportErrorToast && !!showImportSuccessToast;
20
+ // Each outcome requires its own flag, so neither can borrow the other's wording.
21
+ let description = '';
22
+ if (isError) {
23
+ description = formatMessage(messages.errorDescription, { details });
24
+ }
25
+ else if (isSuccess) {
26
+ description = formatMessage(messages.successDescription);
27
+ }
28
+ const hide = () => {
29
+ setShowImportSuccessToast(false);
30
+ setShowImportErrorToast(false);
31
+ };
32
+ return {
33
+ action: {
34
+ label: formatMessage(messages.showHistoryViewBtn),
35
+ onClick: () => {
36
+ setActiveView(views.bulkManagementHistory);
37
+ hide();
38
+ },
39
+ },
40
+ onClose: hide,
41
+ show: isError || isSuccess,
42
+ // Paragon holds `autohide` in state so that hovering or focusing the toast pauses the
43
+ // timer, and spreads extra props over it -- so passing the prop at all pins the value
44
+ // and defeats that pause. Only a failure needs pinning, to wait for the reader to act;
45
+ // a success is left to Paragon, which lets the reader hold it open.
46
+ toastProps: isError ? { autohide: false } : {},
47
+ description,
48
+ };
49
+ };
50
+ export default useImportResultToastData;
51
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../../src/components/GradesView/ImportResultToast/hooks.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEjD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAChD,OAAO,QAAQ,MAAM,YAAY,CAAC;AAElC;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,EAAE;IAC3C,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAC;IAEpC,MAAM,EACJ,sBAAsB,EACtB,yBAAyB,EACzB,oBAAoB,EACpB,uBAAuB,EACvB,sBAAsB,EACtB,aAAa,GACd,GAAG,cAAc,EAAE,CAAC;IAErB,MAAM,OAAO,GAAG,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAElD,yFAAyF;IACzF,0FAA0F;IAC1F,uFAAuF;IACvF,+BAA+B;IAC/B,MAAM,OAAO,GAAG,CAAC,CAAC,oBAAoB,IAAI,CAAC,CAAC,OAAO,CAAC;IACpD,MAAM,SAAS,GAAG,CAAC,oBAAoB,IAAI,CAAC,CAAC,sBAAsB,CAAC;IAEpE,iFAAiF;IACjF,IAAI,WAAW,GAAG,EAAE,CAAC;IACrB,IAAI,OAAO,EAAE,CAAC;QACZ,WAAW,GAAG,aAAa,CAAC,QAAQ,CAAC,gBAAgB,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IACtE,CAAC;SAAM,IAAI,SAAS,EAAE,CAAC;QACrB,WAAW,GAAG,aAAa,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,EAAE;QAChB,yBAAyB,CAAC,KAAK,CAAC,CAAC;QACjC,uBAAuB,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC,CAAC;IAEF,OAAO;QACL,MAAM,EAAE;YACN,KAAK,EAAE,aAAa,CAAC,QAAQ,CAAC,kBAAkB,CAAC;YACjD,OAAO,EAAE,GAAG,EAAE;gBACZ,aAAa,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;gBAC3C,IAAI,EAAE,CAAC;YACT,CAAC;SACF;QACD,OAAO,EAAE,IAAI;QACb,IAAI,EAAE,OAAO,IAAI,SAAS;QAC1B,sFAAsF;QACtF,sFAAsF;QACtF,uFAAuF;QACvF,oEAAoE;QACpE,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE;QAC9C,WAAW;KACZ,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,wBAAwB,CAAC","sourcesContent":["import { useIntl } from '@openedx/frontend-base';\n\nimport { useGradebookUi } from '@src/data/gradebookUiContext';\nimport { views } from '@src/data/constants/app';\nimport messages from './messages';\n\n/**\n * <ImportResultToast />\n * Reports the outcome of a grade upload, whether it succeeded or failed, and links to the\n * Bulk Management History tab.\n */\nexport const useImportResultToastData = () => {\n const { formatMessage } = useIntl();\n\n const {\n showImportSuccessToast,\n setShowImportSuccessToast,\n showImportErrorToast,\n setShowImportErrorToast,\n csvUploadErrorMessages,\n setActiveView,\n } = useGradebookUi();\n\n const details = csvUploadErrorMessages.join('; ');\n\n // A failure needs its message to say anything useful, so it is only reportable once both\n // are present. A flagged failure suppresses the success claim either way: with no message\n // there is nothing to show, but reporting success alongside a known failure is the one\n // outcome never worth risking.\n const isError = !!showImportErrorToast && !!details;\n const isSuccess = !showImportErrorToast && !!showImportSuccessToast;\n\n // Each outcome requires its own flag, so neither can borrow the other's wording.\n let description = '';\n if (isError) {\n description = formatMessage(messages.errorDescription, { details });\n } else if (isSuccess) {\n description = formatMessage(messages.successDescription);\n }\n\n const hide = () => {\n setShowImportSuccessToast(false);\n setShowImportErrorToast(false);\n };\n\n return {\n action: {\n label: formatMessage(messages.showHistoryViewBtn),\n onClick: () => {\n setActiveView(views.bulkManagementHistory);\n hide();\n },\n },\n onClose: hide,\n show: isError || isSuccess,\n // Paragon holds `autohide` in state so that hovering or focusing the toast pauses the\n // timer, and spreads extra props over it -- so passing the prop at all pins the value\n // and defeats that pause. Only a failure needs pinning, to wait for the reader to act;\n // a success is left to Paragon, which lets the reader hold it open.\n toastProps: isError ? { autohide: false } : {},\n description,\n };\n};\n\nexport default useImportResultToastData;\n"]}
@@ -0,0 +1,5 @@
1
+ export function ImportResultToast(): import("react").JSX.Element;
2
+ export namespace ImportResultToast {
3
+ let propTypes: {};
4
+ }
5
+ export default ImportResultToast;
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useRef } from 'react';
3
+ import { Toast } from '@openedx/paragon';
4
+ import useImportResultToastData from './hooks';
5
+ /**
6
+ * <ImportResultToast />
7
+ * Toast component triggered by a grade upload, reporting either that it succeeded or that
8
+ * it failed -- rejected by the server, or applied no grades at all.
9
+ * Provides a link to view the Bulk Management History tab.
10
+ */
11
+ export const ImportResultToast = () => {
12
+ const { action, onClose, show, toastProps, description, } = useImportResultToastData();
13
+ // A failure waits to be dismissed, so switching tabs -- which unmounts this view -- has
14
+ // to retire it. Otherwise it re-announces itself on every return to the Grades tab.
15
+ // The ref keeps the cleanup on unmount only, rather than on every re-render.
16
+ const onCloseRef = useRef(onClose);
17
+ onCloseRef.current = onClose;
18
+ useEffect(() => () => onCloseRef.current(), []);
19
+ return (_jsx(Toast, Object.assign({ action: action, onClose: onClose, show: show }, toastProps, { children: description })));
20
+ };
21
+ ImportResultToast.propTypes = {};
22
+ export default ImportResultToast;
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/components/GradesView/ImportResultToast/index.jsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAE1C,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEzC,OAAO,wBAAwB,MAAM,SAAS,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,EAAE;IACpC,MAAM,EACJ,MAAM,EACN,OAAO,EACP,IAAI,EACJ,UAAU,EACV,WAAW,GACZ,GAAG,wBAAwB,EAAE,CAAC;IAE/B,wFAAwF;IACxF,oFAAoF;IACpF,6EAA6E;IAC7E,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;IAC7B,SAAS,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;IAEhD,OAAO,CACL,KAAC,KAAK,kBACJ,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,IAAI,IACN,UAAU,cAEb,WAAW,IACN,CACT,CAAC;AACJ,CAAC,CAAC;AAEF,iBAAiB,CAAC,SAAS,GAAG,EAAE,CAAC;AAEjC,eAAe,iBAAiB,CAAC","sourcesContent":["import { useEffect, useRef } from 'react';\n\nimport { Toast } from '@openedx/paragon';\n\nimport useImportResultToastData from './hooks';\n\n/**\n * <ImportResultToast />\n * Toast component triggered by a grade upload, reporting either that it succeeded or that\n * it failed -- rejected by the server, or applied no grades at all.\n * Provides a link to view the Bulk Management History tab.\n */\nexport const ImportResultToast = () => {\n const {\n action,\n onClose,\n show,\n toastProps,\n description,\n } = useImportResultToastData();\n\n // A failure waits to be dismissed, so switching tabs -- which unmounts this view -- has\n // to retire it. Otherwise it re-announces itself on every return to the Grades tab.\n // The ref keeps the cleanup on unmount only, rather than on every re-render.\n const onCloseRef = useRef(onClose);\n onCloseRef.current = onClose;\n useEffect(() => () => onCloseRef.current(), []);\n\n return (\n <Toast\n action={action}\n onClose={onClose}\n show={show}\n {...toastProps}\n >\n {description}\n </Toast>\n );\n};\n\nImportResultToast.propTypes = {};\n\nexport default ImportResultToast;\n"]}
@@ -1,16 +1,23 @@
1
1
  export default messages;
2
2
  declare namespace messages {
3
- namespace description {
4
- export let id: string;
5
- export let defaultMessage: string;
6
- let description_1: string;
7
- export { description_1 as description };
3
+ namespace successDescription {
4
+ let id: string;
5
+ let defaultMessage: string;
6
+ let description: string;
8
7
  }
9
- namespace showHistoryViewBtn {
8
+ namespace errorDescription {
10
9
  let id_1: string;
11
10
  export { id_1 as id };
12
11
  let defaultMessage_1: string;
13
12
  export { defaultMessage_1 as defaultMessage };
13
+ let description_1: string;
14
+ export { description_1 as description };
15
+ }
16
+ namespace showHistoryViewBtn {
17
+ let id_2: string;
18
+ export { id_2 as id };
19
+ let defaultMessage_2: string;
20
+ export { defaultMessage_2 as defaultMessage };
14
21
  let description_2: string;
15
22
  export { description_2 as description };
16
23
  }
@@ -1,10 +1,17 @@
1
1
  import { defineMessages } from '@openedx/frontend-base';
2
+ // The success ids keep their original `ImportSuccessToast` names: they are already in the
3
+ // translation pipeline, and renaming an id orphans every translation of it.
2
4
  const messages = defineMessages({
3
- description: {
5
+ successDescription: {
4
6
  id: 'gradebook.GradesView.ImportSuccessToast.description',
5
7
  defaultMessage: 'Import Successful! Grades will be updated momentarily.',
6
8
  description: 'A message congratulating a successful Import of grades',
7
9
  },
10
+ errorDescription: {
11
+ id: 'gradebook.GradesView.ImportResultToast.errorDescription',
12
+ defaultMessage: 'Import failed. {details}',
13
+ description: 'Message shown when a grade import could not be applied',
14
+ },
8
15
  showHistoryViewBtn: {
9
16
  id: 'gradebook.GradesView.ImportSuccessToast.showHistoryViewBtn',
10
17
  defaultMessage: 'View Activity Log',
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../../../src/components/GradesView/ImportResultToast/messages.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,0FAA0F;AAC1F,4EAA4E;AAC5E,MAAM,QAAQ,GAAG,cAAc,CAAC;IAC9B,kBAAkB,EAAE;QAClB,EAAE,EAAE,qDAAqD;QACzD,cAAc,EAAE,wDAAwD;QACxE,WAAW,EAAE,wDAAwD;KACtE;IACD,gBAAgB,EAAE;QAChB,EAAE,EAAE,yDAAyD;QAC7D,cAAc,EAAE,0BAA0B;QAC1C,WAAW,EAAE,wDAAwD;KACtE;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,4DAA4D;QAChE,cAAc,EAAE,mBAAmB;QACnC,WAAW,EAAE,4EAA4E;KAC1F;CACF,CAAC,CAAC;AAEH,eAAe,QAAQ,CAAC","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\n// The success ids keep their original `ImportSuccessToast` names: they are already in the\n// translation pipeline, and renaming an id orphans every translation of it.\nconst messages = defineMessages({\n successDescription: {\n id: 'gradebook.GradesView.ImportSuccessToast.description',\n defaultMessage: 'Import Successful! Grades will be updated momentarily.',\n description: 'A message congratulating a successful Import of grades',\n },\n errorDescription: {\n id: 'gradebook.GradesView.ImportResultToast.errorDescription',\n defaultMessage: 'Import failed. {details}',\n description: 'Message shown when a grade import could not be applied',\n },\n showHistoryViewBtn: {\n id: 'gradebook.GradesView.ImportSuccessToast.showHistoryViewBtn',\n defaultMessage: 'View Activity Log',\n description: 'The text on a button that loads a view of the Bulk Management Activity Log',\n },\n});\n\nexport default messages;\n"]}
@@ -8,6 +8,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { useMemo } from 'react';
11
+ import { useIntl } from '@openedx/frontend-base';
11
12
  import { useMutation, useQueryClient } from '@tanstack/react-query';
12
13
  import lms from '../../../data/services/lms';
13
14
  import { sortAlphaAsc } from '../../../data/formatUtils';
@@ -20,6 +21,7 @@ import { trackGradesDisplayed, trackGradeOverrideSucceeded, trackGradeOverrideFa
20
21
  // Cross-feature invalidation: a successful CSV upload refreshes the bulk-operation
21
22
  // history owned by the BulkManagementHistoryView feature.
22
23
  import { bulkOperationHistoryQueryKeys } from '../../BulkManagementHistoryView/data/queryKeys';
24
+ import messages from '../messages';
23
25
  import { getGradeOverrideHistory, getGrades, getGradesPage } from './api';
24
26
  import { gradeMutationKeys, gradeOverrideHistoryQueryKeys, gradesQueryKeys } from './queryKeys';
25
27
  import { buildGradesFetchParams } from './utils';
@@ -160,9 +162,10 @@ export const useUpdateGrades = () => {
160
162
  * still runs on both success and failure.
161
163
  */
162
164
  export const useSubmitImportGradesButtonData = () => {
165
+ const { formatMessage } = useIntl();
163
166
  const queryClient = useQueryClient();
164
167
  const courseId = useCourseId();
165
- const { setShowImportSuccessToast, resetCsvUpload, markCsvUploadSuccess, setCsvUploadErrors, } = useGradebookUi();
168
+ const { setShowImportSuccessToast, setShowImportErrorToast, resetCsvUpload, markCsvUploadSuccess, setCsvUploadErrors, } = useGradebookUi();
166
169
  const mutation = useMutation({
167
170
  mutationKey: gradeMutationKeys.uploadGradeCsv,
168
171
  mutationFn: (formData) => lms.api.uploadGradeCsv(courseId, formData),
@@ -185,8 +188,9 @@ export const useSubmitImportGradesButtonData = () => {
185
188
  setCsvUploadErrors(errorMessages);
186
189
  }
187
190
  else {
188
- setCsvUploadErrors(['Unknown error.']);
191
+ setCsvUploadErrors([formatMessage(messages.unknownImportError)]);
189
192
  }
193
+ setShowImportErrorToast(true);
190
194
  },
191
195
  });
192
196
  return (formData) => mutation.mutateAsync(formData).catch(() => { });
@@ -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, 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"]}
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 { useIntl } from '@openedx/frontend-base';\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 messages from '../messages';\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 { formatMessage } = useIntl();\n const queryClient = useQueryClient();\n const courseId = useCourseId();\n const {\n setShowImportSuccessToast,\n setShowImportErrorToast,\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([formatMessage(messages.unknownImportError)]);\n }\n setShowImportErrorToast(true);\n },\n });\n return (formData: FormData) => mutation.mutateAsync(formData).catch(() => {});\n};\n"]}
@@ -9,7 +9,7 @@ import FilterBadges from './FilterBadges';
9
9
  import FilteredUsersLabel from './FilteredUsersLabel';
10
10
  import FilterMenuToggle from './FilterMenuToggle';
11
11
  import GradebookTable from './GradebookTable';
12
- import ImportSuccessToast from './ImportSuccessToast';
12
+ import ImportResultToast from './ImportResultToast';
13
13
  import InterventionsReport from './InterventionsReport';
14
14
  import PageButtons from './PageButtons';
15
15
  import ScoreViewInput from './ScoreViewInput';
@@ -27,7 +27,7 @@ export const GradesView = ({ updateQueryParams }) => {
27
27
  updateQueryParams(filterNames.reduce((obj, filterName) => (Object.assign(Object.assign({}, obj), { [filterName]: false })), {}));
28
28
  fetchGrades();
29
29
  };
30
- return (_jsxs(_Fragment, { children: [_jsx(SpinnerIcon, {}), _jsx(InterventionsReport, {}), _jsxs("div", { className: "mb-3", children: [_jsxs("div", { className: "d-flex align-items-center mb-2", children: [_jsx(Bubble, { className: "mr-2", children: "1" }), _jsx("h3", { className: "h4 text-primary-700 my-0", children: formatMessage(messages.filterStepHeading) })] }), _jsxs("div", { className: "d-flex justify-content-between flex-wrap", children: [_jsx(FilterMenuToggle, {}), _jsx(SearchControls, {})] }), _jsx(FilterBadges, { handleClose: handleFilterBadgeClose }), _jsx(StatusAlerts, {}), _jsxs("div", { className: "d-flex align-items-center mb-2", children: [_jsx(Bubble, { className: "mr-2", children: "2" }), _jsx("h3", { className: "h4 text-primary-700 my-0", children: formatMessage(messages.gradebookStepHeading) })] }), _jsxs("div", { className: "d-flex justify-content-between align-items-center mb-2", children: [_jsx(ScoreViewInput, {}), _jsx(BulkManagementControls, {})] })] }), _jsx(FilteredUsersLabel, {}), _jsx(GradebookTable, {}), _jsx(PageButtons, {}), _jsxs("p", { children: ["* ", formatMessage(messages.mastersHint)] }), _jsx(EditModal, {}), _jsx(ImportSuccessToast, {})] }));
30
+ return (_jsxs(_Fragment, { children: [_jsx(SpinnerIcon, {}), _jsx(InterventionsReport, {}), _jsxs("div", { className: "mb-3", children: [_jsxs("div", { className: "d-flex align-items-center mb-2", children: [_jsx(Bubble, { className: "mr-2", children: "1" }), _jsx("h3", { className: "h4 text-primary-700 my-0", children: formatMessage(messages.filterStepHeading) })] }), _jsxs("div", { className: "d-flex justify-content-between flex-wrap", children: [_jsx(FilterMenuToggle, {}), _jsx(SearchControls, {})] }), _jsx(FilterBadges, { handleClose: handleFilterBadgeClose }), _jsx(StatusAlerts, {}), _jsxs("div", { className: "d-flex align-items-center mb-2", children: [_jsx(Bubble, { className: "mr-2", children: "2" }), _jsx("h3", { className: "h4 text-primary-700 my-0", children: formatMessage(messages.gradebookStepHeading) })] }), _jsxs("div", { className: "d-flex justify-content-between align-items-center mb-2", children: [_jsx(ScoreViewInput, {}), _jsx(BulkManagementControls, {})] })] }), _jsx(FilteredUsersLabel, {}), _jsx(GradebookTable, {}), _jsx(PageButtons, {}), _jsxs("p", { children: ["* ", formatMessage(messages.mastersHint)] }), _jsx(EditModal, {}), _jsx(ImportResultToast, {})] }));
31
31
  };
32
32
  GradesView.propTypes = {
33
33
  updateQueryParams: PropTypes.func.isRequired,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/GradesView/index.jsx"],"names":[],"mappings":"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","sourcesContent":["import PropTypes from 'prop-types';\nimport { useIntl } from '@openedx/frontend-base';\nimport { Bubble } from '@openedx/paragon';\n\nimport { useFilters } from '@src/data/filtersContext';\n\nimport BulkManagementControls from './BulkManagementControls';\nimport EditModal from './EditModal';\nimport FilterBadges from './FilterBadges';\nimport FilteredUsersLabel from './FilteredUsersLabel';\nimport FilterMenuToggle from './FilterMenuToggle';\nimport GradebookTable from './GradebookTable';\nimport ImportSuccessToast from './ImportSuccessToast';\nimport InterventionsReport from './InterventionsReport';\nimport PageButtons from './PageButtons';\nimport ScoreViewInput from './ScoreViewInput';\nimport SearchControls from './SearchControls';\nimport SpinnerIcon from './SpinnerIcon';\nimport StatusAlerts from './StatusAlerts';\nimport { useRefetchGrades } from './data/hooks';\nimport messages from './messages';\n\nexport const GradesView = ({ updateQueryParams }) => {\n const { formatMessage } = useIntl();\n const { resetFilters: resetContextFilters } = useFilters();\n const fetchGrades = useRefetchGrades();\n\n const handleFilterBadgeClose = (filterNames) => () => {\n resetContextFilters(filterNames);\n updateQueryParams(filterNames.reduce(\n (obj, filterName) => ({ ...obj, [filterName]: false }),\n {},\n ));\n fetchGrades();\n };\n\n return (\n <>\n <SpinnerIcon />\n\n <InterventionsReport />\n\n <div className=\"mb-3\">\n <div className=\"d-flex align-items-center mb-2\">\n <Bubble className=\"mr-2\">1</Bubble>\n <h3 className=\"h4 text-primary-700 my-0\">\n {formatMessage(messages.filterStepHeading)}\n </h3>\n </div>\n <div className=\"d-flex justify-content-between flex-wrap\">\n <FilterMenuToggle />\n <SearchControls />\n </div>\n\n <FilterBadges handleClose={handleFilterBadgeClose} />\n <StatusAlerts />\n\n <div className=\"d-flex align-items-center mb-2\">\n <Bubble className=\"mr-2\">2</Bubble>\n <h3 className=\"h4 text-primary-700 my-0\">\n {formatMessage(messages.gradebookStepHeading)}\n </h3>\n </div>\n\n <div className=\"d-flex justify-content-between align-items-center mb-2\">\n <ScoreViewInput />\n <BulkManagementControls />\n </div>\n </div>\n\n <FilteredUsersLabel />\n\n <GradebookTable />\n\n <PageButtons />\n <p>* {formatMessage(messages.mastersHint)}</p>\n <EditModal />\n\n <ImportSuccessToast />\n </>\n );\n};\n\nGradesView.propTypes = {\n updateQueryParams: PropTypes.func.isRequired,\n};\n\nexport default GradesView;\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/GradesView/index.jsx"],"names":[],"mappings":"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","sourcesContent":["import PropTypes from 'prop-types';\nimport { useIntl } from '@openedx/frontend-base';\nimport { Bubble } from '@openedx/paragon';\n\nimport { useFilters } from '@src/data/filtersContext';\n\nimport BulkManagementControls from './BulkManagementControls';\nimport EditModal from './EditModal';\nimport FilterBadges from './FilterBadges';\nimport FilteredUsersLabel from './FilteredUsersLabel';\nimport FilterMenuToggle from './FilterMenuToggle';\nimport GradebookTable from './GradebookTable';\nimport ImportResultToast from './ImportResultToast';\nimport InterventionsReport from './InterventionsReport';\nimport PageButtons from './PageButtons';\nimport ScoreViewInput from './ScoreViewInput';\nimport SearchControls from './SearchControls';\nimport SpinnerIcon from './SpinnerIcon';\nimport StatusAlerts from './StatusAlerts';\nimport { useRefetchGrades } from './data/hooks';\nimport messages from './messages';\n\nexport const GradesView = ({ updateQueryParams }) => {\n const { formatMessage } = useIntl();\n const { resetFilters: resetContextFilters } = useFilters();\n const fetchGrades = useRefetchGrades();\n\n const handleFilterBadgeClose = (filterNames) => () => {\n resetContextFilters(filterNames);\n updateQueryParams(filterNames.reduce(\n (obj, filterName) => ({ ...obj, [filterName]: false }),\n {},\n ));\n fetchGrades();\n };\n\n return (\n <>\n <SpinnerIcon />\n\n <InterventionsReport />\n\n <div className=\"mb-3\">\n <div className=\"d-flex align-items-center mb-2\">\n <Bubble className=\"mr-2\">1</Bubble>\n <h3 className=\"h4 text-primary-700 my-0\">\n {formatMessage(messages.filterStepHeading)}\n </h3>\n </div>\n <div className=\"d-flex justify-content-between flex-wrap\">\n <FilterMenuToggle />\n <SearchControls />\n </div>\n\n <FilterBadges handleClose={handleFilterBadgeClose} />\n <StatusAlerts />\n\n <div className=\"d-flex align-items-center mb-2\">\n <Bubble className=\"mr-2\">2</Bubble>\n <h3 className=\"h4 text-primary-700 my-0\">\n {formatMessage(messages.gradebookStepHeading)}\n </h3>\n </div>\n\n <div className=\"d-flex justify-content-between align-items-center mb-2\">\n <ScoreViewInput />\n <BulkManagementControls />\n </div>\n </div>\n\n <FilteredUsersLabel />\n\n <GradebookTable />\n\n <PageButtons />\n <p>* {formatMessage(messages.mastersHint)}</p>\n <EditModal />\n\n <ImportResultToast />\n </>\n );\n};\n\nGradesView.propTypes = {\n updateQueryParams: PropTypes.func.isRequired,\n};\n\nexport default GradesView;\n"]}
@@ -13,7 +13,7 @@ declare namespace messages {
13
13
  let description_1: string;
14
14
  export { description_1 as description };
15
15
  }
16
- namespace mastersHint {
16
+ namespace unknownImportError {
17
17
  let id_2: string;
18
18
  export { id_2 as id };
19
19
  let defaultMessage_2: string;
@@ -21,4 +21,12 @@ declare namespace messages {
21
21
  let description_2: string;
22
22
  export { description_2 as description };
23
23
  }
24
+ namespace mastersHint {
25
+ let id_3: string;
26
+ export { id_3 as id };
27
+ let defaultMessage_3: string;
28
+ export { defaultMessage_3 as defaultMessage };
29
+ let description_3: string;
30
+ export { description_3 as description };
31
+ }
24
32
  }
@@ -10,6 +10,11 @@ const messages = defineMessages({
10
10
  defaultMessage: 'View or Modify Individual Grades',
11
11
  description: 'Gradebook table container heading string',
12
12
  },
13
+ unknownImportError: {
14
+ id: 'gradebook.GradesView.unknownImportError',
15
+ defaultMessage: 'Unknown error.',
16
+ description: 'Shown when a grade import fails without the server saying why',
17
+ },
13
18
  mastersHint: {
14
19
  id: 'gradebook.GradesView.mastersHint',
15
20
  defaultMessage: "available for learners in the Master's track only",
@@ -1 +1 @@
1
- {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../../src/components/GradesView/messages.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,QAAQ,GAAG,cAAc,CAAC;IAC9B,iBAAiB,EAAE;QACjB,EAAE,EAAE,sCAAsC;QAC1C,cAAc,EAAE,yBAAyB;QACzC,WAAW,EAAE,0CAA0C;KACxD;IACD,oBAAoB,EAAE;QACpB,EAAE,EAAE,6CAA6C;QACjD,cAAc,EAAE,kCAAkC;QAClD,WAAW,EAAE,0CAA0C;KACxD;IACD,WAAW,EAAE;QACX,EAAE,EAAE,kCAAkC;QACtC,cAAc,EAAE,mDAAmD;QACnE,WAAW,EAAE,iDAAiD;KAC/D;CACF,CAAC,CAAC;AAEH,eAAe,QAAQ,CAAC","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n filterStepHeading: {\n id: 'gradebook.GradesView.filterHeadingV2',\n defaultMessage: 'Filter the Grade Report',\n description: 'Filter controls container heading string',\n },\n gradebookStepHeading: {\n id: 'gradebook.GradesView.gradebookStepHeadingV2',\n defaultMessage: 'View or Modify Individual Grades',\n description: 'Gradebook table container heading string',\n },\n mastersHint: {\n id: 'gradebook.GradesView.mastersHint',\n defaultMessage: \"available for learners in the Master's track only\",\n description: 'Masters feature availability hint on Grades Tab',\n },\n});\n\nexport default messages;\n"]}
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../../src/components/GradesView/messages.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,QAAQ,GAAG,cAAc,CAAC;IAC9B,iBAAiB,EAAE;QACjB,EAAE,EAAE,sCAAsC;QAC1C,cAAc,EAAE,yBAAyB;QACzC,WAAW,EAAE,0CAA0C;KACxD;IACD,oBAAoB,EAAE;QACpB,EAAE,EAAE,6CAA6C;QACjD,cAAc,EAAE,kCAAkC;QAClD,WAAW,EAAE,0CAA0C;KACxD;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,yCAAyC;QAC7C,cAAc,EAAE,gBAAgB;QAChC,WAAW,EAAE,+DAA+D;KAC7E;IACD,WAAW,EAAE;QACX,EAAE,EAAE,kCAAkC;QACtC,cAAc,EAAE,mDAAmD;QACnE,WAAW,EAAE,iDAAiD;KAC/D;CACF,CAAC,CAAC;AAEH,eAAe,QAAQ,CAAC","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n filterStepHeading: {\n id: 'gradebook.GradesView.filterHeadingV2',\n defaultMessage: 'Filter the Grade Report',\n description: 'Filter controls container heading string',\n },\n gradebookStepHeading: {\n id: 'gradebook.GradesView.gradebookStepHeadingV2',\n defaultMessage: 'View or Modify Individual Grades',\n description: 'Gradebook table container heading string',\n },\n unknownImportError: {\n id: 'gradebook.GradesView.unknownImportError',\n defaultMessage: 'Unknown error.',\n description: 'Shown when a grade import fails without the server saying why',\n },\n mastersHint: {\n id: 'gradebook.GradesView.mastersHint',\n defaultMessage: \"available for learners in the Master's track only\",\n description: 'Masters feature availability hint on Grades Tab',\n },\n});\n\nexport default messages;\n"]}
@@ -41,6 +41,9 @@ export interface GradebookUiContextValue {
41
41
  /** Toast shown after a successful bulk grade CSV import. */
42
42
  showImportSuccessToast: boolean;
43
43
  setShowImportSuccessToast: (value: boolean) => void;
44
+ /** Toast shown after a bulk grade CSV import that failed or applied nothing. */
45
+ showImportErrorToast: boolean;
46
+ setShowImportErrorToast: (value: boolean) => void;
44
47
  /** Active top-level tab: views.grades | views.bulkManagementHistory. */
45
48
  activeView: string;
46
49
  setActiveView: (value: string) => void;
@@ -19,6 +19,7 @@ export const GradebookUiProvider = ({ children }) => {
19
19
  const [gradeFormat, setGradeFormat] = useState('percent');
20
20
  const [showSuccess, setShowSuccess] = useState(false);
21
21
  const [showImportSuccessToast, setShowImportSuccessToast] = useState(false);
22
+ const [showImportErrorToast, setShowImportErrorToast] = useState(false);
22
23
  const [activeView, setActiveView] = useState(views.grades);
23
24
  // Filter sidebar animation state. This mirrors the old Redux `filterMenu` thunks:
24
25
  // `toggle` flips `open` one frame after starting the transition, and the sidebar's
@@ -82,6 +83,8 @@ export const GradebookUiProvider = ({ children }) => {
82
83
  const resetCsvUpload = useCallback(() => {
83
84
  setCsvUploadSuccess(false);
84
85
  setCsvUploadErrorMessages([]);
86
+ setShowImportSuccessToast(false);
87
+ setShowImportErrorToast(false);
85
88
  }, []);
86
89
  const markCsvUploadSuccess = useCallback(() => {
87
90
  setCsvUploadSuccess(true);
@@ -98,6 +101,8 @@ export const GradebookUiProvider = ({ children }) => {
98
101
  setShowSuccess,
99
102
  showImportSuccessToast,
100
103
  setShowImportSuccessToast,
104
+ showImportErrorToast,
105
+ setShowImportErrorToast,
101
106
  activeView,
102
107
  setActiveView,
103
108
  filterMenuOpen,
@@ -117,7 +122,7 @@ export const GradebookUiProvider = ({ children }) => {
117
122
  markCsvUploadSuccess,
118
123
  setCsvUploadErrors,
119
124
  }), [
120
- gradeFormat, showSuccess, showImportSuccessToast, activeView,
125
+ gradeFormat, showSuccess, showImportSuccessToast, showImportErrorToast, activeView,
121
126
  filterMenuOpen, filterMenuTransitioning,
122
127
  toggleFilterMenu, closeFilterMenu, handleFilterMenuTransitionEnd,
123
128
  modalState, setModalState, setModalStateFromTable, closeModal,
@@ -1 +1 @@
1
- {"version":3,"file":"gradebookUiContext.js","sourceRoot":"","sources":["../../src/data/gradebookUiContext.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, {\n createContext, useCallback, useContext, useEffect, useMemo, useRef, useState,\n} from 'react';\n\nimport { views, modalFieldKeys } from '@src/data/constants/app';\nimport { formatDateForDisplay } from '@src/data/formatUtils';\n\n/** Edit-grade modal state (was the Redux `app.modalState` slice). */\nexport interface ModalState {\n open: boolean;\n adjustedGradePossible: string | number;\n adjustedGradeValue: string | number;\n assignmentName: string;\n reasonForChange: string;\n todaysDate: string;\n updateModuleId: string | null;\n updateUserId: number | null;\n updateUserName: string | null;\n}\n\n/** Payload for opening the modal from a grade-table cell. */\nexport interface ModalStateFromTablePayload {\n subsection: {\n attempted?: boolean;\n score_possible?: number;\n subsection_name?: string;\n module_id?: string;\n };\n userEntry: {\n user_id?: number;\n username?: string;\n };\n}\n\n// Mirrors the Redux `app` reducer's `initialState.modalState`.\nconst initialModalState: ModalState = {\n open: false,\n adjustedGradePossible: '',\n adjustedGradeValue: 0,\n assignmentName: '',\n reasonForChange: '',\n todaysDate: '',\n updateModuleId: null,\n updateUserId: null,\n updateUserName: null,\n};\n\n/**\n * Client-side gradebook **UI** state, held in React (Context + useState).\n *\n * Replaces the non-filter UI bits of the old Redux `app` and `grades` slices:\n * grade format toggle, success banner, import toast, active view, filter-menu\n * open/transition, modal state, pagination cursor, and CSV-upload feedback.\n */\nexport interface GradebookUiContextValue {\n /** Grade display format: 'percent' | 'absolute'. */\n gradeFormat: string;\n setGradeFormat: (value: string) => void;\n /** Success banner shown after a grade override is saved. */\n showSuccess: boolean;\n setShowSuccess: (value: boolean) => void;\n /** Toast shown after a successful bulk grade CSV import. */\n showImportSuccessToast: boolean;\n setShowImportSuccessToast: (value: boolean) => void;\n /** Active top-level tab: views.grades | views.bulkManagementHistory. */\n activeView: string;\n setActiveView: (value: string) => void;\n /**\n * Filter sidebar open/close state, with the same two-phase transition the old\n * Redux `app.filterMenu` slice modeled (`open` + `transitioning`). Consumers\n * derive `isClosed`/`isOpening` from these two.\n */\n filterMenuOpen: boolean;\n filterMenuTransitioning: boolean;\n /** Toggle the sidebar (starts the transition, flips `open` on the next frame). */\n toggleFilterMenu: () => void;\n /** Close the sidebar if it is currently open (no-op otherwise). */\n closeFilterMenu: () => void;\n /** onTransitionEnd handler for the sidebar; ends the transition phase. */\n handleFilterMenuTransitionEnd: (event: React.TransitionEvent) => void;\n /** Edit-grade modal state (was Redux `app.modalState`). */\n modalState: ModalState;\n /** Merge a partial update into modalState (filtered to the known modal fields). */\n setModalState: (partial: Partial<ModalState>) => void;\n /** Open the modal seeded from a grade-table cell (subsection + user entry). */\n setModalStateFromTable: (payload: ModalStateFromTablePayload) => void;\n /** Reset the modal to its initial (closed) state. */\n closeModal: () => void;\n /**\n * Current grades pagination cursor: the opaque prev/next URL from the last\n * grades response, or null for the first (unpaged) page. Held here so the\n * `useGrades` query can key/refetch off it; filter changes reset it to null.\n */\n gradesPageEndpoint: string | null;\n setGradesPageEndpoint: (endpoint: string | null) => void;\n /**\n * Bulk grade CSV-upload result feedback shown by `BulkManagementAlerts` (was the\n * Redux `grades.bulkManagement` `uploadSuccess`/`errorMessages`).\n */\n csvUploadSuccess: boolean;\n csvUploadErrorMessages: string[];\n /** Reset the CSV-upload feedback (upload started). */\n resetCsvUpload: () => void;\n /** Mark the CSV upload succeeded. */\n markCsvUploadSuccess: () => void;\n /** Record CSV-upload error messages. */\n setCsvUploadErrors: (messages: string[]) => void;\n}\n\nconst GradebookUiContext = createContext<GradebookUiContextValue | undefined>(undefined);\n\ninterface GradebookUiProviderProps {\n children: React.ReactNode;\n}\n\nexport const GradebookUiProvider = ({ children }: GradebookUiProviderProps) => {\n const [gradeFormat, setGradeFormat] = useState<string>('percent');\n const [showSuccess, setShowSuccess] = useState<boolean>(false);\n const [showImportSuccessToast, setShowImportSuccessToast] = useState<boolean>(false);\n const [activeView, setActiveView] = useState<string>(views.grades);\n\n // Filter sidebar animation state. This mirrors the old Redux `filterMenu` thunks:\n // `toggle` flips `open` one frame after starting the transition, and the sidebar's\n // onTransitionEnd ends the transition. `close` reads the latest `open` via a ref\n // so it can be a stable callback.\n const [filterMenuOpen, setFilterMenuOpen] = useState<boolean>(false);\n const [filterMenuTransitioning, setFilterMenuTransitioning] = useState<boolean>(false);\n const filterMenuOpenRef = useRef<boolean>(filterMenuOpen);\n useEffect(() => {\n filterMenuOpenRef.current = filterMenuOpen;\n }, [filterMenuOpen]);\n\n const toggleFilterMenu = useCallback(() => {\n setFilterMenuTransitioning(true);\n window.requestAnimationFrame(() => {\n window.setTimeout(() => {\n setFilterMenuOpen((open) => !open);\n });\n });\n }, []);\n\n const closeFilterMenu = useCallback(() => {\n if (filterMenuOpenRef.current) {\n toggleFilterMenu();\n }\n }, [toggleFilterMenu]);\n\n const handleFilterMenuTransitionEnd = useCallback((event: React.TransitionEvent) => {\n if (event.currentTarget === event.target) {\n setFilterMenuTransitioning(false);\n }\n }, []);\n\n // Edit-grade modal state. These ops are a faithful port of the Redux `app`\n // reducer cases: `setModalState` merges a field-filtered partial, `closeModal`\n // resets to the initial state, and `setModalStateFromTable` opens the modal\n // seeded from the clicked grade cell.\n const [modalState, setModalStateValue] = useState<ModalState>(initialModalState);\n\n const setModalState = useCallback((partial: Partial<ModalState>) => {\n const filtered = (Object.keys(modalFieldKeys) as (keyof ModalState)[]).reduce(\n (obj, key) => (partial[key] !== undefined ? { ...obj, [key]: partial[key] } : obj),\n {} as Partial<ModalState>,\n );\n setModalStateValue((prev) => ({ ...prev, ...filtered }));\n }, []);\n\n const setModalStateFromTable = useCallback(\n ({ subsection, userEntry }: ModalStateFromTablePayload) => {\n setModalStateValue({\n open: true,\n adjustedGradeValue: '',\n reasonForChange: '',\n todaysDate: formatDateForDisplay(new Date()),\n adjustedGradePossible: subsection.attempted ? (subsection.score_possible ?? '') : '',\n assignmentName: `${subsection.subsection_name}`,\n updateModuleId: subsection.module_id ?? null,\n updateUserId: userEntry.user_id ?? null,\n updateUserName: userEntry.username ?? null,\n });\n },\n [],\n );\n\n const closeModal = useCallback(() => {\n setModalStateValue(initialModalState);\n }, []);\n\n // Grades pagination cursor (opaque prev/next URL, or null for the first page).\n const [gradesPageEndpoint, setGradesPageEndpoint] = useState<string | null>(null);\n\n // Bulk CSV-upload result feedback (was Redux `grades.bulkManagement`).\n const [csvUploadSuccess, setCsvUploadSuccess] = useState<boolean>(false);\n const [csvUploadErrorMessages, setCsvUploadErrorMessages] = useState<string[]>([]);\n const resetCsvUpload = useCallback(() => {\n setCsvUploadSuccess(false);\n setCsvUploadErrorMessages([]);\n }, []);\n const markCsvUploadSuccess = useCallback(() => {\n setCsvUploadSuccess(true);\n setCsvUploadErrorMessages([]);\n }, []);\n const setCsvUploadErrors = useCallback((messages: string[]) => {\n setCsvUploadSuccess(false);\n setCsvUploadErrorMessages(messages);\n }, []);\n\n const value = useMemo<GradebookUiContextValue>(\n () => ({\n gradeFormat,\n setGradeFormat,\n showSuccess,\n setShowSuccess,\n showImportSuccessToast,\n setShowImportSuccessToast,\n activeView,\n setActiveView,\n filterMenuOpen,\n filterMenuTransitioning,\n toggleFilterMenu,\n closeFilterMenu,\n handleFilterMenuTransitionEnd,\n modalState,\n setModalState,\n setModalStateFromTable,\n closeModal,\n gradesPageEndpoint,\n setGradesPageEndpoint,\n csvUploadSuccess,\n csvUploadErrorMessages,\n resetCsvUpload,\n markCsvUploadSuccess,\n setCsvUploadErrors,\n }),\n [\n gradeFormat, showSuccess, showImportSuccessToast, activeView,\n filterMenuOpen, filterMenuTransitioning,\n toggleFilterMenu, closeFilterMenu, handleFilterMenuTransitionEnd,\n modalState, setModalState, setModalStateFromTable, closeModal,\n gradesPageEndpoint,\n csvUploadSuccess, csvUploadErrorMessages,\n resetCsvUpload, markCsvUploadSuccess, setCsvUploadErrors,\n ],\n );\n\n return (\n <GradebookUiContext.Provider value={value}>\n {children}\n </GradebookUiContext.Provider>\n );\n};\n\n/**\n * useGradebookUi()\n * Accessor for the gradebook UI-state context. Throws if used outside a\n * <GradebookUiProvider>.\n */\nexport const useGradebookUi = (): GradebookUiContextValue => {\n const context = useContext(GradebookUiContext);\n if (context === undefined) {\n throw new Error('useGradebookUi must be used within a GradebookUiProvider');\n }\n return context;\n};\n\nexport default GradebookUiContext;\n"]}
1
+ {"version":3,"file":"gradebookUiContext.js","sourceRoot":"","sources":["../../src/data/gradebookUiContext.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, {\n createContext, useCallback, useContext, useEffect, useMemo, useRef, useState,\n} from 'react';\n\nimport { views, modalFieldKeys } from '@src/data/constants/app';\nimport { formatDateForDisplay } from '@src/data/formatUtils';\n\n/** Edit-grade modal state (was the Redux `app.modalState` slice). */\nexport interface ModalState {\n open: boolean;\n adjustedGradePossible: string | number;\n adjustedGradeValue: string | number;\n assignmentName: string;\n reasonForChange: string;\n todaysDate: string;\n updateModuleId: string | null;\n updateUserId: number | null;\n updateUserName: string | null;\n}\n\n/** Payload for opening the modal from a grade-table cell. */\nexport interface ModalStateFromTablePayload {\n subsection: {\n attempted?: boolean;\n score_possible?: number;\n subsection_name?: string;\n module_id?: string;\n };\n userEntry: {\n user_id?: number;\n username?: string;\n };\n}\n\n// Mirrors the Redux `app` reducer's `initialState.modalState`.\nconst initialModalState: ModalState = {\n open: false,\n adjustedGradePossible: '',\n adjustedGradeValue: 0,\n assignmentName: '',\n reasonForChange: '',\n todaysDate: '',\n updateModuleId: null,\n updateUserId: null,\n updateUserName: null,\n};\n\n/**\n * Client-side gradebook **UI** state, held in React (Context + useState).\n *\n * Replaces the non-filter UI bits of the old Redux `app` and `grades` slices:\n * grade format toggle, success banner, import toast, active view, filter-menu\n * open/transition, modal state, pagination cursor, and CSV-upload feedback.\n */\nexport interface GradebookUiContextValue {\n /** Grade display format: 'percent' | 'absolute'. */\n gradeFormat: string;\n setGradeFormat: (value: string) => void;\n /** Success banner shown after a grade override is saved. */\n showSuccess: boolean;\n setShowSuccess: (value: boolean) => void;\n /** Toast shown after a successful bulk grade CSV import. */\n showImportSuccessToast: boolean;\n setShowImportSuccessToast: (value: boolean) => void;\n /** Toast shown after a bulk grade CSV import that failed or applied nothing. */\n showImportErrorToast: boolean;\n setShowImportErrorToast: (value: boolean) => void;\n /** Active top-level tab: views.grades | views.bulkManagementHistory. */\n activeView: string;\n setActiveView: (value: string) => void;\n /**\n * Filter sidebar open/close state, with the same two-phase transition the old\n * Redux `app.filterMenu` slice modeled (`open` + `transitioning`). Consumers\n * derive `isClosed`/`isOpening` from these two.\n */\n filterMenuOpen: boolean;\n filterMenuTransitioning: boolean;\n /** Toggle the sidebar (starts the transition, flips `open` on the next frame). */\n toggleFilterMenu: () => void;\n /** Close the sidebar if it is currently open (no-op otherwise). */\n closeFilterMenu: () => void;\n /** onTransitionEnd handler for the sidebar; ends the transition phase. */\n handleFilterMenuTransitionEnd: (event: React.TransitionEvent) => void;\n /** Edit-grade modal state (was Redux `app.modalState`). */\n modalState: ModalState;\n /** Merge a partial update into modalState (filtered to the known modal fields). */\n setModalState: (partial: Partial<ModalState>) => void;\n /** Open the modal seeded from a grade-table cell (subsection + user entry). */\n setModalStateFromTable: (payload: ModalStateFromTablePayload) => void;\n /** Reset the modal to its initial (closed) state. */\n closeModal: () => void;\n /**\n * Current grades pagination cursor: the opaque prev/next URL from the last\n * grades response, or null for the first (unpaged) page. Held here so the\n * `useGrades` query can key/refetch off it; filter changes reset it to null.\n */\n gradesPageEndpoint: string | null;\n setGradesPageEndpoint: (endpoint: string | null) => void;\n /**\n * Bulk grade CSV-upload result feedback shown by `BulkManagementAlerts` (was the\n * Redux `grades.bulkManagement` `uploadSuccess`/`errorMessages`).\n */\n csvUploadSuccess: boolean;\n csvUploadErrorMessages: string[];\n /** Reset the CSV-upload feedback (upload started). */\n resetCsvUpload: () => void;\n /** Mark the CSV upload succeeded. */\n markCsvUploadSuccess: () => void;\n /** Record CSV-upload error messages. */\n setCsvUploadErrors: (messages: string[]) => void;\n}\n\nconst GradebookUiContext = createContext<GradebookUiContextValue | undefined>(undefined);\n\ninterface GradebookUiProviderProps {\n children: React.ReactNode;\n}\n\nexport const GradebookUiProvider = ({ children }: GradebookUiProviderProps) => {\n const [gradeFormat, setGradeFormat] = useState<string>('percent');\n const [showSuccess, setShowSuccess] = useState<boolean>(false);\n const [showImportSuccessToast, setShowImportSuccessToast] = useState<boolean>(false);\n const [showImportErrorToast, setShowImportErrorToast] = useState<boolean>(false);\n const [activeView, setActiveView] = useState<string>(views.grades);\n\n // Filter sidebar animation state. This mirrors the old Redux `filterMenu` thunks:\n // `toggle` flips `open` one frame after starting the transition, and the sidebar's\n // onTransitionEnd ends the transition. `close` reads the latest `open` via a ref\n // so it can be a stable callback.\n const [filterMenuOpen, setFilterMenuOpen] = useState<boolean>(false);\n const [filterMenuTransitioning, setFilterMenuTransitioning] = useState<boolean>(false);\n const filterMenuOpenRef = useRef<boolean>(filterMenuOpen);\n useEffect(() => {\n filterMenuOpenRef.current = filterMenuOpen;\n }, [filterMenuOpen]);\n\n const toggleFilterMenu = useCallback(() => {\n setFilterMenuTransitioning(true);\n window.requestAnimationFrame(() => {\n window.setTimeout(() => {\n setFilterMenuOpen((open) => !open);\n });\n });\n }, []);\n\n const closeFilterMenu = useCallback(() => {\n if (filterMenuOpenRef.current) {\n toggleFilterMenu();\n }\n }, [toggleFilterMenu]);\n\n const handleFilterMenuTransitionEnd = useCallback((event: React.TransitionEvent) => {\n if (event.currentTarget === event.target) {\n setFilterMenuTransitioning(false);\n }\n }, []);\n\n // Edit-grade modal state. These ops are a faithful port of the Redux `app`\n // reducer cases: `setModalState` merges a field-filtered partial, `closeModal`\n // resets to the initial state, and `setModalStateFromTable` opens the modal\n // seeded from the clicked grade cell.\n const [modalState, setModalStateValue] = useState<ModalState>(initialModalState);\n\n const setModalState = useCallback((partial: Partial<ModalState>) => {\n const filtered = (Object.keys(modalFieldKeys) as (keyof ModalState)[]).reduce(\n (obj, key) => (partial[key] !== undefined ? { ...obj, [key]: partial[key] } : obj),\n {} as Partial<ModalState>,\n );\n setModalStateValue((prev) => ({ ...prev, ...filtered }));\n }, []);\n\n const setModalStateFromTable = useCallback(\n ({ subsection, userEntry }: ModalStateFromTablePayload) => {\n setModalStateValue({\n open: true,\n adjustedGradeValue: '',\n reasonForChange: '',\n todaysDate: formatDateForDisplay(new Date()),\n adjustedGradePossible: subsection.attempted ? (subsection.score_possible ?? '') : '',\n assignmentName: `${subsection.subsection_name}`,\n updateModuleId: subsection.module_id ?? null,\n updateUserId: userEntry.user_id ?? null,\n updateUserName: userEntry.username ?? null,\n });\n },\n [],\n );\n\n const closeModal = useCallback(() => {\n setModalStateValue(initialModalState);\n }, []);\n\n // Grades pagination cursor (opaque prev/next URL, or null for the first page).\n const [gradesPageEndpoint, setGradesPageEndpoint] = useState<string | null>(null);\n\n // Bulk CSV-upload result feedback (was Redux `grades.bulkManagement`).\n const [csvUploadSuccess, setCsvUploadSuccess] = useState<boolean>(false);\n const [csvUploadErrorMessages, setCsvUploadErrorMessages] = useState<string[]>([]);\n const resetCsvUpload = useCallback(() => {\n setCsvUploadSuccess(false);\n setCsvUploadErrorMessages([]);\n setShowImportSuccessToast(false);\n setShowImportErrorToast(false);\n }, []);\n const markCsvUploadSuccess = useCallback(() => {\n setCsvUploadSuccess(true);\n setCsvUploadErrorMessages([]);\n }, []);\n const setCsvUploadErrors = useCallback((messages: string[]) => {\n setCsvUploadSuccess(false);\n setCsvUploadErrorMessages(messages);\n }, []);\n\n const value = useMemo<GradebookUiContextValue>(\n () => ({\n gradeFormat,\n setGradeFormat,\n showSuccess,\n setShowSuccess,\n showImportSuccessToast,\n setShowImportSuccessToast,\n showImportErrorToast,\n setShowImportErrorToast,\n activeView,\n setActiveView,\n filterMenuOpen,\n filterMenuTransitioning,\n toggleFilterMenu,\n closeFilterMenu,\n handleFilterMenuTransitionEnd,\n modalState,\n setModalState,\n setModalStateFromTable,\n closeModal,\n gradesPageEndpoint,\n setGradesPageEndpoint,\n csvUploadSuccess,\n csvUploadErrorMessages,\n resetCsvUpload,\n markCsvUploadSuccess,\n setCsvUploadErrors,\n }),\n [\n gradeFormat, showSuccess, showImportSuccessToast, showImportErrorToast, activeView,\n filterMenuOpen, filterMenuTransitioning,\n toggleFilterMenu, closeFilterMenu, handleFilterMenuTransitionEnd,\n modalState, setModalState, setModalStateFromTable, closeModal,\n gradesPageEndpoint,\n csvUploadSuccess, csvUploadErrorMessages,\n resetCsvUpload, markCsvUploadSuccess, setCsvUploadErrors,\n ],\n );\n\n return (\n <GradebookUiContext.Provider value={value}>\n {children}\n </GradebookUiContext.Provider>\n );\n};\n\n/**\n * useGradebookUi()\n * Accessor for the gradebook UI-state context. Throws if used outside a\n * <GradebookUiProvider>.\n */\nexport const useGradebookUi = (): GradebookUiContextValue => {\n const context = useContext(GradebookUiContext);\n if (context === undefined) {\n throw new Error('useGradebookUi must be used within a GradebookUiProvider');\n }\n return context;\n};\n\nexport default GradebookUiContext;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openedx/frontend-app-gradebook",
3
- "version": "2.0.0-alpha.7",
3
+ "version": "2.0.0-alpha.8",
4
4
  "author": "Open edX",
5
5
  "license": "AGPL-3.0",
6
6
  "description": "edx editable gradebook-ui to manipulate grade overrides on subsections",
@@ -1,10 +0,0 @@
1
- export function useImportSuccessToastData(): {
2
- action: {
3
- label: string;
4
- onClick: () => void;
5
- };
6
- onClose: () => void;
7
- show: boolean;
8
- description: string;
9
- };
10
- export default useImportSuccessToastData;
@@ -1,31 +0,0 @@
1
- import { useIntl } from '@openedx/frontend-base';
2
- import { useGradebookUi } from '../../../data/gradebookUiContext';
3
- import { views } from '../../../data/constants/app';
4
- import messages from './messages';
5
- /**
6
- * <ImportSuccessToast />
7
- * Toast component triggered by successful grade upload.
8
- * Provides a link to view the Bulk Management History tab.
9
- */
10
- export const useImportSuccessToastData = () => {
11
- const { formatMessage } = useIntl();
12
- const { showImportSuccessToast: show, setActiveView, setShowImportSuccessToast } = useGradebookUi();
13
- const onClose = () => {
14
- setShowImportSuccessToast(false);
15
- };
16
- const handleShowHistoryView = () => {
17
- setActiveView(views.bulkManagementHistory);
18
- setShowImportSuccessToast(false);
19
- };
20
- return {
21
- action: {
22
- label: formatMessage(messages.showHistoryViewBtn),
23
- onClick: handleShowHistoryView,
24
- },
25
- onClose,
26
- show,
27
- description: formatMessage(messages.description),
28
- };
29
- };
30
- export default useImportSuccessToastData;
31
- //# sourceMappingURL=hooks.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../../src/components/GradesView/ImportSuccessToast/hooks.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEjD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAChD,OAAO,QAAQ,MAAM,YAAY,CAAC;AAElC;;;;GAIG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,EAAE;IAC5C,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAC;IAEpC,MAAM,EAAE,sBAAsB,EAAE,IAAI,EAAE,aAAa,EAAE,yBAAyB,EAAE,GAAG,cAAc,EAAE,CAAC;IAEpG,MAAM,OAAO,GAAG,GAAG,EAAE;QACnB,yBAAyB,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC,CAAC;IAEF,MAAM,qBAAqB,GAAG,GAAG,EAAE;QACjC,aAAa,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;QAC3C,yBAAyB,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC,CAAC;IAEF,OAAO;QACL,MAAM,EAAE;YACN,KAAK,EAAE,aAAa,CAAC,QAAQ,CAAC,kBAAkB,CAAC;YACjD,OAAO,EAAE,qBAAqB;SAC/B;QACD,OAAO;QACP,IAAI;QACJ,WAAW,EAAE,aAAa,CAAC,QAAQ,CAAC,WAAW,CAAC;KACjD,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,yBAAyB,CAAC","sourcesContent":["import { useIntl } from '@openedx/frontend-base';\n\nimport { useGradebookUi } from '@src/data/gradebookUiContext';\nimport { views } from '@src/data/constants/app';\nimport messages from './messages';\n\n/**\n * <ImportSuccessToast />\n * Toast component triggered by successful grade upload.\n * Provides a link to view the Bulk Management History tab.\n */\nexport const useImportSuccessToastData = () => {\n const { formatMessage } = useIntl();\n\n const { showImportSuccessToast: show, setActiveView, setShowImportSuccessToast } = useGradebookUi();\n\n const onClose = () => {\n setShowImportSuccessToast(false);\n };\n\n const handleShowHistoryView = () => {\n setActiveView(views.bulkManagementHistory);\n setShowImportSuccessToast(false);\n };\n\n return {\n action: {\n label: formatMessage(messages.showHistoryViewBtn),\n onClick: handleShowHistoryView,\n },\n onClose,\n show,\n description: formatMessage(messages.description),\n };\n};\n\nexport default useImportSuccessToastData;\n"]}
@@ -1,5 +0,0 @@
1
- export function ImportSuccessToast(): import("react").JSX.Element;
2
- export namespace ImportSuccessToast {
3
- let propTypes: {};
4
- }
5
- export default ImportSuccessToast;
@@ -1,15 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Toast } from '@openedx/paragon';
3
- import useImportSuccessToastData from './hooks';
4
- /**
5
- * <ImportSuccessToast />
6
- * Toast component triggered by successful grade upload.
7
- * Provides a link to view the Bulk Management History tab.
8
- */
9
- export const ImportSuccessToast = () => {
10
- const { action, onClose, show, description, } = useImportSuccessToastData();
11
- return (_jsx(Toast, { action, onClose, show, children: description }));
12
- };
13
- ImportSuccessToast.propTypes = {};
14
- export default ImportSuccessToast;
15
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/components/GradesView/ImportSuccessToast/index.jsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEzC,OAAO,yBAAyB,MAAM,SAAS,CAAC;AAEhD;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,EAAE;IACrC,MAAM,EACJ,MAAM,EACN,OAAO,EACP,IAAI,EACJ,WAAW,GACZ,GAAG,yBAAyB,EAAE,CAAC;IAChC,OAAO,CACL,KAAC,KAAK,IAAO,MAAM,EAAE,OAAO,EAAE,IAAI,YAC/B,WAAW,GACN,CACT,CAAC;AACJ,CAAC,CAAC;AAEF,kBAAkB,CAAC,SAAS,GAAG,EAAE,CAAC;AAElC,eAAe,kBAAkB,CAAC","sourcesContent":["import { Toast } from '@openedx/paragon';\n\nimport useImportSuccessToastData from './hooks';\n\n/**\n * <ImportSuccessToast />\n * Toast component triggered by successful grade upload.\n * Provides a link to view the Bulk Management History tab.\n */\nexport const ImportSuccessToast = () => {\n const {\n action,\n onClose,\n show,\n description,\n } = useImportSuccessToastData();\n return (\n <Toast {...{ action, onClose, show }}>\n {description}\n </Toast>\n );\n};\n\nImportSuccessToast.propTypes = {};\n\nexport default ImportSuccessToast;\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../../../src/components/GradesView/ImportSuccessToast/messages.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,QAAQ,GAAG,cAAc,CAAC;IAC9B,WAAW,EAAE;QACX,EAAE,EAAE,qDAAqD;QACzD,cAAc,EAAE,wDAAwD;QACxE,WAAW,EAAE,wDAAwD;KACtE;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,4DAA4D;QAChE,cAAc,EAAE,mBAAmB;QACnC,WAAW,EAAE,4EAA4E;KAC1F;CACF,CAAC,CAAC;AAEH,eAAe,QAAQ,CAAC","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n description: {\n id: 'gradebook.GradesView.ImportSuccessToast.description',\n defaultMessage: 'Import Successful! Grades will be updated momentarily.',\n description: 'A message congratulating a successful Import of grades',\n },\n showHistoryViewBtn: {\n id: 'gradebook.GradesView.ImportSuccessToast.showHistoryViewBtn',\n defaultMessage: 'View Activity Log',\n description: 'The text on a button that loads a view of the Bulk Management Activity Log',\n },\n});\n\nexport default messages;\n"]}