@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.
- package/LICENSE +661 -0
- package/README.rst +235 -0
- package/dist/Main.d.ts +3 -0
- package/dist/Main.js +13 -0
- package/dist/Main.js.map +1 -0
- package/dist/app.d.ts +3 -0
- package/dist/app.js +18 -0
- package/dist/app.js.map +1 -0
- package/dist/app.scss +10 -0
- package/dist/certificates/CertificatesPage.d.ts +2 -0
- package/dist/certificates/CertificatesPage.js +6 -0
- package/dist/certificates/CertificatesPage.js.map +1 -0
- package/dist/cohorts/CohortsPage.d.ts +3 -0
- package/dist/cohorts/CohortsPage.js +41 -0
- package/dist/cohorts/CohortsPage.js.map +1 -0
- package/dist/cohorts/CohortsPage.scss +14 -0
- package/dist/cohorts/components/CohortCard.d.ts +6 -0
- package/dist/cohorts/components/CohortCard.js +53 -0
- package/dist/cohorts/components/CohortCard.js.map +1 -0
- package/dist/cohorts/components/CohortContext.d.ts +14 -0
- package/dist/cohorts/components/CohortContext.js +46 -0
- package/dist/cohorts/components/CohortContext.js.map +1 -0
- package/dist/cohorts/components/CohortsForm.d.ts +11 -0
- package/dist/cohorts/components/CohortsForm.js +58 -0
- package/dist/cohorts/components/CohortsForm.js.map +1 -0
- package/dist/cohorts/components/DisableCohortsModal.d.ts +7 -0
- package/dist/cohorts/components/DisableCohortsModal.js +10 -0
- package/dist/cohorts/components/DisableCohortsModal.js.map +1 -0
- package/dist/cohorts/components/DisabledCohortsView.d.ts +5 -0
- package/dist/cohorts/components/DisabledCohortsView.js +10 -0
- package/dist/cohorts/components/DisabledCohortsView.js.map +1 -0
- package/dist/cohorts/components/EnabledCohortsView.d.ts +2 -0
- package/dist/cohorts/components/EnabledCohortsView.js +96 -0
- package/dist/cohorts/components/EnabledCohortsView.js.map +1 -0
- package/dist/cohorts/components/ManageLearners.d.ts +2 -0
- package/dist/cohorts/components/ManageLearners.js +64 -0
- package/dist/cohorts/components/ManageLearners.js.map +1 -0
- package/dist/cohorts/components/SelectedCohortInfo.d.ts +2 -0
- package/dist/cohorts/components/SelectedCohortInfo.js +43 -0
- package/dist/cohorts/components/SelectedCohortInfo.js.map +1 -0
- package/dist/cohorts/constants.d.ts +4 -0
- package/dist/cohorts/constants.js +5 -0
- package/dist/cohorts/constants.js.map +1 -0
- package/dist/cohorts/data/api.d.ts +9 -0
- package/dist/cohorts/data/api.js +54 -0
- package/dist/cohorts/data/api.js.map +1 -0
- package/dist/cohorts/data/apiHook.d.ts +14 -0
- package/dist/cohorts/data/apiHook.js +64 -0
- package/dist/cohorts/data/apiHook.js.map +1 -0
- package/dist/cohorts/data/queryKeys.d.ts +7 -0
- package/dist/cohorts/data/queryKeys.js +9 -0
- package/dist/cohorts/data/queryKeys.js.map +1 -0
- package/dist/cohorts/messages.d.ts +233 -0
- package/dist/cohorts/messages.js +235 -0
- package/dist/cohorts/messages.js.map +1 -0
- package/dist/cohorts/types.d.ts +15 -0
- package/dist/cohorts/types.js +2 -0
- package/dist/cohorts/types.js.map +1 -0
- package/dist/components/ActionCard.d.ts +11 -0
- package/dist/components/ActionCard.js +7 -0
- package/dist/components/ActionCard.js.map +1 -0
- package/dist/components/CSVComponent.d.ts +10 -0
- package/dist/components/CSVComponent.js +21 -0
- package/dist/components/CSVComponent.js.map +1 -0
- package/dist/components/ObjectCell.d.ts +5 -0
- package/dist/components/ObjectCell.js +7 -0
- package/dist/components/ObjectCell.js.map +1 -0
- package/dist/components/PageNotFound.d.ts +2 -0
- package/dist/components/PageNotFound.js +12 -0
- package/dist/components/PageNotFound.js.map +1 -0
- package/dist/components/PendingTasks.d.ts +5 -0
- package/dist/components/PendingTasks.js +38 -0
- package/dist/components/PendingTasks.js.map +1 -0
- package/dist/components/SpecifyLearnerField.d.ts +5 -0
- package/dist/components/SpecifyLearnerField.js +10 -0
- package/dist/components/SpecifyLearnerField.js.map +1 -0
- package/dist/components/messages.d.ts +108 -0
- package/dist/components/messages.js +110 -0
- package/dist/components/messages.js.map +1 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +2 -0
- package/dist/constants.js.map +1 -0
- package/dist/courseInfo/CourseInfoPage.d.ts +2 -0
- package/dist/courseInfo/CourseInfoPage.js +7 -0
- package/dist/courseInfo/CourseInfoPage.js.map +1 -0
- package/dist/courseInfo/components/EnrollmentSummary/EnrollmentCounter.d.ts +8 -0
- package/dist/courseInfo/components/EnrollmentSummary/EnrollmentCounter.js +13 -0
- package/dist/courseInfo/components/EnrollmentSummary/EnrollmentCounter.js.map +1 -0
- package/dist/courseInfo/components/EnrollmentSummary/EnrollmentSummary.d.ts +2 -0
- package/dist/courseInfo/components/EnrollmentSummary/EnrollmentSummary.js +24 -0
- package/dist/courseInfo/components/EnrollmentSummary/EnrollmentSummary.js.map +1 -0
- package/dist/courseInfo/components/EnrollmentSummary/index.d.ts +2 -0
- package/dist/courseInfo/components/EnrollmentSummary/index.js +3 -0
- package/dist/courseInfo/components/EnrollmentSummary/index.js.map +1 -0
- package/dist/courseInfo/components/EnrollmentSummary/messages.d.ts +83 -0
- package/dist/courseInfo/components/EnrollmentSummary/messages.js +85 -0
- package/dist/courseInfo/components/EnrollmentSummary/messages.js.map +1 -0
- package/dist/courseInfo/components/EnrollmentSummary/utils.d.ts +1 -0
- package/dist/courseInfo/components/EnrollmentSummary/utils.js +9 -0
- package/dist/courseInfo/components/EnrollmentSummary/utils.js.map +1 -0
- package/dist/courseInfo/components/generalCourseInfo/GeneralCourseInfo.d.ts +2 -0
- package/dist/courseInfo/components/generalCourseInfo/GeneralCourseInfo.js +46 -0
- package/dist/courseInfo/components/generalCourseInfo/GeneralCourseInfo.js.map +1 -0
- package/dist/courseInfo/components/generalCourseInfo/StatusBadge.d.ts +5 -0
- package/dist/courseInfo/components/generalCourseInfo/StatusBadge.js +15 -0
- package/dist/courseInfo/components/generalCourseInfo/StatusBadge.js.map +1 -0
- package/dist/courseInfo/components/generalCourseInfo/index.d.ts +1 -0
- package/dist/courseInfo/components/generalCourseInfo/index.js +2 -0
- package/dist/courseInfo/components/generalCourseInfo/index.js.map +1 -0
- package/dist/courseInfo/components/generalCourseInfo/messages.d.ts +8 -0
- package/dist/courseInfo/components/generalCourseInfo/messages.js +10 -0
- package/dist/courseInfo/components/generalCourseInfo/messages.js.map +1 -0
- package/dist/courseInfo/messages.d.ts +8 -0
- package/dist/courseInfo/messages.js +10 -0
- package/dist/courseInfo/messages.js.map +1 -0
- package/dist/courseInfo/types.d.ts +27 -0
- package/dist/courseInfo/types.js +2 -0
- package/dist/courseInfo/types.js.map +1 -0
- package/dist/courseTeam/CourseTeamPage.d.ts +2 -0
- package/dist/courseTeam/CourseTeamPage.js +6 -0
- package/dist/courseTeam/CourseTeamPage.js.map +1 -0
- package/dist/data/api.d.ts +14 -0
- package/dist/data/api.js +33 -0
- package/dist/data/api.js.map +1 -0
- package/dist/data/apiHook.d.ts +4 -0
- package/dist/data/apiHook.js +28 -0
- package/dist/data/apiHook.js.map +1 -0
- package/dist/data/queryKeys.d.ts +8 -0
- package/dist/data/queryKeys.js +10 -0
- package/dist/data/queryKeys.js.map +1 -0
- package/dist/dataDownloads/DataDownloadsPage.d.ts +2 -0
- package/dist/dataDownloads/DataDownloadsPage.js +162 -0
- package/dist/dataDownloads/DataDownloadsPage.js.map +1 -0
- package/dist/dataDownloads/components/DataDownloadTable.d.ts +8 -0
- package/dist/dataDownloads/components/DataDownloadTable.js +42 -0
- package/dist/dataDownloads/components/DataDownloadTable.js.map +1 -0
- package/dist/dataDownloads/components/DownloadLinkCell.d.ts +6 -0
- package/dist/dataDownloads/components/DownloadLinkCell.js +13 -0
- package/dist/dataDownloads/components/DownloadLinkCell.js.map +1 -0
- package/dist/dataDownloads/components/GenerateReports.d.ts +8 -0
- package/dist/dataDownloads/components/GenerateReports.js +20 -0
- package/dist/dataDownloads/components/GenerateReports.js.map +1 -0
- package/dist/dataDownloads/components/ReportNameCell.d.ts +3 -0
- package/dist/dataDownloads/components/ReportNameCell.js +6 -0
- package/dist/dataDownloads/components/ReportNameCell.js.map +1 -0
- package/dist/dataDownloads/data/api.d.ts +2 -0
- package/dist/dataDownloads/data/api.js +23 -0
- package/dist/dataDownloads/data/api.js.map +1 -0
- package/dist/dataDownloads/data/apiHook.d.ts +7 -0
- package/dist/dataDownloads/data/apiHook.js +22 -0
- package/dist/dataDownloads/data/apiHook.js.map +1 -0
- package/dist/dataDownloads/data/queryKeys.d.ts +5 -0
- package/dist/dataDownloads/data/queryKeys.js +7 -0
- package/dist/dataDownloads/data/queryKeys.js.map +1 -0
- package/dist/dataDownloads/messages.d.ts +338 -0
- package/dist/dataDownloads/messages.js +341 -0
- package/dist/dataDownloads/messages.js.map +1 -0
- package/dist/dataDownloads/types.d.ts +8 -0
- package/dist/dataDownloads/types.js +2 -0
- package/dist/dataDownloads/types.js.map +1 -0
- package/dist/dataDownloads/utils.d.ts +26 -0
- package/dist/dataDownloads/utils.js +49 -0
- package/dist/dataDownloads/utils.js.map +1 -0
- package/dist/dateExtensions/DateExtensionsPage.d.ts +2 -0
- package/dist/dateExtensions/DateExtensionsPage.js +87 -0
- package/dist/dateExtensions/DateExtensionsPage.js.map +1 -0
- package/dist/dateExtensions/components/AddExtensionModal.d.ts +13 -0
- package/dist/dateExtensions/components/AddExtensionModal.js +34 -0
- package/dist/dateExtensions/components/AddExtensionModal.js.map +1 -0
- package/dist/dateExtensions/components/DateExtensionsList.d.ts +7 -0
- package/dist/dateExtensions/components/DateExtensionsList.js +103 -0
- package/dist/dateExtensions/components/DateExtensionsList.js.map +1 -0
- package/dist/dateExtensions/components/ResetExtensionsModal.d.ts +10 -0
- package/dist/dateExtensions/components/ResetExtensionsModal.js +10 -0
- package/dist/dateExtensions/components/ResetExtensionsModal.js.map +1 -0
- package/dist/dateExtensions/components/SelectGradedSubsection.d.ts +8 -0
- package/dist/dateExtensions/components/SelectGradedSubsection.js +15 -0
- package/dist/dateExtensions/components/SelectGradedSubsection.js.map +1 -0
- package/dist/dateExtensions/data/api.d.ts +6 -0
- package/dist/dateExtensions/data/api.js +40 -0
- package/dist/dateExtensions/data/api.js.map +1 -0
- package/dist/dateExtensions/data/apiHook.d.ts +11 -0
- package/dist/dateExtensions/data/apiHook.js +31 -0
- package/dist/dateExtensions/data/apiHook.js.map +1 -0
- package/dist/dateExtensions/data/queryKeys.d.ts +10 -0
- package/dist/dateExtensions/data/queryKeys.js +17 -0
- package/dist/dateExtensions/data/queryKeys.js.map +1 -0
- package/dist/dateExtensions/messages.d.ts +128 -0
- package/dist/dateExtensions/messages.js +130 -0
- package/dist/dateExtensions/messages.js.map +1 -0
- package/dist/dateExtensions/types.d.ts +24 -0
- package/dist/dateExtensions/types.js +2 -0
- package/dist/dateExtensions/types.js.map +1 -0
- package/dist/enrollments/EnrollmentsPage.d.ts +2 -0
- package/dist/enrollments/EnrollmentsPage.js +32 -0
- package/dist/enrollments/EnrollmentsPage.js.map +1 -0
- package/dist/enrollments/components/EnrollmentStatusModal.d.ts +6 -0
- package/dist/enrollments/components/EnrollmentStatusModal.js +23 -0
- package/dist/enrollments/components/EnrollmentStatusModal.js.map +1 -0
- package/dist/enrollments/components/EnrollmentsList.d.ts +6 -0
- package/dist/enrollments/components/EnrollmentsList.js +99 -0
- package/dist/enrollments/components/EnrollmentsList.js.map +1 -0
- package/dist/enrollments/components/UnenrollModal.d.ts +8 -0
- package/dist/enrollments/components/UnenrollModal.js +7 -0
- package/dist/enrollments/components/UnenrollModal.js.map +1 -0
- package/dist/enrollments/data/api.d.ts +4 -0
- package/dist/enrollments/data/api.js +32 -0
- package/dist/enrollments/data/api.js.map +1 -0
- package/dist/enrollments/data/apiHook.d.ts +3 -0
- package/dist/enrollments/data/apiHook.js +14 -0
- package/dist/enrollments/data/apiHook.js.map +1 -0
- package/dist/enrollments/data/queryKeys.d.ts +7 -0
- package/dist/enrollments/data/queryKeys.js +8 -0
- package/dist/enrollments/data/queryKeys.js.map +1 -0
- package/dist/enrollments/messages.d.ts +108 -0
- package/dist/enrollments/messages.js +110 -0
- package/dist/enrollments/messages.js.map +1 -0
- package/dist/enrollments/types.d.ts +15 -0
- package/dist/enrollments/types.js +2 -0
- package/dist/enrollments/types.js.map +1 -0
- package/dist/grading/GradingPage.d.ts +2 -0
- package/dist/grading/GradingPage.js +6 -0
- package/dist/grading/GradingPage.js.map +1 -0
- package/dist/hooks/useDebouncedFilter.d.ts +16 -0
- package/dist/hooks/useDebouncedFilter.js +27 -0
- package/dist/hooks/useDebouncedFilter.js.map +1 -0
- package/dist/i18n/index.d.ts +25 -0
- package/dist/i18n/index.js +26 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/instructorNav/InstructorNav.d.ts +8 -0
- package/dist/instructorNav/InstructorNav.js +42 -0
- package/dist/instructorNav/InstructorNav.js.map +1 -0
- package/dist/openResponses/OpenResponsesPage.d.ts +2 -0
- package/dist/openResponses/OpenResponsesPage.js +8 -0
- package/dist/openResponses/OpenResponsesPage.js.map +1 -0
- package/dist/openResponses/components/DetailAssessmentsList.d.ts +2 -0
- package/dist/openResponses/components/DetailAssessmentsList.js +44 -0
- package/dist/openResponses/components/DetailAssessmentsList.js.map +1 -0
- package/dist/openResponses/components/OpenResponsesSummary.d.ts +2 -0
- package/dist/openResponses/components/OpenResponsesSummary.js +30 -0
- package/dist/openResponses/components/OpenResponsesSummary.js.map +1 -0
- package/dist/openResponses/data/api.d.ts +4 -0
- package/dist/openResponses/data/api.js +23 -0
- package/dist/openResponses/data/api.js.map +1 -0
- package/dist/openResponses/data/apiHook.d.ts +2 -0
- package/dist/openResponses/data/apiHook.js +14 -0
- package/dist/openResponses/data/apiHook.js.map +1 -0
- package/dist/openResponses/data/queryKeys.d.ts +6 -0
- package/dist/openResponses/data/queryKeys.js +8 -0
- package/dist/openResponses/data/queryKeys.js.map +1 -0
- package/dist/openResponses/messages.d.ts +83 -0
- package/dist/openResponses/messages.js +85 -0
- package/dist/openResponses/messages.js.map +1 -0
- package/dist/openResponses/types.d.ts +12 -0
- package/dist/openResponses/types.js +2 -0
- package/dist/openResponses/types.js.map +1 -0
- package/dist/pageWrapper/PageWrapper.d.ts +4 -0
- package/dist/pageWrapper/PageWrapper.js +11 -0
- package/dist/pageWrapper/PageWrapper.js.map +1 -0
- package/dist/pageWrapper/messages.d.ts +8 -0
- package/dist/pageWrapper/messages.js +10 -0
- package/dist/pageWrapper/messages.js.map +1 -0
- package/dist/providers/AlertProvider.d.ts +41 -0
- package/dist/providers/AlertProvider.js +117 -0
- package/dist/providers/AlertProvider.js.map +1 -0
- package/dist/providers/QueryProvider.d.ts +6 -0
- package/dist/providers/QueryProvider.js +16 -0
- package/dist/providers/QueryProvider.js.map +1 -0
- package/dist/providers.d.ts +3 -0
- package/dist/providers.js +8 -0
- package/dist/providers.js.map +1 -0
- package/dist/routes.d.ts +20 -0
- package/dist/routes.js +72 -0
- package/dist/routes.js.map +1 -0
- package/dist/slots/PlaceholderSlot/PlaceholderSlot.d.ts +1 -0
- package/dist/slots/PlaceholderSlot/PlaceholderSlot.js +6 -0
- package/dist/slots/PlaceholderSlot/PlaceholderSlot.js.map +1 -0
- package/dist/slots/SlotUtils.d.ts +3 -0
- package/dist/slots/SlotUtils.js +17 -0
- package/dist/slots/SlotUtils.js.map +1 -0
- package/dist/slots.d.ts +3 -0
- package/dist/slots.js +3 -0
- package/dist/slots.js.map +1 -0
- package/dist/specialExams/SpecialExamsPage.d.ts +2 -0
- package/dist/specialExams/SpecialExamsPage.js +6 -0
- package/dist/specialExams/SpecialExamsPage.js.map +1 -0
- package/dist/testUtils.d.ts +4 -0
- package/dist/testUtils.js +24 -0
- package/dist/testUtils.js.map +1 -0
- package/dist/types/index.d.ts +40 -0
- package/dist/types/index.js +4 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/formatters.d.ts +1 -0
- package/dist/utils/formatters.js +12 -0
- package/dist/utils/formatters.js.map +1 -0
- 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
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
|
package/dist/Main.js.map
ADDED
|
@@ -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
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
|
package/dist/app.js.map
ADDED
|
@@ -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 @@
|
|
|
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,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,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
|