@openedx/frontend-app-instructor-dashboard 1.0.0-alpha.4 → 1.0.0-alpha.41

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 (303) hide show
  1. package/dist/Main.d.ts +1 -1
  2. package/dist/Main.js +11 -18
  3. package/dist/Main.js.map +1 -1
  4. package/dist/app.js +0 -6
  5. package/dist/app.js.map +1 -1
  6. package/dist/certificates/CertificatesPage.d.ts +1 -0
  7. package/dist/certificates/CertificatesPage.js +315 -2
  8. package/dist/certificates/CertificatesPage.js.map +1 -1
  9. package/dist/certificates/CertificatesPage.scss +90 -0
  10. package/dist/certificates/components/CertificateTable.d.ts +14 -0
  11. package/dist/certificates/components/CertificateTable.js +88 -0
  12. package/dist/certificates/components/CertificateTable.js.map +1 -0
  13. package/dist/certificates/components/CertificatesPageHeader.d.ts +7 -0
  14. package/dist/certificates/components/CertificatesPageHeader.js +11 -0
  15. package/dist/certificates/components/CertificatesPageHeader.js.map +1 -0
  16. package/dist/certificates/components/CertificatesToolbar.d.ts +11 -0
  17. package/dist/certificates/components/CertificatesToolbar.js +33 -0
  18. package/dist/certificates/components/CertificatesToolbar.js.map +1 -0
  19. package/dist/certificates/components/DisableCertificatesModal.d.ts +9 -0
  20. package/dist/certificates/components/DisableCertificatesModal.js +24 -0
  21. package/dist/certificates/components/DisableCertificatesModal.js.map +1 -0
  22. package/dist/certificates/components/FilterDropdown.d.ts +8 -0
  23. package/dist/certificates/components/FilterDropdown.js +50 -0
  24. package/dist/certificates/components/FilterDropdown.js.map +1 -0
  25. package/dist/certificates/components/GenerateCertificatesModal.d.ts +9 -0
  26. package/dist/certificates/components/GenerateCertificatesModal.js +19 -0
  27. package/dist/certificates/components/GenerateCertificatesModal.js.map +1 -0
  28. package/dist/certificates/components/GenerationHistoryTable.d.ts +11 -0
  29. package/dist/certificates/components/GenerationHistoryTable.js +25 -0
  30. package/dist/certificates/components/GenerationHistoryTable.js.map +1 -0
  31. package/dist/certificates/components/GrantExceptionsModal.d.ts +9 -0
  32. package/dist/certificates/components/GrantExceptionsModal.js +55 -0
  33. package/dist/certificates/components/GrantExceptionsModal.js.map +1 -0
  34. package/dist/certificates/components/InvalidateCertificateModal.d.ts +8 -0
  35. package/dist/certificates/components/InvalidateCertificateModal.js +26 -0
  36. package/dist/certificates/components/InvalidateCertificateModal.js.map +1 -0
  37. package/dist/certificates/components/IssuedCertificatesTab.d.ts +18 -0
  38. package/dist/certificates/components/IssuedCertificatesTab.js +6 -0
  39. package/dist/certificates/components/IssuedCertificatesTab.js.map +1 -0
  40. package/dist/certificates/components/LearnerActionModal.d.ts +16 -0
  41. package/dist/certificates/components/LearnerActionModal.js +27 -0
  42. package/dist/certificates/components/LearnerActionModal.js.map +1 -0
  43. package/dist/certificates/components/RegenerateCertificatesModal.d.ts +11 -0
  44. package/dist/certificates/components/RegenerateCertificatesModal.js +46 -0
  45. package/dist/certificates/components/RegenerateCertificatesModal.js.map +1 -0
  46. package/dist/certificates/components/RemoveExceptionModal.d.ts +9 -0
  47. package/dist/certificates/components/RemoveExceptionModal.js +10 -0
  48. package/dist/certificates/components/RemoveExceptionModal.js.map +1 -0
  49. package/dist/certificates/components/RemoveInvalidationModal.d.ts +9 -0
  50. package/dist/certificates/components/RemoveInvalidationModal.js +10 -0
  51. package/dist/certificates/components/RemoveInvalidationModal.js.map +1 -0
  52. package/dist/certificates/constants.d.ts +15 -0
  53. package/dist/certificates/constants.js +16 -0
  54. package/dist/certificates/constants.js.map +1 -0
  55. package/dist/certificates/data/api.d.ts +30 -0
  56. package/dist/certificates/data/api.js +126 -0
  57. package/dist/certificates/data/api.js.map +1 -0
  58. package/dist/certificates/data/apiHook.d.ts +63 -0
  59. package/dist/certificates/data/apiHook.js +133 -0
  60. package/dist/certificates/data/apiHook.js.map +1 -0
  61. package/dist/certificates/data/queryKeys.d.ts +9 -0
  62. package/dist/certificates/data/queryKeys.js +9 -0
  63. package/dist/certificates/data/queryKeys.js.map +1 -0
  64. package/dist/certificates/messages.d.ts +548 -0
  65. package/dist/certificates/messages.js +550 -0
  66. package/dist/certificates/messages.js.map +1 -0
  67. package/dist/certificates/types.d.ts +66 -0
  68. package/dist/certificates/types.js +26 -0
  69. package/dist/certificates/types.js.map +1 -0
  70. package/dist/certificates/utils/errorHandling.d.ts +12 -0
  71. package/dist/certificates/utils/errorHandling.js +24 -0
  72. package/dist/certificates/utils/errorHandling.js.map +1 -0
  73. package/dist/certificates/utils/filterUtils.d.ts +4 -0
  74. package/dist/certificates/utils/filterUtils.js +31 -0
  75. package/dist/certificates/utils/filterUtils.js.map +1 -0
  76. package/dist/certificates/utils/index.d.ts +2 -0
  77. package/dist/certificates/utils/index.js +2 -0
  78. package/dist/certificates/utils/index.js.map +1 -0
  79. package/dist/components/ActionCard.d.ts +2 -2
  80. package/dist/components/ActionCard.js +1 -1
  81. package/dist/components/ActionCard.js.map +1 -1
  82. package/dist/components/CodeEditor.d.ts +5 -0
  83. package/dist/components/CodeEditor.js +34 -0
  84. package/dist/components/CodeEditor.js.map +1 -0
  85. package/dist/components/PendingTasks.d.ts +3 -1
  86. package/dist/components/PendingTasks.js +3 -2
  87. package/dist/components/PendingTasks.js.map +1 -1
  88. package/dist/components/SpecifyLearnerField.d.ts +4 -1
  89. package/dist/components/SpecifyLearnerField.js +55 -15
  90. package/dist/components/SpecifyLearnerField.js.map +1 -1
  91. package/dist/components/SpecifyProblemField.d.ts +13 -0
  92. package/dist/components/SpecifyProblemField.js +52 -0
  93. package/dist/components/SpecifyProblemField.js.map +1 -0
  94. package/dist/components/UsernameFilter.d.ts +7 -0
  95. package/dist/components/UsernameFilter.js +19 -0
  96. package/dist/components/UsernameFilter.js.map +1 -0
  97. package/dist/components/messages.d.ts +45 -0
  98. package/dist/components/messages.js +45 -0
  99. package/dist/components/messages.js.map +1 -1
  100. package/dist/courseInfo/types.d.ts +5 -0
  101. package/dist/courseInfo/types.js.map +1 -1
  102. package/dist/courseTeam/CourseTeamPage.js +25 -2
  103. package/dist/courseTeam/CourseTeamPage.js.map +1 -1
  104. package/dist/courseTeam/components/AddTeamMemberModal.d.ts +6 -0
  105. package/dist/courseTeam/components/AddTeamMemberModal.js +61 -0
  106. package/dist/courseTeam/components/AddTeamMemberModal.js.map +1 -0
  107. package/dist/courseTeam/components/EditTeamMemberModal.d.ts +8 -0
  108. package/dist/courseTeam/components/EditTeamMemberModal.js +102 -0
  109. package/dist/courseTeam/components/EditTeamMemberModal.js.map +1 -0
  110. package/dist/courseTeam/components/MembersContent.d.ts +6 -0
  111. package/dist/courseTeam/components/MembersContent.js +48 -0
  112. package/dist/courseTeam/components/MembersContent.js.map +1 -0
  113. package/dist/courseTeam/components/RoleFilter.d.ts +7 -0
  114. package/dist/courseTeam/components/RoleFilter.js +22 -0
  115. package/dist/courseTeam/components/RoleFilter.js.map +1 -0
  116. package/dist/courseTeam/components/RolesContent.d.ts +3 -0
  117. package/dist/courseTeam/components/RolesContent.js +25 -0
  118. package/dist/courseTeam/components/RolesContent.js.map +1 -0
  119. package/dist/courseTeam/constants.d.ts +3 -0
  120. package/dist/courseTeam/constants.js +4 -0
  121. package/dist/courseTeam/constants.js.map +1 -0
  122. package/dist/courseTeam/data/api.d.ts +6 -0
  123. package/dist/courseTeam/data/api.js +38 -0
  124. package/dist/courseTeam/data/api.js.map +1 -0
  125. package/dist/courseTeam/data/apiHook.d.ts +8 -0
  126. package/dist/courseTeam/data/apiHook.js +32 -0
  127. package/dist/courseTeam/data/apiHook.js.map +1 -0
  128. package/dist/courseTeam/data/queryKeys.d.ts +7 -0
  129. package/dist/courseTeam/data/queryKeys.js +14 -0
  130. package/dist/courseTeam/data/queryKeys.js.map +1 -0
  131. package/dist/courseTeam/messages.d.ts +258 -0
  132. package/dist/courseTeam/messages.js +260 -0
  133. package/dist/courseTeam/messages.js.map +1 -0
  134. package/dist/courseTeam/types.d.ts +29 -0
  135. package/dist/courseTeam/types.js +3 -0
  136. package/dist/courseTeam/types.js.map +1 -0
  137. package/dist/data/api.d.ts +2 -1
  138. package/dist/data/api.js +9 -3
  139. package/dist/data/api.js.map +1 -1
  140. package/dist/data/apiHook.d.ts +1 -0
  141. package/dist/data/apiHook.js +11 -2
  142. package/dist/data/apiHook.js.map +1 -1
  143. package/dist/data/queryKeys.d.ts +4 -0
  144. package/dist/data/queryKeys.js +4 -0
  145. package/dist/data/queryKeys.js.map +1 -1
  146. package/dist/data/utils.d.ts +2 -0
  147. package/dist/data/utils.js +9 -0
  148. package/dist/data/utils.js.map +1 -0
  149. package/dist/dateExtensions/components/AddExtensionModal.d.ts +1 -1
  150. package/dist/dateExtensions/components/AddExtensionModal.js +6 -7
  151. package/dist/dateExtensions/components/AddExtensionModal.js.map +1 -1
  152. package/dist/dateExtensions/components/DateExtensionsList.js +3 -14
  153. package/dist/dateExtensions/components/DateExtensionsList.js.map +1 -1
  154. package/dist/dateExtensions/data/apiHook.d.ts +2 -2
  155. package/dist/dateExtensions/data/apiHook.js +4 -4
  156. package/dist/dateExtensions/data/apiHook.js.map +1 -1
  157. package/dist/dateExtensions/messages.d.ts +0 -5
  158. package/dist/dateExtensions/messages.js +1 -6
  159. package/dist/dateExtensions/messages.js.map +1 -1
  160. package/dist/enrollments/EnrollmentsPage.js +34 -7
  161. package/dist/enrollments/EnrollmentsPage.js.map +1 -1
  162. package/dist/enrollments/components/AddBetaTestersModal.d.ts +6 -0
  163. package/dist/enrollments/components/AddBetaTestersModal.js +69 -0
  164. package/dist/enrollments/components/AddBetaTestersModal.js.map +1 -0
  165. package/dist/enrollments/components/EnrollLearnersModal.d.ts +6 -0
  166. package/dist/enrollments/components/EnrollLearnersModal.js +53 -0
  167. package/dist/enrollments/components/EnrollLearnersModal.js.map +1 -0
  168. package/dist/enrollments/components/EnrollmentStatusModal.js +3 -3
  169. package/dist/enrollments/components/EnrollmentStatusModal.js.map +1 -1
  170. package/dist/enrollments/components/EnrollmentsList.d.ts +3 -2
  171. package/dist/enrollments/components/EnrollmentsList.js +13 -14
  172. package/dist/enrollments/components/EnrollmentsList.js.map +1 -1
  173. package/dist/enrollments/components/UnenrollModal.d.ts +1 -1
  174. package/dist/enrollments/components/UnenrollModal.js +29 -3
  175. package/dist/enrollments/components/UnenrollModal.js.map +1 -1
  176. package/dist/enrollments/components/UpdateBetaTesterModal.d.ts +8 -0
  177. package/dist/enrollments/components/UpdateBetaTesterModal.js +72 -0
  178. package/dist/enrollments/components/UpdateBetaTesterModal.js.map +1 -0
  179. package/dist/enrollments/data/api.d.ts +3 -1
  180. package/dist/enrollments/data/api.js +11 -1
  181. package/dist/enrollments/data/api.js.map +1 -1
  182. package/dist/enrollments/data/apiHook.d.ts +5 -3
  183. package/dist/enrollments/data/apiHook.js +21 -3
  184. package/dist/enrollments/data/apiHook.js.map +1 -1
  185. package/dist/enrollments/data/queryKeys.d.ts +1 -1
  186. package/dist/enrollments/data/queryKeys.js.map +1 -1
  187. package/dist/enrollments/messages.d.ts +131 -1
  188. package/dist/enrollments/messages.js +136 -6
  189. package/dist/enrollments/messages.js.map +1 -1
  190. package/dist/enrollments/types.d.ts +25 -0
  191. package/dist/enrollments/types.js.map +1 -1
  192. package/dist/grading/GradingPage.js +15 -2
  193. package/dist/grading/GradingPage.js.map +1 -1
  194. package/dist/grading/components/GradingActionRow.d.ts +2 -0
  195. package/dist/grading/components/GradingActionRow.js +20 -0
  196. package/dist/grading/components/GradingActionRow.js.map +1 -0
  197. package/dist/grading/components/GradingConfigurationModal.d.ts +6 -0
  198. package/dist/grading/components/GradingConfigurationModal.js +15 -0
  199. package/dist/grading/components/GradingConfigurationModal.js.map +1 -0
  200. package/dist/grading/components/GradingLearnerContent.d.ts +7 -0
  201. package/dist/grading/components/GradingLearnerContent.js +199 -0
  202. package/dist/grading/components/GradingLearnerContent.js.map +1 -0
  203. package/dist/grading/data/api.d.ts +6 -0
  204. package/dist/grading/data/api.js +59 -0
  205. package/dist/grading/data/api.js.map +1 -0
  206. package/dist/grading/data/apiHook.d.ts +6 -0
  207. package/dist/grading/data/apiHook.js +29 -0
  208. package/dist/grading/data/apiHook.js.map +1 -0
  209. package/dist/grading/data/queryKeys.d.ts +9 -0
  210. package/dist/grading/data/queryKeys.js +8 -0
  211. package/dist/grading/data/queryKeys.js.map +1 -0
  212. package/dist/grading/messages.d.ts +248 -0
  213. package/dist/grading/messages.js +250 -0
  214. package/dist/grading/messages.js.map +1 -0
  215. package/dist/grading/types.d.ts +11 -0
  216. package/dist/grading/types.js +2 -0
  217. package/dist/grading/types.js.map +1 -0
  218. package/dist/hooks/useDebouncedFilter.d.ts +1 -0
  219. package/dist/hooks/useDebouncedFilter.js +5 -0
  220. package/dist/hooks/useDebouncedFilter.js.map +1 -1
  221. package/dist/instructorNav/InstructorNav.js +16 -4
  222. package/dist/instructorNav/InstructorNav.js.map +1 -1
  223. package/dist/messages.d.ts +8 -0
  224. package/dist/messages.js +10 -0
  225. package/dist/messages.js.map +1 -0
  226. package/dist/pageWrapper/PageWrapper.js +3 -1
  227. package/dist/pageWrapper/PageWrapper.js.map +1 -1
  228. package/dist/providers/AccessErrorObserver.d.ts +9 -0
  229. package/dist/providers/AccessErrorObserver.js +35 -0
  230. package/dist/providers/AccessErrorObserver.js.map +1 -0
  231. package/dist/providers/AccessErrorProvider.d.ts +19 -0
  232. package/dist/providers/AccessErrorProvider.js +51 -0
  233. package/dist/providers/AccessErrorProvider.js.map +1 -0
  234. package/dist/providers/AlertProvider.js +1 -1
  235. package/dist/providers/AlertProvider.js.map +1 -1
  236. package/dist/providers/messages.d.ts +33 -0
  237. package/dist/providers/messages.js +35 -0
  238. package/dist/providers/messages.js.map +1 -0
  239. package/dist/provides.d.ts +2 -1
  240. package/dist/provides.js +3 -2
  241. package/dist/provides.js.map +1 -1
  242. package/dist/routes.d.ts +1 -1
  243. package/dist/routes.js +2 -2
  244. package/dist/routes.js.map +1 -1
  245. package/dist/slots/CourseInfoSlot/CourseInfoSlot.d.ts +2 -0
  246. package/dist/slots/CourseInfoSlot/CourseInfoSlot.js +14 -0
  247. package/dist/slots/CourseInfoSlot/CourseInfoSlot.js.map +1 -0
  248. package/dist/slots.js +13 -1
  249. package/dist/slots.js.map +1 -1
  250. package/dist/specialExams/SpecialExamsPage.js +14 -2
  251. package/dist/specialExams/SpecialExamsPage.js.map +1 -1
  252. package/dist/specialExams/components/AddAllowanceModal.d.ts +6 -0
  253. package/dist/specialExams/components/AddAllowanceModal.js +84 -0
  254. package/dist/specialExams/components/AddAllowanceModal.js.map +1 -0
  255. package/dist/specialExams/components/Allowances.d.ts +2 -0
  256. package/dist/specialExams/components/Allowances.js +89 -0
  257. package/dist/specialExams/components/Allowances.js.map +1 -0
  258. package/dist/specialExams/components/AllowancesList.d.ts +8 -0
  259. package/dist/specialExams/components/AllowancesList.js +63 -0
  260. package/dist/specialExams/components/AllowancesList.js.map +1 -0
  261. package/dist/specialExams/components/AttemptsList.d.ts +3 -0
  262. package/dist/specialExams/components/AttemptsList.js +50 -0
  263. package/dist/specialExams/components/AttemptsList.js.map +1 -0
  264. package/dist/specialExams/components/DeleteAllowanceModal.d.ts +8 -0
  265. package/dist/specialExams/components/DeleteAllowanceModal.js +29 -0
  266. package/dist/specialExams/components/DeleteAllowanceModal.js.map +1 -0
  267. package/dist/specialExams/components/EditAllowanceModal.d.ts +8 -0
  268. package/dist/specialExams/components/EditAllowanceModal.js +62 -0
  269. package/dist/specialExams/components/EditAllowanceModal.js.map +1 -0
  270. package/dist/specialExams/constants.d.ts +43 -0
  271. package/dist/specialExams/constants.js +19 -0
  272. package/dist/specialExams/constants.js.map +1 -0
  273. package/dist/specialExams/data/api.d.ts +7 -0
  274. package/dist/specialExams/data/api.js +55 -0
  275. package/dist/specialExams/data/api.js.map +1 -0
  276. package/dist/specialExams/data/apiHook.d.ts +6 -0
  277. package/dist/specialExams/data/apiHook.js +37 -0
  278. package/dist/specialExams/data/apiHook.js.map +1 -0
  279. package/dist/specialExams/data/queryKeys.d.ts +8 -0
  280. package/dist/specialExams/data/queryKeys.js +9 -0
  281. package/dist/specialExams/data/queryKeys.js.map +1 -0
  282. package/dist/specialExams/messages.d.ts +228 -0
  283. package/dist/specialExams/messages.js +230 -0
  284. package/dist/specialExams/messages.js.map +1 -0
  285. package/dist/specialExams/types.d.ts +65 -0
  286. package/dist/specialExams/types.js +2 -0
  287. package/dist/specialExams/types.js.map +1 -0
  288. package/dist/style.scss +16 -0
  289. package/dist/testUtils.js +2 -2
  290. package/dist/testUtils.js.map +1 -1
  291. package/dist/types/index.d.ts +15 -0
  292. package/dist/types/index.js.map +1 -1
  293. package/dist/utils/formatters.d.ts +5 -0
  294. package/dist/utils/formatters.js +10 -0
  295. package/dist/utils/formatters.js.map +1 -1
  296. package/package.json +6 -6
  297. package/dist/app.scss +0 -10
  298. package/dist/providers/QueryProvider.d.ts +0 -6
  299. package/dist/providers/QueryProvider.js +0 -16
  300. package/dist/providers/QueryProvider.js.map +0 -1
  301. package/dist/providers.d.ts +0 -3
  302. package/dist/providers.js +0 -8
  303. package/dist/providers.js.map +0 -1
