@openedx/frontend-app-instructor-dashboard 1.0.0-alpha.26 → 1.0.0-alpha.27

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 (103) hide show
  1. package/dist/certificates/CertificatesPage.d.ts +1 -1
  2. package/dist/certificates/CertificatesPage.js +107 -55
  3. package/dist/certificates/CertificatesPage.js.map +1 -1
  4. package/dist/certificates/CertificatesPage.scss +9 -0
  5. package/dist/certificates/components/CertificateTable.d.ts +1 -1
  6. package/dist/certificates/components/CertificateTable.js +9 -6
  7. package/dist/certificates/components/CertificateTable.js.map +1 -1
  8. package/dist/certificates/components/CertificatesPageHeader.js +3 -3
  9. package/dist/certificates/components/CertificatesPageHeader.js.map +1 -1
  10. package/dist/certificates/components/CertificatesToolbar.d.ts +2 -1
  11. package/dist/certificates/components/CertificatesToolbar.js +23 -3
  12. package/dist/certificates/components/CertificatesToolbar.js.map +1 -1
  13. package/dist/certificates/components/DisableCertificatesModal.js +1 -1
  14. package/dist/certificates/components/DisableCertificatesModal.js.map +1 -1
  15. package/dist/certificates/components/FilterDropdown.d.ts +1 -1
  16. package/dist/certificates/components/FilterDropdown.js +2 -2
  17. package/dist/certificates/components/FilterDropdown.js.map +1 -1
  18. package/dist/certificates/components/GenerationHistoryTable.d.ts +2 -2
  19. package/dist/certificates/components/GenerationHistoryTable.js +3 -18
  20. package/dist/certificates/components/GenerationHistoryTable.js.map +1 -1
  21. package/dist/certificates/components/GrantExceptionsModal.d.ts +1 -1
  22. package/dist/certificates/components/GrantExceptionsModal.js +2 -2
  23. package/dist/certificates/components/GrantExceptionsModal.js.map +1 -1
  24. package/dist/certificates/components/InvalidateCertificateModal.d.ts +1 -1
  25. package/dist/certificates/components/InvalidateCertificateModal.js +2 -2
  26. package/dist/certificates/components/InvalidateCertificateModal.js.map +1 -1
  27. package/dist/certificates/components/IssuedCertificatesTab.d.ts +1 -1
  28. package/dist/certificates/components/IssuedCertificatesTab.js +2 -2
  29. package/dist/certificates/components/IssuedCertificatesTab.js.map +1 -1
  30. package/dist/certificates/components/LearnerActionModal.d.ts +1 -1
  31. package/dist/certificates/components/LearnerActionModal.js +7 -2
  32. package/dist/certificates/components/LearnerActionModal.js.map +1 -1
  33. package/dist/certificates/components/RemoveExceptionModal.d.ts +9 -0
  34. package/dist/certificates/components/RemoveExceptionModal.js +10 -0
  35. package/dist/certificates/components/RemoveExceptionModal.js.map +1 -0
  36. package/dist/certificates/components/RemoveInvalidationModal.js +1 -1
  37. package/dist/certificates/components/RemoveInvalidationModal.js.map +1 -1
  38. package/dist/certificates/data/api.d.ts +17 -3
  39. package/dist/certificates/data/api.js +60 -8
  40. package/dist/certificates/data/api.js.map +1 -1
  41. package/dist/certificates/data/apiHook.d.ts +25 -5
  42. package/dist/certificates/data/apiHook.js +30 -2
  43. package/dist/certificates/data/apiHook.js.map +1 -1
  44. package/dist/certificates/data/queryKeys.d.ts +2 -1
  45. package/dist/certificates/data/queryKeys.js +1 -0
  46. package/dist/certificates/data/queryKeys.js.map +1 -1
  47. package/dist/certificates/messages.d.ts +30 -0
  48. package/dist/certificates/messages.js +30 -0
  49. package/dist/certificates/messages.js.map +1 -1
  50. package/dist/certificates/types.d.ts +7 -2
  51. package/dist/certificates/types.js.map +1 -1
  52. package/dist/certificates/utils/filterUtils.d.ts +1 -1
  53. package/dist/certificates/utils/filterUtils.js +1 -1
  54. package/dist/certificates/utils/filterUtils.js.map +1 -1
  55. package/dist/certificates/utils/index.d.ts +2 -2
  56. package/dist/certificates/utils/index.js +1 -1
  57. package/dist/certificates/utils/index.js.map +1 -1
  58. package/dist/components/SpecifyLearnerField.js +10 -4
  59. package/dist/components/SpecifyLearnerField.js.map +1 -1
  60. package/dist/components/messages.d.ts +5 -0
  61. package/dist/components/messages.js +6 -1
  62. package/dist/components/messages.js.map +1 -1
  63. package/dist/courseInfo/types.d.ts +1 -0
  64. package/dist/courseInfo/types.js.map +1 -1
  65. package/dist/specialExams/SpecialExamsPage.js +6 -2
  66. package/dist/specialExams/SpecialExamsPage.js.map +1 -1
  67. package/dist/specialExams/components/AddAllowanceModal.d.ts +6 -0
  68. package/dist/specialExams/components/AddAllowanceModal.js +84 -0
  69. package/dist/specialExams/components/AddAllowanceModal.js.map +1 -0
  70. package/dist/specialExams/components/Allowances.js +28 -2
  71. package/dist/specialExams/components/Allowances.js.map +1 -1
  72. package/dist/specialExams/components/AllowancesList.d.ts +8 -0
  73. package/dist/specialExams/components/AllowancesList.js +58 -0
  74. package/dist/specialExams/components/AllowancesList.js.map +1 -0
  75. package/dist/specialExams/components/AttemptsList.js +8 -7
  76. package/dist/specialExams/components/AttemptsList.js.map +1 -1
  77. package/dist/specialExams/components/DeleteAllowanceModal.d.ts +8 -0
  78. package/dist/specialExams/components/DeleteAllowanceModal.js +29 -0
  79. package/dist/specialExams/components/DeleteAllowanceModal.js.map +1 -0
  80. package/dist/specialExams/components/EditAllowanceModal.d.ts +8 -0
  81. package/dist/specialExams/components/EditAllowanceModal.js +62 -0
  82. package/dist/specialExams/components/EditAllowanceModal.js.map +1 -0
  83. package/dist/specialExams/constants.d.ts +43 -0
  84. package/dist/specialExams/constants.js +19 -0
  85. package/dist/specialExams/constants.js.map +1 -0
  86. package/dist/specialExams/data/api.d.ts +5 -1
  87. package/dist/specialExams/data/api.js +28 -2
  88. package/dist/specialExams/data/api.js.map +1 -1
  89. package/dist/specialExams/data/apiHook.d.ts +5 -1
  90. package/dist/specialExams/data/apiHook.js +30 -2
  91. package/dist/specialExams/data/apiHook.js.map +1 -1
  92. package/dist/specialExams/data/queryKeys.d.ts +4 -1
  93. package/dist/specialExams/data/queryKeys.js +4 -1
  94. package/dist/specialExams/data/queryKeys.js.map +1 -1
  95. package/dist/specialExams/messages.d.ts +165 -0
  96. package/dist/specialExams/messages.js +165 -0
  97. package/dist/specialExams/messages.js.map +1 -1
  98. package/dist/specialExams/types.d.ts +55 -4
  99. package/dist/specialExams/types.js.map +1 -1
  100. package/package.json +1 -1
  101. package/dist/certificates/data/dummyData.d.ts +0 -2
  102. package/dist/certificates/data/dummyData.js +0 -234
  103. package/dist/certificates/data/dummyData.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/courseInfo/types.ts"],"names":[],"mappings":"","sourcesContent":["import { TabProps } from '@src/instructorNav/InstructorNav';\n\nexport interface CourseInfoResponse {\n courseId: string,\n displayName: string,\n courseNumber: string,\n courseRun: string,\n enrollmentCounts: EnrollmentCounts,\n start: string | null,\n end: string | null,\n tabs?: TabProps[],\n totalEnrollment: number,\n studioUrl: string,\n pacing: string,\n org?: string,\n numSections: number,\n hasStarted: boolean,\n hasEnded: boolean,\n enrollmentEnd: string | null,\n enrollmentStart: string | null,\n gradeCutoffs: string | null,\n staffCount: number,\n learnerCount: number,\n permissions: {\n admin: boolean,\n dataResearcher: boolean,\n [key: string]: boolean,\n },\n gradebookUrl: string,\n studioGradingUrl?: string,\n adminConsoleUrl: string | null,\n}\n\ninterface EnrollmentCounts extends Record<string, number> {\n total: number,\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/courseInfo/types.ts"],"names":[],"mappings":"","sourcesContent":["import { TabProps } from '@src/instructorNav/InstructorNav';\n\nexport interface CourseInfoResponse {\n courseId: string,\n displayName: string,\n courseNumber: string,\n courseRun: string,\n enrollmentCounts: EnrollmentCounts,\n start: string | null,\n end: string | null,\n tabs?: TabProps[],\n totalEnrollment: number,\n studioUrl: string,\n pacing: string,\n org?: string,\n numSections: number,\n hasStarted: boolean,\n hasEnded: boolean,\n enrollmentEnd: string | null,\n enrollmentStart: string | null,\n gradeCutoffs: string | null,\n staffCount: number,\n learnerCount: number,\n permissions: {\n admin: boolean,\n dataResearcher: boolean,\n [key: string]: boolean,\n },\n gradebookUrl: string,\n studioGradingUrl?: string,\n certificatesEnabled?: boolean,\n adminConsoleUrl: string | null,\n}\n\ninterface EnrollmentCounts extends Record<string, number> {\n total: number,\n}\n"]}
@@ -5,10 +5,14 @@ import { Button, ButtonGroup, Card } from '@openedx/paragon';
5
5
  import messages from './messages';
6
6
  import Allowances from './components/Allowances';
7
7
  import AttemptsList from './components/AttemptsList';
8
+ const SPECIAL_EXAMS_TAB = {
9
+ ATTEMPTS: 'attempts',
10
+ ALLOWANCES: 'allowances',
11
+ };
8
12
  const SpecialExamsPage = () => {
9
13
  const intl = useIntl();
10
- const [selectedTab, setSelectedTab] = useState('attempts');
11
- return (_jsxs(_Fragment, { children: [_jsx("h3", { className: "text-primary-700", children: intl.formatMessage(messages.specialExamsTitle) }), _jsxs(Card, { className: "bg-light-200 mt-4.5", children: [_jsxs(ButtonGroup, { className: "d-block mx-4 mt-4", children: [_jsx(Button, { variant: selectedTab === 'attempts' ? 'primary' : 'outline-primary', onClick: () => setSelectedTab('attempts'), children: intl.formatMessage(messages.examAttempts) }), _jsx(Button, { variant: selectedTab === 'allowances' ? 'primary' : 'outline-primary', onClick: () => setSelectedTab('allowances'), children: intl.formatMessage(messages.allowances) })] }), selectedTab === 'attempts' ? _jsx(AttemptsList, {}) : _jsx(Allowances, {})] })] }));
14
+ const [selectedTab, setSelectedTab] = useState(SPECIAL_EXAMS_TAB.ATTEMPTS);
15
+ return (_jsxs(_Fragment, { children: [_jsx("h3", { className: "text-primary-700", children: intl.formatMessage(messages.specialExamsTitle) }), _jsxs(Card, { className: "bg-light-200 mt-4.5", children: [_jsxs(ButtonGroup, { className: "d-block mx-4 mt-4", children: [_jsx(Button, { variant: selectedTab === SPECIAL_EXAMS_TAB.ATTEMPTS ? 'primary' : 'outline-primary', onClick: () => setSelectedTab(SPECIAL_EXAMS_TAB.ATTEMPTS), children: intl.formatMessage(messages.examAttempts) }), _jsx(Button, { variant: selectedTab === SPECIAL_EXAMS_TAB.ALLOWANCES ? 'primary' : 'outline-primary', onClick: () => setSelectedTab(SPECIAL_EXAMS_TAB.ALLOWANCES), children: intl.formatMessage(messages.allowances) })] }), selectedTab === SPECIAL_EXAMS_TAB.ATTEMPTS ? _jsx(AttemptsList, {}) : _jsx(Allowances, {})] })] }));
12
16
  };
13
17
  export default SpecialExamsPage;
14
18
  //# sourceMappingURL=SpecialExamsPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SpecialExamsPage.js","sourceRoot":"","sources":["../../src/specialExams/SpecialExamsPage.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,QAAQ,MAAM,YAAY,CAAC;AAClC,OAAO,UAAU,MAAM,yBAAyB,CAAC;AACjD,OAAO,YAAY,MAAM,2BAA2B,CAAC;AAErD,MAAM,gBAAgB,GAAG,GAAG,EAAE;IAC5B,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAA4B,UAAU,CAAC,CAAC;IAEtF,OAAO,CACL,8BACE,aAAI,SAAS,EAAC,kBAAkB,YAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,iBAAiB,CAAC,GAAM,EACtF,MAAC,IAAI,IAAC,SAAS,EAAC,qBAAqB,aACnC,MAAC,WAAW,IAAC,SAAS,EAAC,mBAAmB,aACxC,KAAC,MAAM,IAAC,OAAO,EAAE,WAAW,KAAK,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,iBAAiB,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,UAAU,CAAC,YAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,YAAY,CAAC,GAAU,EAC5K,KAAC,MAAM,IAAC,OAAO,EAAE,WAAW,KAAK,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,iBAAiB,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,YAAY,CAAC,YAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAU,IAClK,EAEZ,WAAW,KAAK,UAAU,CAAC,CAAC,CAAC,KAAC,YAAY,KAAG,CAAC,CAAC,CAAC,KAAC,UAAU,KAAG,IAE3D,IACN,CACJ,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,gBAAgB,CAAC","sourcesContent":["import { useState } from 'react';\nimport { useIntl } from '@openedx/frontend-base';\nimport { Button, ButtonGroup, Card } from '@openedx/paragon';\nimport messages from './messages';\nimport Allowances from './components/Allowances';\nimport AttemptsList from './components/AttemptsList';\n\nconst SpecialExamsPage = () => {\n const intl = useIntl();\n const [selectedTab, setSelectedTab] = useState<'attempts' | 'allowances'>('attempts');\n\n return (\n <>\n <h3 className=\"text-primary-700\">{intl.formatMessage(messages.specialExamsTitle)}</h3>\n <Card className=\"bg-light-200 mt-4.5\">\n <ButtonGroup className=\"d-block mx-4 mt-4\">\n <Button variant={selectedTab === 'attempts' ? 'primary' : 'outline-primary'} onClick={() => setSelectedTab('attempts')}>{intl.formatMessage(messages.examAttempts)}</Button>\n <Button variant={selectedTab === 'allowances' ? 'primary' : 'outline-primary'} onClick={() => setSelectedTab('allowances')}>{intl.formatMessage(messages.allowances)}</Button>\n </ButtonGroup>\n {\n selectedTab === 'attempts' ? <AttemptsList /> : <Allowances />\n }\n </Card>\n </>\n );\n};\n\nexport default SpecialExamsPage;\n"]}
1
+ {"version":3,"file":"SpecialExamsPage.js","sourceRoot":"","sources":["../../src/specialExams/SpecialExamsPage.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,QAAQ,MAAM,YAAY,CAAC;AAClC,OAAO,UAAU,MAAM,yBAAyB,CAAC;AACjD,OAAO,YAAY,MAAM,2BAA2B,CAAC;AAErD,MAAM,iBAAiB,GAAG;IACxB,QAAQ,EAAE,UAAU;IACpB,UAAU,EAAE,YAAY;CACzB,CAAC;AAEF,MAAM,gBAAgB,GAAG,GAAG,EAAE;IAC5B,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAA6D,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAEvI,OAAO,CACL,8BACE,aAAI,SAAS,EAAC,kBAAkB,YAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,iBAAiB,CAAC,GAAM,EACtF,MAAC,IAAI,IAAC,SAAS,EAAC,qBAAqB,aACnC,MAAC,WAAW,IAAC,SAAS,EAAC,mBAAmB,aACxC,KAAC,MAAM,IACL,OAAO,EAAE,WAAW,KAAK,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,iBAAiB,EACnF,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,iBAAiB,CAAC,QAAQ,CAAC,YACzD,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,YAAY,CAAC,GAClC,EACT,KAAC,MAAM,IACL,OAAO,EAAE,WAAW,KAAK,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,iBAAiB,EACrF,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,iBAAiB,CAAC,UAAU,CAAC,YAC3D,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,UAAU,CAAC,GAChC,IACG,EAEZ,WAAW,KAAK,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAC,YAAY,KAAG,CAAC,CAAC,CAAC,KAAC,UAAU,KAAG,IAE3E,IACN,CACJ,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,gBAAgB,CAAC","sourcesContent":["import { useState } from 'react';\nimport { useIntl } from '@openedx/frontend-base';\nimport { Button, ButtonGroup, Card } from '@openedx/paragon';\nimport messages from './messages';\nimport Allowances from './components/Allowances';\nimport AttemptsList from './components/AttemptsList';\n\nconst SPECIAL_EXAMS_TAB = {\n ATTEMPTS: 'attempts',\n ALLOWANCES: 'allowances',\n};\n\nconst SpecialExamsPage = () => {\n const intl = useIntl();\n const [selectedTab, setSelectedTab] = useState<(typeof SPECIAL_EXAMS_TAB)[keyof typeof SPECIAL_EXAMS_TAB]>(SPECIAL_EXAMS_TAB.ATTEMPTS);\n\n return (\n <>\n <h3 className=\"text-primary-700\">{intl.formatMessage(messages.specialExamsTitle)}</h3>\n <Card className=\"bg-light-200 mt-4.5\">\n <ButtonGroup className=\"d-block mx-4 mt-4\">\n <Button\n variant={selectedTab === SPECIAL_EXAMS_TAB.ATTEMPTS ? 'primary' : 'outline-primary'}\n onClick={() => setSelectedTab(SPECIAL_EXAMS_TAB.ATTEMPTS)}\n >{intl.formatMessage(messages.examAttempts)}\n </Button>\n <Button\n variant={selectedTab === SPECIAL_EXAMS_TAB.ALLOWANCES ? 'primary' : 'outline-primary'}\n onClick={() => setSelectedTab(SPECIAL_EXAMS_TAB.ALLOWANCES)}\n >{intl.formatMessage(messages.allowances)}\n </Button>\n </ButtonGroup>\n {\n selectedTab === SPECIAL_EXAMS_TAB.ATTEMPTS ? <AttemptsList /> : <Allowances />\n }\n </Card>\n </>\n );\n};\n\nexport default SpecialExamsPage;\n"]}
@@ -0,0 +1,6 @@
1
+ interface AddAllowanceModalProps {
2
+ isOpen: boolean;
3
+ onClose: () => void;
4
+ }
5
+ declare const AddAllowanceModal: ({ isOpen, onClose }: AddAllowanceModalProps) => import("react/jsx-runtime").JSX.Element;
6
+ export default AddAllowanceModal;
@@ -0,0 +1,84 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useState } from 'react';
3
+ import { useParams } from 'react-router-dom';
4
+ import { useIntl } from '@openedx/frontend-base';
5
+ import { ActionRow, Button, Form, ModalDialog } from '@openedx/paragon';
6
+ import { addLabel, addPlaceholder, allowanceTypesOptions } from '../../specialExams/constants';
7
+ import { useAddAllowance, useSpecialExams } from '../../specialExams/data/apiHook';
8
+ import messages from '../../specialExams/messages';
9
+ import { useAlert } from '../../providers/AlertProvider';
10
+ const examTypeOptions = [
11
+ { value: '', label: messages.selectExamType },
12
+ { value: 'proctored', label: messages.proctored },
13
+ { value: 'timed', label: messages.timed },
14
+ ];
15
+ const emptyAllowanceData = {
16
+ examType: '',
17
+ examIds: [],
18
+ allowanceType: '',
19
+ value: '',
20
+ users: ''
21
+ };
22
+ const AddAllowanceModal = ({ isOpen, onClose }) => {
23
+ const intl = useIntl();
24
+ const { courseId = '' } = useParams();
25
+ const [newAllowance, setNewAllowance] = useState(emptyAllowanceData);
26
+ const { data: specialExams, refetch } = useSpecialExams(courseId, newAllowance.examType);
27
+ const { mutate: addAllowance } = useAddAllowance(courseId);
28
+ const { showModal } = useAlert();
29
+ const enableSubmitButton = useMemo(() => (allowanceData) => {
30
+ const { examType, allowanceType, value, users, examIds } = allowanceData;
31
+ if (!examType || !allowanceType || !value || !users || examIds.length === 0) {
32
+ return false;
33
+ }
34
+ const userIds = users.split(',').map(user => user.trim()).filter(user => user);
35
+ return userIds.length > 0;
36
+ }, []);
37
+ useEffect(() => {
38
+ if (isOpen && newAllowance.examType) {
39
+ refetch();
40
+ }
41
+ }, [isOpen, newAllowance.examType, refetch]);
42
+ const handleClose = () => {
43
+ setNewAllowance(emptyAllowanceData);
44
+ onClose();
45
+ };
46
+ const handleAdd = (e) => {
47
+ e.preventDefault();
48
+ const userIds = newAllowance.users.split(',').map(user => user.trim()).filter(user => user);
49
+ const allowanceData = {
50
+ userIds,
51
+ examType: newAllowance.examType,
52
+ examIds: newAllowance.examIds,
53
+ allowanceType: newAllowance.allowanceType,
54
+ value: newAllowance.value,
55
+ };
56
+ addAllowance(allowanceData, {
57
+ onSuccess: () => {
58
+ handleClose();
59
+ },
60
+ onError: () => {
61
+ showModal({
62
+ message: intl.formatMessage(messages.addAllowanceError),
63
+ variant: 'danger',
64
+ });
65
+ }
66
+ });
67
+ };
68
+ const handleChanges = (e) => {
69
+ const { value, name } = e.target;
70
+ setNewAllowance(prev => (Object.assign(Object.assign({}, prev), { [name]: value })));
71
+ };
72
+ const handleToggleExam = (value) => {
73
+ setNewAllowance(prev => {
74
+ const examIds = prev.examIds.includes(value)
75
+ ? prev.examIds.filter(id => id !== value)
76
+ : [...prev.examIds, value];
77
+ return Object.assign(Object.assign({}, prev), { examIds });
78
+ });
79
+ };
80
+ return (_jsxs(ModalDialog, { isOpen: isOpen, onClose: handleClose, title: intl.formatMessage(messages.addAllowance), isOverflowVisible: false, size: "lg", children: [_jsx(ModalDialog.Header, { className: "border-bottom border-light-700", children: _jsx(ModalDialog.Title, { className: "text-primary-700", children: intl.formatMessage(messages.addAllowance) }) }), _jsxs(Form, { className: "position-relative overflow-auto", onSubmit: handleAdd, children: [_jsxs(ModalDialog.Body, { children: [_jsxs(Form.Group, { controlId: "specify-learners", children: [_jsxs(Form.Label, { className: "text-primary-500 x-small", children: [intl.formatMessage(messages.specifyLearners), ":"] }), _jsx(Form.Control, { as: "textarea", rows: 3, placeholder: intl.formatMessage(messages.specifyLearnersPlaceholder), name: "users", onChange: handleChanges })] }), _jsxs(Form.Group, { controlId: "select-exam-type", children: [_jsxs(Form.Label, { className: "text-primary-500 x-small", children: [intl.formatMessage(messages.selectExamType), ":"] }), _jsx(Form.Control, { as: "select", name: "examType", onChange: handleChanges, children: examTypeOptions.map(option => (_jsx("option", { value: option.value, children: intl.formatMessage(option.label) }, option.value))) })] }), _jsxs(Form.Group, { controlId: "select-exams", children: [_jsxs(Form.Label, { className: "text-primary-500 x-small", children: [intl.formatMessage(messages.selectExams), ":"] }), newAllowance.examType && (_jsx(Form.CheckboxSet, { onChange: (e) => handleToggleExam(Number(e.target.value)), name: "examIds", children: (specialExams || [])
81
+ .map((exam) => (_jsx(Form.Checkbox, { className: "mt-2", value: exam.id, children: exam.examName }, exam.id))) }))] }), _jsxs(Form.Group, { controlId: "select-allowance-type", children: [_jsxs(Form.Label, { className: "text-primary-500 x-small", children: [intl.formatMessage(messages.selectAllowanceType), ":"] }), _jsx(Form.Control, { as: "select", name: "allowanceType", onChange: handleChanges, children: allowanceTypesOptions.map(option => (_jsx("option", { value: option.value, children: intl.formatMessage(option.label) }, option.value))) })] }), _jsxs(Form.Group, { controlId: "allowance-value", children: [_jsxs(Form.Label, { className: "text-primary-500 x-small", children: [intl.formatMessage(addLabel[newAllowance.allowanceType || 'additional_time_granted']), ":"] }), _jsx(Form.Control, { type: newAllowance.allowanceType === 'review_policy_exception' ? 'text' : 'number', placeholder: intl.formatMessage(addPlaceholder[newAllowance.allowanceType || 'additional_time_granted']), name: "value", onChange: handleChanges })] })] }), _jsx(ModalDialog.Footer, { className: "border-top border-light-700", children: _jsxs(ActionRow, { children: [_jsx(Button, { variant: "tertiary", onClick: handleClose, children: intl.formatMessage(messages.cancel) }), _jsx(Button, { disabled: !enableSubmitButton(newAllowance), variant: "primary", type: "submit", children: intl.formatMessage(messages.createAllowance) })] }) })] })] }));
82
+ };
83
+ export default AddAllowanceModal;
84
+ //# sourceMappingURL=AddAllowanceModal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AddAllowanceModal.js","sourceRoot":"","sources":["../../../src/specialExams/components/AddAllowanceModal.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useIntl } from '@openedx/frontend-base';\nimport { ActionRow, Button, Form, ModalDialog } from '@openedx/paragon';\nimport { addLabel, addPlaceholder, allowanceTypesOptions } from '@src/specialExams/constants';\nimport { useAddAllowance, useSpecialExams } from '@src/specialExams/data/apiHook';\nimport messages from '@src/specialExams/messages';\nimport { AddAllowanceForm, AddAllowanceParams } from '@src/specialExams/types';\nimport { useAlert } from '@src/providers/AlertProvider';\n\ninterface AddAllowanceModalProps {\n isOpen: boolean,\n onClose: () => void,\n}\n\nconst examTypeOptions = [\n { value: '', label: messages.selectExamType },\n { value: 'proctored', label: messages.proctored },\n { value: 'timed', label: messages.timed },\n];\n\nconst emptyAllowanceData = {\n examType: '',\n examIds: [],\n allowanceType: '',\n value: '',\n users: ''\n};\n\nconst AddAllowanceModal = ({ isOpen, onClose }: AddAllowanceModalProps) => {\n const intl = useIntl();\n const { courseId = '' } = useParams();\n const [newAllowance, setNewAllowance] = useState<AddAllowanceForm>(emptyAllowanceData);\n const { data: specialExams, refetch } = useSpecialExams(courseId, newAllowance.examType);\n const { mutate: addAllowance } = useAddAllowance(courseId);\n const { showModal } = useAlert();\n\n const enableSubmitButton = useMemo(() => (allowanceData: AddAllowanceForm) => {\n const { examType, allowanceType, value, users, examIds } = allowanceData;\n if (!examType || !allowanceType || !value || !users || examIds.length === 0) {\n return false;\n }\n const userIds = users.split(',').map(user => user.trim()).filter(user => user);\n return userIds.length > 0;\n }, []);\n\n useEffect(() => {\n if (isOpen && newAllowance.examType) {\n refetch();\n }\n }, [isOpen, newAllowance.examType, refetch]);\n\n const handleClose = () => {\n setNewAllowance(emptyAllowanceData);\n onClose();\n };\n\n const handleAdd = (e: React.FormEvent<HTMLFormElement>) => {\n e.preventDefault();\n const userIds = newAllowance.users.split(',').map(user => user.trim()).filter(user => user);\n const allowanceData: AddAllowanceParams = {\n userIds,\n examType: newAllowance.examType,\n examIds: newAllowance.examIds,\n allowanceType: newAllowance.allowanceType,\n value: newAllowance.value,\n };\n\n addAllowance(allowanceData, {\n onSuccess: () => {\n handleClose();\n },\n onError: () => {\n showModal({\n message: intl.formatMessage(messages.addAllowanceError),\n variant: 'danger',\n });\n }\n });\n };\n\n const handleChanges = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n const { value, name } = e.target;\n setNewAllowance(prev => ({\n ...prev,\n [name]: value,\n }));\n };\n\n const handleToggleExam = (value: number) => {\n setNewAllowance(prev => {\n const examIds = prev.examIds.includes(value)\n ? prev.examIds.filter(id => id !== value)\n : [...prev.examIds, value];\n return {\n ...prev,\n examIds,\n };\n });\n };\n\n return (\n <ModalDialog isOpen={isOpen} onClose={handleClose} title={intl.formatMessage(messages.addAllowance)} isOverflowVisible={false} size=\"lg\">\n <ModalDialog.Header className=\"border-bottom border-light-700\">\n <ModalDialog.Title className=\"text-primary-700\">{intl.formatMessage(messages.addAllowance)}</ModalDialog.Title>\n </ModalDialog.Header>\n <Form className=\"position-relative overflow-auto\" onSubmit={handleAdd}>\n <ModalDialog.Body>\n <Form.Group controlId=\"specify-learners\">\n <Form.Label className=\"text-primary-500 x-small\">{intl.formatMessage(messages.specifyLearners)}:</Form.Label>\n <Form.Control\n as=\"textarea\"\n rows={3}\n placeholder={intl.formatMessage(messages.specifyLearnersPlaceholder)}\n name=\"users\"\n onChange={handleChanges}\n />\n </Form.Group>\n <Form.Group controlId=\"select-exam-type\">\n <Form.Label className=\"text-primary-500 x-small\">{intl.formatMessage(messages.selectExamType)}:</Form.Label>\n <Form.Control as=\"select\" name=\"examType\" onChange={handleChanges}>\n {examTypeOptions.map(option => (\n <option key={option.value} value={option.value}>{intl.formatMessage(option.label)}</option>\n ))}\n </Form.Control>\n </Form.Group>\n <Form.Group controlId=\"select-exams\">\n <Form.Label className=\"text-primary-500 x-small\">{intl.formatMessage(messages.selectExams)}:</Form.Label>\n {newAllowance.examType && (\n <Form.CheckboxSet onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleToggleExam(Number(e.target.value))} name=\"examIds\">\n {\n (specialExams || [])\n .map((exam) => (\n <Form.Checkbox className=\"mt-2\" key={exam.id} value={exam.id}>\n {exam.examName}\n </Form.Checkbox>\n ))\n }\n </Form.CheckboxSet>\n )}\n </Form.Group>\n <Form.Group controlId=\"select-allowance-type\">\n <Form.Label className=\"text-primary-500 x-small\">{intl.formatMessage(messages.selectAllowanceType)}:</Form.Label>\n <Form.Control as=\"select\" name=\"allowanceType\" onChange={handleChanges}>\n {allowanceTypesOptions.map(option => (\n <option key={option.value} value={option.value}>{intl.formatMessage(option.label)}</option>\n ))}\n </Form.Control>\n </Form.Group>\n <Form.Group controlId=\"allowance-value\">\n <Form.Label className=\"text-primary-500 x-small\">{intl.formatMessage(addLabel[newAllowance.allowanceType || 'additional_time_granted'])}:</Form.Label>\n <Form.Control\n type={newAllowance.allowanceType === 'review_policy_exception' ? 'text' : 'number'}\n placeholder={intl.formatMessage(addPlaceholder[newAllowance.allowanceType || 'additional_time_granted'])}\n name=\"value\"\n onChange={handleChanges}\n />\n </Form.Group>\n </ModalDialog.Body>\n <ModalDialog.Footer className=\"border-top border-light-700\">\n <ActionRow>\n <Button variant=\"tertiary\" onClick={handleClose}>{intl.formatMessage(messages.cancel)}</Button>\n <Button disabled={!enableSubmitButton(newAllowance)} variant=\"primary\" type=\"submit\">{intl.formatMessage(messages.createAllowance)}</Button>\n </ActionRow>\n </ModalDialog.Footer>\n </Form>\n </ModalDialog>\n );\n};\n\nexport default AddAllowanceModal;\n"]}
@@ -1,6 +1,32 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { useToggle } from '@openedx/paragon';
4
+ import AddAllowanceModal from '../../specialExams/components/AddAllowanceModal';
5
+ import AllowancesList from '../../specialExams/components/AllowancesList';
6
+ import EditAllowanceModal from '../../specialExams/components/EditAllowanceModal';
7
+ import DeleteAllowanceModal from '../../specialExams/components/DeleteAllowanceModal';
2
8
  const Allowances = () => {
3
- return _jsx("div", { children: "Allowances" });
9
+ const [isAddModalOpen, openAddModal, closeAddModal] = useToggle(false);
10
+ const [isEditModalOpen, openEditModal, closeEditModal] = useToggle(false);
11
+ const [isDeleteModalOpen, openDeleteModal, closeDeleteModal] = useToggle(false);
12
+ const [selectedAllowance, setSelectedAllowance] = useState(null);
13
+ const handleEditAllowance = (allowance) => {
14
+ setSelectedAllowance(allowance);
15
+ openEditModal();
16
+ };
17
+ const handleCloseEditModal = () => {
18
+ setSelectedAllowance(null);
19
+ closeEditModal();
20
+ };
21
+ const handleDeleteAllowance = (allowance) => {
22
+ setSelectedAllowance(allowance);
23
+ openDeleteModal();
24
+ };
25
+ const handleCloseDeleteModal = () => {
26
+ setSelectedAllowance(null);
27
+ closeDeleteModal();
28
+ };
29
+ return (_jsxs(_Fragment, { children: [_jsx(AllowancesList, { onClickAdd: openAddModal, onEdit: handleEditAllowance, onDelete: handleDeleteAllowance }), _jsx(AddAllowanceModal, { isOpen: isAddModalOpen, onClose: closeAddModal }), selectedAllowance && _jsx(EditAllowanceModal, { isOpen: isEditModalOpen, onClose: handleCloseEditModal, allowance: selectedAllowance }), selectedAllowance && _jsx(DeleteAllowanceModal, { isOpen: isDeleteModalOpen, onClose: handleCloseDeleteModal, allowance: selectedAllowance })] }));
4
30
  };
5
31
  export default Allowances;
6
32
  //# sourceMappingURL=Allowances.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Allowances.js","sourceRoot":"","sources":["../../../src/specialExams/components/Allowances.tsx"],"names":[],"mappings":";AAAA,MAAM,UAAU,GAAG,GAAG,EAAE;IACtB,OAAO,uCAAqB,CAAC;AAC/B,CAAC,CAAC;AAEF,eAAe,UAAU,CAAC","sourcesContent":["const Allowances = () => {\n return <div>Allowances</div>;\n};\n\nexport default Allowances;\n"]}
1
+ {"version":3,"file":"Allowances.js","sourceRoot":"","sources":["../../../src/specialExams/components/Allowances.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,iBAAiB,MAAM,gDAAgD,CAAC;AAC/E,OAAO,cAAc,MAAM,6CAA6C,CAAC;AACzE,OAAO,kBAAkB,MAAM,iDAAiD,CAAC;AACjF,OAAO,oBAAoB,MAAM,mDAAmD,CAAC;AAGrF,MAAM,UAAU,GAAG,GAAG,EAAE;IACtB,MAAM,CAAC,cAAc,EAAE,YAAY,EAAE,aAAa,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IACvE,MAAM,CAAC,eAAe,EAAE,aAAa,EAAE,cAAc,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IAC1E,MAAM,CAAC,iBAAiB,EAAE,eAAe,EAAE,gBAAgB,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IAChF,MAAM,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,GAAG,QAAQ,CAAmB,IAAI,CAAC,CAAC;IAEnF,MAAM,mBAAmB,GAAG,CAAC,SAAoB,EAAE,EAAE;QACnD,oBAAoB,CAAC,SAAS,CAAC,CAAC;QAChC,aAAa,EAAE,CAAC;IAClB,CAAC,CAAC;IAEF,MAAM,oBAAoB,GAAG,GAAG,EAAE;QAChC,oBAAoB,CAAC,IAAI,CAAC,CAAC;QAC3B,cAAc,EAAE,CAAC;IACnB,CAAC,CAAC;IAEF,MAAM,qBAAqB,GAAG,CAAC,SAAoB,EAAE,EAAE;QACrD,oBAAoB,CAAC,SAAS,CAAC,CAAC;QAChC,eAAe,EAAE,CAAC;IACpB,CAAC,CAAC;IAEF,MAAM,sBAAsB,GAAG,GAAG,EAAE;QAClC,oBAAoB,CAAC,IAAI,CAAC,CAAC;QAC3B,gBAAgB,EAAE,CAAC;IACrB,CAAC,CAAC;IAEF,OAAO,CACL,8BACE,KAAC,cAAc,IAAC,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,mBAAmB,EAAE,QAAQ,EAAE,qBAAqB,GAAI,EAC1G,KAAC,iBAAiB,IAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,aAAa,GAAI,EACpE,iBAAiB,IAAI,KAAC,kBAAkB,IAAC,MAAM,EAAE,eAAe,EAAE,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,iBAAiB,GAAI,EACjI,iBAAiB,IAAI,KAAC,oBAAoB,IAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,sBAAsB,EAAE,SAAS,EAAE,iBAAiB,GAAI,IACvI,CACJ,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,UAAU,CAAC","sourcesContent":["import { useState } from 'react';\nimport { useToggle } from '@openedx/paragon';\nimport AddAllowanceModal from '@src/specialExams/components/AddAllowanceModal';\nimport AllowancesList from '@src/specialExams/components/AllowancesList';\nimport EditAllowanceModal from '@src/specialExams/components/EditAllowanceModal';\nimport DeleteAllowanceModal from '@src/specialExams/components/DeleteAllowanceModal';\nimport { Allowance } from '@src/specialExams/types';\n\nconst Allowances = () => {\n const [isAddModalOpen, openAddModal, closeAddModal] = useToggle(false);\n const [isEditModalOpen, openEditModal, closeEditModal] = useToggle(false);\n const [isDeleteModalOpen, openDeleteModal, closeDeleteModal] = useToggle(false);\n const [selectedAllowance, setSelectedAllowance] = useState<Allowance | null>(null);\n\n const handleEditAllowance = (allowance: Allowance) => {\n setSelectedAllowance(allowance);\n openEditModal();\n };\n\n const handleCloseEditModal = () => {\n setSelectedAllowance(null);\n closeEditModal();\n };\n\n const handleDeleteAllowance = (allowance: Allowance) => {\n setSelectedAllowance(allowance);\n openDeleteModal();\n };\n\n const handleCloseDeleteModal = () => {\n setSelectedAllowance(null);\n closeDeleteModal();\n };\n\n return (\n <>\n <AllowancesList onClickAdd={openAddModal} onEdit={handleEditAllowance} onDelete={handleDeleteAllowance} />\n <AddAllowanceModal isOpen={isAddModalOpen} onClose={closeAddModal} />\n {selectedAllowance && <EditAllowanceModal isOpen={isEditModalOpen} onClose={handleCloseEditModal} allowance={selectedAllowance} />}\n {selectedAllowance && <DeleteAllowanceModal isOpen={isDeleteModalOpen} onClose={handleCloseDeleteModal} allowance={selectedAllowance} />}\n </>\n );\n};\n\nexport default Allowances;\n"]}
@@ -0,0 +1,8 @@
1
+ import { Allowance } from '../../specialExams/types';
2
+ interface AllowanceList {
3
+ onClickAdd: () => void;
4
+ onEdit: (allowance: Allowance) => void;
5
+ onDelete: (allowance: Allowance) => void;
6
+ }
7
+ declare const AllowancesList: ({ onClickAdd, onEdit, onDelete }: AllowanceList) => import("react/jsx-runtime").JSX.Element;
8
+ export default AllowancesList;
@@ -0,0 +1,58 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { useParams } from 'react-router-dom';
4
+ import { useIntl } from '@openedx/frontend-base';
5
+ import { Button, DataTable, IconButton, OverlayTrigger, Popover } from '@openedx/paragon';
6
+ import { MoreVert, Plus } from '@openedx/paragon/icons';
7
+ import UsernameFilter from '../../components/UsernameFilter';
8
+ import { ALLOWANCES_PAGE_SIZE, allowanceTypesOptions } from '../../specialExams/constants';
9
+ import { useAllowances } from '../../specialExams/data/apiHook';
10
+ import messages from '../../specialExams/messages';
11
+ const AllowancesList = ({ onClickAdd, onEdit, onDelete }) => {
12
+ const intl = useIntl();
13
+ const { courseId = '' } = useParams();
14
+ const [filters, setFilters] = useState({ page: 0, emailOrUsername: '' });
15
+ const { data = { results: [], count: 0, numPages: 1 }, isLoading = false } = useAllowances(courseId, Object.assign({ pageSize: ALLOWANCES_PAGE_SIZE }, filters));
16
+ const ActionCustomCell = ({ row: { original } }) => {
17
+ const popoverContent = (_jsx(Popover, { id: `popover-${original.user.username}-${original.proctoredExam.examName}`, className: "border-0 shadow-sm", children: _jsx(Popover.Content, { className: "p-0 border-0", children: _jsxs("div", { className: "dropdown-menu show position-static border shadow-sm", children: [_jsx("button", { type: "button", className: "dropdown-item", onClick: () => onEdit(original), children: intl.formatMessage(messages.edit) }), _jsx("button", { type: "button", className: "dropdown-item", onClick: () => onDelete(original), children: intl.formatMessage(messages.delete) })] }) }) }));
18
+ return (_jsx(_Fragment, { children: _jsx(OverlayTrigger, { trigger: "click", placement: "bottom-end", overlay: popoverContent, rootClose: true, children: _jsx(IconButton, { alt: intl.formatMessage(messages.actions), className: "lead", iconAs: MoreVert }) }) }));
19
+ };
20
+ const columns = [
21
+ { accessor: 'user.username', Header: intl.formatMessage(messages.username), Filter: UsernameFilter, },
22
+ { accessor: 'user.email', Header: intl.formatMessage(messages.email), disableFilters: true, },
23
+ { accessor: 'proctoredExam.examName', Header: intl.formatMessage(messages.examName), disableFilters: true, },
24
+ { accessor: 'key', Header: intl.formatMessage(messages.allowanceType), disableFilters: true,
25
+ Cell: ({ value }) => {
26
+ const allowanceType = allowanceTypesOptions.find(option => option.value === value);
27
+ return allowanceType ? intl.formatMessage(allowanceType.label) : '';
28
+ }
29
+ },
30
+ { accessor: 'value', Header: intl.formatMessage(messages.allowanceValue), disableFilters: true, },
31
+ ];
32
+ const additionalColumns = [{
33
+ id: 'actions',
34
+ Header: '',
35
+ Cell: ActionCustomCell,
36
+ }];
37
+ const handleFetchData = (data) => {
38
+ var _a;
39
+ const emailOrUsernameFilter = (_a = data.filters) === null || _a === void 0 ? void 0 : _a.find((f) => f.id === 'user.username');
40
+ const newEmailOrUsername = emailOrUsernameFilter ? emailOrUsernameFilter.value : '';
41
+ if (filters.emailOrUsername !== newEmailOrUsername) {
42
+ setFilters((prevFilters) => (Object.assign(Object.assign({}, prevFilters), { emailOrUsername: newEmailOrUsername, page: 0 })));
43
+ return;
44
+ }
45
+ if (data.pageIndex !== filters.page) {
46
+ setFilters((prevFilters) => (Object.assign(Object.assign({}, prevFilters), { page: data.pageIndex })));
47
+ }
48
+ };
49
+ return (_jsxs(DataTable, { additionalColumns: additionalColumns, className: "mt-3", columns: columns, data: data.results, state: {
50
+ pageIndex: filters.page,
51
+ pageSize: ALLOWANCES_PAGE_SIZE,
52
+ filters: [
53
+ { id: 'emailOrUsername', value: filters.emailOrUsername }
54
+ ]
55
+ }, fetchData: handleFetchData, isFilterable: true, isLoading: isLoading, isPaginated: true, isSortable: true, itemCount: data.count, manualFilters: true, manualPagination: true, manualSortBy: true, pageSize: ALLOWANCES_PAGE_SIZE, pageCount: data.numPages, FilterStatusComponent: () => null, children: [_jsxs("div", { className: "bg-light-200 d-flex justify-content-between align-items-center p-3", children: [_jsx(DataTable.TableControlBar, { className: "p-0" }), _jsx(Button, { iconBefore: Plus, variant: "primary", onClick: onClickAdd, children: intl.formatMessage(messages.addAllowance) })] }), _jsx(DataTable.Table, {}), _jsx(DataTable.EmptyTable, { content: intl.formatMessage(messages.noAllowances) }), _jsx(DataTable.TableFooter, {})] }));
56
+ };
57
+ export default AllowancesList;
58
+ //# sourceMappingURL=AllowancesList.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AllowancesList.js","sourceRoot":"","sources":["../../../src/specialExams/components/AllowancesList.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useIntl } from '@openedx/frontend-base';\nimport { Button, DataTable, IconButton, OverlayTrigger, Popover } from '@openedx/paragon';\nimport { MoreVert, Plus } from '@openedx/paragon/icons';\nimport UsernameFilter from '@src/components/UsernameFilter';\nimport { ALLOWANCES_PAGE_SIZE, allowanceTypesOptions } from '@src/specialExams/constants';\nimport { useAllowances } from '@src/specialExams/data/apiHook';\nimport messages from '@src/specialExams/messages';\nimport { Allowance } from '@src/specialExams/types';\nimport { DataTableFetchDataProps, TableCellValue } from '@src/types';\n\ninterface AllowanceList {\n onClickAdd: () => void,\n onEdit: (allowance: Allowance) => void,\n onDelete: (allowance: Allowance) => void,\n}\n\nconst AllowancesList = ({ onClickAdd, onEdit, onDelete }: AllowanceList) => {\n const intl = useIntl();\n const { courseId = '' } = useParams<{ courseId: string }>();\n const [filters, setFilters] = useState({ page: 0, emailOrUsername: '' });\n const { data = { results: [], count: 0, numPages: 1 }, isLoading = false } = useAllowances(courseId, {\n pageSize: ALLOWANCES_PAGE_SIZE,\n ...filters,\n });\n\n const ActionCustomCell = ({ row: { original } }: TableCellValue<Allowance>) => {\n const popoverContent = (\n <Popover\n id={`popover-${original.user.username}-${original.proctoredExam.examName}`}\n className=\"border-0 shadow-sm\"\n >\n <Popover.Content className=\"p-0 border-0\">\n <div className=\"dropdown-menu show position-static border shadow-sm\">\n <button\n type=\"button\"\n className=\"dropdown-item\"\n onClick={() => onEdit(original)}\n >\n {intl.formatMessage(messages.edit)}\n </button>\n <button\n type=\"button\"\n className=\"dropdown-item\"\n onClick={() => onDelete(original)}\n >\n {intl.formatMessage(messages.delete)}\n </button>\n </div>\n </Popover.Content>\n </Popover>\n );\n return (\n <>\n <OverlayTrigger\n trigger=\"click\"\n placement=\"bottom-end\"\n overlay={popoverContent}\n rootClose\n >\n <IconButton\n alt={intl.formatMessage(messages.actions)}\n className=\"lead\"\n iconAs={MoreVert}\n />\n </OverlayTrigger>\n </>\n );\n };\n\n const columns = [\n { accessor: 'user.username', Header: intl.formatMessage(messages.username), Filter: UsernameFilter, },\n { accessor: 'user.email', Header: intl.formatMessage(messages.email), disableFilters: true, },\n { accessor: 'proctoredExam.examName', Header: intl.formatMessage(messages.examName), disableFilters: true, },\n { accessor: 'key', Header: intl.formatMessage(messages.allowanceType), disableFilters: true,\n Cell: ({ value }: { value: string }) => {\n const allowanceType = allowanceTypesOptions.find(option => option.value === value);\n return allowanceType ? intl.formatMessage(allowanceType.label) : '';\n }\n },\n { accessor: 'value', Header: intl.formatMessage(messages.allowanceValue), disableFilters: true, },\n ];\n\n const additionalColumns = [{\n id: 'actions',\n Header: '',\n Cell: ActionCustomCell,\n }];\n\n const handleFetchData = (data: DataTableFetchDataProps) => {\n const emailOrUsernameFilter = data.filters?.find((f) => f.id === 'user.username');\n const newEmailOrUsername = emailOrUsernameFilter ? emailOrUsernameFilter.value : '';\n if (filters.emailOrUsername !== newEmailOrUsername) {\n setFilters((prevFilters) => ({ ...prevFilters, emailOrUsername: newEmailOrUsername, page: 0 }));\n return;\n }\n if (data.pageIndex !== filters.page) {\n setFilters((prevFilters) => ({ ...prevFilters, page: data.pageIndex }));\n }\n };\n\n return (\n <DataTable\n additionalColumns={additionalColumns}\n className=\"mt-3\"\n columns={columns}\n data={data.results}\n state={{\n pageIndex: filters.page,\n pageSize: ALLOWANCES_PAGE_SIZE,\n filters: [\n { id: 'emailOrUsername', value: filters.emailOrUsername }\n ]\n }}\n fetchData={handleFetchData}\n isFilterable\n isLoading={isLoading}\n isPaginated\n isSortable\n itemCount={data.count}\n manualFilters\n manualPagination\n manualSortBy\n pageSize={ALLOWANCES_PAGE_SIZE}\n pageCount={data.numPages}\n FilterStatusComponent={() => null}\n >\n <div className=\"bg-light-200 d-flex justify-content-between align-items-center p-3\">\n <DataTable.TableControlBar className=\"p-0\" />\n <Button iconBefore={Plus} variant=\"primary\" onClick={onClickAdd}>\n {intl.formatMessage(messages.addAllowance)}\n </Button>\n </div>\n <DataTable.Table />\n <DataTable.EmptyTable content={intl.formatMessage(messages.noAllowances)} />\n <DataTable.TableFooter />\n </DataTable>\n );\n};\n\nexport default AllowancesList;\n"]}
@@ -13,19 +13,20 @@ const AttemptsList = () => {
13
13
  const [filters, setFilters] = useState({ page: 0, emailOrUsername: '' });
14
14
  const { data = { results: [], count: 0, numPages: 0 }, isLoading = false } = useAttempts(courseId, Object.assign(Object.assign({}, filters), { pageSize: ATTEMPTS_PAGE_SIZE }));
15
15
  const columns = useMemo(() => [
16
- { accessor: 'username', Header: intl.formatMessage(messages.username), Filter: UsernameFilter, },
16
+ { accessor: 'user.username', Header: intl.formatMessage(messages.username), Filter: UsernameFilter, },
17
17
  { accessor: 'examName', Header: intl.formatMessage(messages.examName), disableFilters: true, },
18
- { accessor: 'timeLimit', Header: intl.formatMessage(messages.timeLimit), disableFilters: true, },
18
+ { accessor: 'allowedTimeLimitMins', Header: intl.formatMessage(messages.timeLimit), disableFilters: true, },
19
19
  { accessor: 'type', Header: intl.formatMessage(messages.type), disableFilters: true, },
20
- { accessor: 'startedAt', Header: intl.formatMessage(messages.startedAt), disableFilters: true, },
21
- { accessor: 'completedAt', Header: intl.formatMessage(messages.completedAt), disableFilters: true, },
20
+ { accessor: 'startTime', Header: intl.formatMessage(messages.startedAt), disableFilters: true, },
21
+ { accessor: 'endTime', Header: intl.formatMessage(messages.completedAt), disableFilters: true, },
22
22
  { accessor: 'status', Header: intl.formatMessage(messages.status), disableFilters: true, },
23
23
  ], [intl]);
24
24
  const handleFetchData = (data) => {
25
25
  var _a;
26
- const emailOrUsernameFilter = (_a = data.filters) === null || _a === void 0 ? void 0 : _a.find((f) => f.id === 'username');
27
- if (emailOrUsernameFilter && emailOrUsernameFilter.value !== filters.emailOrUsername) {
28
- setFilters((prevFilters) => (Object.assign(Object.assign({}, prevFilters), { emailOrUsername: emailOrUsernameFilter.value, page: 0 })));
26
+ const emailOrUsernameFilter = (_a = data.filters) === null || _a === void 0 ? void 0 : _a.find((f) => f.id === 'user.username');
27
+ const newEmailOrUsername = emailOrUsernameFilter ? emailOrUsernameFilter.value : '';
28
+ if (filters.emailOrUsername !== newEmailOrUsername) {
29
+ setFilters((prevFilters) => (Object.assign(Object.assign({}, prevFilters), { emailOrUsername: newEmailOrUsername, page: 0 })));
29
30
  return;
30
31
  }
31
32
  if (data.pageIndex !== filters.page) {
@@ -1 +1 @@
1
- {"version":3,"file":"AttemptsList.js","sourceRoot":"","sources":["../../../src/specialExams/components/AttemptsList.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useIntl } from '@openedx/frontend-base';\nimport { DataTable } from '@openedx/paragon';\nimport UsernameFilter from '@src/components/UsernameFilter';\nimport messages from '@src/specialExams/messages';\nimport { useAttempts } from '@src/specialExams/data/apiHook';\nimport { DataTableFetchDataProps } from '@src/types';\n\nexport const ATTEMPTS_PAGE_SIZE = 25;\n\nconst AttemptsList = () => {\n const intl = useIntl();\n const { courseId = '' } = useParams();\n const [filters, setFilters] = useState({ page: 0, emailOrUsername: '' });\n const { data = { results: [], count: 0, numPages: 0 }, isLoading = false } = useAttempts(courseId, {\n ...filters,\n pageSize: ATTEMPTS_PAGE_SIZE\n });\n\n const columns = useMemo(() => [\n { accessor: 'username', Header: intl.formatMessage(messages.username), Filter: UsernameFilter, },\n { accessor: 'examName', Header: intl.formatMessage(messages.examName), disableFilters: true, },\n { accessor: 'timeLimit', Header: intl.formatMessage(messages.timeLimit), disableFilters: true, },\n { accessor: 'type', Header: intl.formatMessage(messages.type), disableFilters: true, },\n { accessor: 'startedAt', Header: intl.formatMessage(messages.startedAt), disableFilters: true, },\n { accessor: 'completedAt', Header: intl.formatMessage(messages.completedAt), disableFilters: true, },\n { accessor: 'status', Header: intl.formatMessage(messages.status), disableFilters: true, },\n ], [intl]);\n\n const handleFetchData = (data: DataTableFetchDataProps) => {\n const emailOrUsernameFilter = data.filters?.find((f) => f.id === 'username');\n if (emailOrUsernameFilter && emailOrUsernameFilter.value !== filters.emailOrUsername) {\n setFilters((prevFilters) => ({ ...prevFilters, emailOrUsername: emailOrUsernameFilter.value, page: 0 }));\n return;\n }\n if (data.pageIndex !== filters.page) {\n setFilters((prevFilters) => ({ ...prevFilters, page: data.pageIndex }));\n }\n };\n\n return (\n <DataTable\n className=\"mt-3\"\n columns={columns}\n data={data.results}\n state={{\n pageIndex: filters.page,\n pageSize: ATTEMPTS_PAGE_SIZE,\n filters: [\n { id: 'emailOrUsername', value: filters.emailOrUsername }\n ]\n }}\n fetchData={handleFetchData}\n isFilterable\n isLoading={isLoading}\n isPaginated\n isSortable\n itemCount={data.count}\n manualFilters\n manualPagination\n manualSortBy\n pageSize={ATTEMPTS_PAGE_SIZE}\n pageCount={data.numPages}\n FilterStatusComponent={() => null}\n >\n <DataTable.TableControlBar className=\"bg-light-200 py-3 px-4\" />\n <DataTable.Table />\n <DataTable.EmptyTable content={intl.formatMessage(messages.noAttempts)} />\n <DataTable.TableFooter />\n </DataTable>\n );\n};\n\nexport default AttemptsList;\n"]}
1
+ {"version":3,"file":"AttemptsList.js","sourceRoot":"","sources":["../../../src/specialExams/components/AttemptsList.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useIntl } from '@openedx/frontend-base';\nimport { DataTable } from '@openedx/paragon';\nimport UsernameFilter from '@src/components/UsernameFilter';\nimport messages from '@src/specialExams/messages';\nimport { useAttempts } from '@src/specialExams/data/apiHook';\nimport { DataTableFetchDataProps } from '@src/types';\n\nexport const ATTEMPTS_PAGE_SIZE = 25;\n\nconst AttemptsList = () => {\n const intl = useIntl();\n const { courseId = '' } = useParams();\n const [filters, setFilters] = useState({ page: 0, emailOrUsername: '' });\n const { data = { results: [], count: 0, numPages: 0 }, isLoading = false } = useAttempts(courseId, {\n ...filters,\n pageSize: ATTEMPTS_PAGE_SIZE\n });\n\n const columns = useMemo(() => [\n { accessor: 'user.username', Header: intl.formatMessage(messages.username), Filter: UsernameFilter, },\n { accessor: 'examName', Header: intl.formatMessage(messages.examName), disableFilters: true, },\n { accessor: 'allowedTimeLimitMins', Header: intl.formatMessage(messages.timeLimit), disableFilters: true, },\n { accessor: 'type', Header: intl.formatMessage(messages.type), disableFilters: true, },\n { accessor: 'startTime', Header: intl.formatMessage(messages.startedAt), disableFilters: true, },\n { accessor: 'endTime', Header: intl.formatMessage(messages.completedAt), disableFilters: true, },\n { accessor: 'status', Header: intl.formatMessage(messages.status), disableFilters: true, },\n ], [intl]);\n\n const handleFetchData = (data: DataTableFetchDataProps) => {\n const emailOrUsernameFilter = data.filters?.find((f) => f.id === 'user.username');\n const newEmailOrUsername = emailOrUsernameFilter ? emailOrUsernameFilter.value : '';\n if (filters.emailOrUsername !== newEmailOrUsername) {\n setFilters((prevFilters) => ({ ...prevFilters, emailOrUsername: newEmailOrUsername, page: 0 }));\n return;\n }\n if (data.pageIndex !== filters.page) {\n setFilters((prevFilters) => ({ ...prevFilters, page: data.pageIndex }));\n }\n };\n\n return (\n <DataTable\n className=\"mt-3\"\n columns={columns}\n data={data.results}\n state={{\n pageIndex: filters.page,\n pageSize: ATTEMPTS_PAGE_SIZE,\n filters: [\n { id: 'emailOrUsername', value: filters.emailOrUsername }\n ]\n }}\n fetchData={handleFetchData}\n isFilterable\n isLoading={isLoading}\n isPaginated\n isSortable\n itemCount={data.count}\n manualFilters\n manualPagination\n manualSortBy\n pageSize={ATTEMPTS_PAGE_SIZE}\n pageCount={data.numPages}\n FilterStatusComponent={() => null}\n >\n <DataTable.TableControlBar className=\"bg-light-200 py-3 px-4\" />\n <DataTable.Table />\n <DataTable.EmptyTable content={intl.formatMessage(messages.noAttempts)} />\n <DataTable.TableFooter />\n </DataTable>\n );\n};\n\nexport default AttemptsList;\n"]}
@@ -0,0 +1,8 @@
1
+ import { Allowance } from '../../specialExams/types';
2
+ interface DeleteAllowanceProps {
3
+ allowance: Allowance;
4
+ isOpen: boolean;
5
+ onClose: () => void;
6
+ }
7
+ declare const DeleteAllowanceModal: ({ allowance, isOpen, onClose }: DeleteAllowanceProps) => import("react/jsx-runtime").JSX.Element;
8
+ export default DeleteAllowanceModal;
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useParams } from 'react-router-dom';
3
+ import { useIntl } from '@openedx/frontend-base';
4
+ import { Button, ModalDialog } from '@openedx/paragon';
5
+ import { useDeleteAllowance } from '../../specialExams/data/apiHook';
6
+ import messages from '../../specialExams/messages';
7
+ import { useAlert } from '../../providers/AlertProvider';
8
+ const DeleteAllowanceModal = ({ allowance, isOpen, onClose }) => {
9
+ const { courseId = '' } = useParams();
10
+ const intl = useIntl();
11
+ const { mutate: deleteAllowance, isPending } = useDeleteAllowance(courseId);
12
+ const { showModal } = useAlert();
13
+ const handleDelete = () => {
14
+ deleteAllowance({ examId: allowance.proctoredExam.id, userIds: [allowance.user.id], allowanceType: allowance.key }, {
15
+ onSuccess: () => {
16
+ onClose();
17
+ },
18
+ onError: () => {
19
+ showModal({
20
+ message: intl.formatMessage(messages.deleteError),
21
+ variant: 'danger',
22
+ });
23
+ }
24
+ });
25
+ };
26
+ return (_jsxs(ModalDialog, { isOpen: isOpen, onClose: onClose, title: intl.formatMessage(messages.deleteAllowance), isOverflowVisible: false, children: [_jsx(ModalDialog.Body, { className: "pt-4 px-4 pb-2.5", children: _jsx("p", { children: intl.formatMessage(messages.deleteConfirmation, { user: allowance.user.username, examName: allowance.proctoredExam.examName }) }) }), _jsxs(ModalDialog.Footer, { className: "pt-2", children: [_jsx(Button, { variant: "tertiary", onClick: onClose, children: intl.formatMessage(messages.cancel) }), _jsx(Button, { className: "ml-2", onClick: handleDelete, disabled: isPending, children: intl.formatMessage(messages.delete) })] })] }));
27
+ };
28
+ export default DeleteAllowanceModal;
29
+ //# sourceMappingURL=DeleteAllowanceModal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DeleteAllowanceModal.js","sourceRoot":"","sources":["../../../src/specialExams/components/DeleteAllowanceModal.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,QAAQ,MAAM,4BAA4B,CAAC;AAElD,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAQxD,MAAM,oBAAoB,GAAG,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAwB,EAAE,EAAE;IACpF,MAAM,EAAE,QAAQ,GAAG,EAAE,EAAE,GAAG,SAAS,EAAE,CAAC;IACtC,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC5E,MAAM,EAAE,SAAS,EAAE,GAAG,QAAQ,EAAE,CAAC;IACjC,MAAM,YAAY,GAAG,GAAG,EAAE;QACxB,eAAe,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,aAAa,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,aAAa,EAAE,SAAS,CAAC,GAAG,EAAE,EAAE;YAClH,SAAS,EAAE,GAAG,EAAE;gBACd,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,OAAO,EAAE,GAAG,EAAE;gBACZ,SAAS,CAAC;oBACR,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,WAAW,CAAC;oBACjD,OAAO,EAAE,QAAQ;iBAClB,CAAC,CAAC;YACL,CAAC;SACF,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO,CACL,MAAC,WAAW,IAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,iBAAiB,EAAE,KAAK,aAC1H,KAAC,WAAW,CAAC,IAAI,IAAC,SAAS,EAAC,kBAAkB,YAC5C,sBAAI,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,kBAAkB,EAAE,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC,GAAK,GACtH,EACnB,MAAC,WAAW,CAAC,MAAM,IAAC,SAAS,EAAC,MAAM,aAClC,KAAC,MAAM,IAAC,OAAO,EAAC,UAAU,EAAC,OAAO,EAAE,OAAO,YAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAU,EAC3F,KAAC,MAAM,IACL,SAAS,EAAC,MAAM,EAChB,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,SAAS,YAElB,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC,GAC7B,IACU,IACT,CACf,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,oBAAoB,CAAC","sourcesContent":["import { useParams } from 'react-router-dom';\nimport { useIntl } from '@openedx/frontend-base';\nimport { Button, ModalDialog } from '@openedx/paragon';\nimport { useDeleteAllowance } from '@src/specialExams/data/apiHook';\nimport messages from '@src/specialExams/messages';\nimport { Allowance } from '@src/specialExams/types';\nimport { useAlert } from '@src/providers/AlertProvider';\n\ninterface DeleteAllowanceProps {\n allowance: Allowance,\n isOpen: boolean,\n onClose: () => void,\n}\n\nconst DeleteAllowanceModal = ({ allowance, isOpen, onClose }: DeleteAllowanceProps) => {\n const { courseId = '' } = useParams();\n const intl = useIntl();\n const { mutate: deleteAllowance, isPending } = useDeleteAllowance(courseId);\n const { showModal } = useAlert();\n const handleDelete = () => {\n deleteAllowance({ examId: allowance.proctoredExam.id, userIds: [allowance.user.id], allowanceType: allowance.key }, {\n onSuccess: () => {\n onClose();\n },\n onError: () => {\n showModal({\n message: intl.formatMessage(messages.deleteError),\n variant: 'danger',\n });\n }\n });\n };\n\n return (\n <ModalDialog isOpen={isOpen} onClose={onClose} title={intl.formatMessage(messages.deleteAllowance)} isOverflowVisible={false}>\n <ModalDialog.Body className=\"pt-4 px-4 pb-2.5\">\n <p>{intl.formatMessage(messages.deleteConfirmation, { user: allowance.user.username, examName: allowance.proctoredExam.examName })}</p>\n </ModalDialog.Body>\n <ModalDialog.Footer className=\"pt-2\">\n <Button variant=\"tertiary\" onClick={onClose}>{intl.formatMessage(messages.cancel)}</Button>\n <Button\n className=\"ml-2\"\n onClick={handleDelete}\n disabled={isPending}\n >\n {intl.formatMessage(messages.delete)}\n </Button>\n </ModalDialog.Footer>\n </ModalDialog>\n );\n};\n\nexport default DeleteAllowanceModal;\n"]}
@@ -0,0 +1,8 @@
1
+ import { Allowance } from '../../specialExams/types';
2
+ interface EditAllowanceModalProps {
3
+ isOpen: boolean;
4
+ onClose: () => void;
5
+ allowance: Allowance;
6
+ }
7
+ declare const EditAllowanceModal: ({ isOpen, onClose, allowance }: EditAllowanceModalProps) => import("react/jsx-runtime").JSX.Element | null;
8
+ export default EditAllowanceModal;
@@ -0,0 +1,62 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useState } from 'react';
3
+ import { useParams } from 'react-router-dom';
4
+ import { useIntl } from '@openedx/frontend-base';
5
+ import { ActionRow, Button, Form, ModalDialog } from '@openedx/paragon';
6
+ import SpecifyLearnerField from '../../components/SpecifyLearnerField';
7
+ import { useLearner } from '../../data/apiHook';
8
+ import { addLabel, addPlaceholder, allowanceTypesOptions } from '../../specialExams/constants';
9
+ import { useAddAllowance } from '../../specialExams/data/apiHook';
10
+ import messages from '../../specialExams/messages';
11
+ import { useAlert } from '../../providers/AlertProvider';
12
+ const EditAllowanceModal = ({ isOpen, onClose, allowance }) => {
13
+ const intl = useIntl();
14
+ const { courseId = '' } = useParams();
15
+ const { mutate: editAllowance } = useAddAllowance(courseId);
16
+ const { data: learner, refetch } = useLearner(courseId, allowance.user.username);
17
+ const [editedAllowance, setEditedAllowance] = useState({
18
+ allowanceType: allowance.key,
19
+ value: allowance.value,
20
+ });
21
+ const { showModal } = useAlert();
22
+ useEffect(() => {
23
+ if (isOpen) {
24
+ // Refetch learner data when modal opens to ensure we have the most up-to-date information
25
+ refetch();
26
+ }
27
+ }, [isOpen, refetch]);
28
+ const handleChanges = (e) => {
29
+ const { name, value } = e.target;
30
+ setEditedAllowance(prev => (Object.assign(Object.assign({}, prev), { [name]: value })));
31
+ };
32
+ const hasChanges = useMemo(() => {
33
+ return (editedAllowance.allowanceType !== allowance.key && editedAllowance.allowanceType !== '')
34
+ || editedAllowance.value !== allowance.value;
35
+ }, [editedAllowance, allowance]);
36
+ const handleEdit = (e) => {
37
+ e.preventDefault();
38
+ editAllowance({
39
+ examType: allowance.proctoredExam.examType,
40
+ allowanceType: editedAllowance.allowanceType,
41
+ value: editedAllowance.value,
42
+ userIds: [allowance.user.username],
43
+ examIds: [allowance.proctoredExam.id],
44
+ }, {
45
+ onSuccess: () => {
46
+ onClose();
47
+ },
48
+ onError: () => {
49
+ showModal({
50
+ message: intl.formatMessage(messages.editAllowanceError),
51
+ variant: 'danger',
52
+ });
53
+ }
54
+ });
55
+ };
56
+ if (!(learner === null || learner === void 0 ? void 0 : learner.username)) {
57
+ return null;
58
+ }
59
+ return (_jsxs(ModalDialog, { isOpen: isOpen, onClose: onClose, title: intl.formatMessage(messages.editAllowance), isOverflowVisible: false, size: "lg", children: [_jsx(ModalDialog.Header, { className: "border-bottom border-light-700", children: _jsx(ModalDialog.Title, { className: "text-primary-700", children: intl.formatMessage(messages.editAllowance) }) }), _jsxs(Form, { className: "position-relative overflow-auto", onSubmit: handleEdit, children: [_jsxs(ModalDialog.Body, { children: [_jsx(Form.Group, { controlId: "learner-info", children: _jsx(SpecifyLearnerField, { learner: learner, onClickSelect: () => { } }) }), _jsxs(Form.Group, { controlId: "select-exam-type", children: [_jsxs(Form.Label, { className: "text-primary-500 x-small", children: [intl.formatMessage(messages.selectExamType), ":"] }), _jsx(Form.Control, { as: "select", disabled: true, children: _jsx("option", { value: "", children: allowance.proctoredExam.examType }) })] }), _jsxs(Form.Group, { controlId: "select-exams", children: [_jsxs(Form.Label, { className: "text-primary-500 x-small", children: [intl.formatMessage(messages.selectExams), ":"] }), _jsx("div", { children: _jsx(Form.Checkbox, { className: "mt-2", checked: true, disabled: true, labelClassName: "text-primary-500", children: allowance.proctoredExam.examName }, allowance.proctoredExam.examName) })] }), _jsxs(Form.Group, { controlId: "select-allowance-type", children: [_jsxs(Form.Label, { className: "text-primary-500 x-small", children: [intl.formatMessage(messages.selectAllowanceType), ":"] }), _jsx(Form.Control, { as: "select", name: "allowanceType", onChange: handleChanges, value: editedAllowance.allowanceType, children: allowanceTypesOptions.map(option => (_jsx("option", { value: option.value, children: intl.formatMessage(option.label) }, option.value))) })] }), _jsxs(Form.Group, { controlId: "allowance-value", children: [_jsxs(Form.Label, { className: "text-primary-500 x-small", children: [intl.formatMessage(addLabel[editedAllowance.allowanceType || 'additional_time_granted']), ":"] }), _jsx(Form.Control, { type: editedAllowance.allowanceType === 'review_policy_exception' ? 'text' : 'number', placeholder: intl.formatMessage(addPlaceholder[editedAllowance.allowanceType || 'additional_time_granted']), name: "value", value: editedAllowance.value, onChange: handleChanges })] })] }), _jsx(ModalDialog.Footer, { className: "border-top border-light-700", children: _jsxs(ActionRow, { children: [_jsx(Button, { variant: "tertiary", onClick: onClose, children: intl.formatMessage(messages.cancel) }), _jsx(Button, { variant: "primary", type: "submit", disabled: !hasChanges, children: intl.formatMessage(messages.editAllowance) })] }) })] })] }));
60
+ };
61
+ export default EditAllowanceModal;
62
+ //# sourceMappingURL=EditAllowanceModal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditAllowanceModal.js","sourceRoot":"","sources":["../../../src/specialExams/components/EditAllowanceModal.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useIntl } from '@openedx/frontend-base';\nimport { ActionRow, Button, Form, ModalDialog } from '@openedx/paragon';\nimport SpecifyLearnerField from '@src/components/SpecifyLearnerField';\nimport { useLearner } from '@src/data/apiHook';\nimport { addLabel, addPlaceholder, allowanceTypesOptions } from '@src/specialExams/constants';\nimport { useAddAllowance } from '@src/specialExams/data/apiHook';\nimport messages from '@src/specialExams/messages';\nimport { Allowance } from '@src/specialExams/types';\nimport { useAlert } from '@src/providers/AlertProvider';\n\ninterface EditAllowanceModalProps {\n isOpen: boolean,\n onClose: () => void,\n allowance: Allowance,\n}\n\nconst EditAllowanceModal = ({ isOpen, onClose, allowance }: EditAllowanceModalProps) => {\n const intl = useIntl();\n const { courseId = '' } = useParams<{ courseId: string }>();\n const { mutate: editAllowance } = useAddAllowance(courseId);\n const { data: learner, refetch } = useLearner(courseId, allowance.user.username);\n const [editedAllowance, setEditedAllowance] = useState({\n allowanceType: allowance.key,\n value: allowance.value,\n });\n const { showModal } = useAlert();\n\n useEffect(() => {\n if (isOpen) {\n // Refetch learner data when modal opens to ensure we have the most up-to-date information\n refetch();\n }\n }, [isOpen, refetch]);\n\n const handleChanges = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {\n const { name, value } = e.target;\n setEditedAllowance(prev => ({ ...prev, [name]: value }));\n };\n\n const hasChanges = useMemo(() => {\n return (editedAllowance.allowanceType !== allowance.key && editedAllowance.allowanceType !== '')\n || editedAllowance.value !== allowance.value;\n }, [editedAllowance, allowance]);\n\n const handleEdit = (e: React.FormEvent<HTMLFormElement>) => {\n e.preventDefault();\n editAllowance({\n examType: allowance.proctoredExam.examType,\n allowanceType: editedAllowance.allowanceType,\n value: editedAllowance.value,\n userIds: [allowance.user.username],\n examIds: [allowance.proctoredExam.id],\n }, {\n onSuccess: () => {\n onClose();\n },\n onError: () => {\n showModal({\n message: intl.formatMessage(messages.editAllowanceError),\n variant: 'danger',\n });\n }\n });\n };\n\n if (!learner?.username) {\n return null;\n }\n\n return (\n <ModalDialog isOpen={isOpen} onClose={onClose} title={intl.formatMessage(messages.editAllowance)} isOverflowVisible={false} size=\"lg\">\n <ModalDialog.Header className=\"border-bottom border-light-700\">\n <ModalDialog.Title className=\"text-primary-700\">{intl.formatMessage(messages.editAllowance)}</ModalDialog.Title>\n </ModalDialog.Header>\n <Form className=\"position-relative overflow-auto\" onSubmit={handleEdit}>\n <ModalDialog.Body>\n <Form.Group controlId=\"learner-info\">\n <SpecifyLearnerField learner={learner} onClickSelect={() => {}} />\n </Form.Group>\n <Form.Group controlId=\"select-exam-type\">\n <Form.Label className=\"text-primary-500 x-small\">{intl.formatMessage(messages.selectExamType)}:</Form.Label>\n <Form.Control as=\"select\" disabled>\n <option value=\"\">{allowance.proctoredExam.examType}</option>\n </Form.Control>\n </Form.Group>\n <Form.Group controlId=\"select-exams\">\n <Form.Label className=\"text-primary-500 x-small\">{intl.formatMessage(messages.selectExams)}:</Form.Label>\n <div>\n <Form.Checkbox className=\"mt-2\" key={allowance.proctoredExam.examName} checked disabled labelClassName=\"text-primary-500\">\n {allowance.proctoredExam.examName}\n </Form.Checkbox>\n </div>\n </Form.Group>\n <Form.Group controlId=\"select-allowance-type\">\n <Form.Label className=\"text-primary-500 x-small\">{intl.formatMessage(messages.selectAllowanceType)}:</Form.Label>\n <Form.Control as=\"select\" name=\"allowanceType\" onChange={handleChanges} value={editedAllowance.allowanceType}>\n {allowanceTypesOptions.map(option => (\n <option key={option.value} value={option.value}>{intl.formatMessage(option.label)}</option>\n ))}\n </Form.Control>\n </Form.Group>\n <Form.Group controlId=\"allowance-value\">\n <Form.Label className=\"text-primary-500 x-small\">{intl.formatMessage(addLabel[editedAllowance.allowanceType || 'additional_time_granted'])}:</Form.Label>\n <Form.Control\n type={editedAllowance.allowanceType === 'review_policy_exception' ? 'text' : 'number'}\n placeholder={intl.formatMessage(addPlaceholder[editedAllowance.allowanceType || 'additional_time_granted'])}\n name=\"value\"\n value={editedAllowance.value}\n onChange={handleChanges}\n />\n </Form.Group>\n </ModalDialog.Body>\n <ModalDialog.Footer className=\"border-top border-light-700\">\n <ActionRow>\n <Button variant=\"tertiary\" onClick={onClose}>{intl.formatMessage(messages.cancel)}</Button>\n <Button variant=\"primary\" type=\"submit\" disabled={!hasChanges}>{intl.formatMessage(messages.editAllowance)}</Button>\n </ActionRow>\n </ModalDialog.Footer>\n </Form>\n </ModalDialog>\n );\n};\n\nexport default EditAllowanceModal;\n"]}
@@ -0,0 +1,43 @@
1
+ export declare const ALLOWANCES_PAGE_SIZE = 25;
2
+ export declare const addLabel: {
3
+ additional_time_granted: {
4
+ id: string;
5
+ defaultMessage: string;
6
+ description: string;
7
+ };
8
+ review_policy_exception: {
9
+ id: string;
10
+ defaultMessage: string;
11
+ description: string;
12
+ };
13
+ time_multiplier: {
14
+ id: string;
15
+ defaultMessage: string;
16
+ description: string;
17
+ };
18
+ };
19
+ export declare const addPlaceholder: {
20
+ additional_time_granted: {
21
+ id: string;
22
+ defaultMessage: string;
23
+ description: string;
24
+ };
25
+ review_policy_exception: {
26
+ id: string;
27
+ defaultMessage: string;
28
+ description: string;
29
+ };
30
+ time_multiplier: {
31
+ id: string;
32
+ defaultMessage: string;
33
+ description: string;
34
+ };
35
+ };
36
+ export declare const allowanceTypesOptions: {
37
+ value: string;
38
+ label: {
39
+ id: string;
40
+ defaultMessage: string;
41
+ description: string;
42
+ };
43
+ }[];
@@ -0,0 +1,19 @@
1
+ import messages from '../specialExams/messages';
2
+ export const ALLOWANCES_PAGE_SIZE = 25;
3
+ export const addLabel = {
4
+ additional_time_granted: messages.addAdditionalTimeGranted,
5
+ review_policy_exception: messages.addReviewPolicyException,
6
+ time_multiplier: messages.addTimeMultiplier,
7
+ };
8
+ export const addPlaceholder = {
9
+ additional_time_granted: messages.addTimePlaceholder,
10
+ review_policy_exception: messages.exceptionPlaceholder,
11
+ time_multiplier: messages.timeMultiplierPlaceholder,
12
+ };
13
+ export const allowanceTypesOptions = [
14
+ { value: '', label: messages.allowanceType },
15
+ { value: 'additional_time_granted', label: messages.additionalTime },
16
+ { value: 'review_policy_exception', label: messages.reviewPolicy },
17
+ { value: 'time_multiplier', label: messages.timeMultiplier },
18
+ ];
19
+ //# sourceMappingURL=constants.js.map