@openedx/frontend-app-instructor-dashboard 1.0.0-alpha.0

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 (299) hide show
  1. package/LICENSE +661 -0
  2. package/README.rst +235 -0
  3. package/dist/Main.d.ts +3 -0
  4. package/dist/Main.js +13 -0
  5. package/dist/Main.js.map +1 -0
  6. package/dist/app.d.ts +3 -0
  7. package/dist/app.js +18 -0
  8. package/dist/app.js.map +1 -0
  9. package/dist/app.scss +10 -0
  10. package/dist/certificates/CertificatesPage.d.ts +2 -0
  11. package/dist/certificates/CertificatesPage.js +6 -0
  12. package/dist/certificates/CertificatesPage.js.map +1 -0
  13. package/dist/cohorts/CohortsPage.d.ts +3 -0
  14. package/dist/cohorts/CohortsPage.js +41 -0
  15. package/dist/cohorts/CohortsPage.js.map +1 -0
  16. package/dist/cohorts/CohortsPage.scss +14 -0
  17. package/dist/cohorts/components/CohortCard.d.ts +6 -0
  18. package/dist/cohorts/components/CohortCard.js +53 -0
  19. package/dist/cohorts/components/CohortCard.js.map +1 -0
  20. package/dist/cohorts/components/CohortContext.d.ts +14 -0
  21. package/dist/cohorts/components/CohortContext.js +46 -0
  22. package/dist/cohorts/components/CohortContext.js.map +1 -0
  23. package/dist/cohorts/components/CohortsForm.d.ts +11 -0
  24. package/dist/cohorts/components/CohortsForm.js +58 -0
  25. package/dist/cohorts/components/CohortsForm.js.map +1 -0
  26. package/dist/cohorts/components/DisableCohortsModal.d.ts +7 -0
  27. package/dist/cohorts/components/DisableCohortsModal.js +10 -0
  28. package/dist/cohorts/components/DisableCohortsModal.js.map +1 -0
  29. package/dist/cohorts/components/DisabledCohortsView.d.ts +5 -0
  30. package/dist/cohorts/components/DisabledCohortsView.js +10 -0
  31. package/dist/cohorts/components/DisabledCohortsView.js.map +1 -0
  32. package/dist/cohorts/components/EnabledCohortsView.d.ts +2 -0
  33. package/dist/cohorts/components/EnabledCohortsView.js +96 -0
  34. package/dist/cohorts/components/EnabledCohortsView.js.map +1 -0
  35. package/dist/cohorts/components/ManageLearners.d.ts +2 -0
  36. package/dist/cohorts/components/ManageLearners.js +64 -0
  37. package/dist/cohorts/components/ManageLearners.js.map +1 -0
  38. package/dist/cohorts/components/SelectedCohortInfo.d.ts +2 -0
  39. package/dist/cohorts/components/SelectedCohortInfo.js +43 -0
  40. package/dist/cohorts/components/SelectedCohortInfo.js.map +1 -0
  41. package/dist/cohorts/constants.d.ts +4 -0
  42. package/dist/cohorts/constants.js +5 -0
  43. package/dist/cohorts/constants.js.map +1 -0
  44. package/dist/cohorts/data/api.d.ts +9 -0
  45. package/dist/cohorts/data/api.js +54 -0
  46. package/dist/cohorts/data/api.js.map +1 -0
  47. package/dist/cohorts/data/apiHook.d.ts +14 -0
  48. package/dist/cohorts/data/apiHook.js +64 -0
  49. package/dist/cohorts/data/apiHook.js.map +1 -0
  50. package/dist/cohorts/data/queryKeys.d.ts +7 -0
  51. package/dist/cohorts/data/queryKeys.js +9 -0
  52. package/dist/cohorts/data/queryKeys.js.map +1 -0
  53. package/dist/cohorts/messages.d.ts +233 -0
  54. package/dist/cohorts/messages.js +235 -0
  55. package/dist/cohorts/messages.js.map +1 -0
  56. package/dist/cohorts/types.d.ts +15 -0
  57. package/dist/cohorts/types.js +2 -0
  58. package/dist/cohorts/types.js.map +1 -0
  59. package/dist/components/ActionCard.d.ts +11 -0
  60. package/dist/components/ActionCard.js +7 -0
  61. package/dist/components/ActionCard.js.map +1 -0
  62. package/dist/components/CSVComponent.d.ts +10 -0
  63. package/dist/components/CSVComponent.js +21 -0
  64. package/dist/components/CSVComponent.js.map +1 -0
  65. package/dist/components/ObjectCell.d.ts +5 -0
  66. package/dist/components/ObjectCell.js +7 -0
  67. package/dist/components/ObjectCell.js.map +1 -0
  68. package/dist/components/PageNotFound.d.ts +2 -0
  69. package/dist/components/PageNotFound.js +12 -0
  70. package/dist/components/PageNotFound.js.map +1 -0
  71. package/dist/components/PendingTasks.d.ts +5 -0
  72. package/dist/components/PendingTasks.js +38 -0
  73. package/dist/components/PendingTasks.js.map +1 -0
  74. package/dist/components/SpecifyLearnerField.d.ts +5 -0
  75. package/dist/components/SpecifyLearnerField.js +10 -0
  76. package/dist/components/SpecifyLearnerField.js.map +1 -0
  77. package/dist/components/messages.d.ts +108 -0
  78. package/dist/components/messages.js +110 -0
  79. package/dist/components/messages.js.map +1 -0
  80. package/dist/constants.d.ts +1 -0
  81. package/dist/constants.js +2 -0
  82. package/dist/constants.js.map +1 -0
  83. package/dist/courseInfo/CourseInfoPage.d.ts +2 -0
  84. package/dist/courseInfo/CourseInfoPage.js +7 -0
  85. package/dist/courseInfo/CourseInfoPage.js.map +1 -0
  86. package/dist/courseInfo/components/EnrollmentSummary/EnrollmentCounter.d.ts +8 -0
  87. package/dist/courseInfo/components/EnrollmentSummary/EnrollmentCounter.js +13 -0
  88. package/dist/courseInfo/components/EnrollmentSummary/EnrollmentCounter.js.map +1 -0
  89. package/dist/courseInfo/components/EnrollmentSummary/EnrollmentSummary.d.ts +2 -0
  90. package/dist/courseInfo/components/EnrollmentSummary/EnrollmentSummary.js +24 -0
  91. package/dist/courseInfo/components/EnrollmentSummary/EnrollmentSummary.js.map +1 -0
  92. package/dist/courseInfo/components/EnrollmentSummary/index.d.ts +2 -0
  93. package/dist/courseInfo/components/EnrollmentSummary/index.js +3 -0
  94. package/dist/courseInfo/components/EnrollmentSummary/index.js.map +1 -0
  95. package/dist/courseInfo/components/EnrollmentSummary/messages.d.ts +83 -0
  96. package/dist/courseInfo/components/EnrollmentSummary/messages.js +85 -0
  97. package/dist/courseInfo/components/EnrollmentSummary/messages.js.map +1 -0
  98. package/dist/courseInfo/components/EnrollmentSummary/utils.d.ts +1 -0
  99. package/dist/courseInfo/components/EnrollmentSummary/utils.js +9 -0
  100. package/dist/courseInfo/components/EnrollmentSummary/utils.js.map +1 -0
  101. package/dist/courseInfo/components/generalCourseInfo/GeneralCourseInfo.d.ts +2 -0
  102. package/dist/courseInfo/components/generalCourseInfo/GeneralCourseInfo.js +46 -0
  103. package/dist/courseInfo/components/generalCourseInfo/GeneralCourseInfo.js.map +1 -0
  104. package/dist/courseInfo/components/generalCourseInfo/StatusBadge.d.ts +5 -0
  105. package/dist/courseInfo/components/generalCourseInfo/StatusBadge.js +15 -0
  106. package/dist/courseInfo/components/generalCourseInfo/StatusBadge.js.map +1 -0
  107. package/dist/courseInfo/components/generalCourseInfo/index.d.ts +1 -0
  108. package/dist/courseInfo/components/generalCourseInfo/index.js +2 -0
  109. package/dist/courseInfo/components/generalCourseInfo/index.js.map +1 -0
  110. package/dist/courseInfo/components/generalCourseInfo/messages.d.ts +8 -0
  111. package/dist/courseInfo/components/generalCourseInfo/messages.js +10 -0
  112. package/dist/courseInfo/components/generalCourseInfo/messages.js.map +1 -0
  113. package/dist/courseInfo/messages.d.ts +8 -0
  114. package/dist/courseInfo/messages.js +10 -0
  115. package/dist/courseInfo/messages.js.map +1 -0
  116. package/dist/courseInfo/types.d.ts +27 -0
  117. package/dist/courseInfo/types.js +2 -0
  118. package/dist/courseInfo/types.js.map +1 -0
  119. package/dist/courseTeam/CourseTeamPage.d.ts +2 -0
  120. package/dist/courseTeam/CourseTeamPage.js +6 -0
  121. package/dist/courseTeam/CourseTeamPage.js.map +1 -0
  122. package/dist/data/api.d.ts +14 -0
  123. package/dist/data/api.js +33 -0
  124. package/dist/data/api.js.map +1 -0
  125. package/dist/data/apiHook.d.ts +4 -0
  126. package/dist/data/apiHook.js +28 -0
  127. package/dist/data/apiHook.js.map +1 -0
  128. package/dist/data/queryKeys.d.ts +8 -0
  129. package/dist/data/queryKeys.js +10 -0
  130. package/dist/data/queryKeys.js.map +1 -0
  131. package/dist/dataDownloads/DataDownloadsPage.d.ts +2 -0
  132. package/dist/dataDownloads/DataDownloadsPage.js +162 -0
  133. package/dist/dataDownloads/DataDownloadsPage.js.map +1 -0
  134. package/dist/dataDownloads/components/DataDownloadTable.d.ts +8 -0
  135. package/dist/dataDownloads/components/DataDownloadTable.js +42 -0
  136. package/dist/dataDownloads/components/DataDownloadTable.js.map +1 -0
  137. package/dist/dataDownloads/components/DownloadLinkCell.d.ts +6 -0
  138. package/dist/dataDownloads/components/DownloadLinkCell.js +13 -0
  139. package/dist/dataDownloads/components/DownloadLinkCell.js.map +1 -0
  140. package/dist/dataDownloads/components/GenerateReports.d.ts +8 -0
  141. package/dist/dataDownloads/components/GenerateReports.js +20 -0
  142. package/dist/dataDownloads/components/GenerateReports.js.map +1 -0
  143. package/dist/dataDownloads/components/ReportNameCell.d.ts +3 -0
  144. package/dist/dataDownloads/components/ReportNameCell.js +6 -0
  145. package/dist/dataDownloads/components/ReportNameCell.js.map +1 -0
  146. package/dist/dataDownloads/data/api.d.ts +2 -0
  147. package/dist/dataDownloads/data/api.js +23 -0
  148. package/dist/dataDownloads/data/api.js.map +1 -0
  149. package/dist/dataDownloads/data/apiHook.d.ts +7 -0
  150. package/dist/dataDownloads/data/apiHook.js +22 -0
  151. package/dist/dataDownloads/data/apiHook.js.map +1 -0
  152. package/dist/dataDownloads/data/queryKeys.d.ts +5 -0
  153. package/dist/dataDownloads/data/queryKeys.js +7 -0
  154. package/dist/dataDownloads/data/queryKeys.js.map +1 -0
  155. package/dist/dataDownloads/messages.d.ts +338 -0
  156. package/dist/dataDownloads/messages.js +341 -0
  157. package/dist/dataDownloads/messages.js.map +1 -0
  158. package/dist/dataDownloads/types.d.ts +8 -0
  159. package/dist/dataDownloads/types.js +2 -0
  160. package/dist/dataDownloads/types.js.map +1 -0
  161. package/dist/dataDownloads/utils.d.ts +26 -0
  162. package/dist/dataDownloads/utils.js +49 -0
  163. package/dist/dataDownloads/utils.js.map +1 -0
  164. package/dist/dateExtensions/DateExtensionsPage.d.ts +2 -0
  165. package/dist/dateExtensions/DateExtensionsPage.js +87 -0
  166. package/dist/dateExtensions/DateExtensionsPage.js.map +1 -0
  167. package/dist/dateExtensions/components/AddExtensionModal.d.ts +13 -0
  168. package/dist/dateExtensions/components/AddExtensionModal.js +34 -0
  169. package/dist/dateExtensions/components/AddExtensionModal.js.map +1 -0
  170. package/dist/dateExtensions/components/DateExtensionsList.d.ts +7 -0
  171. package/dist/dateExtensions/components/DateExtensionsList.js +103 -0
  172. package/dist/dateExtensions/components/DateExtensionsList.js.map +1 -0
  173. package/dist/dateExtensions/components/ResetExtensionsModal.d.ts +10 -0
  174. package/dist/dateExtensions/components/ResetExtensionsModal.js +10 -0
  175. package/dist/dateExtensions/components/ResetExtensionsModal.js.map +1 -0
  176. package/dist/dateExtensions/components/SelectGradedSubsection.d.ts +8 -0
  177. package/dist/dateExtensions/components/SelectGradedSubsection.js +15 -0
  178. package/dist/dateExtensions/components/SelectGradedSubsection.js.map +1 -0
  179. package/dist/dateExtensions/data/api.d.ts +6 -0
  180. package/dist/dateExtensions/data/api.js +40 -0
  181. package/dist/dateExtensions/data/api.js.map +1 -0
  182. package/dist/dateExtensions/data/apiHook.d.ts +11 -0
  183. package/dist/dateExtensions/data/apiHook.js +31 -0
  184. package/dist/dateExtensions/data/apiHook.js.map +1 -0
  185. package/dist/dateExtensions/data/queryKeys.d.ts +10 -0
  186. package/dist/dateExtensions/data/queryKeys.js +17 -0
  187. package/dist/dateExtensions/data/queryKeys.js.map +1 -0
  188. package/dist/dateExtensions/messages.d.ts +128 -0
  189. package/dist/dateExtensions/messages.js +130 -0
  190. package/dist/dateExtensions/messages.js.map +1 -0
  191. package/dist/dateExtensions/types.d.ts +24 -0
  192. package/dist/dateExtensions/types.js +2 -0
  193. package/dist/dateExtensions/types.js.map +1 -0
  194. package/dist/enrollments/EnrollmentsPage.d.ts +2 -0
  195. package/dist/enrollments/EnrollmentsPage.js +32 -0
  196. package/dist/enrollments/EnrollmentsPage.js.map +1 -0
  197. package/dist/enrollments/components/EnrollmentStatusModal.d.ts +6 -0
  198. package/dist/enrollments/components/EnrollmentStatusModal.js +23 -0
  199. package/dist/enrollments/components/EnrollmentStatusModal.js.map +1 -0
  200. package/dist/enrollments/components/EnrollmentsList.d.ts +6 -0
  201. package/dist/enrollments/components/EnrollmentsList.js +99 -0
  202. package/dist/enrollments/components/EnrollmentsList.js.map +1 -0
  203. package/dist/enrollments/components/UnenrollModal.d.ts +8 -0
  204. package/dist/enrollments/components/UnenrollModal.js +7 -0
  205. package/dist/enrollments/components/UnenrollModal.js.map +1 -0
  206. package/dist/enrollments/data/api.d.ts +4 -0
  207. package/dist/enrollments/data/api.js +32 -0
  208. package/dist/enrollments/data/api.js.map +1 -0
  209. package/dist/enrollments/data/apiHook.d.ts +3 -0
  210. package/dist/enrollments/data/apiHook.js +14 -0
  211. package/dist/enrollments/data/apiHook.js.map +1 -0
  212. package/dist/enrollments/data/queryKeys.d.ts +7 -0
  213. package/dist/enrollments/data/queryKeys.js +8 -0
  214. package/dist/enrollments/data/queryKeys.js.map +1 -0
  215. package/dist/enrollments/messages.d.ts +108 -0
  216. package/dist/enrollments/messages.js +110 -0
  217. package/dist/enrollments/messages.js.map +1 -0
  218. package/dist/enrollments/types.d.ts +15 -0
  219. package/dist/enrollments/types.js +2 -0
  220. package/dist/enrollments/types.js.map +1 -0
  221. package/dist/grading/GradingPage.d.ts +2 -0
  222. package/dist/grading/GradingPage.js +6 -0
  223. package/dist/grading/GradingPage.js.map +1 -0
  224. package/dist/hooks/useDebouncedFilter.d.ts +16 -0
  225. package/dist/hooks/useDebouncedFilter.js +27 -0
  226. package/dist/hooks/useDebouncedFilter.js.map +1 -0
  227. package/dist/i18n/index.d.ts +25 -0
  228. package/dist/i18n/index.js +26 -0
  229. package/dist/i18n/index.js.map +1 -0
  230. package/dist/index.d.ts +3 -0
  231. package/dist/index.js +4 -0
  232. package/dist/index.js.map +1 -0
  233. package/dist/instructorNav/InstructorNav.d.ts +8 -0
  234. package/dist/instructorNav/InstructorNav.js +42 -0
  235. package/dist/instructorNav/InstructorNav.js.map +1 -0
  236. package/dist/openResponses/OpenResponsesPage.d.ts +2 -0
  237. package/dist/openResponses/OpenResponsesPage.js +8 -0
  238. package/dist/openResponses/OpenResponsesPage.js.map +1 -0
  239. package/dist/openResponses/components/DetailAssessmentsList.d.ts +2 -0
  240. package/dist/openResponses/components/DetailAssessmentsList.js +44 -0
  241. package/dist/openResponses/components/DetailAssessmentsList.js.map +1 -0
  242. package/dist/openResponses/components/OpenResponsesSummary.d.ts +2 -0
  243. package/dist/openResponses/components/OpenResponsesSummary.js +30 -0
  244. package/dist/openResponses/components/OpenResponsesSummary.js.map +1 -0
  245. package/dist/openResponses/data/api.d.ts +4 -0
  246. package/dist/openResponses/data/api.js +23 -0
  247. package/dist/openResponses/data/api.js.map +1 -0
  248. package/dist/openResponses/data/apiHook.d.ts +2 -0
  249. package/dist/openResponses/data/apiHook.js +14 -0
  250. package/dist/openResponses/data/apiHook.js.map +1 -0
  251. package/dist/openResponses/data/queryKeys.d.ts +6 -0
  252. package/dist/openResponses/data/queryKeys.js +8 -0
  253. package/dist/openResponses/data/queryKeys.js.map +1 -0
  254. package/dist/openResponses/messages.d.ts +83 -0
  255. package/dist/openResponses/messages.js +85 -0
  256. package/dist/openResponses/messages.js.map +1 -0
  257. package/dist/openResponses/types.d.ts +12 -0
  258. package/dist/openResponses/types.js +2 -0
  259. package/dist/openResponses/types.js.map +1 -0
  260. package/dist/pageWrapper/PageWrapper.d.ts +4 -0
  261. package/dist/pageWrapper/PageWrapper.js +11 -0
  262. package/dist/pageWrapper/PageWrapper.js.map +1 -0
  263. package/dist/pageWrapper/messages.d.ts +8 -0
  264. package/dist/pageWrapper/messages.js +10 -0
  265. package/dist/pageWrapper/messages.js.map +1 -0
  266. package/dist/providers/AlertProvider.d.ts +41 -0
  267. package/dist/providers/AlertProvider.js +117 -0
  268. package/dist/providers/AlertProvider.js.map +1 -0
  269. package/dist/providers/QueryProvider.d.ts +6 -0
  270. package/dist/providers/QueryProvider.js +16 -0
  271. package/dist/providers/QueryProvider.js.map +1 -0
  272. package/dist/providers.d.ts +3 -0
  273. package/dist/providers.js +8 -0
  274. package/dist/providers.js.map +1 -0
  275. package/dist/routes.d.ts +20 -0
  276. package/dist/routes.js +72 -0
  277. package/dist/routes.js.map +1 -0
  278. package/dist/slots/PlaceholderSlot/PlaceholderSlot.d.ts +1 -0
  279. package/dist/slots/PlaceholderSlot/PlaceholderSlot.js +6 -0
  280. package/dist/slots/PlaceholderSlot/PlaceholderSlot.js.map +1 -0
  281. package/dist/slots/SlotUtils.d.ts +3 -0
  282. package/dist/slots/SlotUtils.js +17 -0
  283. package/dist/slots/SlotUtils.js.map +1 -0
  284. package/dist/slots.d.ts +3 -0
  285. package/dist/slots.js +3 -0
  286. package/dist/slots.js.map +1 -0
  287. package/dist/specialExams/SpecialExamsPage.d.ts +2 -0
  288. package/dist/specialExams/SpecialExamsPage.js +6 -0
  289. package/dist/specialExams/SpecialExamsPage.js.map +1 -0
  290. package/dist/testUtils.d.ts +4 -0
  291. package/dist/testUtils.js +24 -0
  292. package/dist/testUtils.js.map +1 -0
  293. package/dist/types/index.d.ts +40 -0
  294. package/dist/types/index.js +4 -0
  295. package/dist/types/index.js.map +1 -0
  296. package/dist/utils/formatters.d.ts +1 -0
  297. package/dist/utils/formatters.js +12 -0
  298. package/dist/utils/formatters.js.map +1 -0
  299. package/package.json +79 -0