package/dist/Main.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- import './app.scss';
1
+ import './style.scss';
2
2
  declare const Main: () => import("react/jsx-runtime").JSX.Element;
3
3
  export default Main;
package/dist/Main.js CHANGED
@@ -1,25 +1,18 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { lazy, Suspense } from 'react';
3
- import { CurrentAppProvider } from '@openedx/frontend-base';
4
- import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
2
+ import { CurrentAppProvider, getSiteConfig, useIntl } from '@openedx/frontend-base';
3
+ import { Helmet } from 'react-helmet';
5
4
  import { Outlet } from 'react-router-dom';
6
5
  import { AlertProvider } from './providers/AlertProvider';
6
+ import { AccessErrorProvider } from './providers/AccessErrorProvider';
7
7
  import { appId } from './constants';
8
+ import messages from './messages';
8
9
  import PageWrapper from './pageWrapper/PageWrapper';
9
- import './app.scss';
10
- /*
11
- * Use a dynamic import guarded by process.env.NODE_ENV so the consumer's
12
- * webpack dead-code-eliminates this in production builds. webpackIgnore
13
- * tells webpack not to resolve the module at build time in dev mode, and
14
- * the .catch() gracefully handles the case where it is not installed.
15
- */
16
- const loadDevtools = () => import(/* webpackIgnore: true */ '@tanstack/react-query-devtools')
17
- .then((m) => ({ default: m.ReactQueryDevtools }))
18
- .catch(() => ({ default: () => null }));
19
- const ReactQueryDevtools = process.env.NODE_ENV === 'development'
20
- ? lazy(loadDevtools)
21
- : null;
22
- const queryClient = new QueryClient();
23
- const Main = () => (_jsx(CurrentAppProvider, { appId: appId, children: _jsx(QueryClientProvider, { client: queryClient, children: _jsx(AlertProvider, { children: _jsxs("main", { className: "d-flex flex-column flex-grow-1", children: [_jsx(PageWrapper, { children: _jsx(Outlet, {}) }), ReactQueryDevtools && (_jsx(Suspense, { fallback: null, children: _jsx(ReactQueryDevtools, { initialIsOpen: false }) }))] }) }) }) }));
10
+ import './style.scss';
11
+ const Main = () => {
12
+ const { formatMessage } = useIntl();
13
+ return (_jsxs(CurrentAppProvider, { appId: appId, children: [_jsx(Helmet, { children: _jsx("title", { children: formatMessage(messages['instructor-dashboard.page.title'], {
14
+ siteName: getSiteConfig().siteName,
15
+ }) }) }), _jsx(AlertProvider, { children: _jsx(AccessErrorProvider, { children: _jsx("main", { className: "d-flex flex-column flex-grow-1", children: _jsx(PageWrapper, { children: _jsx(Outlet, {}) }) }) }) })] }));
16
+ };
24
17
  export default Main;
