forked from openedx/frontend-app-authoring
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/master' into jill/FAL-3859-1-lib…
…rary-loading-text
- Loading branch information
Showing
65 changed files
with
2,553 additions
and
1,361 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,69 +1,176 @@ | ||
import React from 'react'; | ||
import { useEffect, useState, useRef } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import * as Yup from 'yup'; | ||
|
||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; | ||
import { useIntl } from '@edx/frontend-platform/i18n'; | ||
import { useDispatch, useSelector } from 'react-redux'; | ||
|
||
import { Hyperlink } from '@openedx/paragon'; | ||
import { useModel } from 'CourseAuthoring/generic/model-store'; | ||
import { | ||
ActionRow, Alert, Badge, Form, Hyperlink, ModalDialog, StatefulButton, | ||
} from '@openedx/paragon'; | ||
import { Info } from '@openedx/paragon/icons'; | ||
import { updateModel, useModel } from 'CourseAuthoring/generic/model-store'; | ||
|
||
import { RequestStatus } from 'CourseAuthoring/data/constants'; | ||
import FormSwitchGroup from 'CourseAuthoring/generic/FormSwitchGroup'; | ||
import { useAppSetting } from 'CourseAuthoring/utils'; | ||
import AppSettingsModal from 'CourseAuthoring/pages-and-resources/app-settings-modal/AppSettingsModal'; | ||
import Loading from 'CourseAuthoring/generic/Loading'; | ||
import PermissionDeniedAlert from 'CourseAuthoring/generic/PermissionDeniedAlert'; | ||
import ConnectionErrorAlert from 'CourseAuthoring/generic/ConnectionErrorAlert'; | ||
import { useAppSetting, useIsMobile } from 'CourseAuthoring/utils'; | ||
import { getLoadingStatus, getSavingStatus } from 'CourseAuthoring/pages-and-resources/data/selectors'; | ||
import { updateSavingStatus } from 'CourseAuthoring/pages-and-resources/data/slice'; | ||
|
||
import messages from './messages'; | ||
|
||
const ORASettings = ({ intl, onClose }) => { | ||
const ORASettings = ({ onClose }) => { | ||
const dispatch = useDispatch(); | ||
const { formatMessage } = useIntl(); | ||
const alertRef = useRef(null); | ||
const updateSettingsRequestStatus = useSelector(getSavingStatus); | ||
const loadingStatus = useSelector(getLoadingStatus); | ||
const isMobile = useIsMobile(); | ||
const modalVariant = isMobile ? 'dark' : 'default'; | ||
const appId = 'ora_settings'; | ||
const appInfo = useModel('courseApps', appId); | ||
|
||
const [enableFlexiblePeerGrade, saveSetting] = useAppSetting( | ||
'forceOnFlexiblePeerOpenassessments', | ||
); | ||
const initialFormValues = { enableFlexiblePeerGrade }; | ||
|
||
const [formValues, setFormValues] = useState(initialFormValues); | ||
const [saveError, setSaveError] = useState(false); | ||
|
||
const submitButtonState = updateSettingsRequestStatus === RequestStatus.IN_PROGRESS ? 'pending' : 'default'; | ||
const handleSettingsSave = (values) => saveSetting(values.enableFlexiblePeerGrade); | ||
|
||
const title = ( | ||
<div> | ||
<p>{intl.formatMessage(messages.heading)}</p> | ||
<div className="pt-3"> | ||
<Hyperlink | ||
className="text-primary-500 small" | ||
destination={appInfo.documentationLinks?.learnMoreConfiguration} | ||
target="_blank" | ||
rel="noreferrer noopener" | ||
> | ||
{intl.formatMessage(messages.ORASettingsHelpLink)} | ||
</Hyperlink> | ||
</div> | ||
</div> | ||
); | ||
const handleSubmit = async (event) => { | ||
let success = true; | ||
event.preventDefault(); | ||
|
||
success = success && await handleSettingsSave(formValues); | ||
await setSaveError(!success); | ||
if ((initialFormValues.enableFlexiblePeerGrade !== formValues.enableFlexiblePeerGrade) && success) { | ||
success = await dispatch(updateModel({ | ||
modelType: 'courseApps', | ||
model: { | ||
id: appId, enabled: formValues.enableFlexiblePeerGrade, | ||
}, | ||
})); | ||
} | ||
!success && alertRef?.current.scrollIntoView(); // eslint-disable-line @typescript-eslint/no-unused-expressions | ||
}; | ||
|
||
const handleChange = (e) => { | ||
setFormValues({ enableFlexiblePeerGrade: e.target.checked }); | ||
}; | ||
|
||
useEffect(() => { | ||
if (updateSettingsRequestStatus === RequestStatus.SUCCESSFUL) { | ||
dispatch(updateSavingStatus({ status: '' })); | ||
onClose(); | ||
} | ||
}, [updateSettingsRequestStatus]); | ||
|
||
const renderBody = () => { | ||
switch (loadingStatus) { | ||
case RequestStatus.SUCCESSFUL: | ||
return ( | ||
<> | ||
{saveError && ( | ||
<Alert variant="danger" icon={Info} ref={alertRef}> | ||
<Alert.Heading> | ||
{formatMessage(messages.errorSavingTitle)} | ||
</Alert.Heading> | ||
{formatMessage(messages.errorSavingMessage)} | ||
</Alert> | ||
)} | ||
<FormSwitchGroup | ||
id="enable-flexible-peer-grade" | ||
name="enableFlexiblePeerGrade" | ||
label={( | ||
<div className="d-flex align-items-center"> | ||
{formatMessage(messages.enableFlexPeerGradeLabel)} | ||
{formValues.enableFlexiblePeerGrade && ( | ||
<Badge className="ml-2" variant="success" data-testid="enable-badge"> | ||
{formatMessage(messages.enabledBadgeLabel)} | ||
</Badge> | ||
)} | ||
</div> | ||
)} | ||
helpText={( | ||
<div> | ||
<p>{formatMessage(messages.enableFlexPeerGradeHelp)}</p> | ||
<span className="py-3"> | ||
<Hyperlink | ||
className="text-primary-500 small" | ||
destination={appInfo.documentationLinks?.learnMoreConfiguration} | ||
target="_blank" | ||
rel="noreferrer noopener" | ||
> | ||
{formatMessage(messages.ORASettingsHelpLink)} | ||
</Hyperlink> | ||
</span> | ||
</div> | ||
)} | ||
onChange={handleChange} | ||
checked={formValues.enableFlexiblePeerGrade} | ||
/> | ||
</> | ||
); | ||
case RequestStatus.DENIED: | ||
return <PermissionDeniedAlert />; | ||
case RequestStatus.FAILED: | ||
return <ConnectionErrorAlert />; | ||
default: | ||
return <Loading />; | ||
} | ||
}; | ||
|
||
return ( | ||
<AppSettingsModal | ||
appId={appId} | ||
title={title} | ||
<ModalDialog | ||
title={formatMessage(messages.heading)} | ||
isOpen | ||
onClose={onClose} | ||
initialValues={{ enableFlexiblePeerGrade }} | ||
validationSchema={{ enableFlexiblePeerGrade: Yup.boolean() }} | ||
onSettingsSave={handleSettingsSave} | ||
hideAppToggle | ||
size="lg" | ||
variant={modalVariant} | ||
hasCloseButton={isMobile} | ||
isFullscreenScroll | ||
isFullscreenOnMobile | ||
> | ||
{({ values, handleChange, handleBlur }) => ( | ||
<FormSwitchGroup | ||
id="enable-flexible-peer-grade" | ||
name="enableFlexiblePeerGrade" | ||
label={intl.formatMessage(messages.enableFlexPeerGradeLabel)} | ||
helpText={intl.formatMessage(messages.enableFlexPeerGradeHelp)} | ||
onChange={handleChange} | ||
onBlur={handleBlur} | ||
checked={values.enableFlexiblePeerGrade} | ||
/> | ||
)} | ||
</AppSettingsModal> | ||
<Form onSubmit={handleSubmit} data-testid="proctoringForm"> | ||
<ModalDialog.Header> | ||
<ModalDialog.Title> | ||
{formatMessage(messages.heading)} | ||
</ModalDialog.Title> | ||
</ModalDialog.Header> | ||
<ModalDialog.Body> | ||
{renderBody()} | ||
</ModalDialog.Body> | ||
<ModalDialog.Footer className="p-4"> | ||
<ActionRow> | ||
<ModalDialog.CloseButton variant="tertiary"> | ||
{formatMessage(messages.cancelLabel)} | ||
</ModalDialog.CloseButton> | ||
<StatefulButton | ||
labels={{ | ||
default: formatMessage(messages.saveLabel), | ||
pending: formatMessage(messages.pendingSaveLabel), | ||
}} | ||
description="Form save button" | ||
data-testid="submissionButton" | ||
disabled={submitButtonState === RequestStatus.IN_PROGRESS} | ||
state={submitButtonState} | ||
type="submit" | ||
/> | ||
</ActionRow> | ||
</ModalDialog.Footer> | ||
</Form> | ||
</ModalDialog> | ||
); | ||
}; | ||
|
||
ORASettings.propTypes = { | ||
intl: intlShape.isRequired, | ||
onClose: PropTypes.func.isRequired, | ||
}; | ||
|
||
export default injectIntl(ORASettings); | ||
export default ORASettings; |
Oops, something went wrong.