package/README.rst ADDED
@@ -0,0 +1,235 @@
1
+ frontend-app-instructor-dashboard
2
+ #################################
3
+
4
+ |license-badge| |status-badge| |ci-badge| |codecov-badge|
5
+
6
+
7
+ Purpose
8
+ *******
9
+
10
+ This repository implements a micro-frontend for Instructor Dashboard, providing a seamless
11
+ and integrated user experience for instructors. It focuses on providing tools and features
12
+ specifically designed for instructors to track student progress, and facilitate communication with learners.
13
+
14
+ ### What is the domain of this MFE?
15
+ - Course information (Enrollment info, Basic course info, Pending tasks)
16
+ - Membership
17
+ - Cohorts
18
+ - Extensions
19
+ - Student Admin
20
+ - Data Download
21
+ - Special Exams
22
+ - Certificates
23
+ - Open Responses
24
+
25
+ Getting Started
26
+ ***************
27
+
28
+ After copying the template repository, you'll want to do a find-and-replace to
29
+ replace all instances of ``frontend-app-instructor-dashboard`` with the name of
30
+ your new repository. Also edit index.html to replace "Application Template"
31
+ with a friendly name for this application that users will see in their browser
32
+ tab.
33
+
34
+ Prerequisites
35
+ =============
36
+
37
+ `Tutor`_ is recommended as the development environment for your new frontend
38
+ app. You can refer to the `relevant tutor-mfe documentation`_ to get started
39
+ using it.
40
+
41
+ .. _Tutor: https://github.com/overhangio/tutor
42
+
43
+ .. _relevant tutor-mfe documentation: https://github.com/overhangio/tutor-mfe#mfe-development
44
+
45
+ Cloning and Startup
46
+ ===================
47
+
48
+ 1. Clone your new repo:
49
+
50
+ ``git clone https://github.com/openedx/frontend-app-instructor-dashboard.git``
51
+
52
+ 2. Use node v20.x.
53
+
54
+ The current version of the micro-frontend build scripts support node 20.
55
+ Using other major versions of node *may* work, but this is unsupported. For
56
+ convenience, this repository includes an .nvmrc file to help in setting the
57
+ correct node version via `nvm <https://github.com/nvm-sh/nvm>`_.
58
+
59
+ 3. Install npm dependencies:
60
+
61
+ ``cd frontend-app-instructor-dashboard && npm install``
62
+
63
+ 4. Update the application port to use for local development:
64
+
65
+ Default port is 8080. If this does not work for you, update the line
66
+ `PORT=8080` to your port in ``site.config.dev.tsx``.
67
+
68
+ 5. Start the dev server:
69
+
70
+ ``npm run dev``
71
+
72
+ The dev server is running at `http://apps.local.openedx.io:8080 <http://apps.local.openedx.io:8080>`_
73
+ or whatever port you setup.
74
+
75
+ Project Structure
76
+ =================
77
+
78
+ The source for this project is organized into nested submodules according to
79
+ the `Feature-based Application Organization ADR`_.
80
+
81
+ .. _Feature-based Application Organization ADR: https://github.com/openedx/frontend-app-instructor-dashboard/blob/master/docs/decisions/0002-feature-based-application-organization.rst
82
+
83
+ Internationalization
84
+ ====================
85
+
86
+ Please see refer to the `frontend-base i18n howto`_ for documentation on
87
+ internationalization.
88
+
89
+ .. _frontend-base i18n howto: https://github.com/openedx/frontend-base/blob/master/docs/how_tos/i18n.rst
90
+
91
+ AlertsProvider
92
+ ==============
93
+
94
+ The AlertsProvider is a centralized alert management system that provides four types of alerts:
95
+
96
+ **Toast Alerts**
97
+ Temporary notifications that appear in the corner and auto-dismiss after 5 seconds (customizable).
98
+
99
+ .. code-block:: jsx
100
+
101
+ import { useAlert } from './providers/AlertProvider';
102
+
103
+ const { showToast } = useAlert();
104
+ showToast('Report generated successfully!', 10000); // 10 second duration
105
+
106
+ **Modal Alerts**
107
+ Blocking dialogs that require user action. Supports queuing multiple modals and optional titles.
108
+
109
+ .. code-block:: jsx
110
+
111
+ import { useAlert } from './providers/AlertProvider';
112
+
113
+ const { showModal } = useAlert();
114
+ showModal({
115
+ title: 'Delete Report', // Optional
116
+ message: 'Are you sure you want to delete this report?',
117
+ variant: 'danger', // 'default' | 'success' | 'warning' | 'danger'
118
+ confirmText: 'Delete',
119
+ cancelText: 'Cancel',
120
+ onConfirm: () => console.log('Confirmed'),
121
+ onCancel: () => console.log('Cancelled'),
122
+ });
123
+
124
+ **Standard Alerts (with AlertOutlet)**
125
+ Drop-in replacement for AlertContext from PR #113. Alerts are rendered via the ``AlertOutlet`` component.
126
+
127
+ .. code-block:: jsx
128
+
129
+ import { useAlert, AlertOutlet } from './providers/AlertProvider';
130
+
131
+ const { addAlert } = useAlert();
132
+ addAlert({ type: 'success', message: 'Cohort created!' });
133
+
134
+ // Place AlertOutlet where you want alerts to appear
135
+ <AlertOutlet />
136
+
137
+ **Inline Alerts**
138
+ Persistent messages you control the rendering for. Useful for form validation or contextual messages.
139
+
140
+ .. code-block:: jsx
141
+
142
+ import { useAlert } from './providers/AlertProvider';
143
+
144
+ const { showInlineAlert, dismissInlineAlert, inlineAlerts } = useAlert();
145
+ showInlineAlert('This is an inline message', 'info', true);
146
+
147
+ // Render inline alerts manually
148
+ {inlineAlerts.map(alert => (
149
+ <div key={alert.id}>
150
+ {alert.message}
151
+ {alert.dismissible && (
152
+ <button onClick={() => dismissInlineAlert(alert.id)}>Dismiss</button>
153
+ )}
154
+ </div>
155
+ ))}
156
+
157
+ Getting Help
158
+ ************
159
+
160
+ If you're having trouble, we have discussion forums at
161
+ https://discuss.openedx.org where you can connect with others in the community.
162
+
163
+ Our real-time conversations are on Slack. You can request a `Slack
164
+ invitation`_, then join our `community Slack workspace`_. Because this is a
165
+ frontend repository, the best place to discuss it would be in the `#wg-frontend
166
+ channel`_.
167
+
168
+ For anything non-trivial, the best path is to open an issue in this repository
169
+ with as many details about the issue you are facing as you can provide.
170
+
171
+ https://github.com/openedx/frontend-app-instructor-dashboard/issues
172
+
173
+ For more information about these options, see the `Getting Help`_ page.
174
+
175
+ .. _Slack invitation: https://openedx.org/slack
176
+ .. _community Slack workspace: https://openedx.slack.com/
177
+ .. _#wg-frontend channel: https://openedx.slack.com/archives/C04BM6YC7A6
178
+ .. _Getting Help: https://openedx.org/getting-help
179
+
180
+ License
181
+ *******
182
+
183
+ The code in this repository is licensed under the AGPLv3 unless otherwise
184
+ noted.
185
+
186
+ Please see `LICENSE <LICENSE>`_ for details.
187
+
188
+ Contributing
189
+ ************
190
+
191
+ Contributions are very welcome. Please read `How To Contribute`_ for details.
192
+
193
+ .. _How To Contribute: https://openedx.org/r/how-to-contribute
194
+
195
+ This project is currently accepting all types of contributions, bug fixes,
196
+ security fixes, maintenance work, or new features. However, please make sure
197
+ to have a discussion about your new feature idea with the maintainers prior to
198
+ beginning development to maximize the chances of your change being accepted.
199
+ You can start a conversation by creating a new issue on this repo summarizing
200
+ your idea.
201
+
202
+ The Open edX Code of Conduct
203
+ ****************************
204
+
205
+ All community members are expected to follow the `Open edX Code of Conduct`_.
206
+
207
+ .. _Open edX Code of Conduct: https://openedx.org/code-of-conduct/
208
+
209
+ People
210
+ ******
211
+
212
+ The assigned maintainers for this component and other project details may be
213
+ found in `Backstage`_. Backstage pulls this data from the ``catalog-info.yaml``
214
+ file in this repo.
215
+
216
+ .. _Backstage: https://open-edx-backstage.herokuapp.com/catalog/default/component/frontend-app-instructor-dashboard
217
+
218
+ Reporting Security Issues
219
+ *************************
220
+
221
+ Please do not report security issues in public, and email security@openedx.org instead.
222
+
223
+ .. |license-badge| image:: https://img.shields.io/github/license/openedx/frontend-app-instructor-dashboard.svg
224
+ :target: https://github.com/openedx/frontend-app-instructor-dashboard/blob/main/LICENSE
225
+ :alt: License
226
+
227
+ .. |status-badge| image:: https://img.shields.io/badge/Status-Maintained-brightgreen
228
+
229
+ .. |ci-badge| image:: https://github.com/openedx/frontend-app-instructor-dashboard/actions/workflows/ci.yml/badge.svg
230
+ :target: https://github.com/openedx/frontend-app-instructor-dashboard/actions/workflows/ci.yml
231
+ :alt: Continuous Integration
232
+
233
+ .. |codecov-badge| image:: https://codecov.io/github/openedx/frontend-app-instructor-dashboard/coverage.svg?branch=main
234
+ :target: https://codecov.io/github/openedx/frontend-app-instructor-dashboard?branch=main
235
+ :alt: Codecov
package/dist/Main.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import './app.scss';
2
+ declare const Main: () => import("react/jsx-runtime").JSX.Element;
3
+ export default Main;
package/dist/Main.js ADDED
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { CurrentAppProvider, getAppConfig } from '@openedx/frontend-base';
3
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
4
+ import { Outlet } from 'react-router-dom';
5
+ import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
6
+ import { AlertProvider } from './providers/AlertProvider';
7
+ import { appId } from './constants';
8
+ import PageWrapper from './pageWrapper/PageWrapper';
9
+ import './app.scss';
10
+ const queryClient = new QueryClient();
11
+ 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, {}) }), getAppConfig(appId).NODE_ENV === 'development' && _jsx(ReactQueryDevtools, { initialIsOpen: false })] }) }) }) }));
12
+ export default Main;
13
+ //# sourceMappingURL=Main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Main.js","sourceRoot":"","sources":["../src/Main.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,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,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,EACZ,YAAY,CAAC,KAAK,CAAC,CAAC,QAAQ,KAAK,aAAa,IAAI,KAAC,kBAAkB,IAAC,aAAa,EAAE,KAAK,GAAI,IAC3F,GACO,GACI,GACH,CACtB,CAAC;AAEF,eAAe,IAAI,CAAC","sourcesContent":["import { CurrentAppProvider, getAppConfig } from '@openedx/frontend-base';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\nimport { Outlet } from 'react-router-dom';\nimport { ReactQueryDevtools } from '@tanstack/react-query-devtools';\nimport { AlertProvider } from './providers/AlertProvider';\nimport { appId } from './constants';\nimport PageWrapper from './pageWrapper/PageWrapper';\n\nimport './app.scss';\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 { getAppConfig(appId).NODE_ENV === 'development' && <ReactQueryDevtools initialIsOpen={false} /> }\n </main>\n </AlertProvider>\n </QueryClientProvider>\n </CurrentAppProvider>\n);\n\nexport default Main;\n"]}
package/dist/app.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import { App } from '@openedx/frontend-base';
2
+ declare const app: App;
3
+ export default app;
package/dist/app.js ADDED
@@ -0,0 +1,18 @@
1
+ import { appId } from './constants';
2
+ import routes from './routes';
3
+ import messages from './i18n';
4
+ import slots from './slots';
5
+ import providers from './providers';
6
+ const app = {
7
+ appId,
8
+ routes,
9
+ messages,
10
+ providers,
11
+ slots,
12
+ config: {
13
+ NODE_ENV: 'development',
14
+ LMS_BASE_URL: 'http://local.openedx.io:8000'
15
+ }
16
+ };
17
+ export default app;
18
+ //# sourceMappingURL=app.js.map
@@ -0,0 +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,QAAQ,MAAM,WAAW,CAAC;AACjC,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,SAAS,MAAM,gBAAgB,CAAC;AAEvC,MAAM,GAAG,GAAQ;IACf,KAAK;IACL,MAAM;IACN,QAAQ;IACR,SAAS;IACT,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 messages from '@src/i18n';\nimport slots from '@src/slots';\nimport providers from '@src/providers';\n\nconst app: App = {\n appId,\n routes,\n messages,\n providers,\n slots,\n config: {\n NODE_ENV: 'development',\n LMS_BASE_URL: 'http://local.openedx.io:8000'\n }\n};\n\nexport default app;\n"]}
package/dist/app.scss ADDED
@@ -0,0 +1,10 @@
1
+ @use "@openedx/frontend-base/shell/app.scss";
2
+
3
+ .toast-container {
4
+ left: unset;
5
+ right: var(--pgn-spacing-toast-container-gutter-lg);
6
+ }
7
+
8
+ .text-prewrap {
9
+ white-space: pre-wrap;
10
+ }
@@ -0,0 +1,2 @@
1
+ declare const CertificatesPage: () => import("react/jsx-runtime").JSX.Element;
2
+ export default CertificatesPage;
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ const CertificatesPage = () => {
3
+ return (_jsx("div", { children: _jsx("h3", { children: "Certificates" }) }));
4
+ };
5
+ export default CertificatesPage;
6
+ //# sourceMappingURL=CertificatesPage.js.map
@@ -0,0 +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"]}
@@ -0,0 +1,3 @@
1
+ import './CohortsPage.scss';
2
+ declare const CohortsPage: () => import("react/jsx-runtime").JSX.Element;
3
+ export default CohortsPage;
@@ -0,0 +1,41 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useIntl } from '@openedx/frontend-base';
3
+ import { IconButton } from '@openedx/paragon';
4
+ import { Settings } from '@openedx/paragon/icons';
5
+ import { useParams } from 'react-router-dom';
6
+ import { useState } from 'react';
7
+ import { CohortProvider, useCohortContext } from '../cohorts/components/CohortContext';
8
+ import DisableCohortsModal from '../cohorts/components/DisableCohortsModal';
9
+ import DisabledCohortsView from '../cohorts/components/DisabledCohortsView';
10
+ import EnabledCohortsView from '../cohorts/components/EnabledCohortsView';
11
+ import { useCohortStatus, useToggleCohorts } from '../cohorts/data/apiHook';
12
+ import messages from '../cohorts/messages';
13
+ import './CohortsPage.scss';
14
+ const CohortsPageContent = () => {
15
+ const intl = useIntl();
16
+ const { courseId = '' } = useParams();
17
+ const { data: cohortStatus } = useCohortStatus(courseId);
18
+ const { mutate: toggleCohortsMutate } = useToggleCohorts(courseId);
19
+ const [isOpenDisableModal, setIsOpenDisableModal] = useState(false);
20
+ const { clearSelectedCohort } = useCohortContext();
21
+ const { isCohorted = false } = cohortStatus !== null && cohortStatus !== void 0 ? cohortStatus : {};
22
+ const handleEnableCohorts = () => {
23
+ toggleCohortsMutate({ isCohorted: true }, {
24
+ onError: (error) => console.log(error)
25
+ });
26
+ };
27
+ const handleDisableCohorts = () => {
28
+ toggleCohortsMutate({ isCohorted: false }, {
29
+ onSuccess: () => clearSelectedCohort(),
30
+ onError: (error) => console.log(error)
31
+ });
32
+ setIsOpenDisableModal(false);
33
+ };
34
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "d-inline-flex align-items-center", children: [_jsx("h3", { className: "mb-0 text-gray-700", children: intl.formatMessage(messages.cohortsTitle) }), isCohorted && (_jsx("div", { className: "small", children: _jsx(IconButton, { alt: intl.formatMessage(messages.disableCohorts), iconAs: Settings, iconClassNames: "mb-2 text-gray-500", size: "sm", variant: "secondary", onClick: () => setIsOpenDisableModal(true) }) }))] }), isCohorted ? (_jsx(EnabledCohortsView, {})) : (_jsx(DisabledCohortsView, { onEnableCohorts: handleEnableCohorts })), _jsx(DisableCohortsModal, { isOpen: isOpenDisableModal, onClose: () => setIsOpenDisableModal(false), onConfirmDisable: handleDisableCohorts })] }));
35
+ };
36
+ // It was necessary to wrap the entire content with CohortProvider here to avoid errors in the use of cohort hooks within a provider
37
+ const CohortsPage = () => {
38
+ return (_jsx(CohortProvider, { children: _jsx(CohortsPageContent, {}) }));
39
+ };
40
+ export default CohortsPage;
41
+ //# sourceMappingURL=CohortsPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CohortsPage.js","sourceRoot":"","sources":["../../src/cohorts/CohortsPage.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useIntl } from '@openedx/frontend-base';\nimport { IconButton } from '@openedx/paragon';\nimport { Settings } from '@openedx/paragon/icons';\nimport { useParams } from 'react-router-dom';\nimport { useState } from 'react';\nimport { CohortProvider, useCohortContext } from '@src/cohorts/components/CohortContext';\nimport DisableCohortsModal from '@src/cohorts/components/DisableCohortsModal';\nimport DisabledCohortsView from '@src/cohorts/components/DisabledCohortsView';\nimport EnabledCohortsView from '@src/cohorts/components/EnabledCohortsView';\nimport { useCohortStatus, useToggleCohorts } from '@src/cohorts/data/apiHook';\nimport messages from '@src/cohorts/messages';\nimport './CohortsPage.scss';\n\nconst CohortsPageContent = () => {\n const intl = useIntl();\n const { courseId = '' } = useParams();\n const { data: cohortStatus } = useCohortStatus(courseId);\n const { mutate: toggleCohortsMutate } = useToggleCohorts(courseId);\n const [isOpenDisableModal, setIsOpenDisableModal] = useState(false);\n const { clearSelectedCohort } = useCohortContext();\n const { isCohorted = false } = cohortStatus ?? {};\n\n const handleEnableCohorts = () => {\n toggleCohortsMutate({ isCohorted: true },\n {\n onError: (error) => console.log(error)\n });\n };\n\n const handleDisableCohorts = () => {\n toggleCohortsMutate({ isCohorted: false },\n {\n onSuccess: () => clearSelectedCohort(),\n onError: (error) => console.log(error)\n });\n setIsOpenDisableModal(false);\n };\n\n return (\n <>\n <div className=\"d-inline-flex align-items-center\">\n <h3 className=\"mb-0 text-gray-700\">{intl.formatMessage(messages.cohortsTitle)}</h3>\n {isCohorted && (\n <div className=\"small\">\n <IconButton\n alt={intl.formatMessage(messages.disableCohorts)}\n iconAs={Settings}\n iconClassNames=\"mb-2 text-gray-500\"\n size=\"sm\"\n variant=\"secondary\"\n onClick={() => setIsOpenDisableModal(true)}\n />\n </div>\n )}\n </div>\n {isCohorted ? (\n <EnabledCohortsView />\n ) : (\n <DisabledCohortsView onEnableCohorts={handleEnableCohorts} />\n )}\n <DisableCohortsModal isOpen={isOpenDisableModal} onClose={() => setIsOpenDisableModal(false)} onConfirmDisable={handleDisableCohorts} />\n </>\n );\n};\n\n// It was necessary to wrap the entire content with CohortProvider here to avoid errors in the use of cohort hooks within a provider\nconst CohortsPage = () => {\n return (\n <CohortProvider>\n <CohortsPageContent />\n </CohortProvider>\n );\n};\n\nexport default CohortsPage;\n"]}
@@ -0,0 +1,14 @@
1
+ .assignment-tooltip .tooltip-inner {
2
+ background-color: var(--pgn-color-primary-700);
3
+ max-width: none;
4
+ }
5
+
6
+ .collapsible-basic.collapsible-csv {
7
+ .collapsible-trigger {
8
+ text-decoration: none;
9
+ }
10
+
11
+ .collapsible-icon {
12
+ color: var(--pgn-color-info-500);
13
+ }
14
+ }
@@ -0,0 +1,6 @@
1
+ export declare const assignmentLink: {
2
+ random: string;
3
+ manual: string;
4
+ };
5
+ declare const CohortCard: () => import("react/jsx-runtime").JSX.Element | null;
6
+ export default CohortCard;
@@ -0,0 +1,53 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useParams } from 'react-router-dom';
3
+ import { useRef, useState } from 'react';
4
+ import { FormattedMessage, getExternalLinkUrl, useIntl } from '@openedx/frontend-base';
5
+ import { Card, Hyperlink, Tab, Tabs, Toast } from '@openedx/paragon';
6
+ import messages from '../../cohorts/messages';
7
+ import { usePatchCohort } from '../../cohorts/data/apiHook';
8
+ import CohortsForm from '../../cohorts/components/CohortsForm';
9
+ import ManageLearners from '../../cohorts/components/ManageLearners';
10
+ import { useCohortContext } from '../../cohorts/components/CohortContext';
11
+ import { useAlert } from '../../providers/AlertProvider';
12
+ export const assignmentLink = {
13
+ random: 'https://docs.openedx.org/en/latest/educators/references/advanced_features/managing_cohort_assignment.html#about-auto-cohorts',
14
+ manual: 'https://docs.openedx.org/en/latest/educators/how-tos/advanced_features/manage_cohorts.html#assign-learners-to-cohorts-manually',
15
+ };
16
+ const warningMessage = {
17
+ random: messages.automaticCohortWarning,
18
+ manual: messages.manualCohortWarning,
19
+ };
20
+ const CohortCard = () => {
21
+ var _a;
22
+ const intl = useIntl();
23
+ const { courseId = '' } = useParams();
24
+ const { selectedCohort, setSelectedCohort } = useCohortContext();
25
+ const { mutate: editCohort } = usePatchCohort(courseId);
26
+ const formRef = useRef(null);
27
+ const [showSuccessMessage, setShowSuccessMessage] = useState(false);
28
+ const { clearAlerts, showModal } = useAlert();
29
+ if (!selectedCohort) {
30
+ return null;
31
+ }
32
+ const handleEditCohort = (updatedCohort) => {
33
+ clearAlerts();
34
+ editCohort({ cohortId: selectedCohort.id, cohortInfo: updatedCohort }, {
35
+ onSuccess: () => {
36
+ setShowSuccessMessage(true);
37
+ setSelectedCohort(Object.assign(Object.assign({}, selectedCohort), updatedCohort));
38
+ },
39
+ onError: (error) => {
40
+ showModal({
41
+ message: error.message,
42
+ });
43
+ }
44
+ });
45
+ };
46
+ const handleCancelForm = () => {
47
+ var _a;
48
+ (_a = formRef.current) === null || _a === void 0 ? void 0 : _a.resetForm();
49
+ };
50
+ return (_jsxs(_Fragment, { children: [_jsxs(Card, { className: "bg-light-200 mt-3", children: [_jsxs("div", { className: "mx-4 my-3.5", children: [_jsxs("div", { className: "d-flex align-items-center", children: [_jsx("h3", { className: "text-primary-700 mb-0", children: selectedCohort === null || selectedCohort === void 0 ? void 0 : selectedCohort.name }), _jsx("p", { className: "ml-3 text-primary-700 mb-0", children: intl.formatMessage(messages.studentsOnCohort, { users: (_a = selectedCohort === null || selectedCohort === void 0 ? void 0 : selectedCohort.userCount) !== null && _a !== void 0 ? _a : 0 }) })] }), _jsxs("p", { className: "x-small mb-0 mt-2", children: [_jsx(FormattedMessage, Object.assign({}, warningMessage[selectedCohort.assignmentType])), " ", _jsx(Hyperlink, { showLaunchIcon: false, target: "_blank", destination: getExternalLinkUrl(assignmentLink[selectedCohort.assignmentType]), children: intl.formatMessage(messages.warningCohortLink) })] })] }), _jsxs(Tabs, { id: "cohort-management-tabs", className: "mx-0", onSelect: () => { }, children: [_jsx(Tab, { eventKey: "manage-learners", title: intl.formatMessage(messages.manageLearners), children: _jsx(ManageLearners, {}) }, "manage-learners"), _jsx(Tab, { eventKey: "settings", title: intl.formatMessage(messages.settings), children: _jsx(CohortsForm, { ref: formRef, onCancel: handleCancelForm, onSubmit: handleEditCohort }) }, "settings")] })] }), _jsx(Toast, { show: showSuccessMessage, onClose: () => setShowSuccessMessage(false), className: "text-break", children: intl.formatMessage(messages.cohortUpdateSuccessMessage) })] }));
51
+ };
52
+ export default CohortCard;
53
+ //# sourceMappingURL=CohortCard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CohortCard.js","sourceRoot":"","sources":["../../../src/cohorts/components/CohortCard.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACvF,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,QAAQ,MAAM,uBAAuB,CAAC;AAE7C,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,WAAW,MAAM,qCAAqC,CAAC;AAC9D,OAAO,cAAc,MAAM,wCAAwC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAExD,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,MAAM,EAAE,8HAA8H;IACtI,MAAM,EAAE,gIAAgI;CACzI,CAAC;AAEF,MAAM,cAAc,GAAG;IACrB,MAAM,EAAE,QAAQ,CAAC,sBAAsB;IACvC,MAAM,EAAE,QAAQ,CAAC,mBAAmB;CACrC,CAAC;AAEF,MAAM,UAAU,GAAG,GAAG,EAAE;;IACtB,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,MAAM,EAAE,QAAQ,GAAG,EAAE,EAAE,GAAG,SAAS,EAAwB,CAAC;IAC5D,MAAM,EAAE,cAAc,EAAE,iBAAiB,EAAE,GAAG,gBAAgB,EAAE,CAAC;IACjE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,MAAM,CAA4B,IAAI,CAAC,CAAC;IACxD,MAAM,CAAC,kBAAkB,EAAE,qBAAqB,CAAC,GAAG,QAAQ,CAAU,KAAK,CAAC,CAAC;IAC7E,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,QAAQ,EAAE,CAAC;IAE9C,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,gBAAgB,GAAG,CAAC,aAAyB,EAAE,EAAE;QACrD,WAAW,EAAE,CAAC;QACd,UAAU,CAAC,EAAE,QAAQ,EAAE,cAAc,CAAC,EAAE,EAAE,UAAU,EAAE,aAAa,EAAE,EACnE;YACE,SAAS,EAAE,GAAG,EAAE;gBACd,qBAAqB,CAAC,IAAI,CAAC,CAAC;gBAC5B,iBAAiB,iCAAM,cAAc,GAAK,aAAa,EAAG,CAAC;YAC7D,CAAC;YACD,OAAO,EAAE,CAAC,KAAY,EAAE,EAAE;gBACxB,SAAS,CAAC;oBACR,OAAO,EAAE,KAAK,CAAC,OAAO;iBACvB,CAAC,CAAC;YACL,CAAC;SACF,CACF,CAAC;IACJ,CAAC,CAAC;IAEF,MAAM,gBAAgB,GAAG,GAAG,EAAE;;QAC5B,MAAA,OAAO,CAAC,OAAO,0CAAE,SAAS,EAAE,CAAC;IAC/B,CAAC,CAAC;IAEF,OAAO,CACL,8BACE,MAAC,IAAI,IAAC,SAAS,EAAC,mBAAmB,aACjC,eAAK,SAAS,EAAC,aAAa,aAC1B,eAAK,SAAS,EAAC,2BAA2B,aACxC,aAAI,SAAS,EAAC,uBAAuB,YAAE,cAAc,aAAd,cAAc,uBAAd,cAAc,CAAE,IAAI,GAAM,EACjE,YAAG,SAAS,EAAC,4BAA4B,YAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,gBAAgB,EAAE,EAAE,KAAK,EAAE,MAAA,cAAc,aAAd,cAAc,uBAAd,cAAc,CAAE,SAAS,mCAAI,CAAC,EAAE,CAAC,GAAK,IACpI,EACN,aAAG,SAAS,EAAC,mBAAmB,aAC9B,KAAC,gBAAgB,oBAAK,cAAc,CAAC,cAAc,CAAC,cAAc,CAAC,EAAI,OAAC,KAAC,SAAS,IAAC,cAAc,EAAE,KAAK,EAAE,MAAM,EAAC,QAAQ,EAAC,WAAW,EAAE,kBAAkB,CAAC,cAAc,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC,YAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,iBAAiB,CAAC,GAAa,IACpQ,IACA,EACN,MAAC,IAAI,IAAC,EAAE,EAAC,wBAAwB,EAAC,SAAS,EAAC,MAAM,EAAC,QAAQ,EAAE,GAAG,EAAE,GAAE,CAAC,aACnE,KAAC,GAAG,IAAuB,QAAQ,EAAC,iBAAiB,EAAC,KAAK,EAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,cAAc,CAAC,YACtG,KAAC,cAAc,KAAG,IADX,iBAAiB,CAEpB,EACN,KAAC,GAAG,IAAgB,QAAQ,EAAC,UAAU,EAAC,KAAK,EAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,YAClF,KAAC,WAAW,IACV,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,gBAAgB,EAC1B,QAAQ,EAAE,gBAAgB,GAC1B,IALK,UAAU,CAMb,IACD,IACF,EACP,KAAC,KAAK,IAAC,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,SAAS,EAAC,YAAY,YACjG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,0BAA0B,CAAC,GAClD,IACP,CACJ,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,UAAU,CAAC","sourcesContent":["import { useParams } from 'react-router-dom';\nimport { useRef, useState } from 'react';\nimport { FormattedMessage, getExternalLinkUrl, useIntl } from '@openedx/frontend-base';\nimport { Card, Hyperlink, Tab, Tabs, Toast } from '@openedx/paragon';\nimport messages from '@src/cohorts/messages';\nimport { CohortData } from '@src/cohorts/types';\nimport { usePatchCohort } from '@src/cohorts/data/apiHook';\nimport CohortsForm from '@src/cohorts/components/CohortsForm';\nimport ManageLearners from '@src/cohorts/components/ManageLearners';\nimport { useCohortContext } from '@src/cohorts/components/CohortContext';\nimport { useAlert } from '@src/providers/AlertProvider';\n\nexport const assignmentLink = {\n random: 'https://docs.openedx.org/en/latest/educators/references/advanced_features/managing_cohort_assignment.html#about-auto-cohorts',\n manual: 'https://docs.openedx.org/en/latest/educators/how-tos/advanced_features/manage_cohorts.html#assign-learners-to-cohorts-manually',\n};\n\nconst warningMessage = {\n random: messages.automaticCohortWarning,\n manual: messages.manualCohortWarning,\n};\n\nconst CohortCard = () => {\n const intl = useIntl();\n const { courseId = '' } = useParams<{ courseId: string }>();\n const { selectedCohort, setSelectedCohort } = useCohortContext();\n const { mutate: editCohort } = usePatchCohort(courseId);\n const formRef = useRef<{ resetForm: () => void }>(null);\n const [showSuccessMessage, setShowSuccessMessage] = useState<boolean>(false);\n const { clearAlerts, showModal } = useAlert();\n\n if (!selectedCohort) {\n return null;\n }\n\n const handleEditCohort = (updatedCohort: CohortData) => {\n clearAlerts();\n editCohort({ cohortId: selectedCohort.id, cohortInfo: updatedCohort },\n {\n onSuccess: () => {\n setShowSuccessMessage(true);\n setSelectedCohort({ ...selectedCohort, ...updatedCohort });\n },\n onError: (error: Error) => {\n showModal({\n message: error.message,\n });\n }\n }\n );\n };\n\n const handleCancelForm = () => {\n formRef.current?.resetForm();\n };\n\n return (\n <>\n <Card className=\"bg-light-200 mt-3\">\n <div className=\"mx-4 my-3.5\">\n <div className=\"d-flex align-items-center\">\n <h3 className=\"text-primary-700 mb-0\">{selectedCohort?.name}</h3>\n <p className=\"ml-3 text-primary-700 mb-0\">{intl.formatMessage(messages.studentsOnCohort, { users: selectedCohort?.userCount ?? 0 })}</p>\n </div>\n <p className=\"x-small mb-0 mt-2\">\n <FormattedMessage {...warningMessage[selectedCohort.assignmentType]} /> <Hyperlink showLaunchIcon={false} target=\"_blank\" destination={getExternalLinkUrl(assignmentLink[selectedCohort.assignmentType])}>{intl.formatMessage(messages.warningCohortLink)}</Hyperlink>\n </p>\n </div>\n <Tabs id=\"cohort-management-tabs\" className=\"mx-0\" onSelect={() => {}}>\n <Tab key=\"manage-learners\" eventKey=\"manage-learners\" title={intl.formatMessage(messages.manageLearners)}>\n <ManageLearners />\n </Tab>\n <Tab key=\"settings\" eventKey=\"settings\" title={intl.formatMessage(messages.settings)}>\n <CohortsForm\n ref={formRef}\n onCancel={handleCancelForm}\n onSubmit={handleEditCohort}\n />\n </Tab>\n </Tabs>\n </Card>\n <Toast show={showSuccessMessage} onClose={() => setShowSuccessMessage(false)} className=\"text-break\">\n {intl.formatMessage(messages.cohortUpdateSuccessMessage)}\n </Toast>\n </>\n );\n};\n\nexport default CohortCard;\n"]}
@@ -0,0 +1,14 @@
1
+ import { ReactNode, FC } from 'react';
2
+ import { CohortData } from '../types';
3
+ interface CohortContextType {
4
+ selectedCohort: CohortData | null;
5
+ setSelectedCohort: (cohort: CohortData) => void;
6
+ clearSelectedCohort: () => void;
7
+ updateCohortField: (field: keyof CohortData, value: string) => void;
8
+ }
9
+ interface CohortProviderProps {
10
+ children: ReactNode;
11
+ }
12
+ export declare const CohortProvider: FC<CohortProviderProps>;
13
+ export declare const useCohortContext: () => CohortContextType;
14
+ export {};
@@ -0,0 +1,46 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext, useState, useCallback, useMemo } from 'react';
3
+ const CohortContext = createContext(undefined);
4
+ const areCohortsEqual = (prev, current) => {
5
+ if (!prev)
6
+ return false;
7
+ return prev.id === current.id
8
+ && prev.name === current.name
9
+ && prev.assignmentType === current.assignmentType
10
+ && prev.userPartitionId === current.userPartitionId
11
+ && prev.userCount === current.userCount
12
+ && prev.groupId === current.groupId;
13
+ };
14
+ export const CohortProvider = ({ children }) => {
15
+ const [selectedCohort, setSelectedCohortState] = useState(null);
16
+ const setSelectedCohort = useCallback((cohort) => {
17
+ setSelectedCohortState(prev => {
18
+ // Only update if the values actually changed
19
+ if (!areCohortsEqual(prev, cohort)) {
20
+ return cohort;
21
+ }
22
+ return prev;
23
+ });
24
+ }, []);
25
+ const clearSelectedCohort = useCallback(() => {
26
+ setSelectedCohortState(null);
27
+ }, []);
28
+ const updateCohortField = useCallback((field, value) => {
29
+ setSelectedCohortState(prev => prev ? Object.assign(Object.assign({}, prev), { [field]: value }) : null);
30
+ }, []);
31
+ const value = useMemo(() => ({
32
+ selectedCohort,
33
+ setSelectedCohort,
34
+ clearSelectedCohort,
35
+ updateCohortField
36
+ }), [selectedCohort, setSelectedCohort, clearSelectedCohort, updateCohortField]);
37
+ return (_jsx(CohortContext.Provider, { value: value, children: children }));
38
+ };
39
+ export const useCohortContext = () => {
40
+ const context = useContext(CohortContext);
41
+ if (context === undefined) {
42
+ throw new Error('useCohortContext must be used within a CohortProvider');
43
+ }
44
+ return context;
45
+ };
46
+ //# sourceMappingURL=CohortContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CohortContext.js","sourceRoot":"","sources":["../../../src/cohorts/components/CohortContext.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAa,WAAW,EAAM,OAAO,EAAE,MAAM,OAAO,CAAC;AAUjG,MAAM,aAAa,GAAG,aAAa,CAAgC,SAAS,CAAC,CAAC;AAM9E,MAAM,eAAe,GAAG,CAAC,IAAuB,EAAE,OAAmB,EAAW,EAAE;IAChF,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,OAAO,IAAI,CAAC,EAAE,KAAK,OAAO,CAAC,EAAE;WACxB,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI;WAC1B,IAAI,CAAC,cAAc,KAAK,OAAO,CAAC,cAAc;WAC9C,IAAI,CAAC,eAAe,KAAK,OAAO,CAAC,eAAe;WAChD,IAAI,CAAC,SAAS,KAAK,OAAO,CAAC,SAAS;WACpC,IAAI,CAAC,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC;AACxC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAA4B,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE;IACtE,MAAM,CAAC,cAAc,EAAE,sBAAsB,CAAC,GAAG,QAAQ,CAAoB,IAAI,CAAC,CAAC;IAEnF,MAAM,iBAAiB,GAAG,WAAW,CAAC,CAAC,MAAkB,EAAE,EAAE;QAC3D,sBAAsB,CAAC,IAAI,CAAC,EAAE;YAC5B,6CAA6C;YAC7C,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;gBACnC,OAAO,MAAM,CAAC;YAChB,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,mBAAmB,GAAG,WAAW,CAAC,GAAG,EAAE;QAC3C,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,iBAAiB,GAAG,WAAW,CAAC,CAAC,KAAuB,EAAE,KAAa,EAAE,EAAE;QAC/E,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAC5B,IAAI,CAAC,CAAC,iCAAM,IAAI,KAAE,CAAC,KAAK,CAAC,EAAE,KAAK,IAAG,CAAC,CAAC,IAAI,CAC1C,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QAC3B,cAAc;QACd,iBAAiB;QACjB,mBAAmB;QACnB,iBAAiB;KAClB,CAAC,EAAE,CAAC,cAAc,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAEjF,OAAO,CACL,KAAC,aAAa,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YACjC,QAAQ,GACc,CAC1B,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAsB,EAAE;IACtD,MAAM,OAAO,GAAG,UAAU,CAAC,aAAa,CAAC,CAAC;IAC1C,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC","sourcesContent":["import { createContext, useContext, useState, ReactNode, useCallback, FC, useMemo } from 'react';\nimport { CohortData } from '../types';\n\ninterface CohortContextType {\n selectedCohort: CohortData | null,\n setSelectedCohort: (cohort: CohortData) => void,\n clearSelectedCohort: () => void,\n updateCohortField: (field: keyof CohortData, value: string) => void,\n}\n\nconst CohortContext = createContext<CohortContextType | undefined>(undefined);\n\ninterface CohortProviderProps {\n children: ReactNode,\n}\n\nconst areCohortsEqual = (prev: CohortData | null, current: CohortData): boolean => {\n if (!prev) return false;\n return prev.id === current.id\n && prev.name === current.name\n && prev.assignmentType === current.assignmentType\n && prev.userPartitionId === current.userPartitionId\n && prev.userCount === current.userCount\n && prev.groupId === current.groupId;\n};\n\nexport const CohortProvider: FC<CohortProviderProps> = ({ children }) => {\n const [selectedCohort, setSelectedCohortState] = useState<CohortData | null>(null);\n\n const setSelectedCohort = useCallback((cohort: CohortData) => {\n setSelectedCohortState(prev => {\n // Only update if the values actually changed\n if (!areCohortsEqual(prev, cohort)) {\n return cohort;\n }\n return prev;\n });\n }, []);\n\n const clearSelectedCohort = useCallback(() => {\n setSelectedCohortState(null);\n }, []);\n\n const updateCohortField = useCallback((field: keyof CohortData, value: string) => {\n setSelectedCohortState(prev =>\n prev ? { ...prev, [field]: value } : null\n );\n }, []);\n\n const value = useMemo(() => ({\n selectedCohort,\n setSelectedCohort,\n clearSelectedCohort,\n updateCohortField\n }), [selectedCohort, setSelectedCohort, clearSelectedCohort, updateCohortField]);\n\n return (\n <CohortContext.Provider value={value}>\n {children}\n </CohortContext.Provider>\n );\n};\n\nexport const useCohortContext = (): CohortContextType => {\n const context = useContext(CohortContext);\n if (context === undefined) {\n throw new Error('useCohortContext must be used within a CohortProvider');\n }\n return context;\n};\n"]}
@@ -0,0 +1,11 @@
1
+ import { CohortData } from '../../cohorts/types';
2
+ interface CohortsFormProps {
3
+ disableManualAssignment?: boolean;
4
+ onCancel: () => void;
5
+ onSubmit: (data: Partial<CohortData>) => void;
6
+ }
7
+ export interface CohortsFormRef {
8
+ resetForm: () => void;
9
+ }
10
+ declare const CohortsForm: import("react").ForwardRefExoticComponent<CohortsFormProps & import("react").RefAttributes<CohortsFormRef>>;
11
+ export default CohortsForm;
@@ -0,0 +1,58 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useParams } from 'react-router-dom';
3
+ import { useState, useEffect, forwardRef, useImperativeHandle, useCallback } from 'react';
4
+ import { ActionRow, Button, Form, FormControl, FormGroup, FormLabel, FormRadioSet, Hyperlink, Icon, OverlayTrigger, Tooltip } from '@openedx/paragon';
5
+ import { useIntl } from '@openedx/frontend-base';
6
+ import messages from '../../cohorts/messages';
7
+ import { useContentGroupsData } from '../../cohorts/data/apiHook';
8
+ import { Warning } from '@openedx/paragon/icons';
9
+ import { assignmentTypes } from '../../cohorts/constants';
10
+ import { useCohortContext } from '../../cohorts/components/CohortContext';
11
+ const CohortsForm = forwardRef(({ disableManualAssignment = false, onCancel, onSubmit }, ref) => {
12
+ var _a, _b;
13
+ const intl = useIntl();
14
+ const { courseId = '' } = useParams();
15
+ const { data = { groups: [], id: null, studioContentGroupsLink: '' } } = useContentGroupsData(courseId);
16
+ const { selectedCohort } = useCohortContext();
17
+ const initialCohortName = (_a = (selectedCohort === null || selectedCohort === void 0 ? void 0 : selectedCohort.name)) !== null && _a !== void 0 ? _a : '';
18
+ const initialAssignmentType = (_b = selectedCohort === null || selectedCohort === void 0 ? void 0 : selectedCohort.assignmentType) !== null && _b !== void 0 ? _b : assignmentTypes.automatic;
19
+ const initialContentGroupOption = (selectedCohort === null || selectedCohort === void 0 ? void 0 : selectedCohort.groupId) ? 'selectContentGroup' : 'noContentGroup';
20
+ const initialContentGroup = (selectedCohort === null || selectedCohort === void 0 ? void 0 : selectedCohort.groupId) ? selectedCohort.groupId : null;
21
+ const [selectedContentGroup, setSelectedContentGroup] = useState(initialContentGroup);
22
+ const [selectedContentGroupOption, setSelectedContentGroupOption] = useState(initialContentGroupOption);
23
+ const [selectedAssignmentType, setSelectedAssignmentType] = useState(initialAssignmentType);
24
+ const [name, setName] = useState(initialCohortName);
25
+ const resetToInitialValues = useCallback(() => {
26
+ var _a;
27
+ if (selectedCohort) {
28
+ setName(selectedCohort.name);
29
+ setSelectedAssignmentType(selectedCohort.assignmentType);
30
+ setSelectedContentGroupOption(selectedCohort.groupId ? 'selectContentGroup' : 'noContentGroup');
31
+ setSelectedContentGroup((_a = selectedCohort.groupId) !== null && _a !== void 0 ? _a : null);
32
+ }
33
+ }, [selectedCohort]);
34
+ useImperativeHandle(ref, () => ({
35
+ resetForm: resetToInitialValues
36
+ }));
37
+ useEffect(() => {
38
+ resetToInitialValues();
39
+ // eslint-disable-next-line react-hooks/exhaustive-deps
40
+ }, [selectedCohort]);
41
+ const contentGroups = [{ id: 'null', name: intl.formatMessage(messages.notSelected) }, ...data.groups];
42
+ const handleSubmit = (event) => {
43
+ event.preventDefault();
44
+ onSubmit({
45
+ name,
46
+ assignmentType: selectedAssignmentType,
47
+ groupId: selectedContentGroup,
48
+ userPartitionId: data.id,
49
+ });
50
+ };
51
+ return (_jsxs(Form, { className: "my-3.5 mx-4", onSubmit: handleSubmit, children: [_jsxs(FormGroup, { className: "w-md-50", children: [_jsx(FormLabel, { className: "text-primary-500", children: intl.formatMessage(messages.cohortName) }), _jsx(FormControl, { value: name, onChange: (e) => setName(e.target.value), placeholder: intl.formatMessage(messages.cohortName) })] }), _jsxs(FormGroup, { children: [_jsx(FormLabel, { className: "text-primary-500", children: intl.formatMessage(messages.cohortAssignmentMethod) }), _jsxs(FormRadioSet, { name: "assignmentType", value: selectedAssignmentType, onChange: (e) => setSelectedAssignmentType(e.target.value), children: [_jsx(Form.Radio, { className: "mb-2", value: assignmentTypes.automatic, children: intl.formatMessage(messages.automatic) }), disableManualAssignment ? (_jsx(OverlayTrigger, { placement: "top", overlay: (_jsx(Tooltip, { id: "manual-assignment-tooltip", className: "assignment-tooltip", children: intl.formatMessage(messages.manualAssignmentDisabledTooltip) })), children: _jsx("span", { children: _jsx(Form.Radio, { disabled: true, value: assignmentTypes.manual, children: intl.formatMessage(messages.manual) }) }) }))
52
+ : _jsx(Form.Radio, { value: assignmentTypes.manual, children: intl.formatMessage(messages.manual) })] })] }), _jsxs(FormGroup, { className: "mb-3.5", children: [_jsx(FormLabel, { className: "text-primary-500", children: intl.formatMessage(messages.associatedContentGroup) }), _jsxs(FormRadioSet, { name: "associatedContentGroup", value: selectedContentGroupOption, onChange: (e) => setSelectedContentGroupOption(e.target.value), children: [_jsx(Form.Radio, { value: "noContentGroup", children: intl.formatMessage(messages.noContentGroup) }), _jsxs("div", { className: "d-flex align-items-center", children: [_jsx(Form.Radio, { value: "selectContentGroup", disabled: data.groups.length === 0, children: intl.formatMessage(messages.selectAContentGroup) }), data.groups.length > 0
53
+ ? (_jsx(FormControl, { as: "select", className: "ml-2", size: "sm", disabled: selectedContentGroupOption !== 'selectContentGroup', name: "contentGroup", onChange: (e) => setSelectedContentGroup(Number(e.target.value)), value: selectedContentGroup, children: contentGroups.map((contentGroup) => (_jsx("option", { value: contentGroup.id, children: contentGroup.name }, contentGroup.id))) }))
54
+ : (_jsxs("div", { className: "d-flex align-items-center small", children: [_jsx(Icon, { className: "ml-2 text-danger-500", src: Warning, size: "sm" }), _jsx("p", { className: "mb-0 ml-1 text-danger-500", children: intl.formatMessage(messages.noContentGroups) }), _jsx(Hyperlink, { className: "ml-1", destination: data.studioContentGroupsLink, children: intl.formatMessage(messages.createContentGroup) })] }))] })] })] }), _jsxs(ActionRow, { children: [_jsx(Button, { variant: "tertiary", onClick: onCancel, children: intl.formatMessage(messages.cancelLabel) }), _jsx(Button, { type: "submit", disabled: name.trim() === '', children: intl.formatMessage(messages.saveLabel) })] })] }));
55
+ });
56
+ CohortsForm.displayName = 'CohortsForm';
57
+ export default CohortsForm;
58
+ //# sourceMappingURL=CohortsForm.js.map