25
18
  //# sourceMappingURL=Main.js.map
package/dist/Main.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Main.js","sourceRoot":"","sources":["../src/Main.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,WAAW,MAAM,2BAA2B,CAAC;AAEpD,OAAO,YAAY,CAAC;AAEpB;;;;;GAKG;AACH,MAAM,YAAY,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,yBAAyB,CAAC,gCAAgC,CAAC;KAC1F,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,kBAAkB,EAAE,CAAC,CAAC;KAChD,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;AAE1C,MAAM,kBAAkB,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa;IAC/D,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC;IACpB,CAAC,CAAC,IAAI,CAAC;AAET,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;AAEtC,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,CACjB,KAAC,kBAAkB,IAAC,KAAK,EAAE,KAAK,YAC9B,KAAC,mBAAmB,IAAC,MAAM,EAAE,WAAW,YACtC,KAAC,aAAa,cACZ,gBAAM,SAAS,EAAC,gCAAgC,aAC9C,KAAC,WAAW,cACV,KAAC,MAAM,KAAG,GACE,EACb,kBAAkB,IAAI,CACrB,KAAC,QAAQ,IAAC,QAAQ,EAAE,IAAI,YACtB,KAAC,kBAAkB,IAAC,aAAa,EAAE,KAAK,GAAI,GACnC,CACZ,IACI,GACO,GACI,GACH,CACtB,CAAC;AAEF,eAAe,IAAI,CAAC","sourcesContent":["import { lazy, Suspense } from 'react';\nimport { CurrentAppProvider } from '@openedx/frontend-base';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\nimport { Outlet } from 'react-router-dom';\nimport { AlertProvider } from './providers/AlertProvider';\nimport { appId } from './constants';\nimport PageWrapper from './pageWrapper/PageWrapper';\n\nimport './app.scss';\n\n/*\n * Use a dynamic import guarded by process.env.NODE_ENV so the consumer's\n * webpack dead-code-eliminates this in production builds. webpackIgnore\n * tells webpack not to resolve the module at build time in dev mode, and\n * the .catch() gracefully handles the case where it is not installed.\n */\nconst loadDevtools = () => import(/* webpackIgnore: true */ '@tanstack/react-query-devtools')\n .then((m) => ({ default: m.ReactQueryDevtools }))\n .catch(() => ({ default: () => null }));\n\nconst ReactQueryDevtools = process.env.NODE_ENV === 'development'\n ? lazy(loadDevtools)\n : null;\n\nconst queryClient = new QueryClient();\n\nconst Main = () => (\n <CurrentAppProvider appId={appId}>\n <QueryClientProvider client={queryClient}>\n <AlertProvider>\n <main className=\"d-flex flex-column flex-grow-1\">\n <PageWrapper>\n <Outlet />\n </PageWrapper>\n {ReactQueryDevtools && (\n <Suspense fallback={null}>\n <ReactQueryDevtools initialIsOpen={false} />\n </Suspense>\n )}\n </main>\n </AlertProvider>\n </QueryClientProvider>\n </CurrentAppProvider>\n);\n\nexport default Main;\n"]}
1
+ {"version":3,"file":"Main.js","sourceRoot":"","sources":["../src/Main.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACpF,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,QAAQ,MAAM,YAAY,CAAC;AAClC,OAAO,WAAW,MAAM,2BAA2B,CAAC;AAEpD,OAAO,cAAc,CAAC;AAEtB,MAAM,IAAI,GAAG,GAAG,EAAE;IAChB,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAC;IACpC,OAAO,CACL,MAAC,kBAAkB,IAAC,KAAK,EAAE,KAAK,aAC9B,KAAC,MAAM,cACL,0BACG,aAAa,CAAC,QAAQ,CAAC,iCAAiC,CAAC,EAAE;wBAC1D,QAAQ,EAAE,aAAa,EAAE,CAAC,QAAQ;qBACnC,CAAC,GACI,GACD,EACT,KAAC,aAAa,cACZ,KAAC,mBAAmB,cAClB,eAAM,SAAS,EAAC,gCAAgC,YAC9C,KAAC,WAAW,cACV,KAAC,MAAM,KAAG,GACE,GACT,GACa,GACR,IACG,CACtB,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,IAAI,CAAC","sourcesContent":["import { CurrentAppProvider, getSiteConfig, useIntl } from '@openedx/frontend-base';\nimport { Helmet } from 'react-helmet';\nimport { Outlet } from 'react-router-dom';\nimport { AlertProvider } from './providers/AlertProvider';\nimport { AccessErrorProvider } from './providers/AccessErrorProvider';\nimport { appId } from './constants';\nimport messages from './messages';\nimport PageWrapper from './pageWrapper/PageWrapper';\n\nimport './style.scss';\n\nconst Main = () => {\n const { formatMessage } = useIntl();\n return (\n <CurrentAppProvider appId={appId}>\n <Helmet>\n <title>\n {formatMessage(messages['instructor-dashboard.page.title'], {\n siteName: getSiteConfig().siteName,\n })}\n </title>\n </Helmet>\n <AlertProvider>\n <AccessErrorProvider>\n <main className=\"d-flex flex-column flex-grow-1\">\n <PageWrapper>\n <Outlet />\n </PageWrapper>\n </main>\n </AccessErrorProvider>\n </AlertProvider>\n </CurrentAppProvider>\n );\n};\n\nexport default Main;\n"]}
package/dist/app.js CHANGED
@@ -1,18 +1,12 @@
1
1
  import { appId } from './constants';
2
2
  import routes from './routes';
3
3
  import slots from './slots';
4
- import providers from './providers';
5
4
  import provides from './provides';
6
5
  const app = {
7
6
  appId,
8
7
  routes,
9
- providers,
10
8
  provides,
11
9
  slots,
12
- config: {
13
- NODE_ENV: 'development',
14
- LMS_BASE_URL: 'http://local.openedx.io:8000'
15
- },
16
10
  };
17
11
  export default app;
18
12
  //# sourceMappingURL=app.js.map
package/dist/app.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACvC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,SAAS,MAAM,gBAAgB,CAAC;AACvC,OAAO,QAAQ,MAAM,eAAe,CAAC;AAErC,MAAM,GAAG,GAAQ;IACf,KAAK;IACL,MAAM;IACN,SAAS;IACT,QAAQ;IACR,KAAK;IACL,MAAM,EAAE;QACN,QAAQ,EAAE,aAAa;QACvB,YAAY,EAAE,8BAA8B;KAC7C;CACF,CAAC;AAEF,eAAe,GAAG,CAAC","sourcesContent":["import { App } from '@openedx/frontend-base';\nimport { appId } from '@src/constants';\nimport routes from '@src/routes';\nimport slots from '@src/slots';\nimport providers from '@src/providers';\nimport provides from '@src/provides';\n\nconst app: App = {\n appId,\n routes,\n providers,\n provides,\n slots,\n config: {\n NODE_ENV: 'development',\n LMS_BASE_URL: 'http://local.openedx.io:8000'\n },\n};\n\nexport default app;\n"]}
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACvC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,QAAQ,MAAM,eAAe,CAAC;AAErC,MAAM,GAAG,GAAQ;IACf,KAAK;IACL,MAAM;IACN,QAAQ;IACR,KAAK;CACN,CAAC;AAEF,eAAe,GAAG,CAAC","sourcesContent":["import { App } from '@openedx/frontend-base';\nimport { appId } from '@src/constants';\nimport routes from '@src/routes';\nimport slots from '@src/slots';\nimport provides from '@src/provides';\n\nconst app: App = {\n appId,\n routes,\n provides,\n slots,\n};\n\nexport default app;\n"]}
@@ -1,2 +1,3 @@
1
+ import './CertificatesPage.scss';
1
2
  declare const CertificatesPage: () => import("react/jsx-runtime").JSX.Element;
2
3
  export default CertificatesPage;
@@ -1,6 +1,319 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useCallback } from 'react';
3
+ import { useParams } from 'react-router-dom';
4
+ import { Card, Container, Button, ButtonGroup, Alert } from '@openedx/paragon';
5
+ import { useIntl } from '@openedx/frontend-base';
6
+ import { useAlert } from '../providers/AlertProvider';
7
+ import { useCourseInfo } from '../data/apiHook';
8
+ import { PendingTasks } from '../components/PendingTasks';
9
+ import CertificatesPageHeader from '../certificates/components/CertificatesPageHeader';
10
+ import IssuedCertificatesTab from '../certificates/components/IssuedCertificatesTab';
11
+ import GenerationHistoryTable from '../certificates/components/GenerationHistoryTable';
12
+ import GrantExceptionsModal from '../certificates/components/GrantExceptionsModal';
13
+ import InvalidateCertificateModal from '../certificates/components/InvalidateCertificateModal';
14
+ import RemoveExceptionModal from '../certificates/components/RemoveExceptionModal';
15
+ import RemoveInvalidationModal from '../certificates/components/RemoveInvalidationModal';
16
+ import DisableCertificatesModal from '../certificates/components/DisableCertificatesModal';
17
+ import RegenerateCertificatesModal from '../certificates/components/RegenerateCertificatesModal';
18
+ import GenerateCertificatesModal from '../certificates/components/GenerateCertificatesModal';
19
+ import { useCertificateGenerationHistory, useGrantBulkExceptions, useInvalidateCertificate, useIssuedCertificates, useRegenerateCertificates, useRemoveException, useRemoveInvalidation, useToggleCertificateGeneration, useUploadBulkExceptionsCsv, } from '../certificates/data/apiHook';
20
+ import { CertificateFilter } from '../certificates/types';
21
+ import { CERTIFICATES_PAGE_SIZE, TAB_KEYS, MODAL_TITLES, ALERT_VARIANTS } from '../certificates/constants';
22
+ import { getErrorMessage } from '../certificates/utils/errorHandling';
23
+ import messages from '../certificates/messages';
24
+ import './CertificatesPage.scss';
2
25
  const CertificatesPage = () => {
3
- return (_jsx("div", { children: _jsx("h3", { children: "Certificates" }) }));
26
+ const intl = useIntl();
27
+ const { courseId = '' } = useParams();
28
+ const { showToast, showModal } = useAlert();
29
+ const { data: courseInfo } = useCourseInfo(courseId);
30
+ const [filter, setFilter] = useState(CertificateFilter.ALL_LEARNERS);
31
+ const [search, setSearch] = useState('');
32
+ const [certificatesPage, setCertificatesPage] = useState(0);
33
+ const [tasksPage, setTasksPage] = useState(0);
34
+ const [activeTab, setActiveTab] = useState(TAB_KEYS.ISSUED);
35
+ const [selectedUsername, setSelectedUsername] = useState('');
36
+ const [selectedEmail, setSelectedEmail] = useState('');
37
+ const [isCertificateGenerationEnabled, setIsCertificateGenerationEnabled] = useState(true);
38
+ const [isGrantExceptionsOpen, setIsGrantExceptionsOpen] = useState(false);
39
+ const [isInvalidateCertificateOpen, setIsInvalidateCertificateOpen] = useState(false);
40
+ const [isRemoveExceptionOpen, setIsRemoveExceptionOpen] = useState(false);
41
+ const [isRemoveInvalidationOpen, setIsRemoveInvalidationOpen] = useState(false);
42
+ const [isDisableCertificatesOpen, setIsDisableCertificatesOpen] = useState(false);
43
+ const [isRegenerateModalOpen, setIsRegenerateModalOpen] = useState(false);
44
+ const [isGenerateModalOpen, setIsGenerateModalOpen] = useState(false);
45
+ const [isPendingTasksOpen, setIsPendingTasksOpen] = useState(false);
46
+ const { data: certificatesData, isLoading: isLoadingCertificates, } = useIssuedCertificates(courseId, {
47
+ page: certificatesPage,
48
+ pageSize: CERTIFICATES_PAGE_SIZE,
49
+ filter,
50
+ search,
51
+ });
52
+ const { data: historyData, isLoading: isLoadingHistory, } = useCertificateGenerationHistory(courseId, {
53
+ page: tasksPage,
54
+ pageSize: CERTIFICATES_PAGE_SIZE,
55
+ });
56
+ const { mutate: grantExceptions, isPending: isGrantingExceptions } = useGrantBulkExceptions(courseId);
57
+ const { mutate: uploadCsvExceptions, isPending: isUploadingCsv } = useUploadBulkExceptionsCsv(courseId);
58
+ const { mutate: invalidateCert, isPending: isInvalidating } = useInvalidateCertificate(courseId);
59
+ const { mutate: removeExcept, isPending: isRemovingException } = useRemoveException(courseId);
60
+ const { mutate: removeInval, isPending: isRemovingInvalidation } = useRemoveInvalidation(courseId);
61
+ const { mutate: toggleGeneration, isPending: isTogglingGeneration } = useToggleCertificateGeneration(courseId);
62
+ const { mutate: regenerateCerts } = useRegenerateCertificates(courseId);
63
+ const handleGrantExceptions = useCallback((learners, notes) => {
64
+ grantExceptions({ learners, notes }, {
65
+ onSuccess: (data) => {
66
+ setIsGrantExceptionsOpen(false);
67
+ if (data.errors && data.errors.length > 0) {
68
+ const errorMessages = data.errors.map(err => `${err.learner}: ${err.message}`).join('\n');
69
+ showModal({
70
+ title: intl.formatMessage(messages.errorModalTitle),
71
+ message: `Some exceptions failed:\n${errorMessages}`,
72
+ variant: ALERT_VARIANTS.WARNING,
73
+ });
74
+ }
75
+ if (data.success && data.success.length > 0) {
76
+ showToast(intl.formatMessage(messages.exceptionsGrantedToast, { count: data.success.length }));
77
+ }
78
+ },
79
+ onError: (error) => {
80
+ showModal({
81
+ title: intl.formatMessage(messages.errorModalTitle),
82
+ message: getErrorMessage(error, intl.formatMessage(messages.errorGrantException)),
83
+ variant: ALERT_VARIANTS.DANGER,
84
+ });
85
+ },
86
+ });
87
+ }, [grantExceptions, showToast, showModal, intl]);
88
+ const handleUploadCsvExceptions = useCallback((file) => {
89
+ uploadCsvExceptions(file, {
90
+ onSuccess: (data) => {
91
+ setIsGrantExceptionsOpen(false);
92
+ if (data.errors && data.errors.length > 0) {
93
+ const errorMessages = data.errors.map(err => `${err.learner}: ${err.message}`).join('\n');
94
+ showModal({
95
+ title: intl.formatMessage(messages.errorModalTitle),
96
+ message: `Some exceptions failed:\n${errorMessages}`,
97
+ variant: ALERT_VARIANTS.WARNING,
98
+ });
99
+ }
100
+ if (data.success && data.success.length > 0) {
101
+ showToast(intl.formatMessage(messages.exceptionsGrantedToast, { count: data.success.length }));
102
+ }
103
+ },
104
+ onError: (error) => {
105
+ showModal({
106
+ title: intl.formatMessage(messages.errorModalTitle),
107
+ message: getErrorMessage(error, intl.formatMessage(messages.errorGrantException)),
108
+ variant: ALERT_VARIANTS.DANGER,
109
+ });
110
+ },
111
+ });
112
+ }, [uploadCsvExceptions, showToast, showModal, intl]);
113
+ const handleInvalidateCertificate = useCallback((learners, notes) => {
114
+ invalidateCert({ learners, notes }, {
115
+ onSuccess: (data) => {
116
+ setIsInvalidateCertificateOpen(false);
117
+ if (data.errors && data.errors.length > 0) {
118
+ const errorMessages = data.errors.map(err => `${err.learner}: ${err.message}`).join('\n');
119
+ showModal({
120
+ title: intl.formatMessage(messages.errorModalTitle),
121
+ message: `Some invalidations failed:\n${errorMessages}`,
122
+ variant: ALERT_VARIANTS.WARNING,
123
+ confirmText: intl.formatMessage(messages.close),
124
+ });
125
+ }
126
+ if (data.success && data.success.length > 0) {
127
+ showToast(intl.formatMessage(messages.certificatesInvalidatedToast, { count: data.success.length }));
128
+ }
129
+ },
130
+ onError: (error) => {
131
+ showModal({
132
+ title: intl.formatMessage(messages.errorModalTitle),
133
+ message: getErrorMessage(error, intl.formatMessage(messages.errorInvalidateCertificate)),
134
+ variant: ALERT_VARIANTS.DANGER,
135
+ });
136
+ },
137
+ });
138
+ }, [invalidateCert, showToast, showModal, intl]);
139
+ const handleRemoveExceptionClick = useCallback((username, email) => {
140
+ setSelectedUsername(username);
141
+ setSelectedEmail(email);
142
+ setIsRemoveExceptionOpen(true);
143
+ }, []);
144
+ const handleRemoveExceptionConfirm = useCallback(() => {
145
+ // Backend accepts either username or email - use whichever is available
146
+ const identifier = selectedUsername || selectedEmail;
147
+ if (!identifier) {
148
+ showModal({
149
+ title: MODAL_TITLES.ERROR,
150
+ message: intl.formatMessage(messages.errorRemoveException) + ': Username or email is required',
151
+ variant: ALERT_VARIANTS.DANGER,
152
+ });
153
+ return;
154
+ }
155
+ removeExcept({ username: identifier }, {
156
+ onSuccess: () => {
157
+ setIsRemoveExceptionOpen(false);
158
+ setSelectedUsername('');
159
+ setSelectedEmail('');
160
+ showToast(intl.formatMessage(messages.exceptionRemovedToast, { email: selectedEmail }));
161
+ },
162
+ onError: (error) => {
163
+ showModal({
164
+ title: intl.formatMessage(messages.errorModalTitle),
165
+ message: getErrorMessage(error, intl.formatMessage(messages.errorRemoveException)),
166
+ variant: ALERT_VARIANTS.DANGER,
167
+ });
168
+ },
169
+ });
170
+ }, [removeExcept, selectedUsername, selectedEmail, showToast, showModal, intl]);
171
+ const handleRemoveInvalidationClick = useCallback((username, email) => {
172
+ setSelectedUsername(username);
173
+ setSelectedEmail(email);
174
+ setIsRemoveInvalidationOpen(true);
175
+ }, []);
176
+ const handleRemoveInvalidationConfirm = useCallback(() => {
177
+ // Backend accepts either username or email - use whichever is available
178
+ const identifier = selectedUsername || selectedEmail;
179
+ if (!identifier) {
180
+ showModal({
181
+ title: MODAL_TITLES.ERROR,
182
+ message: intl.formatMessage(messages.errorRemoveInvalidation) + ': Username or email is required',
183
+ variant: ALERT_VARIANTS.DANGER,
184
+ });
185
+ return;
186
+ }
187
+ removeInval({ username: identifier }, {
188
+ onSuccess: () => {
189
+ setIsRemoveInvalidationOpen(false);
190
+ setSelectedUsername('');
191
+ setSelectedEmail('');
192
+ showToast(intl.formatMessage(messages.invalidationRemovedToast, { email: selectedEmail }));
193
+ },
194
+ onError: (error) => {
195
+ showModal({
196
+ title: intl.formatMessage(messages.errorModalTitle),
197
+ message: getErrorMessage(error, intl.formatMessage(messages.errorRemoveInvalidation)),
198
+ variant: ALERT_VARIANTS.DANGER,
199
+ });
200
+ },
201
+ });
202
+ }, [removeInval, selectedUsername, selectedEmail, showToast, showModal, intl]);
203
+ const handleToggleCertificateGeneration = useCallback(() => {
204
+ const newState = !isCertificateGenerationEnabled;
205
+ toggleGeneration(newState, {
206
+ onSuccess: () => {
207
+ setIsCertificateGenerationEnabled(newState);
208
+ setIsDisableCertificatesOpen(false);
209
+ showToast(newState
210
+ ? intl.formatMessage(messages.successEnableCertificates)
211
+ : intl.formatMessage(messages.successDisableCertificates));
212
+ },
213
+ onError: (error) => {
214
+ showModal({
215
+ title: MODAL_TITLES.ERROR,
216
+ message: getErrorMessage(error, intl.formatMessage(messages.errorToggleCertificateGeneration)),
217
+ variant: ALERT_VARIANTS.DANGER,
218
+ });
219
+ },
220
+ });
221
+ }, [isCertificateGenerationEnabled, toggleGeneration, showToast, showModal, intl]);
222
+ const handleRegenerateCertificatesClick = useCallback(() => {
223
+ // Don't open modal for disabled filters
224
+ if (filter === CertificateFilter.ALL_LEARNERS || filter === CertificateFilter.INVALIDATED) {
225
+ return;
226
+ }
227
+ // For granted exceptions, open the generate modal
228
+ if (filter === CertificateFilter.GRANTED_EXCEPTIONS) {
229
+ setIsGenerateModalOpen(true);
230
+ }
231
+ else {
232
+ // For other filters, open the regenerate modal
233
+ setIsRegenerateModalOpen(true);
234
+ }
235
+ }, [filter]);
236
+ const handleRegenerateCertificatesConfirm = useCallback(() => {
237
+ regenerateCerts({ filter, onlyWithoutCertificate: false }, {
238
+ onSuccess: () => {
239
+ setIsRegenerateModalOpen(false);
240
+ showToast(intl.formatMessage(messages.certificatesRegeneratedToast));
241
+ },
242
+ onError: (error) => {
243
+ setIsRegenerateModalOpen(false);
244
+ const errorMessage = getErrorMessage(error, intl.formatMessage(messages.errorRegenerateCertificates));
245
+ showModal({
246
+ title: MODAL_TITLES.ERROR,
247
+ message: errorMessage,
248
+ variant: ALERT_VARIANTS.DANGER,
249
+ });
250
+ },
251
+ });
252
+ }, [regenerateCerts, filter, showToast, showModal, intl]);
253
+ const handleGenerateCertificatesConfirm = useCallback((onlyWithoutCertificate) => {
254
+ regenerateCerts({ filter, onlyWithoutCertificate }, {
255
+ onSuccess: () => {
256
+ setIsGenerateModalOpen(false);
257
+ showToast(intl.formatMessage(messages.certificatesRegeneratedToast));
258
+ },
259
+ onError: (error) => {
260
+ setIsGenerateModalOpen(false);
261
+ const errorMessage = getErrorMessage(error, intl.formatMessage(messages.errorRegenerateCertificates));
262
+ showModal({
263
+ title: MODAL_TITLES.ERROR,
264
+ message: errorMessage,
265
+ variant: ALERT_VARIANTS.DANGER,
266
+ });
267
+ },
268
+ });
269
+ }, [regenerateCerts, filter, showToast, showModal, intl]);
270
+ const handleTabKeyDown = useCallback((event) => {
271
+ const tabs = [TAB_KEYS.ISSUED, TAB_KEYS.HISTORY];
272
+ const currentIndex = tabs.indexOf(activeTab);
273
+ let nextIndex = currentIndex;
274
+ switch (event.key) {
275
+ case 'ArrowRight':
276
+ case 'ArrowDown':
277
+ event.preventDefault();
278
+ nextIndex = (currentIndex + 1) % tabs.length;
279
+ break;
280
+ case 'ArrowLeft':
281
+ case 'ArrowUp':
282
+ event.preventDefault();
283
+ nextIndex = (currentIndex - 1 + tabs.length) % tabs.length;
284
+ break;
285
+ case 'Home':
286
+ event.preventDefault();
287
+ nextIndex = 0;
288
+ break;
289
+ case 'End':
290
+ event.preventDefault();
291
+ nextIndex = tabs.length - 1;
292
+ break;
293
+ default:
294
+ return;
295
+ }
296
+ const nextTab = tabs[nextIndex];
297
+ setActiveTab(nextTab);
298
+ // Focus the new tab
299
+ setTimeout(() => {
300
+ var _a;
301
+ (_a = document.getElementById(`certificates-tab-${nextTab}`)) === null || _a === void 0 ? void 0 : _a.focus();
302
+ }, 0);
303
+ }, [activeTab]);
304
+ // Check if certificate management is disabled
305
+ if (courseInfo && !courseInfo.certificatesEnabled) {
306
+ return (_jsx(Container, { className: "mt-4.5 mb-4", fluid: true, children: _jsx(Alert, { variant: "warning", children: intl.formatMessage(messages.certificatesDisabledMessage) }) }));
307
+ }
308
+ return (_jsxs(Container, { className: "mt-4.5 mb-4", fluid: true, children: [_jsx(CertificatesPageHeader, { onGrantExceptions: () => setIsGrantExceptionsOpen(true), onInvalidateCertificate: () => setIsInvalidateCertificateOpen(true), onStudentGeneratedCertificates: () => setIsDisableCertificatesOpen(true) }), _jsxs(Card, { variant: "muted", className: "pt-3 pt-md-4 pb-4 pb-md-6 certificates-card", children: [_jsxs(ButtonGroup, { className: "d-block mx-4", role: "tablist", children: [_jsx(Button, { id: "certificates-tab-issued", role: "tab", "aria-controls": "certificates-tabpanel-issued", "aria-selected": activeTab === TAB_KEYS.ISSUED, tabIndex: activeTab === TAB_KEYS.ISSUED ? 0 : -1, onClick: () => setActiveTab(TAB_KEYS.ISSUED), onKeyDown: handleTabKeyDown, variant: activeTab === TAB_KEYS.ISSUED ? 'primary' : 'outline-primary', children: intl.formatMessage(messages.issuedCertificatesTab) }), _jsx(Button, { id: "certificates-tab-history", role: "tab", "aria-controls": "certificates-tabpanel-history", "aria-selected": activeTab === TAB_KEYS.HISTORY, tabIndex: activeTab === TAB_KEYS.HISTORY ? 0 : -1, onClick: () => setActiveTab(TAB_KEYS.HISTORY), onKeyDown: handleTabKeyDown, variant: activeTab === TAB_KEYS.HISTORY ? 'primary' : 'outline-primary', children: intl.formatMessage(messages.generationHistoryTab) })] }), _jsx("div", { id: "certificates-tabpanel-issued", role: "tabpanel", "aria-labelledby": "certificates-tab-issued", hidden: activeTab !== TAB_KEYS.ISSUED, children: activeTab === TAB_KEYS.ISSUED && (_jsx(IssuedCertificatesTab, { data: (certificatesData === null || certificatesData === void 0 ? void 0 : certificatesData.results) || [], isLoading: isLoadingCertificates, itemCount: (certificatesData === null || certificatesData === void 0 ? void 0 : certificatesData.count) || 0, pageCount: (certificatesData === null || certificatesData === void 0 ? void 0 : certificatesData.numPages) || 0, search: search, onSearchChange: setSearch, filter: filter, onFilterChange: setFilter, currentPage: certificatesPage, onPageChange: setCertificatesPage, onRemoveException: handleRemoveExceptionClick, onRemoveInvalidation: handleRemoveInvalidationClick, onRegenerateCertificates: handleRegenerateCertificatesClick })) }), _jsx("div", { id: "certificates-tabpanel-history", role: "tabpanel", "aria-labelledby": "certificates-tab-history", hidden: activeTab !== TAB_KEYS.HISTORY, children: activeTab === TAB_KEYS.HISTORY && (_jsx("div", { className: "d-flex flex-column mt-3 mt-md-4", children: _jsx(GenerationHistoryTable, { data: (historyData === null || historyData === void 0 ? void 0 : historyData.results) || [], isLoading: isLoadingHistory, itemCount: (historyData === null || historyData === void 0 ? void 0 : historyData.count) || 0, pageCount: (historyData === null || historyData === void 0 ? void 0 : historyData.numPages) || 0, currentPage: tasksPage, onPageChange: setTasksPage }) })) })] }), _jsx(GrantExceptionsModal, { isOpen: isGrantExceptionsOpen, onClose: () => setIsGrantExceptionsOpen(false), onSubmit: handleGrantExceptions, onUploadCsv: handleUploadCsvExceptions, isSubmitting: isGrantingExceptions || isUploadingCsv }), _jsx(InvalidateCertificateModal, { isOpen: isInvalidateCertificateOpen, onClose: () => setIsInvalidateCertificateOpen(false), onSubmit: handleInvalidateCertificate, isSubmitting: isInvalidating }), _jsx(RemoveExceptionModal, { isOpen: isRemoveExceptionOpen, email: selectedEmail, onClose: () => {
309
+ setIsRemoveExceptionOpen(false);
310
+ setSelectedUsername('');
311
+ setSelectedEmail('');
312
+ }, onConfirm: handleRemoveExceptionConfirm, isSubmitting: isRemovingException }), _jsx(RemoveInvalidationModal, { isOpen: isRemoveInvalidationOpen, email: selectedEmail, onClose: () => {
313
+ setIsRemoveInvalidationOpen(false);
314
+ setSelectedUsername('');
315
+ setSelectedEmail('');
316
+ }, onConfirm: handleRemoveInvalidationConfirm, isSubmitting: isRemovingInvalidation }), _jsx(DisableCertificatesModal, { isOpen: isDisableCertificatesOpen, isEnabled: isCertificateGenerationEnabled, onClose: () => setIsDisableCertificatesOpen(false), onConfirm: handleToggleCertificateGeneration, isSubmitting: isTogglingGeneration }), _jsx(RegenerateCertificatesModal, { isOpen: isRegenerateModalOpen, onClose: () => setIsRegenerateModalOpen(false), onConfirm: handleRegenerateCertificatesConfirm, isSubmitting: false, filter: filter, learnerCount: (certificatesData === null || certificatesData === void 0 ? void 0 : certificatesData.count) || 0 }), _jsx(GenerateCertificatesModal, { isOpen: isGenerateModalOpen, onClose: () => setIsGenerateModalOpen(false), onConfirm: handleGenerateCertificatesConfirm, isSubmitting: false, learnerCount: (certificatesData === null || certificatesData === void 0 ? void 0 : certificatesData.count) || 0 }), _jsx(PendingTasks, { isOpen: isPendingTasksOpen, onToggle: () => setIsPendingTasksOpen(prev => !prev) })] }));
4
317
  };
