@openedx/frontend-app-instructor-dashboard 1.0.0-alpha.4 → 1.0.0-alpha.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Main.d.ts +1 -1
- package/dist/Main.js +11 -18
- package/dist/Main.js.map +1 -1
- package/dist/app.js +0 -6
- package/dist/app.js.map +1 -1
- package/dist/certificates/CertificatesPage.d.ts +1 -0
- package/dist/certificates/CertificatesPage.js +315 -2
- package/dist/certificates/CertificatesPage.js.map +1 -1
- package/dist/certificates/CertificatesPage.scss +90 -0
- package/dist/certificates/components/CertificateTable.d.ts +14 -0
- package/dist/certificates/components/CertificateTable.js +88 -0
- package/dist/certificates/components/CertificateTable.js.map +1 -0
- package/dist/certificates/components/CertificatesPageHeader.d.ts +7 -0
- package/dist/certificates/components/CertificatesPageHeader.js +11 -0
- package/dist/certificates/components/CertificatesPageHeader.js.map +1 -0
- package/dist/certificates/components/CertificatesToolbar.d.ts +11 -0
- package/dist/certificates/components/CertificatesToolbar.js +33 -0
- package/dist/certificates/components/CertificatesToolbar.js.map +1 -0
- package/dist/certificates/components/DisableCertificatesModal.d.ts +9 -0
- package/dist/certificates/components/DisableCertificatesModal.js +24 -0
- package/dist/certificates/components/DisableCertificatesModal.js.map +1 -0
- package/dist/certificates/components/FilterDropdown.d.ts +8 -0
- package/dist/certificates/components/FilterDropdown.js +50 -0
- package/dist/certificates/components/FilterDropdown.js.map +1 -0
- package/dist/certificates/components/GenerateCertificatesModal.d.ts +9 -0
- package/dist/certificates/components/GenerateCertificatesModal.js +19 -0
- package/dist/certificates/components/GenerateCertificatesModal.js.map +1 -0
- package/dist/certificates/components/GenerationHistoryTable.d.ts +11 -0
- package/dist/certificates/components/GenerationHistoryTable.js +25 -0
- package/dist/certificates/components/GenerationHistoryTable.js.map +1 -0
- package/dist/certificates/components/GrantExceptionsModal.d.ts +9 -0
- package/dist/certificates/components/GrantExceptionsModal.js +55 -0
- package/dist/certificates/components/GrantExceptionsModal.js.map +1 -0
- package/dist/certificates/components/InvalidateCertificateModal.d.ts +8 -0
- package/dist/certificates/components/InvalidateCertificateModal.js +26 -0
- package/dist/certificates/components/InvalidateCertificateModal.js.map +1 -0
- package/dist/certificates/components/IssuedCertificatesTab.d.ts +18 -0
- package/dist/certificates/components/IssuedCertificatesTab.js +6 -0
- package/dist/certificates/components/IssuedCertificatesTab.js.map +1 -0
- package/dist/certificates/components/LearnerActionModal.d.ts +16 -0
- package/dist/certificates/components/LearnerActionModal.js +27 -0
- package/dist/certificates/components/LearnerActionModal.js.map +1 -0
- package/dist/certificates/components/RegenerateCertificatesModal.d.ts +11 -0
- package/dist/certificates/components/RegenerateCertificatesModal.js +46 -0
- package/dist/certificates/components/RegenerateCertificatesModal.js.map +1 -0
- package/dist/certificates/components/RemoveExceptionModal.d.ts +9 -0
- package/dist/certificates/components/RemoveExceptionModal.js +10 -0
- package/dist/certificates/components/RemoveExceptionModal.js.map +1 -0
- package/dist/certificates/components/RemoveInvalidationModal.d.ts +9 -0
- package/dist/certificates/components/RemoveInvalidationModal.js +10 -0
- package/dist/certificates/components/RemoveInvalidationModal.js.map +1 -0
- package/dist/certificates/constants.d.ts +15 -0
- package/dist/certificates/constants.js +16 -0
- package/dist/certificates/constants.js.map +1 -0
- package/dist/certificates/data/api.d.ts +30 -0
- package/dist/certificates/data/api.js +126 -0
- package/dist/certificates/data/api.js.map +1 -0
- package/dist/certificates/data/apiHook.d.ts +63 -0
- package/dist/certificates/data/apiHook.js +133 -0
- package/dist/certificates/data/apiHook.js.map +1 -0
- package/dist/certificates/data/queryKeys.d.ts +9 -0
- package/dist/certificates/data/queryKeys.js +9 -0
- package/dist/certificates/data/queryKeys.js.map +1 -0
- package/dist/certificates/messages.d.ts +548 -0
- package/dist/certificates/messages.js +550 -0
- package/dist/certificates/messages.js.map +1 -0
- package/dist/certificates/types.d.ts +66 -0
- package/dist/certificates/types.js +26 -0
- package/dist/certificates/types.js.map +1 -0
- package/dist/certificates/utils/errorHandling.d.ts +12 -0
- package/dist/certificates/utils/errorHandling.js +24 -0
- package/dist/certificates/utils/errorHandling.js.map +1 -0
- package/dist/certificates/utils/filterUtils.d.ts +4 -0
- package/dist/certificates/utils/filterUtils.js +31 -0
- package/dist/certificates/utils/filterUtils.js.map +1 -0
- package/dist/certificates/utils/index.d.ts +2 -0
- package/dist/certificates/utils/index.js +2 -0
- package/dist/certificates/utils/index.js.map +1 -0
- package/dist/components/ActionCard.d.ts +2 -2
- package/dist/components/ActionCard.js +1 -1
- package/dist/components/ActionCard.js.map +1 -1
- package/dist/components/CodeEditor.d.ts +5 -0
- package/dist/components/CodeEditor.js +34 -0
- package/dist/components/CodeEditor.js.map +1 -0
- package/dist/components/PendingTasks.d.ts +3 -1
- package/dist/components/PendingTasks.js +3 -2
- package/dist/components/PendingTasks.js.map +1 -1
- package/dist/components/SpecifyLearnerField.d.ts +4 -1
- package/dist/components/SpecifyLearnerField.js +55 -15
- package/dist/components/SpecifyLearnerField.js.map +1 -1
- package/dist/components/SpecifyProblemField.d.ts +13 -0
- package/dist/components/SpecifyProblemField.js +52 -0
- package/dist/components/SpecifyProblemField.js.map +1 -0
- package/dist/components/UsernameFilter.d.ts +7 -0
- package/dist/components/UsernameFilter.js +19 -0
- package/dist/components/UsernameFilter.js.map +1 -0
- package/dist/components/messages.d.ts +45 -0
- package/dist/components/messages.js +45 -0
- package/dist/components/messages.js.map +1 -1
- package/dist/courseInfo/types.d.ts +5 -0
- package/dist/courseInfo/types.js.map +1 -1
- package/dist/courseTeam/CourseTeamPage.js +25 -2
- package/dist/courseTeam/CourseTeamPage.js.map +1 -1
- package/dist/courseTeam/components/AddTeamMemberModal.d.ts +6 -0
- package/dist/courseTeam/components/AddTeamMemberModal.js +61 -0
- package/dist/courseTeam/components/AddTeamMemberModal.js.map +1 -0
- package/dist/courseTeam/components/EditTeamMemberModal.d.ts +8 -0
- package/dist/courseTeam/components/EditTeamMemberModal.js +102 -0
- package/dist/courseTeam/components/EditTeamMemberModal.js.map +1 -0
- package/dist/courseTeam/components/MembersContent.d.ts +6 -0
- package/dist/courseTeam/components/MembersContent.js +48 -0
- package/dist/courseTeam/components/MembersContent.js.map +1 -0
- package/dist/courseTeam/components/RoleFilter.d.ts +7 -0
- package/dist/courseTeam/components/RoleFilter.js +22 -0
- package/dist/courseTeam/components/RoleFilter.js.map +1 -0
- package/dist/courseTeam/components/RolesContent.d.ts +3 -0
- package/dist/courseTeam/components/RolesContent.js +25 -0
- package/dist/courseTeam/components/RolesContent.js.map +1 -0
- package/dist/courseTeam/constants.d.ts +3 -0
- package/dist/courseTeam/constants.js +4 -0
- package/dist/courseTeam/constants.js.map +1 -0
- package/dist/courseTeam/data/api.d.ts +6 -0
- package/dist/courseTeam/data/api.js +38 -0
- package/dist/courseTeam/data/api.js.map +1 -0
- package/dist/courseTeam/data/apiHook.d.ts +8 -0
- package/dist/courseTeam/data/apiHook.js +32 -0
- package/dist/courseTeam/data/apiHook.js.map +1 -0
- package/dist/courseTeam/data/queryKeys.d.ts +7 -0
- package/dist/courseTeam/data/queryKeys.js +14 -0
- package/dist/courseTeam/data/queryKeys.js.map +1 -0
- package/dist/courseTeam/messages.d.ts +258 -0
- package/dist/courseTeam/messages.js +260 -0
- package/dist/courseTeam/messages.js.map +1 -0
- package/dist/courseTeam/types.d.ts +29 -0
- package/dist/courseTeam/types.js +3 -0
- package/dist/courseTeam/types.js.map +1 -0
- package/dist/data/api.d.ts +2 -1
- package/dist/data/api.js +9 -3
- package/dist/data/api.js.map +1 -1
- package/dist/data/apiHook.d.ts +1 -0
- package/dist/data/apiHook.js +11 -2
- package/dist/data/apiHook.js.map +1 -1
- package/dist/data/queryKeys.d.ts +4 -0
- package/dist/data/queryKeys.js +4 -0
- package/dist/data/queryKeys.js.map +1 -1
- package/dist/data/utils.d.ts +2 -0
- package/dist/data/utils.js +9 -0
- package/dist/data/utils.js.map +1 -0
- package/dist/dateExtensions/components/AddExtensionModal.d.ts +1 -1
- package/dist/dateExtensions/components/AddExtensionModal.js +6 -7
- package/dist/dateExtensions/components/AddExtensionModal.js.map +1 -1
- package/dist/dateExtensions/components/DateExtensionsList.js +3 -14
- package/dist/dateExtensions/components/DateExtensionsList.js.map +1 -1
- package/dist/dateExtensions/data/apiHook.d.ts +2 -2
- package/dist/dateExtensions/data/apiHook.js +4 -4
- package/dist/dateExtensions/data/apiHook.js.map +1 -1
- package/dist/dateExtensions/messages.d.ts +0 -5
- package/dist/dateExtensions/messages.js +1 -6
- package/dist/dateExtensions/messages.js.map +1 -1
- package/dist/enrollments/EnrollmentsPage.js +34 -7
- package/dist/enrollments/EnrollmentsPage.js.map +1 -1
- package/dist/enrollments/components/AddBetaTestersModal.d.ts +6 -0
- package/dist/enrollments/components/AddBetaTestersModal.js +69 -0
- package/dist/enrollments/components/AddBetaTestersModal.js.map +1 -0
- package/dist/enrollments/components/EnrollLearnersModal.d.ts +6 -0
- package/dist/enrollments/components/EnrollLearnersModal.js +53 -0
- package/dist/enrollments/components/EnrollLearnersModal.js.map +1 -0
- package/dist/enrollments/components/EnrollmentStatusModal.js +3 -3
- package/dist/enrollments/components/EnrollmentStatusModal.js.map +1 -1
- package/dist/enrollments/components/EnrollmentsList.d.ts +3 -2
- package/dist/enrollments/components/EnrollmentsList.js +13 -14
- package/dist/enrollments/components/EnrollmentsList.js.map +1 -1
- package/dist/enrollments/components/UnenrollModal.d.ts +1 -1
- package/dist/enrollments/components/UnenrollModal.js +29 -3
- package/dist/enrollments/components/UnenrollModal.js.map +1 -1
- package/dist/enrollments/components/UpdateBetaTesterModal.d.ts +8 -0
- package/dist/enrollments/components/UpdateBetaTesterModal.js +72 -0
- package/dist/enrollments/components/UpdateBetaTesterModal.js.map +1 -0
- package/dist/enrollments/data/api.d.ts +3 -1
- package/dist/enrollments/data/api.js +11 -1
- package/dist/enrollments/data/api.js.map +1 -1
- package/dist/enrollments/data/apiHook.d.ts +5 -3
- package/dist/enrollments/data/apiHook.js +21 -3
- package/dist/enrollments/data/apiHook.js.map +1 -1
- package/dist/enrollments/data/queryKeys.d.ts +1 -1
- package/dist/enrollments/data/queryKeys.js.map +1 -1
- package/dist/enrollments/messages.d.ts +131 -1
- package/dist/enrollments/messages.js +136 -6
- package/dist/enrollments/messages.js.map +1 -1
- package/dist/enrollments/types.d.ts +25 -0
- package/dist/enrollments/types.js.map +1 -1
- package/dist/grading/GradingPage.js +15 -2
- package/dist/grading/GradingPage.js.map +1 -1
- package/dist/grading/components/GradingActionRow.d.ts +2 -0
- package/dist/grading/components/GradingActionRow.js +20 -0
- package/dist/grading/components/GradingActionRow.js.map +1 -0
- package/dist/grading/components/GradingConfigurationModal.d.ts +6 -0
- package/dist/grading/components/GradingConfigurationModal.js +15 -0
- package/dist/grading/components/GradingConfigurationModal.js.map +1 -0
- package/dist/grading/components/GradingLearnerContent.d.ts +7 -0
- package/dist/grading/components/GradingLearnerContent.js +199 -0
- package/dist/grading/components/GradingLearnerContent.js.map +1 -0
- package/dist/grading/data/api.d.ts +6 -0
- package/dist/grading/data/api.js +59 -0
- package/dist/grading/data/api.js.map +1 -0
- package/dist/grading/data/apiHook.d.ts +6 -0
- package/dist/grading/data/apiHook.js +29 -0
- package/dist/grading/data/apiHook.js.map +1 -0
- package/dist/grading/data/queryKeys.d.ts +9 -0
- package/dist/grading/data/queryKeys.js +8 -0
- package/dist/grading/data/queryKeys.js.map +1 -0
- package/dist/grading/messages.d.ts +248 -0
- package/dist/grading/messages.js +250 -0
- package/dist/grading/messages.js.map +1 -0
- package/dist/grading/types.d.ts +11 -0
- package/dist/grading/types.js +2 -0
- package/dist/grading/types.js.map +1 -0
- package/dist/hooks/useDebouncedFilter.d.ts +1 -0
- package/dist/hooks/useDebouncedFilter.js +5 -0
- package/dist/hooks/useDebouncedFilter.js.map +1 -1
- package/dist/instructorNav/InstructorNav.js +16 -4
- package/dist/instructorNav/InstructorNav.js.map +1 -1
- package/dist/messages.d.ts +8 -0
- package/dist/messages.js +10 -0
- package/dist/messages.js.map +1 -0
- package/dist/pageWrapper/PageWrapper.js +3 -1
- package/dist/pageWrapper/PageWrapper.js.map +1 -1
- package/dist/providers/AccessErrorObserver.d.ts +9 -0
- package/dist/providers/AccessErrorObserver.js +35 -0
- package/dist/providers/AccessErrorObserver.js.map +1 -0
- package/dist/providers/AccessErrorProvider.d.ts +19 -0
- package/dist/providers/AccessErrorProvider.js +51 -0
- package/dist/providers/AccessErrorProvider.js.map +1 -0
- package/dist/providers/AlertProvider.js +1 -1
- package/dist/providers/AlertProvider.js.map +1 -1
- package/dist/providers/messages.d.ts +33 -0
- package/dist/providers/messages.js +35 -0
- package/dist/providers/messages.js.map +1 -0
- package/dist/provides.d.ts +2 -1
- package/dist/provides.js +3 -2
- package/dist/provides.js.map +1 -1
- package/dist/routes.d.ts +1 -1
- package/dist/routes.js +2 -2
- package/dist/routes.js.map +1 -1
- package/dist/slots/CourseInfoSlot/CourseInfoSlot.d.ts +2 -0
- package/dist/slots/CourseInfoSlot/CourseInfoSlot.js +14 -0
- package/dist/slots/CourseInfoSlot/CourseInfoSlot.js.map +1 -0
- package/dist/slots.js +13 -1
- package/dist/slots.js.map +1 -1
- package/dist/specialExams/SpecialExamsPage.js +14 -2
- package/dist/specialExams/SpecialExamsPage.js.map +1 -1
- package/dist/specialExams/components/AddAllowanceModal.d.ts +6 -0
- package/dist/specialExams/components/AddAllowanceModal.js +84 -0
- package/dist/specialExams/components/AddAllowanceModal.js.map +1 -0
- package/dist/specialExams/components/Allowances.d.ts +2 -0
- package/dist/specialExams/components/Allowances.js +89 -0
- package/dist/specialExams/components/Allowances.js.map +1 -0
- package/dist/specialExams/components/AllowancesList.d.ts +8 -0
- package/dist/specialExams/components/AllowancesList.js +63 -0
- package/dist/specialExams/components/AllowancesList.js.map +1 -0
- package/dist/specialExams/components/AttemptsList.d.ts +3 -0
- package/dist/specialExams/components/AttemptsList.js +50 -0
- package/dist/specialExams/components/AttemptsList.js.map +1 -0
- package/dist/specialExams/components/DeleteAllowanceModal.d.ts +8 -0
- package/dist/specialExams/components/DeleteAllowanceModal.js +29 -0
- package/dist/specialExams/components/DeleteAllowanceModal.js.map +1 -0
- package/dist/specialExams/components/EditAllowanceModal.d.ts +8 -0
- package/dist/specialExams/components/EditAllowanceModal.js +62 -0
- package/dist/specialExams/components/EditAllowanceModal.js.map +1 -0
- package/dist/specialExams/constants.d.ts +43 -0
- package/dist/specialExams/constants.js +19 -0
- package/dist/specialExams/constants.js.map +1 -0
- package/dist/specialExams/data/api.d.ts +7 -0
- package/dist/specialExams/data/api.js +55 -0
- package/dist/specialExams/data/api.js.map +1 -0
- package/dist/specialExams/data/apiHook.d.ts +6 -0
- package/dist/specialExams/data/apiHook.js +37 -0
- package/dist/specialExams/data/apiHook.js.map +1 -0
- package/dist/specialExams/data/queryKeys.d.ts +8 -0
- package/dist/specialExams/data/queryKeys.js +9 -0
- package/dist/specialExams/data/queryKeys.js.map +1 -0
- package/dist/specialExams/messages.d.ts +228 -0
- package/dist/specialExams/messages.js +230 -0
- package/dist/specialExams/messages.js.map +1 -0
- package/dist/specialExams/types.d.ts +65 -0
- package/dist/specialExams/types.js +2 -0
- package/dist/specialExams/types.js.map +1 -0
- package/dist/style.scss +16 -0
- package/dist/testUtils.js +2 -2
- package/dist/testUtils.js.map +1 -1
- package/dist/types/index.d.ts +15 -0
- package/dist/types/index.js.map +1 -1
- package/dist/utils/formatters.d.ts +5 -0
- package/dist/utils/formatters.js +10 -0
- package/dist/utils/formatters.js.map +1 -1
- package/package.json +6 -6
- package/dist/app.scss +0 -10
- package/dist/providers/QueryProvider.d.ts +0 -6
- package/dist/providers/QueryProvider.js +0 -16
- package/dist/providers/QueryProvider.js.map +0 -1
- package/dist/providers.d.ts +0 -3
- package/dist/providers.js +0 -8
- package/dist/providers.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpecifyProblemField.js","sourceRoot":"","sources":["../../src/components/SpecifyProblemField.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useState, useImperativeHandle, forwardRef } from 'react';\nimport { isAxiosError } from 'axios';\nimport { useParams } from 'react-router-dom';\nimport { Button, Form, Icon, OverlayTrigger, Tooltip, useToggle } from '@openedx/paragon';\nimport { InfoOutline, SpinnerIcon } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport messages from './messages';\nimport { useDebouncedFilter } from '@src/hooks/useDebouncedFilter';\nimport { useProblemDetails } from '@src/data/apiHook';\n\ninterface SpecifyProblemFieldProps {\n buttonLabel: string,\n disabled?: boolean,\n fieldLabel: string,\n problemResponsesError?: string,\n usernameOrEmail?: string,\n onClickSelect: (problemLocation: string, event: React.MouseEvent<HTMLButtonElement>) => void,\n}\n\ninterface SpecifyProblemFieldRef {\n reset: () => void,\n}\n\nconst SpecifyProblemField = forwardRef<SpecifyProblemFieldRef, SpecifyProblemFieldProps>(({\n buttonLabel,\n disabled,\n fieldLabel,\n problemResponsesError,\n usernameOrEmail = '',\n onClickSelect,\n}, ref) => {\n const intl = useIntl();\n const { courseId = '' } = useParams<{ courseId: string }>();\n const [problemLocation, setProblemLocation] = useState('');\n const [showSelectedLocation, enableShowSelectedLocation, disableShowSelectedLocation] = useToggle(false);\n\n const { inputValue, handleChange, resetFilter } = useDebouncedFilter({\n filterValue: problemLocation,\n setFilter: setProblemLocation,\n });\n const { data = { breadcrumbs: [], name: '', id: '' }, refetch, error } = useProblemDetails(courseId, inputValue, usernameOrEmail);\n\n useImperativeHandle(ref, () => ({\n reset: () => {\n resetFilter();\n disableShowSelectedLocation();\n }\n }));\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n handleChange(e.target.value);\n\n if (showSelectedLocation) {\n disableShowSelectedLocation();\n }\n };\n\n const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n if (inputValue) {\n refetch().then(() => {\n onClickSelect(inputValue, event);\n enableShowSelectedLocation();\n });\n }\n };\n\n return (\n <Form.Group className=\"mb-0\" isInvalid={!!problemResponsesError} size=\"sm\">\n <Form.Label className=\"d-flex align-content-end align-items-center gap-2 text-primary-500\">\n {showSelectedLocation ? intl.formatMessage(messages.selectedProblem)\n : (\n <>\n {fieldLabel}\n <OverlayTrigger\n placement=\"top\"\n overlay={(\n <Tooltip id=\"problem-location-tooltip\" className=\"info-tooltip\">\n {intl.formatMessage(messages.problemLocationTooltip)}\n </Tooltip>\n )}\n >\n <Icon src={InfoOutline} size=\"sm\" aria-label={intl.formatMessage(messages.problemLocationInfoIconLabel)} />\n </OverlayTrigger>\n </>\n )}\n </Form.Label>\n <div className=\"d-flex align-items-center\">\n {showSelectedLocation && data && !error ? (\n <div className=\"d-flex gap-3 align-items-center col-8 p-0\">\n <div className=\"d-block w-100\">\n <p className=\"x-small mb-0 text-primary-500 text-truncate\">\n {data.breadcrumbs\n .slice(1, -1)\n .map(breadcrumb => breadcrumb.displayName)\n .join(' > ')}\n </p>\n <p className=\"text-primary-500 mb-0\">{data.name}</p>\n <p className=\"x-small text-gray-700 text-truncate mb-0\">{data.id}</p>\n </div>\n <Button iconBefore={SpinnerIcon} onClick={disableShowSelectedLocation}>{intl.formatMessage(messages.change)}</Button>\n </div>\n ) : (\n <>\n <Form.Control\n type=\"text\"\n placeholder={intl.formatMessage(messages.problemLocationPlaceholder)}\n value={inputValue}\n onChange={handleInputChange}\n className=\"flex-grow-1\"\n size=\"md\"\n />\n {problemResponsesError && (\n <Form.Control.Feedback type=\"invalid\">\n {problemResponsesError}\n </Form.Control.Feedback>\n )}\n <Button\n variant=\"primary\"\n onClick={handleClick}\n disabled={disabled || !inputValue}\n className=\"text-nowrap\"\n >\n {buttonLabel}\n </Button>\n </>\n )}\n </div>\n {showSelectedLocation && error\n && isAxiosError(error)\n && ((error.response?.status === 400) || (error.response?.status === 404)) && (\n <p className=\"text-danger-500 mb-0 x-small mt-2\">\n {intl.formatMessage(messages.problemNotFound, { identifier: inputValue })}\n </p>\n )}\n </Form.Group>\n );\n});\n\nSpecifyProblemField.displayName = 'SpecifyProblemField';\n\nexport default SpecifyProblemField;\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useIntl } from '@openedx/frontend-base';
|
|
3
|
+
import { FormControl, Icon } from '@openedx/paragon';
|
|
4
|
+
import { Search } from '@openedx/paragon/icons';
|
|
5
|
+
import { useDebouncedFilter } from '../hooks/useDebouncedFilter';
|
|
6
|
+
import messages from '../components/messages';
|
|
7
|
+
const UsernameFilter = ({ column: { filterValue, setFilter } }) => {
|
|
8
|
+
const intl = useIntl();
|
|
9
|
+
const { inputValue, handleChange } = useDebouncedFilter({
|
|
10
|
+
filterValue,
|
|
11
|
+
setFilter,
|
|
12
|
+
});
|
|
13
|
+
const handleInputChange = (e) => {
|
|
14
|
+
handleChange(e.target.value);
|
|
15
|
+
};
|
|
16
|
+
return (_jsx(FormControl, { className: "mb-0 username", onChange: handleInputChange, placeholder: intl.formatMessage(messages.searchPlaceholder), trailingElement: _jsx(Icon, { src: Search }), value: inputValue }));
|
|
17
|
+
};
|
|
18
|
+
export default UsernameFilter;
|
|
19
|
+
//# sourceMappingURL=UsernameFilter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UsernameFilter.js","sourceRoot":"","sources":["../../src/components/UsernameFilter.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,QAAQ,MAAM,0BAA0B,CAAC;AAEhD,MAAM,cAAc,GAAG,CAAC,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,EAA2E,EAAE,EAAE;IACzI,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,GAAG,kBAAkB,CAAC;QACtD,WAAW;QACX,SAAS;KACV,CAAC,CAAC;IAEH,MAAM,iBAAiB,GAAG,CAAC,CAAsC,EAAE,EAAE;QACnE,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC,CAAC;IAEF,OAAO,CACL,KAAC,WAAW,IACV,SAAS,EAAC,eAAe,EACzB,QAAQ,EAAE,iBAAiB,EAC3B,WAAW,EAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAC3D,eAAe,EAAE,KAAC,IAAI,IAAC,GAAG,EAAE,MAAM,GAAI,EACtC,KAAK,EAAE,UAAU,GACjB,CACH,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,cAAc,CAAC","sourcesContent":["import { useIntl } from '@openedx/frontend-base';\nimport { FormControl, Icon } from '@openedx/paragon';\nimport { Search } from '@openedx/paragon/icons';\nimport { useDebouncedFilter } from '@src/hooks/useDebouncedFilter';\nimport messages from '@src/components/messages';\n\nconst UsernameFilter = ({ column: { filterValue, setFilter } }: { column: { filterValue: string, setFilter: (value: string) => void } }) => {\n const intl = useIntl();\n const { inputValue, handleChange } = useDebouncedFilter({\n filterValue,\n setFilter,\n });\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n handleChange(e.target.value);\n };\n\n return (\n <FormControl\n className=\"mb-0 username\"\n onChange={handleInputChange}\n placeholder={intl.formatMessage(messages.searchPlaceholder)}\n trailingElement={<Icon src={Search} />}\n value={inputValue}\n />\n );\n};\n\nexport default UsernameFilter;\n"]}
|
|
@@ -114,5 +114,50 @@ declare const messages: {
|
|
|
114
114
|
defaultMessage: string;
|
|
115
115
|
description: string;
|
|
116
116
|
};
|
|
117
|
+
problemNotFound: {
|
|
118
|
+
id: string;
|
|
119
|
+
defaultMessage: string;
|
|
120
|
+
description: string;
|
|
121
|
+
};
|
|
122
|
+
searchPlaceholder: {
|
|
123
|
+
id: string;
|
|
124
|
+
defaultMessage: string;
|
|
125
|
+
description: string;
|
|
126
|
+
};
|
|
127
|
+
problemLocationPlaceholder: {
|
|
128
|
+
id: string;
|
|
129
|
+
defaultMessage: string;
|
|
130
|
+
description: string;
|
|
131
|
+
};
|
|
132
|
+
problemLocationInfoIconLabel: {
|
|
133
|
+
id: string;
|
|
134
|
+
defaultMessage: string;
|
|
135
|
+
description: string;
|
|
136
|
+
};
|
|
137
|
+
problemLocationTooltip: {
|
|
138
|
+
id: string;
|
|
139
|
+
defaultMessage: string;
|
|
140
|
+
description: string;
|
|
141
|
+
};
|
|
142
|
+
selectedProblem: {
|
|
143
|
+
id: string;
|
|
144
|
+
defaultMessage: string;
|
|
145
|
+
description: string;
|
|
146
|
+
};
|
|
147
|
+
learnerNotEnrolled: {
|
|
148
|
+
id: string;
|
|
149
|
+
defaultMessage: string;
|
|
150
|
+
description: string;
|
|
151
|
+
};
|
|
152
|
+
selectedLearner: {
|
|
153
|
+
id: string;
|
|
154
|
+
defaultMessage: string;
|
|
155
|
+
description: string;
|
|
156
|
+
};
|
|
157
|
+
learnerGenericError: {
|
|
158
|
+
id: string;
|
|
159
|
+
defaultMessage: string;
|
|
160
|
+
description: string;
|
|
161
|
+
};
|
|
117
162
|
};
|
|
118
163
|
export default messages;
|
|
@@ -115,6 +115,51 @@ const messages = defineMessages({
|
|
|
115
115
|
defaultMessage: 'Could not find student matching identifier: {identifier}',
|
|
116
116
|
description: 'Error message displayed when a learner cannot be found based on the provided identifier (email or username)',
|
|
117
117
|
},
|
|
118
|
+
problemNotFound: {
|
|
119
|
+
id: 'instruct.specifyProblemField.problemNotFound',
|
|
120
|
+
defaultMessage: 'Could not find problem matching identifier: {identifier}',
|
|
121
|
+
description: 'Error message displayed when a problem cannot be found based on the provided identifier',
|
|
122
|
+
},
|
|
123
|
+
searchPlaceholder: {
|
|
124
|
+
id: 'instruct.usernameFilter.searchPlaceholder',
|
|
125
|
+
defaultMessage: 'Search By Username or Email',
|
|
126
|
+
description: 'Placeholder text for the username filter input',
|
|
127
|
+
},
|
|
128
|
+
problemLocationPlaceholder: {
|
|
129
|
+
id: 'instruct.specifyProblemField.locationPlaceholder',
|
|
130
|
+
defaultMessage: 'Problem location',
|
|
131
|
+
description: 'Placeholder text for problem location input',
|
|
132
|
+
},
|
|
133
|
+
problemLocationInfoIconLabel: {
|
|
134
|
+
id: 'instruct.specifyProblemField.infoIconLabel',
|
|
135
|
+
defaultMessage: 'Example format for problem location',
|
|
136
|
+
description: 'Aria label for the info icon next to the problem location input',
|
|
137
|
+
},
|
|
138
|
+
problemLocationTooltip: {
|
|
139
|
+
id: 'instruct.specifyProblemField.locationTooltip',
|
|
140
|
+
defaultMessage: 'Example: block-v1:edX+DemoX+2015+type@problem+block@618c5933b8b544e4a4cc103d3e508378',
|
|
141
|
+
description: 'Tooltip text showing an example problem location format',
|
|
142
|
+
},
|
|
143
|
+
selectedProblem: {
|
|
144
|
+
id: 'instruct.specifyProblemField.selectedProblem',
|
|
145
|
+
defaultMessage: 'Selected Problem:',
|
|
146
|
+
description: 'Label for specify problem field when a problem has been selected',
|
|
147
|
+
},
|
|
148
|
+
learnerNotEnrolled: {
|
|
149
|
+
id: 'instruct.specifyLearner.learnerNotEnrolled',
|
|
150
|
+
defaultMessage: '{identifier} is not enrolled in this course',
|
|
151
|
+
description: 'Error message displayed when a learner is found based on the provided identifier (email or username) but is not enrolled in the course',
|
|
152
|
+
},
|
|
153
|
+
selectedLearner: {
|
|
154
|
+
id: 'instruct.specifyLearner.selectedLearner',
|
|
155
|
+
defaultMessage: 'Selected Learner:',
|
|
156
|
+
description: 'Label for specify learner field when a learner has been selected',
|
|
157
|
+
},
|
|
158
|
+
learnerGenericError: {
|
|
159
|
+
id: 'instruct.specifyLearner.learnerGenericError',
|
|
160
|
+
defaultMessage: 'An error occurred while looking up {identifier}. Please try again.',
|
|
161
|
+
description: 'Generic error message displayed when there is an error looking up a learner based on the provided identifier (email or username)',
|
|
162
|
+
}
|
|
118
163
|
});
|
|
119
164
|
export default messages;
|
|
120
165
|
//# sourceMappingURL=messages.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/components/messages.ts"],"names":[],"mappings":"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","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n select: {\n id: 'instruct.specifyLearner.select',\n defaultMessage: 'Select',\n description: 'Label for select dropdown in specify learner field',\n },\n specifyLearner: {\n id: 'instruct.specifyLearner.label',\n defaultMessage: 'Specify Learner:',\n description: 'Label for specify learner field',\n },\n specifyLearnerPlaceholder: {\n id: 'instruct.specifyLearner.placeholder',\n defaultMessage: 'Learner email address or username',\n description: 'Placeholder text for specify learner input field',\n },\n pageNotFoundHeader: {\n id: 'instruct.pageNotFound.header',\n defaultMessage: 'Page not found',\n description: 'Header for page not found error',\n },\n pageNotFoundBody: {\n id: 'instruct.pageNotFound.body',\n defaultMessage: \"The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again.\",\n description: 'Body text for page not found error',\n },\n pendingTasksTitle: {\n id: 'instruct.pendingTasks.section.title',\n defaultMessage: 'Pending Tasks',\n description: 'Title for the pending tasks section',\n },\n noTasksMessage: {\n id: 'instruct.pendingTasks.section.noTasks',\n defaultMessage: 'No tasks currently running.',\n description: 'Message displayed when there are no pending tasks',\n },\n taskTypeColumnName: {\n id: 'instruct.pendingTasks.table.column.taskType',\n defaultMessage: 'Task Type',\n description: 'Column name for task type in pending tasks table',\n },\n taskInputColumnName: {\n id: 'instruct.pendingTasks.table.column.taskInput',\n defaultMessage: 'Task Input',\n description: 'Column name for task input in pending tasks table',\n },\n taskIdColumnName: {\n id: 'instruct.pendingTasks.table.column.taskId',\n defaultMessage: 'Task ID',\n description: 'Column name for task ID in pending tasks table',\n },\n requesterColumnName: {\n id: 'instruct.pendingTasks.table.column.requester',\n defaultMessage: 'Requester',\n description: 'Column name for requester in pending tasks table',\n },\n taskStateColumnName: {\n id: 'instruct.pendingTasks.table.column.taskState',\n defaultMessage: 'Task State',\n description: 'Column name for task state in pending tasks table',\n },\n createdColumnName: {\n id: 'instruct.pendingTasks.table.column.created',\n defaultMessage: 'Created',\n description: 'Column name for created date in pending tasks table',\n },\n taskOutputColumnName: {\n id: 'instruct.pendingTasks.table.column.taskOutput',\n defaultMessage: 'Task Output',\n description: 'Column name for task output in pending tasks table',\n },\n durationColumnName: {\n id: 'instruct.pendingTasks.table.column.duration',\n defaultMessage: 'Duration (sec)',\n description: 'Column name for duration in pending tasks table',\n },\n statusColumnName: {\n id: 'instruct.pendingTasks.table.column.status',\n defaultMessage: 'Status',\n description: 'Column name for status in pending tasks table',\n },\n taskMessageColumnName: {\n id: 'instruct.pendingTasks.table.column.taskMessage',\n defaultMessage: 'Task Message',\n description: 'Column name for task message in pending tasks table',\n },\n downloadCSVTitle: {\n id: 'instruct.csvComponent.downloadCSVTitle',\n defaultMessage: 'Upload CSV File',\n description: 'Title for the upload CSV file section'\n },\n downloadCSVDescription: {\n id: 'instruct.csvComponent.downloadCSVDescription',\n defaultMessage: 'Only properly formatted CSV files will be accepted',\n description: 'Description for the upload CSV file section'\n },\n viewCSVTemplate: {\n id: 'instruct.csvComponent.viewCSVTemplate',\n defaultMessage: 'View Template',\n description: 'Label for the view CSV template link'\n },\n uploadingFileMessage: {\n id: 'instruct.csvComponent.uploadingFileMessage',\n defaultMessage: 'File chosen: {fileName}',\n description: 'Message displayed when a file is being uploaded, with the file name included'\n },\n change: {\n id: 'instruct.specifyLearner.change',\n defaultMessage: 'Change',\n description: 'Label for change button in specify learner field',\n },\n learnerNotFound: {\n id: 'instruct.specifyLearner.learnerNotFound',\n defaultMessage: 'Could not find student matching identifier: {identifier}',\n description: 'Error message displayed when a learner cannot be found based on the provided identifier (email or username)',\n },\n});\n\nexport default messages;\n"]}
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/components/messages.ts"],"names":[],"mappings":"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","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n select: {\n id: 'instruct.specifyLearner.select',\n defaultMessage: 'Select',\n description: 'Label for select dropdown in specify learner field',\n },\n specifyLearner: {\n id: 'instruct.specifyLearner.label',\n defaultMessage: 'Specify Learner:',\n description: 'Label for specify learner field',\n },\n specifyLearnerPlaceholder: {\n id: 'instruct.specifyLearner.placeholder',\n defaultMessage: 'Learner email address or username',\n description: 'Placeholder text for specify learner input field',\n },\n pageNotFoundHeader: {\n id: 'instruct.pageNotFound.header',\n defaultMessage: 'Page not found',\n description: 'Header for page not found error',\n },\n pageNotFoundBody: {\n id: 'instruct.pageNotFound.body',\n defaultMessage: \"The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again.\",\n description: 'Body text for page not found error',\n },\n pendingTasksTitle: {\n id: 'instruct.pendingTasks.section.title',\n defaultMessage: 'Pending Tasks',\n description: 'Title for the pending tasks section',\n },\n noTasksMessage: {\n id: 'instruct.pendingTasks.section.noTasks',\n defaultMessage: 'No tasks currently running.',\n description: 'Message displayed when there are no pending tasks',\n },\n taskTypeColumnName: {\n id: 'instruct.pendingTasks.table.column.taskType',\n defaultMessage: 'Task Type',\n description: 'Column name for task type in pending tasks table',\n },\n taskInputColumnName: {\n id: 'instruct.pendingTasks.table.column.taskInput',\n defaultMessage: 'Task Input',\n description: 'Column name for task input in pending tasks table',\n },\n taskIdColumnName: {\n id: 'instruct.pendingTasks.table.column.taskId',\n defaultMessage: 'Task ID',\n description: 'Column name for task ID in pending tasks table',\n },\n requesterColumnName: {\n id: 'instruct.pendingTasks.table.column.requester',\n defaultMessage: 'Requester',\n description: 'Column name for requester in pending tasks table',\n },\n taskStateColumnName: {\n id: 'instruct.pendingTasks.table.column.taskState',\n defaultMessage: 'Task State',\n description: 'Column name for task state in pending tasks table',\n },\n createdColumnName: {\n id: 'instruct.pendingTasks.table.column.created',\n defaultMessage: 'Created',\n description: 'Column name for created date in pending tasks table',\n },\n taskOutputColumnName: {\n id: 'instruct.pendingTasks.table.column.taskOutput',\n defaultMessage: 'Task Output',\n description: 'Column name for task output in pending tasks table',\n },\n durationColumnName: {\n id: 'instruct.pendingTasks.table.column.duration',\n defaultMessage: 'Duration (sec)',\n description: 'Column name for duration in pending tasks table',\n },\n statusColumnName: {\n id: 'instruct.pendingTasks.table.column.status',\n defaultMessage: 'Status',\n description: 'Column name for status in pending tasks table',\n },\n taskMessageColumnName: {\n id: 'instruct.pendingTasks.table.column.taskMessage',\n defaultMessage: 'Task Message',\n description: 'Column name for task message in pending tasks table',\n },\n downloadCSVTitle: {\n id: 'instruct.csvComponent.downloadCSVTitle',\n defaultMessage: 'Upload CSV File',\n description: 'Title for the upload CSV file section'\n },\n downloadCSVDescription: {\n id: 'instruct.csvComponent.downloadCSVDescription',\n defaultMessage: 'Only properly formatted CSV files will be accepted',\n description: 'Description for the upload CSV file section'\n },\n viewCSVTemplate: {\n id: 'instruct.csvComponent.viewCSVTemplate',\n defaultMessage: 'View Template',\n description: 'Label for the view CSV template link'\n },\n uploadingFileMessage: {\n id: 'instruct.csvComponent.uploadingFileMessage',\n defaultMessage: 'File chosen: {fileName}',\n description: 'Message displayed when a file is being uploaded, with the file name included'\n },\n change: {\n id: 'instruct.specifyLearner.change',\n defaultMessage: 'Change',\n description: 'Label for change button in specify learner field',\n },\n learnerNotFound: {\n id: 'instruct.specifyLearner.learnerNotFound',\n defaultMessage: 'Could not find student matching identifier: {identifier}',\n description: 'Error message displayed when a learner cannot be found based on the provided identifier (email or username)',\n },\n problemNotFound: {\n id: 'instruct.specifyProblemField.problemNotFound',\n defaultMessage: 'Could not find problem matching identifier: {identifier}',\n description: 'Error message displayed when a problem cannot be found based on the provided identifier',\n },\n searchPlaceholder: {\n id: 'instruct.usernameFilter.searchPlaceholder',\n defaultMessage: 'Search By Username or Email',\n description: 'Placeholder text for the username filter input',\n },\n problemLocationPlaceholder: {\n id: 'instruct.specifyProblemField.locationPlaceholder',\n defaultMessage: 'Problem location',\n description: 'Placeholder text for problem location input',\n },\n problemLocationInfoIconLabel: {\n id: 'instruct.specifyProblemField.infoIconLabel',\n defaultMessage: 'Example format for problem location',\n description: 'Aria label for the info icon next to the problem location input',\n },\n problemLocationTooltip: {\n id: 'instruct.specifyProblemField.locationTooltip',\n defaultMessage: 'Example: block-v1:edX+DemoX+2015+type@problem+block@618c5933b8b544e4a4cc103d3e508378',\n description: 'Tooltip text showing an example problem location format',\n },\n selectedProblem: {\n id: 'instruct.specifyProblemField.selectedProblem',\n defaultMessage: 'Selected Problem:',\n description: 'Label for specify problem field when a problem has been selected',\n },\n learnerNotEnrolled: {\n id: 'instruct.specifyLearner.learnerNotEnrolled',\n defaultMessage: '{identifier} is not enrolled in this course',\n description: 'Error message displayed when a learner is found based on the provided identifier (email or username) but is not enrolled in the course',\n },\n selectedLearner: {\n id: 'instruct.specifyLearner.selectedLearner',\n defaultMessage: 'Selected Learner:',\n description: 'Label for specify learner field when a learner has been selected',\n },\n learnerGenericError: {\n id: 'instruct.specifyLearner.learnerGenericError',\n defaultMessage: 'An error occurred while looking up {identifier}. Please try again.',\n description: 'Generic error message displayed when there is an error looking up a learner based on the provided identifier (email or username)',\n }\n});\n\nexport default messages;\n"]}
|
|
@@ -25,6 +25,11 @@ export interface CourseInfoResponse {
|
|
|
25
25
|
dataResearcher: boolean;
|
|
26
26
|
[key: string]: boolean;
|
|
27
27
|
};
|
|
28
|
+
gradebookUrl: string;
|
|
29
|
+
studioGradingUrl?: string;
|
|
30
|
+
certificatesEnabled?: boolean;
|
|
31
|
+
adminConsoleUrl: string | null;
|
|
32
|
+
username: string;
|
|
28
33
|
}
|
|
29
34
|
interface EnrollmentCounts extends Record<string, number> {
|
|
30
35
|
total: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/courseInfo/types.ts"],"names":[],"mappings":"","sourcesContent":["import { TabProps } from '@src/instructorNav/InstructorNav';\n\nexport interface CourseInfoResponse {\n courseId: string,\n displayName: string,\n courseNumber: string,\n courseRun: string,\n enrollmentCounts: EnrollmentCounts,\n start: string | null,\n end: string | null,\n tabs?: TabProps[],\n totalEnrollment: number,\n studioUrl: string,\n pacing: string,\n org?: string,\n numSections: number,\n hasStarted: boolean,\n hasEnded: boolean,\n enrollmentEnd: string | null,\n enrollmentStart: string | null,\n gradeCutoffs: string | null,\n staffCount: number,\n learnerCount: number,\n permissions: {\n admin: boolean,\n dataResearcher: boolean,\n [key: string]: boolean,\n },\n}\n\ninterface EnrollmentCounts extends Record<string, number> {\n total: number,\n}\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/courseInfo/types.ts"],"names":[],"mappings":"","sourcesContent":["import { TabProps } from '@src/instructorNav/InstructorNav';\n\nexport interface CourseInfoResponse {\n courseId: string,\n displayName: string,\n courseNumber: string,\n courseRun: string,\n enrollmentCounts: EnrollmentCounts,\n start: string | null,\n end: string | null,\n tabs?: TabProps[],\n totalEnrollment: number,\n studioUrl: string,\n pacing: string,\n org?: string,\n numSections: number,\n hasStarted: boolean,\n hasEnded: boolean,\n enrollmentEnd: string | null,\n enrollmentStart: string | null,\n gradeCutoffs: string | null,\n staffCount: number,\n learnerCount: number,\n permissions: {\n admin: boolean,\n dataResearcher: boolean,\n [key: string]: boolean,\n },\n gradebookUrl: string,\n studioGradingUrl?: string,\n certificatesEnabled?: boolean,\n adminConsoleUrl: string | null,\n username: string,\n}\n\ninterface EnrollmentCounts extends Record<string, number> {\n total: number,\n}\n"]}
|
|
@@ -1,6 +1,29 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { useIntl } from '@openedx/frontend-base';
|
|
4
|
+
import { Button, Tab, Tabs, useToggle } from '@openedx/paragon';
|
|
5
|
+
import { Plus, TrendingUp } from '@openedx/paragon/icons';
|
|
6
|
+
import AddTeamMemberModal from '../courseTeam/components/AddTeamMemberModal';
|
|
7
|
+
import EditTeamMemberModal from '../courseTeam/components/EditTeamMemberModal';
|
|
8
|
+
import MembersContent from '../courseTeam/components/MembersContent';
|
|
9
|
+
import RolesContent from '../courseTeam/components/RolesContent';
|
|
10
|
+
import messages from '../courseTeam/messages';
|
|
11
|
+
import { AlertOutlet } from '../providers/AlertProvider';
|
|
12
|
+
import { useParams } from 'react-router-dom';
|
|
13
|
+
import { useCourseInfo } from '../data/apiHook';
|
|
2
14
|
const CourseTeamPage = () => {
|
|
3
|
-
|
|
15
|
+
const intl = useIntl();
|
|
16
|
+
const { courseId = '' } = useParams();
|
|
17
|
+
const [isOpenAddModal, openAddModal, closeAddModal] = useToggle(false);
|
|
18
|
+
const [isOpenEditModal, openEditModal, closeEditModal] = useToggle(false);
|
|
19
|
+
const [selectedUser, setSelectedUser] = useState(null);
|
|
20
|
+
const { data } = useCourseInfo(courseId);
|
|
21
|
+
const { adminConsoleUrl = '' } = data || {};
|
|
22
|
+
const handleEdit = (user) => {
|
|
23
|
+
setSelectedUser(user);
|
|
24
|
+
openEditModal();
|
|
25
|
+
};
|
|
26
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "d-flex justify-content-between align-items-center mb-3", children: [_jsx("h3", { className: "text-primary-700 mb-0", children: intl.formatMessage(messages.courseTeamTitle) }), _jsxs("div", { children: [adminConsoleUrl && _jsx(Button, { iconBefore: TrendingUp, variant: "outline-primary", className: "mr-3", as: "a", href: adminConsoleUrl, children: intl.formatMessage(messages.viewStudioRoles) }), _jsx(Button, { iconBefore: Plus, variant: "primary", onClick: openAddModal, children: intl.formatMessage(messages.addTeamMember) })] })] }), _jsx(AlertOutlet, {}), _jsxs(Tabs, { children: [_jsx(Tab, { eventKey: "members", title: intl.formatMessage(messages.membersTab), children: _jsx(MembersContent, { onEdit: handleEdit }) }), _jsx(Tab, { eventKey: "roles", title: intl.formatMessage(messages.rolesTab), children: _jsx(RolesContent, {}) })] }), isOpenAddModal && _jsx(AddTeamMemberModal, { isOpen: isOpenAddModal, onClose: closeAddModal }), isOpenEditModal && selectedUser && _jsx(EditTeamMemberModal, { isOpen: isOpenEditModal, user: selectedUser, onClose: closeEditModal })] }));
|
|
4
27
|
};
|
|
5
28
|
export default CourseTeamPage;
|
|
6
29
|
//# sourceMappingURL=CourseTeamPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CourseTeamPage.js","sourceRoot":"","sources":["../../src/courseTeam/CourseTeamPage.tsx"],"names":[],"mappings":";AAAA,MAAM,cAAc,GAAG,GAAG,EAAE;IAC1B,OAAO,CACL,
|
|
1
|
+
{"version":3,"file":"CourseTeamPage.js","sourceRoot":"","sources":["../../src/courseTeam/CourseTeamPage.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useState } from 'react';\nimport { useIntl } from '@openedx/frontend-base';\nimport { Button, Tab, Tabs, useToggle } from '@openedx/paragon';\nimport { Plus, TrendingUp } from '@openedx/paragon/icons';\nimport AddTeamMemberModal from '@src/courseTeam/components/AddTeamMemberModal';\nimport EditTeamMemberModal from '@src/courseTeam/components/EditTeamMemberModal';\nimport MembersContent from '@src/courseTeam/components/MembersContent';\nimport RolesContent from '@src/courseTeam/components/RolesContent';\nimport messages from '@src/courseTeam/messages';\nimport { AlertOutlet } from '@src/providers/AlertProvider';\nimport { CourseTeamMember } from '@src/courseTeam/types';\nimport { useParams } from 'react-router-dom';\nimport { useCourseInfo } from '@src/data/apiHook';\n\nconst CourseTeamPage = () => {\n const intl = useIntl();\n const { courseId = '' } = useParams();\n const [isOpenAddModal, openAddModal, closeAddModal] = useToggle(false);\n const [isOpenEditModal, openEditModal, closeEditModal] = useToggle(false);\n const [selectedUser, setSelectedUser] = useState<CourseTeamMember | null>(null);\n const { data } = useCourseInfo(courseId);\n const { adminConsoleUrl = '' } = data || {};\n\n const handleEdit = (user: CourseTeamMember) => {\n setSelectedUser(user);\n openEditModal();\n };\n\n return (\n <>\n <div className=\"d-flex justify-content-between align-items-center mb-3\">\n <h3 className=\"text-primary-700 mb-0\">{intl.formatMessage(messages.courseTeamTitle)}</h3>\n <div>\n {adminConsoleUrl && <Button iconBefore={TrendingUp} variant=\"outline-primary\" className=\"mr-3\" as=\"a\" href={adminConsoleUrl}>{intl.formatMessage(messages.viewStudioRoles)}</Button>}\n <Button iconBefore={Plus} variant=\"primary\" onClick={openAddModal}>{intl.formatMessage(messages.addTeamMember)}</Button>\n </div>\n </div>\n <AlertOutlet />\n <Tabs>\n <Tab eventKey=\"members\" title={intl.formatMessage(messages.membersTab)}>\n <MembersContent onEdit={handleEdit} />\n </Tab>\n <Tab eventKey=\"roles\" title={intl.formatMessage(messages.rolesTab)}>\n <RolesContent />\n </Tab>\n </Tabs>\n {isOpenAddModal && <AddTeamMemberModal isOpen={isOpenAddModal} onClose={closeAddModal} />}\n {isOpenEditModal && selectedUser && <EditTeamMemberModal isOpen={isOpenEditModal} user={selectedUser} onClose={closeEditModal} />}\n </>\n );\n};\n\nexport default CourseTeamPage;\n"]}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { useParams } from 'react-router-dom';
|
|
4
|
+
import { useIntl } from '@openedx/frontend-base';
|
|
5
|
+
import { ActionRow, Button, Form, ModalDialog } from '@openedx/paragon';
|
|
6
|
+
import { useAddTeamMember, useRoles } from '../../courseTeam/data/apiHook';
|
|
7
|
+
import messages from '../../courseTeam/messages';
|
|
8
|
+
import { useCourseInfo } from '../../data/apiHook';
|
|
9
|
+
import { useDebouncedFilter } from '../../hooks/useDebouncedFilter';
|
|
10
|
+
import { useAlert } from '../../providers/AlertProvider';
|
|
11
|
+
import { TEAM_MEMBER_ACTION } from '../constants';
|
|
12
|
+
const AddTeamMemberModal = ({ isOpen, onClose, }) => {
|
|
13
|
+
const intl = useIntl();
|
|
14
|
+
const { courseId = '' } = useParams();
|
|
15
|
+
const { data: { displayName } = { displayName: '' } } = useCourseInfo(courseId);
|
|
16
|
+
const { data: { results } = { results: [] } } = useRoles(courseId);
|
|
17
|
+
const [users, setUsers] = useState('');
|
|
18
|
+
const [selectedRole, setSelectedRole] = useState('');
|
|
19
|
+
const { inputValue, handleChange } = useDebouncedFilter({
|
|
20
|
+
filterValue: users,
|
|
21
|
+
setFilter: setUsers,
|
|
22
|
+
});
|
|
23
|
+
const { mutate: addTeamMember } = useAddTeamMember(courseId);
|
|
24
|
+
const { addAlert, showModal } = useAlert();
|
|
25
|
+
const roles = [{ role: '', displayName: intl.formatMessage(messages.rolePlaceholder) }, ...(results || [])];
|
|
26
|
+
const handleInputChange = (e) => {
|
|
27
|
+
handleChange(e.target.value);
|
|
28
|
+
};
|
|
29
|
+
const handleSelectChange = (e) => {
|
|
30
|
+
setSelectedRole(e.target.value);
|
|
31
|
+
};
|
|
32
|
+
const handleSave = () => {
|
|
33
|
+
const identifiers = inputValue.split(',').map(user => user.trim()).filter(user => user);
|
|
34
|
+
addTeamMember({ identifiers, role: selectedRole, action: TEAM_MEMBER_ACTION.ALLOW }, {
|
|
35
|
+
onSuccess: (data) => {
|
|
36
|
+
var _a;
|
|
37
|
+
const failedUsernames = ((_a = data.results) === null || _a === void 0 ? void 0 : _a.filter(user => user.userDoesNotExist).map(user => user.identifier)) || [];
|
|
38
|
+
if (failedUsernames.length > 0) {
|
|
39
|
+
addAlert({
|
|
40
|
+
type: 'danger',
|
|
41
|
+
message: intl.formatMessage(messages.failedToAddTeamMembers),
|
|
42
|
+
extraContent: (failedUsernames.map((learner) => (_jsxs("p", { className: "mb-0", children: ["\u2022 ", intl.formatMessage(messages.unknownLearner, { learner })] }, learner))))
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
setUsers('');
|
|
46
|
+
setSelectedRole('');
|
|
47
|
+
onClose();
|
|
48
|
+
},
|
|
49
|
+
onError: () => {
|
|
50
|
+
showModal({
|
|
51
|
+
message: intl.formatMessage(messages.addTeamMemberError),
|
|
52
|
+
variant: 'danger',
|
|
53
|
+
confirmText: intl.formatMessage(messages.closeButton),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
return (_jsxs(ModalDialog, { isOpen: isOpen, onClose: onClose, title: intl.formatMessage(messages.addNewTeamMember), isOverflowVisible: false, size: "lg", children: [_jsx(ModalDialog.Header, { className: "border-light-700 border-bottom", children: _jsx("h3", { className: "text-primary-500", children: intl.formatMessage(messages.addNewTeamMember) }) }), _jsxs(ModalDialog.Body, { className: "position-relative overflow-auto", children: [_jsx("p", { children: intl.formatMessage(messages.addNewTeamMemberDescription, { courseName: displayName }) }), _jsxs(Form.Group, { children: [_jsx(Form.Label, { children: intl.formatMessage(messages.addUsersLabel) }), _jsx(Form.Control, { as: "textarea", rows: 3, placeholder: intl.formatMessage(messages.usersPlaceholder), value: inputValue, onChange: handleInputChange })] }), _jsxs(Form.Group, { children: [_jsx(Form.Label, { children: intl.formatMessage(messages.roleLabel) }), _jsx(Form.Control, { as: "select", defaultValue: "", disabled: roles.length === 1, onChange: handleSelectChange, children: roles.map((role) => (_jsx("option", { value: role.role, children: role.displayName }, role.role))) })] })] }), _jsx(ModalDialog.Footer, { className: "border-light-700 border-top", children: _jsxs(ActionRow, { children: [_jsx(Button, { variant: "tertiary", onClick: onClose, children: intl.formatMessage(messages.cancelButton) }), _jsx(Button, { variant: "primary", onClick: handleSave, disabled: !selectedRole || !inputValue, children: intl.formatMessage(messages.saveButton) })] }) })] }));
|
|
59
|
+
};
|
|
60
|
+
export default AddTeamMemberModal;
|
|
61
|
+
//# sourceMappingURL=AddTeamMemberModal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AddTeamMemberModal.js","sourceRoot":"","sources":["../../../src/courseTeam/components/AddTeamMemberModal.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useIntl } from '@openedx/frontend-base';\nimport { ActionRow, Button, Form, ModalDialog } from '@openedx/paragon';\nimport { useAddTeamMember, useRoles } from '@src/courseTeam/data/apiHook';\nimport messages from '@src/courseTeam/messages';\nimport { useCourseInfo } from '@src/data/apiHook';\nimport { useDebouncedFilter } from '@src/hooks/useDebouncedFilter';\nimport { useAlert } from '@src/providers/AlertProvider';\nimport { TEAM_MEMBER_ACTION } from '../constants';\n\ninterface AddTeamMemberModalProps {\n isOpen: boolean,\n onClose: () => void,\n}\n\nconst AddTeamMemberModal = ({\n isOpen,\n onClose,\n}: AddTeamMemberModalProps) => {\n const intl = useIntl();\n const { courseId = '' } = useParams<{ courseId: string }>();\n const { data: { displayName } = { displayName: '' } } = useCourseInfo(courseId);\n const { data: { results } = { results: [] } } = useRoles(courseId);\n const [users, setUsers] = useState('');\n const [selectedRole, setSelectedRole] = useState('');\n const { inputValue, handleChange } = useDebouncedFilter({\n filterValue: users,\n setFilter: setUsers,\n });\n const { mutate: addTeamMember } = useAddTeamMember(courseId);\n const { addAlert, showModal } = useAlert();\n\n const roles = [{ role: '', displayName: intl.formatMessage(messages.rolePlaceholder) }, ...(results || [])];\n\n const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n handleChange(e.target.value);\n };\n\n const handleSelectChange = (e: React.ChangeEvent<HTMLSelectElement>) => {\n setSelectedRole(e.target.value);\n };\n\n const handleSave = () => {\n const identifiers = inputValue.split(',').map(user => user.trim()).filter(user => user);\n addTeamMember({ identifiers, role: selectedRole, action: TEAM_MEMBER_ACTION.ALLOW }, {\n onSuccess: (data) => {\n const failedUsernames = data.results?.filter(user => user.userDoesNotExist).map(user => user.identifier) || [];\n if (failedUsernames.length > 0) {\n addAlert({\n type: 'danger',\n message: intl.formatMessage(messages.failedToAddTeamMembers),\n extraContent: (\n failedUsernames.map((learner: string) => (\n <p key={learner} className=\"mb-0\">• {intl.formatMessage(messages.unknownLearner, { learner })}</p>\n ))\n )\n });\n }\n setUsers('');\n setSelectedRole('');\n onClose();\n },\n onError: () => {\n showModal({\n message: intl.formatMessage(messages.addTeamMemberError),\n variant: 'danger',\n confirmText: intl.formatMessage(messages.closeButton),\n });\n }\n });\n };\n\n return (\n <ModalDialog isOpen={isOpen} onClose={onClose} title={intl.formatMessage(messages.addNewTeamMember)} isOverflowVisible={false} size=\"lg\">\n <ModalDialog.Header className=\"border-light-700 border-bottom\">\n <h3 className=\"text-primary-500\">{intl.formatMessage(messages.addNewTeamMember)}</h3>\n </ModalDialog.Header>\n <ModalDialog.Body className=\"position-relative overflow-auto\">\n <p>{intl.formatMessage(messages.addNewTeamMemberDescription, { courseName: displayName })}</p>\n <Form.Group>\n <Form.Label>{intl.formatMessage(messages.addUsersLabel)}</Form.Label>\n <Form.Control as=\"textarea\" rows={3} placeholder={intl.formatMessage(messages.usersPlaceholder)} value={inputValue} onChange={handleInputChange} />\n </Form.Group>\n <Form.Group>\n <Form.Label>{intl.formatMessage(messages.roleLabel)}</Form.Label>\n <Form.Control as=\"select\" defaultValue=\"\" disabled={roles.length === 1} onChange={handleSelectChange}>\n {\n roles.map((role) => (\n <option key={role.role} value={role.role}>\n {role.displayName}\n </option>\n ))\n }\n </Form.Control>\n </Form.Group>\n </ModalDialog.Body>\n <ModalDialog.Footer className=\"border-light-700 border-top\">\n <ActionRow>\n <Button variant=\"tertiary\" onClick={onClose}>{intl.formatMessage(messages.cancelButton)}</Button>\n <Button variant=\"primary\" onClick={handleSave} disabled={!selectedRole || !inputValue}>{intl.formatMessage(messages.saveButton)}</Button>\n </ActionRow>\n </ModalDialog.Footer>\n </ModalDialog>\n );\n};\n\nexport default AddTeamMemberModal;\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { CourseTeamMember } from '../../courseTeam/types';
|
|
2
|
+
interface EditTeamMemberModalProps {
|
|
3
|
+
isOpen: boolean;
|
|
4
|
+
user: CourseTeamMember;
|
|
5
|
+
onClose: () => void;
|
|
6
|
+
}
|
|
7
|
+
declare const EditTeamMemberModal: ({ isOpen, user, onClose }: EditTeamMemberModalProps) => import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export default EditTeamMemberModal;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { useParams } from 'react-router-dom';
|
|
4
|
+
import { useIntl } from '@openedx/frontend-base';
|
|
5
|
+
import { ActionRow, Button, Form, FormControl, FormLabel, ModalDialog, OverlayTrigger, Tooltip } from '@openedx/paragon';
|
|
6
|
+
import { useAddTeamMember, useRemoveTeamMember, useRoles } from '../../courseTeam/data/apiHook';
|
|
7
|
+
import messages from '../../courseTeam/messages';
|
|
8
|
+
import { useAlert } from '../../providers/AlertProvider';
|
|
9
|
+
import { TEAM_MEMBER_ACTION } from '../constants';
|
|
10
|
+
import { useCourseInfo } from '../../data/apiHook';
|
|
11
|
+
const EditTeamMemberModal = ({ isOpen, user, onClose }) => {
|
|
12
|
+
const intl = useIntl();
|
|
13
|
+
const { courseId = '' } = useParams();
|
|
14
|
+
const [selectedRole, setSelectedRole] = useState('');
|
|
15
|
+
const [keepRoles, setKeepRoles] = useState([]);
|
|
16
|
+
const { mutate: addTeamMember, isPending: isAdding } = useAddTeamMember(courseId);
|
|
17
|
+
const { mutate: removeTeamMember, isPending: isRemoving } = useRemoveTeamMember(courseId);
|
|
18
|
+
const { showModal } = useAlert();
|
|
19
|
+
const { data: { results } = { results: [] } } = useRoles(courseId, true);
|
|
20
|
+
const { data } = useCourseInfo(courseId);
|
|
21
|
+
const { username: currentUser } = data || {};
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (isOpen) {
|
|
24
|
+
setKeepRoles(user.roles.map(role => role.role));
|
|
25
|
+
setSelectedRole('');
|
|
26
|
+
}
|
|
27
|
+
}, [isOpen, user]);
|
|
28
|
+
const filteredRoles = (results === null || results === void 0 ? void 0 : results.filter(role => !user.roles.some(userRole => userRole.role === role.role))) || [];
|
|
29
|
+
const roles = [{ role: '', displayName: intl.formatMessage(messages.rolePlaceholder) }, ...filteredRoles];
|
|
30
|
+
const handleToggleRole = (roleName) => {
|
|
31
|
+
if (keepRoles.includes(roleName)) {
|
|
32
|
+
setKeepRoles(keepRoles.filter(role => role !== roleName));
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
setKeepRoles([...keepRoles, roleName]);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const handleSave = () => {
|
|
39
|
+
const rolesToRemove = user.roles.filter(role => !keepRoles.includes(role.role)).map(role => role.role);
|
|
40
|
+
const hasRolesToAdd = selectedRole;
|
|
41
|
+
const hasRolesToRemove = rolesToRemove.length > 0;
|
|
42
|
+
// Sequential approach: remove roles first, then add new role
|
|
43
|
+
if (hasRolesToRemove) {
|
|
44
|
+
removeTeamMember({ identifier: user.username, roles: rolesToRemove }, {
|
|
45
|
+
onSuccess: () => {
|
|
46
|
+
// After successful removal, add new role if needed
|
|
47
|
+
if (hasRolesToAdd) {
|
|
48
|
+
addTeamMember({ identifiers: [user.username], role: selectedRole, action: TEAM_MEMBER_ACTION.ALLOW }, {
|
|
49
|
+
onSuccess: () => {
|
|
50
|
+
onClose();
|
|
51
|
+
},
|
|
52
|
+
onError: () => {
|
|
53
|
+
showModal({
|
|
54
|
+
message: intl.formatMessage(messages.addRoleError),
|
|
55
|
+
variant: 'danger',
|
|
56
|
+
confirmText: intl.formatMessage(messages.closeButton),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
onClose();
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
onError: () => {
|
|
66
|
+
showModal({
|
|
67
|
+
message: intl.formatMessage(messages.removeTeamMemberError, { username: user.username }),
|
|
68
|
+
variant: 'danger',
|
|
69
|
+
confirmText: intl.formatMessage(messages.closeButton),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
else if (hasRolesToAdd) {
|
|
75
|
+
// Only add operation needed
|
|
76
|
+
addTeamMember({ identifiers: [user.username], role: selectedRole, action: TEAM_MEMBER_ACTION.ALLOW }, {
|
|
77
|
+
onSuccess: () => {
|
|
78
|
+
onClose();
|
|
79
|
+
},
|
|
80
|
+
onError: () => {
|
|
81
|
+
showModal({
|
|
82
|
+
message: intl.formatMessage(messages.addRoleError),
|
|
83
|
+
variant: 'danger',
|
|
84
|
+
confirmText: intl.formatMessage(messages.closeButton),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
return (_jsxs(ModalDialog, { isOpen: isOpen, title: intl.formatMessage(messages.editTeamTitle, { username: user.username }), onClose: onClose, isOverflowVisible: false, children: [_jsx(ModalDialog.Header, { className: "border-light-700 border-bottom", children: _jsx("h3", { className: "text-primary-500", children: intl.formatMessage(messages.editTeamTitle, { username: user.username }) }) }), _jsxs(ModalDialog.Body, { className: "position-relative overflow-auto", children: [_jsx("p", { children: intl.formatMessage(messages.editInstructions, { username: user.username }) }), _jsx(Form.CheckboxSet, { onChange: (e) => handleToggleRole(e.target.value), value: keepRoles, name: "keepRoles", children: (user.roles || [])
|
|
91
|
+
.map((role) => {
|
|
92
|
+
// Disable roles that user is not available to edit based on his current role
|
|
93
|
+
// Also Admins cannot remove their own Admin role
|
|
94
|
+
// Discussion Admins cannot remove their own Discussion Admins role, unless they have Admin role as well
|
|
95
|
+
const isDiscussionAdminWithoutAdminRole = currentUser === user.username && role.role === 'Administrator' && !user.roles.some(r => r.role === 'instructor');
|
|
96
|
+
const isOwnAdminRole = currentUser === user.username && role.role === 'instructor';
|
|
97
|
+
const isEditAvailable = (results === null || results === void 0 ? void 0 : results.some(availableRole => availableRole.role === role.role)) && !isOwnAdminRole && !isDiscussionAdminWithoutAdminRole;
|
|
98
|
+
return (isEditAvailable ? (_jsx(Form.Checkbox, { className: "mt-2", value: role.role, children: role.displayName }, role.role)) : (_jsx(OverlayTrigger, { placement: "top", overlay: _jsx(Tooltip, { id: `tooltip-${role.role}`, className: "info-tooltip", children: isOwnAdminRole || isDiscussionAdminWithoutAdminRole ? intl.formatMessage(messages.cannotRemoveOwnRole) : intl.formatMessage(messages.roleNotEditable) }), children: _jsx(Form.Checkbox, { className: "mt-2", value: role.role, disabled: true, children: role.displayName }, role.role) })));
|
|
99
|
+
}) }), _jsx(FormLabel, { className: "mt-4", children: intl.formatMessage(messages.addRole) }), _jsx(FormControl, { as: "select", disabled: roles.length === 1, onChange: (e) => setSelectedRole(e.target.value), value: selectedRole, children: roles.map((role) => (_jsx("option", { value: role.role, children: role.displayName }, role.role))) })] }), _jsx(ModalDialog.Footer, { className: "border-light-700 border-top", children: _jsxs(ActionRow, { children: [_jsx(Button, { variant: "tertiary", onClick: onClose, children: intl.formatMessage(messages.cancelButton) }), _jsx(Button, { variant: "primary", onClick: handleSave, disabled: isAdding || isRemoving || (keepRoles.length === user.roles.length && !selectedRole), children: intl.formatMessage(messages.saveButton) })] }) })] }));
|
|
100
|
+
};
|
|
101
|
+
export default EditTeamMemberModal;
|
|
102
|
+
//# sourceMappingURL=EditTeamMemberModal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EditTeamMemberModal.js","sourceRoot":"","sources":["../../../src/courseTeam/components/EditTeamMemberModal.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useIntl } from '@openedx/frontend-base';\nimport { ActionRow, Button, Form, FormControl, FormLabel, ModalDialog, OverlayTrigger, Tooltip } from '@openedx/paragon';\nimport { useAddTeamMember, useRemoveTeamMember, useRoles } from '@src/courseTeam/data/apiHook';\nimport messages from '@src/courseTeam/messages';\nimport { CourseTeamMember, Role } from '@src/courseTeam/types';\nimport { useAlert } from '@src/providers/AlertProvider';\nimport { TEAM_MEMBER_ACTION } from '../constants';\nimport { useCourseInfo } from '@src/data/apiHook';\n\ninterface EditTeamMemberModalProps {\n isOpen: boolean,\n user: CourseTeamMember,\n onClose: () => void,\n}\n\nconst EditTeamMemberModal = ({ isOpen, user, onClose }: EditTeamMemberModalProps) => {\n const intl = useIntl();\n const { courseId = '' } = useParams<{ courseId: string }>();\n const [selectedRole, setSelectedRole] = useState('');\n const [keepRoles, setKeepRoles] = useState<string[]>([]);\n const { mutate: addTeamMember, isPending: isAdding } = useAddTeamMember(courseId);\n const { mutate: removeTeamMember, isPending: isRemoving } = useRemoveTeamMember(courseId);\n const { showModal } = useAlert();\n\n const { data: { results } = { results: [] } } = useRoles(courseId, true);\n\n const { data } = useCourseInfo(courseId);\n const { username: currentUser } = data || {};\n\n useEffect(() => {\n if (isOpen) {\n setKeepRoles(user.roles.map(role => role.role));\n setSelectedRole('');\n }\n }, [isOpen, user]);\n\n const filteredRoles = results?.filter(role => !user.roles.some(userRole => userRole.role === role.role)) || [];\n\n const roles = [{ role: '', displayName: intl.formatMessage(messages.rolePlaceholder) }, ...filteredRoles];\n\n const handleToggleRole = (roleName: string) => {\n if (keepRoles.includes(roleName)) {\n setKeepRoles(keepRoles.filter(role => role !== roleName));\n } else {\n setKeepRoles([...keepRoles, roleName]);\n }\n };\n\n const handleSave = () => {\n const rolesToRemove = user.roles.filter(role => !keepRoles.includes(role.role)).map(role => role.role);\n const hasRolesToAdd = selectedRole;\n const hasRolesToRemove = rolesToRemove.length > 0;\n\n // Sequential approach: remove roles first, then add new role\n if (hasRolesToRemove) {\n removeTeamMember({ identifier: user.username, roles: rolesToRemove }, {\n onSuccess: () => {\n // After successful removal, add new role if needed\n if (hasRolesToAdd) {\n addTeamMember({ identifiers: [user.username], role: selectedRole, action: TEAM_MEMBER_ACTION.ALLOW }, {\n onSuccess: () => {\n onClose();\n },\n onError: () => {\n showModal({\n message: intl.formatMessage(messages.addRoleError),\n variant: 'danger',\n confirmText: intl.formatMessage(messages.closeButton),\n });\n }\n });\n } else {\n onClose();\n }\n },\n onError: () => {\n showModal({\n message: intl.formatMessage(messages.removeTeamMemberError, { username: user.username }),\n variant: 'danger',\n confirmText: intl.formatMessage(messages.closeButton),\n });\n }\n });\n } else if (hasRolesToAdd) {\n // Only add operation needed\n addTeamMember({ identifiers: [user.username], role: selectedRole, action: TEAM_MEMBER_ACTION.ALLOW }, {\n onSuccess: () => {\n onClose();\n },\n onError: () => {\n showModal({\n message: intl.formatMessage(messages.addRoleError),\n variant: 'danger',\n confirmText: intl.formatMessage(messages.closeButton),\n });\n }\n });\n }\n };\n\n return (\n <ModalDialog\n isOpen={isOpen}\n title={intl.formatMessage(messages.editTeamTitle, { username: user.username })}\n onClose={onClose}\n isOverflowVisible={false}\n >\n <ModalDialog.Header className=\"border-light-700 border-bottom\">\n <h3 className=\"text-primary-500\">{intl.formatMessage(messages.editTeamTitle, { username: user.username })}</h3>\n </ModalDialog.Header>\n <ModalDialog.Body className=\"position-relative overflow-auto\">\n <p>{intl.formatMessage(messages.editInstructions, { username: user.username })}</p>\n <Form.CheckboxSet onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleToggleRole(e.target.value)} value={keepRoles} name=\"keepRoles\">\n {\n (user.roles || [])\n .map((role: Role) => {\n // Disable roles that user is not available to edit based on his current role\n // Also Admins cannot remove their own Admin role\n // Discussion Admins cannot remove their own Discussion Admins role, unless they have Admin role as well\n const isDiscussionAdminWithoutAdminRole = currentUser === user.username && role.role === 'Administrator' && !user.roles.some(r => r.role === 'instructor');\n const isOwnAdminRole = currentUser === user.username && role.role === 'instructor';\n const isEditAvailable = results?.some(availableRole => availableRole.role === role.role) && !isOwnAdminRole && !isDiscussionAdminWithoutAdminRole;\n return (\n isEditAvailable ? (\n <Form.Checkbox className=\"mt-2\" key={role.role} value={role.role}>\n {role.displayName}\n </Form.Checkbox>\n ) : (\n <OverlayTrigger\n placement=\"top\"\n overlay={\n <Tooltip id={`tooltip-${role.role}`} className=\"info-tooltip\">{isOwnAdminRole || isDiscussionAdminWithoutAdminRole ? intl.formatMessage(messages.cannotRemoveOwnRole) : intl.formatMessage(messages.roleNotEditable)}</Tooltip>\n }\n >\n <Form.Checkbox className=\"mt-2\" key={role.role} value={role.role} disabled>\n {role.displayName}\n </Form.Checkbox>\n </OverlayTrigger>\n )\n );\n })\n }\n </Form.CheckboxSet>\n <FormLabel className=\"mt-4\">{intl.formatMessage(messages.addRole)}</FormLabel>\n <FormControl\n as=\"select\"\n disabled={roles.length === 1}\n onChange={(e: React.ChangeEvent<HTMLSelectElement>) => setSelectedRole(e.target.value)}\n value={selectedRole}\n >\n {\n roles.map((role) => (\n <option key={role.role} value={role.role}>\n {role.displayName}\n </option>\n ))\n }\n </FormControl>\n </ModalDialog.Body>\n <ModalDialog.Footer className=\"border-light-700 border-top\">\n <ActionRow>\n <Button variant=\"tertiary\" onClick={onClose}>{intl.formatMessage(messages.cancelButton)}</Button>\n <Button variant=\"primary\" onClick={handleSave} disabled={isAdding || isRemoving || (keepRoles.length === user.roles.length && !selectedRole)}>\n {intl.formatMessage(messages.saveButton)}\n </Button>\n </ActionRow>\n </ModalDialog.Footer>\n </ModalDialog>\n );\n};\n\nexport default EditTeamMemberModal;\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { CourseTeamMember } from '../../courseTeam/types';
|
|
2
|
+
interface MembersContentProps {
|
|
3
|
+
onEdit: (user: CourseTeamMember) => void;
|
|
4
|
+
}
|
|
5
|
+
declare const MembersContent: ({ onEdit }: MembersContentProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export default MembersContent;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useMemo } from 'react';
|
|
3
|
+
import { useParams } from 'react-router-dom';
|
|
4
|
+
import { useIntl } from '@openedx/frontend-base';
|
|
5
|
+
import { Button, DataTable } from '@openedx/paragon';
|
|
6
|
+
import UsernameFilter from '../../components/UsernameFilter';
|
|
7
|
+
import RoleFilter from '../../courseTeam/components/RoleFilter';
|
|
8
|
+
import { useTeamMembers } from '../../courseTeam/data/apiHook';
|
|
9
|
+
import messages from '../../courseTeam/messages';
|
|
10
|
+
const TEAM_MEMBERS_PAGE_SIZE = 25;
|
|
11
|
+
const MembersContent = ({ onEdit }) => {
|
|
12
|
+
const intl = useIntl();
|
|
13
|
+
const { courseId = '' } = useParams();
|
|
14
|
+
const [filters, setFilters] = useState({ page: 0, emailOrUsername: '', role: '' });
|
|
15
|
+
const { data: { results: teamMembers = [], numPages = 1, count = 0 } = {}, isLoading = false } = useTeamMembers(courseId, Object.assign(Object.assign({}, filters), { pageSize: TEAM_MEMBERS_PAGE_SIZE }));
|
|
16
|
+
const tableColumns = useMemo(() => [
|
|
17
|
+
{ accessor: 'username', Header: intl.formatMessage(messages.username), Filter: UsernameFilter },
|
|
18
|
+
{ accessor: 'email', Header: intl.formatMessage(messages.email), disableFilters: true },
|
|
19
|
+
{ accessor: 'roles', Header: intl.formatMessage(messages.role), Cell: ({ cell: { value } }) => value.map(role => role.displayName).join(', '), Filter: RoleFilter },
|
|
20
|
+
], [intl]);
|
|
21
|
+
const additionalColumns = useMemo(() => [{
|
|
22
|
+
id: 'actions',
|
|
23
|
+
Header: intl.formatMessage(messages.actions),
|
|
24
|
+
Cell: ({ row }) => (_jsx(Button, { variant: "link", size: "inline", onClick: () => onEdit(row.original), children: intl.formatMessage(messages.edit) }))
|
|
25
|
+
}], [intl, onEdit]);
|
|
26
|
+
const handleFetchData = (data) => {
|
|
27
|
+
var _a, _b;
|
|
28
|
+
const usernameFilter = (_a = data.filters) === null || _a === void 0 ? void 0 : _a.find((f) => f.id === 'username');
|
|
29
|
+
const newEmailOrUsername = usernameFilter ? usernameFilter.value : '';
|
|
30
|
+
const rolesFilter = (_b = data.filters) === null || _b === void 0 ? void 0 : _b.find((f) => f.id === 'roles');
|
|
31
|
+
const newRole = rolesFilter ? rolesFilter.value : '';
|
|
32
|
+
const filtersChanged = (newEmailOrUsername !== filters.emailOrUsername) || (newRole !== filters.role);
|
|
33
|
+
if (filtersChanged) {
|
|
34
|
+
setFilters((prevFilters) => (Object.assign(Object.assign({}, prevFilters), { emailOrUsername: newEmailOrUsername, role: newRole, page: 0 })));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (data.pageIndex !== filters.page) {
|
|
38
|
+
setFilters((prevFilters) => (Object.assign(Object.assign({}, prevFilters), { page: data.pageIndex })));
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const tableState = useMemo(() => ({
|
|
42
|
+
pageIndex: filters.page,
|
|
43
|
+
pageSize: TEAM_MEMBERS_PAGE_SIZE,
|
|
44
|
+
}), [filters.page]);
|
|
45
|
+
return (_jsxs(DataTable, { additionalColumns: additionalColumns, columns: tableColumns, data: teamMembers, fetchData: handleFetchData, state: tableState, isFilterable: true, isLoading: isLoading, isPaginated: true, itemCount: count, manualFilters: true, manualPagination: true, numBreakoutFilters: 2, pageSize: TEAM_MEMBERS_PAGE_SIZE, pageCount: numPages, RowStatusComponent: () => null, children: [_jsx(DataTable.TableControlBar, {}), _jsx(DataTable.Table, {}), _jsx(DataTable.EmptyTable, { content: intl.formatMessage(messages.noTeamMembers) }), _jsx(DataTable.TableFooter, {})] }));
|
|
46
|
+
};
|
|
47
|
+
export default MembersContent;
|
|
48
|
+
//# sourceMappingURL=MembersContent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MembersContent.js","sourceRoot":"","sources":["../../../src/courseTeam/components/MembersContent.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useState, useMemo } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useIntl } from '@openedx/frontend-base';\nimport { Button, DataTable } from '@openedx/paragon';\nimport UsernameFilter from '@src/components/UsernameFilter';\nimport RoleFilter from '@src/courseTeam/components/RoleFilter';\nimport { useTeamMembers } from '@src/courseTeam/data/apiHook';\nimport messages from '@src/courseTeam/messages';\nimport { CourseTeamMember, Role } from '@src/courseTeam/types';\nimport { DataTableFetchDataProps } from '@src/types';\n\nconst TEAM_MEMBERS_PAGE_SIZE = 25;\n\ninterface MembersContentProps {\n onEdit: (user: CourseTeamMember) => void,\n}\n\nconst MembersContent = ({ onEdit }: MembersContentProps) => {\n const intl = useIntl();\n const { courseId = '' } = useParams<{ courseId: string }>();\n const [filters, setFilters] = useState({ page: 0, emailOrUsername: '', role: '' });\n const { data: { results: teamMembers = [], numPages = 1, count = 0 } = {}, isLoading = false } = useTeamMembers(courseId, { ...filters, pageSize: TEAM_MEMBERS_PAGE_SIZE });\n\n const tableColumns = useMemo(() => [\n { accessor: 'username', Header: intl.formatMessage(messages.username), Filter: UsernameFilter },\n { accessor: 'email', Header: intl.formatMessage(messages.email), disableFilters: true },\n { accessor: 'roles', Header: intl.formatMessage(messages.role), Cell: ({ cell: { value } }: { cell: { value: Role[] } }) => value.map(role => role.displayName).join(', '), Filter: RoleFilter },\n ], [intl]);\n\n const additionalColumns = useMemo(() => [{\n id: 'actions',\n Header: intl.formatMessage(messages.actions),\n Cell: ({ row }: { row: { original: any } }) => (\n <Button variant=\"link\" size=\"inline\" onClick={() => onEdit(row.original)}>\n {intl.formatMessage(messages.edit)}\n </Button>\n )\n }], [intl, onEdit]);\n\n const handleFetchData = (data: DataTableFetchDataProps) => {\n const usernameFilter = data.filters?.find((f) => f.id === 'username');\n const newEmailOrUsername = usernameFilter ? usernameFilter.value : '';\n const rolesFilter = data.filters?.find((f) => f.id === 'roles');\n const newRole = rolesFilter ? rolesFilter.value : '';\n const filtersChanged = (newEmailOrUsername !== filters.emailOrUsername) || (newRole !== filters.role);\n\n if (filtersChanged) {\n setFilters((prevFilters) => ({\n ...prevFilters,\n emailOrUsername: newEmailOrUsername,\n role: newRole,\n page: 0,\n }));\n return;\n }\n\n if (data.pageIndex !== filters.page) {\n setFilters((prevFilters) => ({ ...prevFilters, page: data.pageIndex }));\n }\n };\n\n const tableState = useMemo(() => ({\n pageIndex: filters.page,\n pageSize: TEAM_MEMBERS_PAGE_SIZE,\n }), [filters.page]);\n\n return (\n <DataTable\n additionalColumns={additionalColumns}\n columns={tableColumns}\n data={teamMembers}\n fetchData={handleFetchData}\n state={tableState}\n isFilterable\n isLoading={isLoading}\n isPaginated\n itemCount={count}\n manualFilters\n manualPagination\n numBreakoutFilters={2}\n pageSize={TEAM_MEMBERS_PAGE_SIZE}\n pageCount={numPages}\n RowStatusComponent={() => null}\n >\n <DataTable.TableControlBar />\n <DataTable.Table />\n <DataTable.EmptyTable content={intl.formatMessage(messages.noTeamMembers)} />\n <DataTable.TableFooter />\n </DataTable>\n );\n};\n\nexport default MembersContent;\n"]}
|