feat: add xpert summaries configuration by default for units (#567)
* feat: add xpert summaries configuration by default for units
This commit is contained in:
@@ -13,3 +13,4 @@
|
||||
@import "grading-settings/scss/GradingSettings";
|
||||
@import "generic/styles";
|
||||
@import "schedule-and-details/ScheduleAndDetails";
|
||||
@import "pages-and-resources/PagesAndResources";
|
||||
|
||||
1
src/pages-and-resources/PagesAndResources.scss
Normal file
1
src/pages-and-resources/PagesAndResources.scss
Normal file
@@ -0,0 +1 @@
|
||||
@import "./xpert-unit-summary/settings-modal/SettingsModal";
|
||||
@@ -6,3 +6,4 @@ export const getCourseAppsApiStatus = (state) => state.pagesAndResources.courseA
|
||||
export const getCourseAppSettingValue = (setting) => (state) => (
|
||||
state.pagesAndResources.courseAppSettings[setting]?.value
|
||||
);
|
||||
export const getResetStatus = (state) => state.pagesAndResources.resetStatus;
|
||||
|
||||
@@ -9,6 +9,7 @@ const slice = createSlice({
|
||||
courseAppIds: [],
|
||||
loadingStatus: RequestStatus.IN_PROGRESS,
|
||||
savingStatus: '',
|
||||
resetStatus: '',
|
||||
courseAppsApiStatus: {},
|
||||
courseAppSettings: {},
|
||||
},
|
||||
@@ -22,6 +23,9 @@ const slice = createSlice({
|
||||
updateSavingStatus: (state, { payload }) => {
|
||||
state.savingStatus = payload.status;
|
||||
},
|
||||
updateResetStatus: (state, { payload }) => {
|
||||
state.resetStatus = payload.status;
|
||||
},
|
||||
updateCourseAppsApiStatus: (state, { payload }) => {
|
||||
state.courseAppsApiStatus = payload.status;
|
||||
},
|
||||
@@ -38,6 +42,7 @@ export const {
|
||||
fetchCourseAppsSuccess,
|
||||
updateLoadingStatus,
|
||||
updateSavingStatus,
|
||||
updateResetStatus,
|
||||
updateCourseAppsApiStatus,
|
||||
fetchCourseAppsSettingsSuccess,
|
||||
updateCourseAppsSettingsSuccess,
|
||||
|
||||
@@ -44,6 +44,8 @@ const XpertUnitSummarySettings = ({ intl }) => {
|
||||
}
|
||||
enableAppLabel={intl.formatMessage(messages.enableXpertUnitSummaryLabel)}
|
||||
learnMoreText={intl.formatMessage(messages.enableXpertUnitSummaryLink)}
|
||||
allUnitsEnabledText={intl.formatMessage(messages.allUnitsEnabledByDefault)}
|
||||
noUnitsEnabledText={intl.formatMessage(messages.noUnitsEnabledByDefault)}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -112,12 +112,12 @@ describe('XpertUnitSummarySettings', () => {
|
||||
renderComponent();
|
||||
});
|
||||
|
||||
test('Shows enabled if enabled from backend', async () => {
|
||||
test('Shows switch on if enabled from backend', async () => {
|
||||
expect(container.querySelector('#enable-xpert-unit-summary-toggle').checked).toBeTruthy();
|
||||
expect(queryByTestId(container, 'enable-badge')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('Does not show enabled if disabled from backend', async () => {
|
||||
test('Shows switch on if disabled from backend', async () => {
|
||||
axiosMock.onGet(API.getXpertSettingsUrl(courseId))
|
||||
.reply(200, generateCourseLevelAPIRepsonse({
|
||||
success: true,
|
||||
@@ -126,8 +126,25 @@ describe('XpertUnitSummarySettings', () => {
|
||||
|
||||
renderComponent();
|
||||
await waitFor(() => expect(container.querySelector('#enable-xpert-unit-summary-toggle')).toBeTruthy());
|
||||
expect(container.querySelector('#enable-xpert-unit-summary-toggle').checked).not.toBeTruthy();
|
||||
expect(queryByTestId(container, 'enable-badge')).not.toBeTruthy();
|
||||
expect(container.querySelector('#enable-xpert-unit-summary-toggle').checked).toBeTruthy();
|
||||
expect(queryByTestId(container, 'enable-badge')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('Shows enable radio selected if enabled from backend', async () => {
|
||||
await waitFor(() => expect(container.querySelector('#enable-xpert-unit-summary-toggle')).toBeTruthy());
|
||||
expect(queryByTestId(container, 'enable-radio').checked).toBeTruthy();
|
||||
});
|
||||
|
||||
test('Shows disable radio selected if enabled from backend', async () => {
|
||||
axiosMock.onGet(API.getXpertSettingsUrl(courseId))
|
||||
.reply(200, generateCourseLevelAPIRepsonse({
|
||||
success: true,
|
||||
enabled: false,
|
||||
}));
|
||||
|
||||
renderComponent();
|
||||
await waitFor(() => expect(container.querySelector('#enable-xpert-unit-summary-toggle')).toBeTruthy());
|
||||
expect(queryByTestId(container, 'disable-radio').checked).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -136,7 +153,7 @@ describe('XpertUnitSummarySettings', () => {
|
||||
axiosMock.onGet(API.getXpertSettingsUrl(courseId))
|
||||
.reply(400, generateCourseLevelAPIRepsonse({
|
||||
success: false,
|
||||
enabled: false,
|
||||
enabled: undefined,
|
||||
}));
|
||||
|
||||
renderComponent();
|
||||
@@ -151,6 +168,12 @@ describe('XpertUnitSummarySettings', () => {
|
||||
|
||||
describe('saving configuration changes', () => {
|
||||
beforeEach(() => {
|
||||
axiosMock.onGet(API.getXpertSettingsUrl(courseId))
|
||||
.reply(200, generateCourseLevelAPIRepsonse({
|
||||
success: true,
|
||||
enabled: false,
|
||||
}));
|
||||
|
||||
axiosMock.onPost(API.getXpertSettingsUrl(courseId))
|
||||
.reply(200, generateCourseLevelAPIRepsonse({
|
||||
success: true,
|
||||
@@ -164,8 +187,10 @@ describe('XpertUnitSummarySettings', () => {
|
||||
jest.spyOn(API, 'postXpertSettings');
|
||||
|
||||
await waitFor(() => expect(container.querySelector('#enable-xpert-unit-summary-toggle')).toBeTruthy());
|
||||
expect(queryByTestId(container, 'disable-radio').checked).toBeTruthy();
|
||||
fireEvent.click(queryByTestId(container, 'enable-radio'));
|
||||
fireEvent.click(getByText(container, 'Save'));
|
||||
await waitFor(() => expect(container.querySelector('#enable-xpert-unit-summary-toggle')).not.toBeTruthy());
|
||||
await waitFor(() => expect(container.querySelector('#enable-xpert-unit-summary-toggle')).toBeTruthy());
|
||||
expect(API.postXpertSettings).toBeCalled();
|
||||
});
|
||||
});
|
||||
@@ -186,4 +211,78 @@ describe('XpertUnitSummarySettings', () => {
|
||||
expect(API.getXpertPluginConfigurable).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('removing course configuration', () => {
|
||||
beforeEach(() => {
|
||||
axiosMock.onGet(API.getXpertSettingsUrl(courseId))
|
||||
.reply(200, generateCourseLevelAPIRepsonse({
|
||||
success: true,
|
||||
enabled: true,
|
||||
}));
|
||||
|
||||
axiosMock.onDelete(API.getXpertSettingsUrl(courseId))
|
||||
.reply(200, generateCourseLevelAPIRepsonse({
|
||||
success: true,
|
||||
enabled: undefined,
|
||||
}));
|
||||
|
||||
renderComponent();
|
||||
});
|
||||
|
||||
test('Deleting course configuration', async () => {
|
||||
jest.spyOn(API, 'deleteXpertSettings');
|
||||
|
||||
await waitFor(() => expect(container.querySelector('#enable-xpert-unit-summary-toggle')).toBeTruthy());
|
||||
fireEvent.click(container.querySelector('#enable-xpert-unit-summary-toggle'));
|
||||
fireEvent.click(getByText(container, 'Save'));
|
||||
await waitFor(() => expect(container.querySelector('#enable-xpert-unit-summary-toggle')).toBeTruthy());
|
||||
expect(API.deleteXpertSettings).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('resetting course units', () => {
|
||||
test('reset all units to be enabled', async () => {
|
||||
axiosMock.onGet(API.getXpertSettingsUrl(courseId))
|
||||
.reply(200, generateCourseLevelAPIRepsonse({
|
||||
success: true,
|
||||
enabled: true,
|
||||
}));
|
||||
|
||||
axiosMock.onPost(API.getXpertSettingsUrl(courseId))
|
||||
.reply(200, generateCourseLevelAPIRepsonse({
|
||||
success: true,
|
||||
enabled: true,
|
||||
}));
|
||||
|
||||
renderComponent();
|
||||
|
||||
jest.spyOn(API, 'postXpertSettings');
|
||||
|
||||
await waitFor(() => expect(container.querySelector('#enable-xpert-unit-summary-toggle')).toBeTruthy());
|
||||
fireEvent.click(queryByTestId(container, 'reset-units'));
|
||||
expect(API.postXpertSettings).toBeCalledWith(courseId, { reset: true, enabled: true });
|
||||
});
|
||||
|
||||
test('reset all units to be disabled', async () => {
|
||||
axiosMock.onGet(API.getXpertSettingsUrl(courseId))
|
||||
.reply(200, generateCourseLevelAPIRepsonse({
|
||||
success: true,
|
||||
enabled: false,
|
||||
}));
|
||||
|
||||
axiosMock.onPost(API.getXpertSettingsUrl(courseId))
|
||||
.reply(200, generateCourseLevelAPIRepsonse({
|
||||
success: true,
|
||||
enabled: false,
|
||||
}));
|
||||
|
||||
renderComponent();
|
||||
|
||||
jest.spyOn(API, 'postXpertSettings');
|
||||
|
||||
await waitFor(() => expect(container.querySelector('#enable-xpert-unit-summary-toggle')).toBeTruthy());
|
||||
fireEvent.click(queryByTestId(container, 'reset-units'));
|
||||
expect(API.postXpertSettings).toBeCalledWith(courseId, { reset: true, enabled: false });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,6 +20,7 @@ export async function postXpertSettings(courseId, state) {
|
||||
const { data } = await getAuthenticatedHttpClient()
|
||||
.post(getXpertSettingsUrl(courseId), {
|
||||
enabled: state.enabled,
|
||||
reset: state.reset,
|
||||
});
|
||||
|
||||
return data;
|
||||
@@ -31,3 +32,10 @@ export async function getXpertPluginConfigurable(courseId) {
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function deleteXpertSettings(courseId) {
|
||||
const { data } = await getAuthenticatedHttpClient()
|
||||
.delete(getXpertSettingsUrl(courseId));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { getXpertSettings, postXpertSettings, getXpertPluginConfigurable } from './api';
|
||||
import {
|
||||
getXpertSettings, postXpertSettings, getXpertPluginConfigurable, deleteXpertSettings,
|
||||
} from './api';
|
||||
|
||||
import { updateSavingStatus, updateLoadingStatus } from '../../data/slice';
|
||||
import { updateSavingStatus, updateLoadingStatus, updateResetStatus } from '../../data/slice';
|
||||
import { RequestStatus } from '../../../data/constants';
|
||||
|
||||
import { addModel, updateModel } from '../../../generic/model-store';
|
||||
@@ -27,13 +29,13 @@ export function updateXpertSettings(courseId, state) {
|
||||
|
||||
export function fetchXpertPluginConfigurable(courseId) {
|
||||
return async (dispatch) => {
|
||||
let enabled = false;
|
||||
let enabled;
|
||||
dispatch(updateLoadingStatus({ status: RequestStatus.PENDING }));
|
||||
try {
|
||||
const { response } = await getXpertPluginConfigurable(courseId);
|
||||
enabled = response?.enabled;
|
||||
} catch (e) {
|
||||
enabled = false;
|
||||
enabled = undefined;
|
||||
}
|
||||
|
||||
dispatch(addModel({
|
||||
@@ -48,14 +50,14 @@ export function fetchXpertPluginConfigurable(courseId) {
|
||||
|
||||
export function fetchXpertSettings(courseId) {
|
||||
return async (dispatch) => {
|
||||
let enabled = false;
|
||||
let enabled;
|
||||
dispatch(updateLoadingStatus({ status: RequestStatus.PENDING }));
|
||||
|
||||
try {
|
||||
const { response } = await getXpertSettings(courseId);
|
||||
enabled = response?.enabled;
|
||||
} catch (e) {
|
||||
enabled = false;
|
||||
enabled = undefined;
|
||||
}
|
||||
|
||||
dispatch(addModel({
|
||||
@@ -69,3 +71,44 @@ export function fetchXpertSettings(courseId) {
|
||||
dispatch(updateLoadingStatus({ status: RequestStatus.SUCCESSFUL }));
|
||||
};
|
||||
}
|
||||
|
||||
export function removeXpertSettings(courseId) {
|
||||
return async (dispatch) => {
|
||||
dispatch(updateSavingStatus({ status: RequestStatus.PENDING }));
|
||||
|
||||
try {
|
||||
const { response } = await deleteXpertSettings(courseId);
|
||||
const { success } = response;
|
||||
if (success) {
|
||||
const model = { id: 'xpert-unit-summary', enabled: undefined };
|
||||
dispatch(updateModel({ modelType: 'XpertSettings', model }));
|
||||
dispatch(updateSavingStatus({ status: RequestStatus.SUCCESSFUL }));
|
||||
return true;
|
||||
}
|
||||
dispatch(updateSavingStatus({ status: RequestStatus.FAILED }));
|
||||
return false;
|
||||
} catch (error) {
|
||||
dispatch(updateSavingStatus({ status: RequestStatus.FAILED }));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function resetXpertSettings(courseId, state) {
|
||||
return async (dispatch) => {
|
||||
dispatch(updateResetStatus({ status: RequestStatus.PENDING }));
|
||||
try {
|
||||
const { response } = await postXpertSettings(courseId, state);
|
||||
const { success } = response;
|
||||
if (success) {
|
||||
dispatch(updateResetStatus({ status: RequestStatus.SUCCESSFUL }));
|
||||
return true;
|
||||
}
|
||||
dispatch(updateResetStatus({ status: RequestStatus.FAILED }));
|
||||
return false;
|
||||
} catch (error) {
|
||||
dispatch(updateResetStatus({ status: RequestStatus.FAILED }));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,12 +4,17 @@ import {
|
||||
Alert,
|
||||
Badge,
|
||||
Form,
|
||||
Icon,
|
||||
ModalDialog,
|
||||
OverlayTrigger,
|
||||
StatefulButton,
|
||||
Tooltip,
|
||||
TransitionReplace,
|
||||
Hyperlink,
|
||||
} from '@edx/paragon';
|
||||
import { Info } from '@edx/paragon/icons';
|
||||
import {
|
||||
Info, CheckCircleOutline, RotateLeft, SpinnerSimple,
|
||||
} from '@edx/paragon/icons';
|
||||
|
||||
import { Formik } from 'formik';
|
||||
import PropTypes from 'prop-types';
|
||||
@@ -26,9 +31,9 @@ import Loading from '../../../generic/Loading';
|
||||
import { useModel } from '../../../generic/model-store';
|
||||
import PermissionDeniedAlert from '../../../generic/PermissionDeniedAlert';
|
||||
import { useIsMobile } from '../../../utils';
|
||||
import { getLoadingStatus, getSavingStatus } from '../../data/selectors';
|
||||
import { updateSavingStatus } from '../../data/slice';
|
||||
import { updateXpertSettings } from '../data/thunks';
|
||||
import { getLoadingStatus, getSavingStatus, getResetStatus } from '../../data/selectors';
|
||||
import { updateSavingStatus, updateResetStatus } from '../../data/slice';
|
||||
import { updateXpertSettings, resetXpertSettings, removeXpertSettings } from '../data/thunks';
|
||||
import AppConfigFormDivider from '../../discussions/app-config-form/apps/shared/AppConfigFormDivider';
|
||||
import { PagesAndResourcesContext } from '../../PagesAndResourcesProvider';
|
||||
import messages from './messages';
|
||||
@@ -105,6 +110,84 @@ SettingsModalBase.defaultProps = {
|
||||
footer: null,
|
||||
};
|
||||
|
||||
const ResetUnitsButton = ({
|
||||
intl,
|
||||
courseId,
|
||||
checked,
|
||||
visible,
|
||||
}) => {
|
||||
const resetStatusRequestStatus = useSelector(getResetStatus);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
if (resetStatusRequestStatus === RequestStatus.SUCCESSFUL) {
|
||||
setTimeout(() => {
|
||||
dispatch(updateResetStatus({ status: '' }));
|
||||
}, 2000);
|
||||
}
|
||||
}, [resetStatusRequestStatus]);
|
||||
|
||||
const handleResetUnits = () => {
|
||||
dispatch(resetXpertSettings(courseId, { enabled: checked === 'true', reset: true }));
|
||||
};
|
||||
|
||||
const getResetButtonState = () => {
|
||||
switch (resetStatusRequestStatus) {
|
||||
case RequestStatus.PENDING:
|
||||
return 'pending';
|
||||
case RequestStatus.SUCCESSFUL:
|
||||
return 'finish';
|
||||
default:
|
||||
return 'default';
|
||||
}
|
||||
};
|
||||
|
||||
if (!visible) { return null; }
|
||||
|
||||
const messageKey = checked === 'true' ? 'resetAllUnitsTooltipChecked' : 'resetAllUnitsTooltipUnchecked';
|
||||
|
||||
return (
|
||||
<OverlayTrigger
|
||||
placement="right"
|
||||
overlay={(
|
||||
<Tooltip id={`tooltip-reset-${checked}`}>
|
||||
{intl.formatMessage(messages[messageKey])}
|
||||
</Tooltip>
|
||||
)}
|
||||
>
|
||||
<StatefulButton
|
||||
className="reset-units-button"
|
||||
labels={{
|
||||
default: intl.formatMessage(messages.resetAllUnits),
|
||||
pending: '',
|
||||
finish: intl.formatMessage(messages.reset),
|
||||
}}
|
||||
icons={{
|
||||
default: <Icon src={RotateLeft} />,
|
||||
pending: <Icon src={SpinnerSimple} className="icon-spin" />,
|
||||
finish: <Icon src={CheckCircleOutline} />,
|
||||
}}
|
||||
state={getResetButtonState()}
|
||||
onClick={handleResetUnits}
|
||||
disabledStates={['pending', 'finish']}
|
||||
variant="outline"
|
||||
data-testid="reset-units"
|
||||
/>
|
||||
</OverlayTrigger>
|
||||
);
|
||||
};
|
||||
|
||||
ResetUnitsButton.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
courseId: PropTypes.string.isRequired,
|
||||
checked: PropTypes.oneOf(['true', 'false']).isRequired,
|
||||
visible: PropTypes.bool,
|
||||
};
|
||||
|
||||
ResetUnitsButton.defaultProps = {
|
||||
visible: false,
|
||||
};
|
||||
|
||||
const SettingsModal = ({
|
||||
intl,
|
||||
appId,
|
||||
@@ -119,6 +202,8 @@ const SettingsModal = ({
|
||||
enableAppHelp,
|
||||
learnMoreText,
|
||||
enableReinitialize,
|
||||
allUnitsEnabledText,
|
||||
noUnitsEnabledText,
|
||||
}) => {
|
||||
const { courseId } = useContext(PagesAndResourcesContext);
|
||||
const loadingStatus = useSelector(getLoadingStatus);
|
||||
@@ -139,9 +224,15 @@ const SettingsModal = ({
|
||||
}
|
||||
}, [updateSettingsRequestStatus]);
|
||||
|
||||
const handleFormSubmit = async (values) => {
|
||||
let success = true;
|
||||
success = await dispatch(updateXpertSettings(courseId, values));
|
||||
const handleFormSubmit = async ({ enabled, checked, ...rest }) => {
|
||||
let success;
|
||||
const values = { ...rest, enabled: enabled ? checked === 'true' : undefined };
|
||||
|
||||
if (enabled) {
|
||||
success = await dispatch(updateXpertSettings(courseId, values));
|
||||
} else {
|
||||
success = await dispatch(removeXpertSettings(courseId));
|
||||
}
|
||||
|
||||
if (onSettingsSave) {
|
||||
success = success && await onSettingsSave(values);
|
||||
@@ -174,13 +265,15 @@ const SettingsModal = ({
|
||||
return (
|
||||
<Formik
|
||||
initialValues={{
|
||||
enabled: !!xpertSettings?.enabled,
|
||||
enabled: xpertSettings?.enabled !== undefined,
|
||||
checked: xpertSettings?.enabled?.toString() || 'true',
|
||||
...initialValues,
|
||||
}}
|
||||
validationSchema={
|
||||
Yup.object()
|
||||
.shape({
|
||||
enabled: Yup.boolean(),
|
||||
checked: Yup.string().oneOf(['true', 'false']),
|
||||
...validationSchema,
|
||||
})
|
||||
}
|
||||
@@ -206,6 +299,7 @@ const SettingsModal = ({
|
||||
}}
|
||||
state={submitButtonState}
|
||||
onClick={handleFormikSubmit(formikProps)}
|
||||
disabled={!formikProps.dirty}
|
||||
/>
|
||||
)}
|
||||
>
|
||||
@@ -220,7 +314,7 @@ const SettingsModal = ({
|
||||
<FormSwitchGroup
|
||||
id={`enable-${appId}-toggle`}
|
||||
name="enabled"
|
||||
onChange={(event) => formikProps.handleChange(event)}
|
||||
onChange={formikProps.handleChange}
|
||||
onBlur={formikProps.handleBlur}
|
||||
checked={formikProps.values.enabled}
|
||||
label={(
|
||||
@@ -240,6 +334,41 @@ const SettingsModal = ({
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
{(formikProps.values.enabled || configureBeforeEnable) && (
|
||||
<Form.RadioSet
|
||||
name="checked"
|
||||
onChange={formikProps.handleChange}
|
||||
onBlur={formikProps.handleBlur}
|
||||
value={formikProps.values.checked}
|
||||
>
|
||||
<Form.Radio
|
||||
className="summary-radio m-2 px-3"
|
||||
data-testid="enable-radio"
|
||||
value="true"
|
||||
>
|
||||
{allUnitsEnabledText}
|
||||
<ResetUnitsButton
|
||||
intl={intl}
|
||||
courseId={courseId}
|
||||
checked={formikProps.values.checked}
|
||||
visible={formikProps.values.checked === 'true'}
|
||||
/>
|
||||
</Form.Radio>
|
||||
<Form.Radio
|
||||
className="summary-radio m-2 px-3"
|
||||
data-testid="disable-radio"
|
||||
value="false"
|
||||
>
|
||||
{noUnitsEnabledText}
|
||||
<ResetUnitsButton
|
||||
intl={intl}
|
||||
courseId={courseId}
|
||||
checked={formikProps.values.checked}
|
||||
visible={formikProps.values.checked === 'false'}
|
||||
/>
|
||||
</Form.Radio>
|
||||
</Form.RadioSet>
|
||||
)}
|
||||
{(formikProps.values.enabled || configureBeforeEnable) && children
|
||||
&& <AppConfigFormDivider marginAdj={{ default: 0, sm: 0 }} />}
|
||||
<AppSettingsForm formikProps={formikProps} showForm={formikProps.values.enabled || configureBeforeEnable}>
|
||||
@@ -281,6 +410,8 @@ SettingsModal.propTypes = {
|
||||
enableAppLabel: PropTypes.string.isRequired,
|
||||
enableAppHelp: PropTypes.string.isRequired,
|
||||
learnMoreText: PropTypes.string.isRequired,
|
||||
allUnitsEnabledText: PropTypes.string.isRequired,
|
||||
noUnitsEnabledText: PropTypes.string.isRequired,
|
||||
configureBeforeEnable: PropTypes.bool,
|
||||
enableReinitialize: PropTypes.bool,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
.summary-radio {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
border-width: $border-width;
|
||||
border-color: $border-color;
|
||||
border-radius: $border-radius;
|
||||
border-style: solid;
|
||||
|
||||
&:has(input:checked) {
|
||||
border-width: 3px;
|
||||
border-color: theme-color("primary");
|
||||
}
|
||||
|
||||
> div {
|
||||
flex: 1;
|
||||
|
||||
> label {
|
||||
height: 80px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.reset-units-button {
|
||||
color: $link-color;
|
||||
border-width: $border-width;
|
||||
border-color: $border-color;
|
||||
border-radius: $border-radius;
|
||||
border-style: solid;
|
||||
margin-left: auto;
|
||||
}
|
||||
@@ -29,6 +29,22 @@ const messages = defineMessages({
|
||||
id: 'course-authoring.pages-resources.app-settings-modal.badge.disabled',
|
||||
defaultMessage: 'Disabled',
|
||||
},
|
||||
resetAllUnits: {
|
||||
id: 'course-authoring.pages-resources.app-settings-modal.reset-all-units',
|
||||
defaultMessage: 'Reset all units',
|
||||
},
|
||||
resetAllUnitsTooltipChecked: {
|
||||
id: 'course-authoring.pages-resources.app-settings-modal.reset-all-units-tooltip.checked',
|
||||
defaultMessage: 'Immediately reset any unit-level changes and checked "Enable summaries" on all units.',
|
||||
},
|
||||
resetAllUnitsTooltipUnchecked: {
|
||||
id: 'course-authoring.pages-resources.app-settings-modal.reset-all-units-tooltip.unchecked',
|
||||
defaultMessage: 'Immediately reset any unit-level changes and unchecked "Enable summaries" on all units.',
|
||||
},
|
||||
reset: {
|
||||
id: 'course-authoring.pages-resources.app-settings-modal.reset',
|
||||
defaultMessage: 'Reset',
|
||||
},
|
||||
errorSavingTitle: {
|
||||
id: 'course-authoring.pages-resources.app-settings-modal.save-error.title',
|
||||
defaultMessage: 'We couldn\'t apply your changes.',
|
||||
|
||||
Reference in New Issue
Block a user