5
318
  export default CertificatesPage;
6
319
  //# sourceMappingURL=CertificatesPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CertificatesPage.js","sourceRoot":"","sources":["../../src/certificates/CertificatesPage.tsx"],"names":[],"mappings":";AAAA,MAAM,gBAAgB,GAAG,GAAG,EAAE;IAC5B,OAAO,CACL,wBACE,wCAAqB,GACjB,CACP,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,gBAAgB,CAAC","sourcesContent":["const CertificatesPage = () => {\n return (\n <div>\n <h3>Certificates</h3>\n </div>\n );\n};\n\nexport default CertificatesPage;\n"]}
1
+ {"version":3,"file":"CertificatesPage.js","sourceRoot":"","sources":["../../src/certificates/CertificatesPage.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useState, useCallback } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { Card, Container, Button, ButtonGroup, Alert } from '@openedx/paragon';\nimport { useIntl } from '@openedx/frontend-base';\nimport { useAlert } from '@src/providers/AlertProvider';\nimport { useCourseInfo } from '@src/data/apiHook';\nimport { PendingTasks } from '@src/components/PendingTasks';\nimport CertificatesPageHeader from '@src/certificates/components/CertificatesPageHeader';\nimport IssuedCertificatesTab from '@src/certificates/components/IssuedCertificatesTab';\nimport GenerationHistoryTable from '@src/certificates/components/GenerationHistoryTable';\nimport GrantExceptionsModal from '@src/certificates/components/GrantExceptionsModal';\nimport InvalidateCertificateModal from '@src/certificates/components/InvalidateCertificateModal';\nimport RemoveExceptionModal from '@src/certificates/components/RemoveExceptionModal';\nimport RemoveInvalidationModal from '@src/certificates/components/RemoveInvalidationModal';\nimport DisableCertificatesModal from '@src/certificates/components/DisableCertificatesModal';\nimport RegenerateCertificatesModal from '@src/certificates/components/RegenerateCertificatesModal';\nimport GenerateCertificatesModal from '@src/certificates/components/GenerateCertificatesModal';\nimport {\n useCertificateGenerationHistory,\n useGrantBulkExceptions,\n useInvalidateCertificate,\n useIssuedCertificates,\n useRegenerateCertificates,\n useRemoveException,\n useRemoveInvalidation,\n useToggleCertificateGeneration,\n useUploadBulkExceptionsCsv,\n} from '@src/certificates/data/apiHook';\nimport { CertificateFilter } from '@src/certificates/types';\nimport { CERTIFICATES_PAGE_SIZE, TAB_KEYS, MODAL_TITLES, ALERT_VARIANTS } from '@src/certificates/constants';\nimport { getErrorMessage } from '@src/certificates/utils/errorHandling';\nimport messages from '@src/certificates/messages';\nimport './CertificatesPage.scss';\n\nconst CertificatesPage = () => {\n const intl = useIntl();\n const { courseId = '' } = useParams<{ courseId: string }>();\n const { showToast, showModal } = useAlert();\n const { data: courseInfo } = useCourseInfo(courseId);\n\n const [filter, setFilter] = useState<CertificateFilter>(CertificateFilter.ALL_LEARNERS);\n const [search, setSearch] = useState('');\n const [certificatesPage, setCertificatesPage] = useState(0);\n const [tasksPage, setTasksPage] = useState(0);\n const [activeTab, setActiveTab] = useState<typeof TAB_KEYS.ISSUED | typeof TAB_KEYS.HISTORY>(TAB_KEYS.ISSUED);\n const [selectedUsername, setSelectedUsername] = useState('');\n const [selectedEmail, setSelectedEmail] = useState('');\n const [isCertificateGenerationEnabled, setIsCertificateGenerationEnabled] = useState(true);\n\n const [isGrantExceptionsOpen, setIsGrantExceptionsOpen] = useState(false);\n const [isInvalidateCertificateOpen, setIsInvalidateCertificateOpen] = useState(false);\n const [isRemoveExceptionOpen, setIsRemoveExceptionOpen] = useState(false);\n const [isRemoveInvalidationOpen, setIsRemoveInvalidationOpen] = useState(false);\n const [isDisableCertificatesOpen, setIsDisableCertificatesOpen] = useState(false);\n const [isRegenerateModalOpen, setIsRegenerateModalOpen] = useState(false);\n const [isGenerateModalOpen, setIsGenerateModalOpen] = useState(false);\n const [isPendingTasksOpen, setIsPendingTasksOpen] = useState(false);\n\n const {\n data: certificatesData,\n isLoading: isLoadingCertificates,\n } = useIssuedCertificates(courseId, {\n page: certificatesPage,\n pageSize: CERTIFICATES_PAGE_SIZE,\n filter,\n search,\n });\n\n const {\n data: historyData,\n isLoading: isLoadingHistory,\n } = useCertificateGenerationHistory(courseId, {\n page: tasksPage,\n pageSize: CERTIFICATES_PAGE_SIZE,\n });\n\n const { mutate: grantExceptions, isPending: isGrantingExceptions } = useGrantBulkExceptions(courseId);\n const { mutate: uploadCsvExceptions, isPending: isUploadingCsv } = useUploadBulkExceptionsCsv(courseId);\n const { mutate: invalidateCert, isPending: isInvalidating } = useInvalidateCertificate(courseId);\n const { mutate: removeExcept, isPending: isRemovingException } = useRemoveException(courseId);\n const { mutate: removeInval, isPending: isRemovingInvalidation } = useRemoveInvalidation(courseId);\n const { mutate: toggleGeneration, isPending: isTogglingGeneration } = useToggleCertificateGeneration(courseId);\n const { mutate: regenerateCerts } = useRegenerateCertificates(courseId);\n\n const handleGrantExceptions = useCallback((learners: string[], notes: string) => {\n grantExceptions(\n { learners, notes },\n {\n onSuccess: (data) => {\n setIsGrantExceptionsOpen(false);\n if (data.errors && data.errors.length > 0) {\n const errorMessages = data.errors.map(err => `${err.learner}: ${err.message}`).join('\\n');\n showModal({\n title: intl.formatMessage(messages.errorModalTitle),\n message: `Some exceptions failed:\\n${errorMessages}`,\n variant: ALERT_VARIANTS.WARNING,\n });\n }\n if (data.success && data.success.length > 0) {\n showToast(intl.formatMessage(messages.exceptionsGrantedToast, { count: data.success.length }));\n }\n },\n onError: (error) => {\n showModal({\n title: intl.formatMessage(messages.errorModalTitle),\n message: getErrorMessage(error, intl.formatMessage(messages.errorGrantException)),\n variant: ALERT_VARIANTS.DANGER,\n });\n },\n },\n );\n }, [grantExceptions, showToast, showModal, intl]);\n\n const handleUploadCsvExceptions = useCallback((file: File) => {\n uploadCsvExceptions(\n file,\n {\n onSuccess: (data) => {\n setIsGrantExceptionsOpen(false);\n if (data.errors && data.errors.length > 0) {\n const errorMessages = data.errors.map(err => `${err.learner}: ${err.message}`).join('\\n');\n showModal({\n title: intl.formatMessage(messages.errorModalTitle),\n message: `Some exceptions failed:\\n${errorMessages}`,\n variant: ALERT_VARIANTS.WARNING,\n });\n }\n if (data.success && data.success.length > 0) {\n showToast(intl.formatMessage(messages.exceptionsGrantedToast, { count: data.success.length }));\n }\n },\n onError: (error) => {\n showModal({\n title: intl.formatMessage(messages.errorModalTitle),\n message: getErrorMessage(error, intl.formatMessage(messages.errorGrantException)),\n variant: ALERT_VARIANTS.DANGER,\n });\n },\n },\n );\n }, [uploadCsvExceptions, showToast, showModal, intl]);\n\n const handleInvalidateCertificate = useCallback((learners: string[], notes: string) => {\n invalidateCert(\n { learners, notes },\n {\n onSuccess: (data) => {\n setIsInvalidateCertificateOpen(false);\n if (data.errors && data.errors.length > 0) {\n const errorMessages = data.errors.map(err => `${err.learner}: ${err.message}`).join('\\n');\n showModal({\n title: intl.formatMessage(messages.errorModalTitle),\n message: `Some invalidations failed:\\n${errorMessages}`,\n variant: ALERT_VARIANTS.WARNING,\n confirmText: intl.formatMessage(messages.close),\n });\n }\n if (data.success && data.success.length > 0) {\n showToast(intl.formatMessage(messages.certificatesInvalidatedToast, { count: data.success.length }));\n }\n },\n onError: (error) => {\n showModal({\n title: intl.formatMessage(messages.errorModalTitle),\n message: getErrorMessage(error, intl.formatMessage(messages.errorInvalidateCertificate)),\n variant: ALERT_VARIANTS.DANGER,\n });\n },\n },\n );\n }, [invalidateCert, showToast, showModal, intl]);\n\n const handleRemoveExceptionClick = useCallback((username: string, email: string) => {\n setSelectedUsername(username);\n setSelectedEmail(email);\n setIsRemoveExceptionOpen(true);\n }, []);\n\n const handleRemoveExceptionConfirm = useCallback(() => {\n // Backend accepts either username or email - use whichever is available\n const identifier = selectedUsername || selectedEmail;\n\n if (!identifier) {\n showModal({\n title: MODAL_TITLES.ERROR,\n message: intl.formatMessage(messages.errorRemoveException) + ': Username or email is required',\n variant: ALERT_VARIANTS.DANGER,\n });\n return;\n }\n\n removeExcept(\n { username: identifier },\n {\n onSuccess: () => {\n setIsRemoveExceptionOpen(false);\n setSelectedUsername('');\n setSelectedEmail('');\n showToast(intl.formatMessage(messages.exceptionRemovedToast, { email: selectedEmail }));\n },\n onError: (error) => {\n showModal({\n title: intl.formatMessage(messages.errorModalTitle),\n message: getErrorMessage(error, intl.formatMessage(messages.errorRemoveException)),\n variant: ALERT_VARIANTS.DANGER,\n });\n },\n },\n );\n }, [removeExcept, selectedUsername, selectedEmail, showToast, showModal, intl]);\n\n const handleRemoveInvalidationClick = useCallback((username: string, email: string) => {\n setSelectedUsername(username);\n setSelectedEmail(email);\n setIsRemoveInvalidationOpen(true);\n }, []);\n\n const handleRemoveInvalidationConfirm = useCallback(() => {\n // Backend accepts either username or email - use whichever is available\n const identifier = selectedUsername || selectedEmail;\n\n if (!identifier) {\n showModal({\n title: MODAL_TITLES.ERROR,\n message: intl.formatMessage(messages.errorRemoveInvalidation) + ': Username or email is required',\n variant: ALERT_VARIANTS.DANGER,\n });\n return;\n }\n\n removeInval(\n { username: identifier },\n {\n onSuccess: () => {\n setIsRemoveInvalidationOpen(false);\n setSelectedUsername('');\n setSelectedEmail('');\n showToast(intl.formatMessage(messages.invalidationRemovedToast, { email: selectedEmail }));\n },\n onError: (error) => {\n showModal({\n title: intl.formatMessage(messages.errorModalTitle),\n message: getErrorMessage(error, intl.formatMessage(messages.errorRemoveInvalidation)),\n variant: ALERT_VARIANTS.DANGER,\n });\n },\n },\n );\n }, [removeInval, selectedUsername, selectedEmail, showToast, showModal, intl]);\n\n const handleToggleCertificateGeneration = useCallback(() => {\n const newState = !isCertificateGenerationEnabled;\n toggleGeneration(newState, {\n onSuccess: () => {\n setIsCertificateGenerationEnabled(newState);\n setIsDisableCertificatesOpen(false);\n showToast(\n newState\n ? intl.formatMessage(messages.successEnableCertificates)\n : intl.formatMessage(messages.successDisableCertificates),\n );\n },\n onError: (error) => {\n showModal({\n title: MODAL_TITLES.ERROR,\n message: getErrorMessage(error, intl.formatMessage(messages.errorToggleCertificateGeneration)),\n variant: ALERT_VARIANTS.DANGER,\n });\n },\n });\n }, [isCertificateGenerationEnabled, toggleGeneration, showToast, showModal, intl]);\n\n const handleRegenerateCertificatesClick = useCallback(() => {\n // Don't open modal for disabled filters\n if (filter === CertificateFilter.ALL_LEARNERS || filter === CertificateFilter.INVALIDATED) {\n return;\n }\n\n // For granted exceptions, open the generate modal\n if (filter === CertificateFilter.GRANTED_EXCEPTIONS) {\n setIsGenerateModalOpen(true);\n } else {\n // For other filters, open the regenerate modal\n setIsRegenerateModalOpen(true);\n }\n }, [filter]);\n\n const handleRegenerateCertificatesConfirm = useCallback(() => {\n regenerateCerts({ filter, onlyWithoutCertificate: false }, {\n onSuccess: () => {\n setIsRegenerateModalOpen(false);\n showToast(intl.formatMessage(messages.certificatesRegeneratedToast));\n },\n onError: (error) => {\n setIsRegenerateModalOpen(false);\n const errorMessage = getErrorMessage(error, intl.formatMessage(messages.errorRegenerateCertificates));\n showModal({\n title: MODAL_TITLES.ERROR,\n message: errorMessage,\n variant: ALERT_VARIANTS.DANGER,\n });\n },\n });\n }, [regenerateCerts, filter, showToast, showModal, intl]);\n\n const handleGenerateCertificatesConfirm = useCallback((onlyWithoutCertificate: boolean) => {\n regenerateCerts({ filter, onlyWithoutCertificate }, {\n onSuccess: () => {\n setIsGenerateModalOpen(false);\n showToast(intl.formatMessage(messages.certificatesRegeneratedToast));\n },\n onError: (error) => {\n setIsGenerateModalOpen(false);\n const errorMessage = getErrorMessage(error, intl.formatMessage(messages.errorRegenerateCertificates));\n showModal({\n title: MODAL_TITLES.ERROR,\n message: errorMessage,\n variant: ALERT_VARIANTS.DANGER,\n });\n },\n });\n }, [regenerateCerts, filter, showToast, showModal, intl]);\n\n const handleTabKeyDown = useCallback((event: React.KeyboardEvent) => {\n const tabs = [TAB_KEYS.ISSUED, TAB_KEYS.HISTORY];\n const currentIndex = tabs.indexOf(activeTab);\n\n let nextIndex = currentIndex;\n\n switch (event.key) {\n case 'ArrowRight':\n case 'ArrowDown':\n event.preventDefault();\n nextIndex = (currentIndex + 1) % tabs.length;\n break;\n case 'ArrowLeft':\n case 'ArrowUp':\n event.preventDefault();\n nextIndex = (currentIndex - 1 + tabs.length) % tabs.length;\n break;\n case 'Home':\n event.preventDefault();\n nextIndex = 0;\n break;\n case 'End':\n event.preventDefault();\n nextIndex = tabs.length - 1;\n break;\n default:\n return;\n }\n\n const nextTab = tabs[nextIndex];\n setActiveTab(nextTab);\n\n // Focus the new tab\n setTimeout(() => {\n document.getElementById(`certificates-tab-${nextTab}`)?.focus();\n }, 0);\n }, [activeTab]);\n\n // Check if certificate management is disabled\n if (courseInfo && !courseInfo.certificatesEnabled) {\n return (\n <Container className=\"mt-4.5 mb-4\" fluid>\n <Alert variant=\"warning\">\n {intl.formatMessage(messages.certificatesDisabledMessage)}\n </Alert>\n </Container>\n );\n }\n\n return (\n <Container className=\"mt-4.5 mb-4\" fluid>\n <CertificatesPageHeader\n onGrantExceptions={() => setIsGrantExceptionsOpen(true)}\n onInvalidateCertificate={() => setIsInvalidateCertificateOpen(true)}\n onStudentGeneratedCertificates={() => setIsDisableCertificatesOpen(true)}\n />\n\n <Card variant=\"muted\" className=\"pt-3 pt-md-4 pb-4 pb-md-6 certificates-card\">\n <ButtonGroup className=\"d-block mx-4\" role=\"tablist\">\n <Button\n id=\"certificates-tab-issued\"\n role=\"tab\"\n aria-controls=\"certificates-tabpanel-issued\"\n aria-selected={activeTab === TAB_KEYS.ISSUED}\n tabIndex={activeTab === TAB_KEYS.ISSUED ? 0 : -1}\n onClick={() => setActiveTab(TAB_KEYS.ISSUED)}\n onKeyDown={handleTabKeyDown}\n variant={activeTab === TAB_KEYS.ISSUED ? 'primary' : 'outline-primary'}\n >\n {intl.formatMessage(messages.issuedCertificatesTab)}\n </Button>\n <Button\n id=\"certificates-tab-history\"\n role=\"tab\"\n aria-controls=\"certificates-tabpanel-history\"\n aria-selected={activeTab === TAB_KEYS.HISTORY}\n tabIndex={activeTab === TAB_KEYS.HISTORY ? 0 : -1}\n onClick={() => setActiveTab(TAB_KEYS.HISTORY)}\n onKeyDown={handleTabKeyDown}\n variant={activeTab === TAB_KEYS.HISTORY ? 'primary' : 'outline-primary'}\n >\n {intl.formatMessage(messages.generationHistoryTab)}\n </Button>\n </ButtonGroup>\n <div\n id=\"certificates-tabpanel-issued\"\n role=\"tabpanel\"\n aria-labelledby=\"certificates-tab-issued\"\n hidden={activeTab !== TAB_KEYS.ISSUED}\n >\n {activeTab === TAB_KEYS.ISSUED && (\n <IssuedCertificatesTab\n data={certificatesData?.results || []}\n isLoading={isLoadingCertificates}\n itemCount={certificatesData?.count || 0}\n pageCount={certificatesData?.numPages || 0}\n search={search}\n onSearchChange={setSearch}\n filter={filter}\n onFilterChange={setFilter}\n currentPage={certificatesPage}\n onPageChange={setCertificatesPage}\n onRemoveException={handleRemoveExceptionClick}\n onRemoveInvalidation={handleRemoveInvalidationClick}\n onRegenerateCertificates={handleRegenerateCertificatesClick}\n />\n )}\n </div>\n <div\n id=\"certificates-tabpanel-history\"\n role=\"tabpanel\"\n aria-labelledby=\"certificates-tab-history\"\n hidden={activeTab !== TAB_KEYS.HISTORY}\n >\n {activeTab === TAB_KEYS.HISTORY && (\n <div className=\"d-flex flex-column mt-3 mt-md-4\">\n <GenerationHistoryTable\n data={historyData?.results || []}\n isLoading={isLoadingHistory}\n itemCount={historyData?.count || 0}\n pageCount={historyData?.numPages || 0}\n currentPage={tasksPage}\n onPageChange={setTasksPage}\n />\n </div>\n )}\n </div>\n </Card>\n\n <GrantExceptionsModal\n isOpen={isGrantExceptionsOpen}\n onClose={() => setIsGrantExceptionsOpen(false)}\n onSubmit={handleGrantExceptions}\n onUploadCsv={handleUploadCsvExceptions}\n isSubmitting={isGrantingExceptions || isUploadingCsv}\n />\n <InvalidateCertificateModal\n isOpen={isInvalidateCertificateOpen}\n onClose={() => setIsInvalidateCertificateOpen(false)}\n onSubmit={handleInvalidateCertificate}\n isSubmitting={isInvalidating}\n />\n <RemoveExceptionModal\n isOpen={isRemoveExceptionOpen}\n email={selectedEmail}\n onClose={() => {\n setIsRemoveExceptionOpen(false);\n setSelectedUsername('');\n setSelectedEmail('');\n }}\n onConfirm={handleRemoveExceptionConfirm}\n isSubmitting={isRemovingException}\n />\n <RemoveInvalidationModal\n isOpen={isRemoveInvalidationOpen}\n email={selectedEmail}\n onClose={() => {\n setIsRemoveInvalidationOpen(false);\n setSelectedUsername('');\n setSelectedEmail('');\n }}\n onConfirm={handleRemoveInvalidationConfirm}\n isSubmitting={isRemovingInvalidation}\n />\n <DisableCertificatesModal\n isOpen={isDisableCertificatesOpen}\n isEnabled={isCertificateGenerationEnabled}\n onClose={() => setIsDisableCertificatesOpen(false)}\n onConfirm={handleToggleCertificateGeneration}\n isSubmitting={isTogglingGeneration}\n />\n <RegenerateCertificatesModal\n isOpen={isRegenerateModalOpen}\n onClose={() => setIsRegenerateModalOpen(false)}\n onConfirm={handleRegenerateCertificatesConfirm}\n isSubmitting={false}\n filter={filter}\n learnerCount={certificatesData?.count || 0}\n />\n <GenerateCertificatesModal\n isOpen={isGenerateModalOpen}\n onClose={() => setIsGenerateModalOpen(false)}\n onConfirm={handleGenerateCertificatesConfirm}\n isSubmitting={false}\n learnerCount={certificatesData?.count || 0}\n />\n <PendingTasks isOpen={isPendingTasksOpen} onToggle={() => setIsPendingTasksOpen(prev => !prev)} />\n </Container>\n );\n};\n\nexport default CertificatesPage;\n"]}
@@ -0,0 +1,90 @@
1
+ .filter-dropdown-item {
2
+ &:hover,
3
+ &.active {
4
+ text-decoration: none;
5
+ color: var(--pgn-color-menu-item-hover-color);
6
+ border-color: var(--pgn-color-menu-item-hover-border);
7
+ background: var(--pgn-color-menu-item-hover-bg);
8
+ }
9
+
10
+ &.active {
11
+ font-weight: 600;
12
+ }
13
+ }
14
+
15
+ .certificates-filter-dropdown,
16
+ .certificate-actions-dropdown {
17
+ position: relative;
18
+ z-index: 1050;
19
+
20
+ .dropdown-menu {
21
+ position: absolute;
22
+ z-index: 9999;
23
+ max-height: 400px;
24
+ overflow-y: auto;
25
+ }
26
+ }
27
+
28
+ .certificate-actions-dropdown {
29
+ position: static;
30
+
31
+ .dropdown-toggle {
32
+ position: relative;
33
+ z-index: 1;
34
+ }
35
+
36
+ .dropdown-menu {
37
+ will-change: transform;
38
+ }
39
+ }
40
+
41
+ .certificates-card {
42
+ position: relative;
43
+ overflow: visible;
44
+ overflow-x: hidden;
45
+
46
+ .card-body,
47
+ .pgn__tabs,
48
+ .tab-content,
49
+ .tab-pane {
50
+ overflow: visible;
51
+ }
52
+ }
53
+
54
+ .certificates-tab-container {
55
+ overflow: visible;
56
+ }
57
+
58
+ .certificates-table-wrapper {
59
+ overflow-x: auto;
60
+ overflow-y: visible;
61
+ position: relative;
62
+ }
63
+
64
+ .certificates-card .pgn__data-table-layout {
65
+ overflow: visible;
66
+
67
+ table,
68
+ tbody,
69
+ tr,
70
+ td {
71
+ position: relative;
72
+ }
73
+ }
74
+
75
+ .certificates-card {
76
+ .pgn__data-table-footer,
77
+ .pgn__data-table-pagination {
78
+ position: relative;
79
+ z-index: 1;
80
+ }
81
+ }
82
+
83
+ .certificates-toolbar-wrapper {
84
+ min-width: 0;
85
+ }
86
+
87
+ .certificates-search-field {
88
+ min-width: 320px;
89
+ max-width: 400px;
90
+ }
@@ -0,0 +1,14 @@
1
+ import type { CertificateData, CertificateFilter } from '../../certificates/types';
2
+ interface CertificateTableProps {
3
+ data: CertificateData[];
4
+ isLoading: boolean;
5
+ itemCount: number;
6
+ pageCount: number;
7
+ currentPage: number;
8
+ filter: CertificateFilter;
9
+ onPageChange: (pageIndex: number) => void;
10
+ onRemoveException: (username: string, email: string) => void;
11
+ onRemoveInvalidation: (username: string, email: string) => void;
12
+ }
13
+ declare const CertificateTable: ({ data, isLoading, itemCount, filter, onRemoveException, onRemoveInvalidation, }: CertificateTableProps) => import("react/jsx-runtime").JSX.Element;
14
+ export default CertificateTable;