add Studio base URL to configuration and add Proctored Exam Settings component (#4)
This commit is contained in:
@@ -9,6 +9,7 @@ LMS_BASE_URL='http://localhost:18000'
|
||||
LOGIN_URL='http://localhost:18000/login'
|
||||
LOGOUT_URL='http://localhost:18000/logout'
|
||||
MARKETING_SITE_BASE_URL='http://localhost:18000'
|
||||
STUDIO_BASE_URL='http://localhost:18010'
|
||||
ORDER_HISTORY_URL='localhost:1996/orders'
|
||||
PORT=2001
|
||||
REFRESH_ACCESS_TOKEN_ENDPOINT='http://localhost:18000/login_refresh'
|
||||
|
||||
@@ -7,6 +7,7 @@ LANGUAGE_PREFERENCE_COOKIE_NAME='openedx-language-preference'
|
||||
LMS_BASE_URL='http://localhost:18000'
|
||||
LOGIN_URL='http://localhost:18000/login'
|
||||
LOGOUT_URL='http://localhost:18000/logout'
|
||||
STUDIO_BASE_URL='http://localhost:18010'
|
||||
MARKETING_SITE_BASE_URL='http://localhost:18000'
|
||||
ORDER_HISTORY_URL='localhost:1996/orders'
|
||||
PORT=2001
|
||||
|
||||
11
.eslintrc.js
11
.eslintrc.js
@@ -1,3 +1,12 @@
|
||||
const { createConfig } = require('@edx/frontend-build');
|
||||
|
||||
module.exports = createConfig('eslint');
|
||||
module.exports = createConfig('eslint',
|
||||
{
|
||||
"rules": {
|
||||
"jsx-a11y/label-has-associated-control": [ 2, {
|
||||
"controlComponents": ["Input"],
|
||||
}],
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
13727
package-lock.json
generated
13727
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -36,7 +36,7 @@
|
||||
"dependencies": {
|
||||
"@edx/frontend-component-footer": "10.0.9",
|
||||
"@edx/frontend-component-header": "2.0.5",
|
||||
"@edx/frontend-platform": "1.1.14",
|
||||
"@edx/frontend-platform": "1.5.2",
|
||||
"@edx/paragon": "7.2.1",
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.28",
|
||||
"@fortawesome/free-brands-svg-icons": "5.11.2",
|
||||
|
||||
18
src/data/services/StudioApiService.js
Normal file
18
src/data/services/StudioApiService.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { getConfig, ensureConfig } from '@edx/frontend-platform';
|
||||
|
||||
ensureConfig([
|
||||
'STUDIO_BASE_URL',
|
||||
], 'Studio API service');
|
||||
|
||||
const studioBaseUrl = getConfig().STUDIO_BASE_URL;
|
||||
|
||||
class StudioApiService {
|
||||
static getProctoredExamSettingsData(courseID) {
|
||||
const apiClient = getAuthenticatedHttpClient();
|
||||
const url = `${studioBaseUrl}/api/contentstore/v1/proctored_exam_settings/${courseID}`;
|
||||
return apiClient.get(url);
|
||||
}
|
||||
}
|
||||
|
||||
export default StudioApiService;
|
||||
@@ -6,12 +6,14 @@ import {
|
||||
import { AppProvider, ErrorPage } from '@edx/frontend-platform/react';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
|
||||
import Header, { messages as headerMessages } from '@edx/frontend-component-header';
|
||||
import Footer, { messages as footerMessages } from '@edx/frontend-component-footer';
|
||||
|
||||
import appMessages from './i18n';
|
||||
import ExamplePage from './example/ExamplePage';
|
||||
import ProctoredExamSettings from './proctored-exam-settings/ProctoredExamSettings';
|
||||
|
||||
import './index.scss';
|
||||
import './assets/favicon.ico';
|
||||
@@ -20,7 +22,14 @@ subscribe(APP_READY, () => {
|
||||
ReactDOM.render(
|
||||
<AppProvider>
|
||||
<Header />
|
||||
<ExamplePage />
|
||||
<Switch>
|
||||
<Route path="/proctored-exam-settings">
|
||||
<ProctoredExamSettings />
|
||||
</Route>
|
||||
<Route path="/example">
|
||||
<ExamplePage />
|
||||
</Route>
|
||||
</Switch>
|
||||
<Footer />
|
||||
</AppProvider>,
|
||||
document.getElementById('root'),
|
||||
@@ -37,4 +46,5 @@ initialize({
|
||||
headerMessages,
|
||||
footerMessages,
|
||||
],
|
||||
requireAuthenticatedUser: true,
|
||||
});
|
||||
|
||||
151
src/proctored-exam-settings/ProctoredExamSettings.jsx
Normal file
151
src/proctored-exam-settings/ProctoredExamSettings.jsx
Normal file
@@ -0,0 +1,151 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Button, CheckBox, Input, ValidationFormGroup,
|
||||
} from '@edx/paragon';
|
||||
|
||||
import StudioApiService from '../data/services/StudioApiService';
|
||||
|
||||
function ExamSettings() {
|
||||
const [enableProctoredExams, setEnableProctoredExams] = useState(true);
|
||||
const [allowOptingOut, setAllowOptingOut] = useState(false);
|
||||
const [proctoringProvider, setProctoringProvider] = useState('');
|
||||
const [availableProctoringProviders, setAvailableProctoringProviders] = useState([]);
|
||||
// TODO: we'll probably want to hide this field when proctortrack is not selected; currently,
|
||||
// this causes some errors in the browser console
|
||||
const [proctortrackEscalationEmail, setProctortrackEscalationEmail] = useState(null);
|
||||
const [createZendeskTickets, setCreateZendeskTickets] = useState(false);
|
||||
|
||||
function onEnableProctoredExamsChange(event) {
|
||||
setEnableProctoredExams(event);
|
||||
}
|
||||
|
||||
function onAllowOptingOutChange(event) {
|
||||
setAllowOptingOut(event);
|
||||
}
|
||||
|
||||
function onCreateZendeskTicketsChange(event) {
|
||||
setAllowOptingOut(event);
|
||||
}
|
||||
|
||||
function onProctoringProviderChange(event) {
|
||||
setProctoringProvider(event.target.value);
|
||||
}
|
||||
|
||||
function onProctortrackEscalationEmailChange(event) {
|
||||
setProctortrackEscalationEmail(event.target.value);
|
||||
}
|
||||
|
||||
function onButtonClick() {
|
||||
// TODO: implement POST
|
||||
}
|
||||
|
||||
function getProctoringProviderOptions(providers) {
|
||||
return providers.reduce(
|
||||
(accumulator, currentValue) => {
|
||||
accumulator.push({ value: currentValue, label: currentValue }); return accumulator;
|
||||
}, [],
|
||||
);
|
||||
}
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
StudioApiService.getProctoredExamSettingsData('course-v1:edX+DemoX+Demo_Course')
|
||||
.then(
|
||||
response => {
|
||||
const proctoredExamSettings = response.data.proctored_exam_settings;
|
||||
|
||||
setEnableProctoredExams(proctoredExamSettings.enable_proctored_exams);
|
||||
setAllowOptingOut(proctoredExamSettings.allow_proctoring_opt_out);
|
||||
setProctoringProvider(proctoredExamSettings.proctoring_provider);
|
||||
setAvailableProctoringProviders(response.data.available_proctoring_providers);
|
||||
setProctortrackEscalationEmail(proctoredExamSettings.proctoring_escalation_email);
|
||||
setCreateZendeskTickets(proctoredExamSettings.create_zendesk_tickets);
|
||||
},
|
||||
);
|
||||
// TODO: handle error
|
||||
}, [],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<h2 className="mb-1">
|
||||
Proctored Exam Settings
|
||||
</h2>
|
||||
<ValidationFormGroup
|
||||
for="enableProctoredExams"
|
||||
helpText="If checked, proctored exams are enabled in your course."
|
||||
>
|
||||
<CheckBox
|
||||
id="enableProctoredExams"
|
||||
name="enable_proctored_exams"
|
||||
label="Enable Proctored Exams"
|
||||
checked={enableProctoredExams}
|
||||
onChange={onEnableProctoredExamsChange}
|
||||
/>
|
||||
</ValidationFormGroup>
|
||||
<ValidationFormGroup
|
||||
for="allowingOptingOut"
|
||||
helpText="If checked, learners can choose to take proctored exams without proctoring. If not checked, all learners must take the exam with proctoring."
|
||||
>
|
||||
<CheckBox
|
||||
id="allowingOptingOut"
|
||||
name="allow_opting_out"
|
||||
label="Allow Opting Out of Proctored Exams"
|
||||
checked={allowOptingOut}
|
||||
onChange={onAllowOptingOutChange}
|
||||
/>
|
||||
</ValidationFormGroup>
|
||||
<ValidationFormGroup
|
||||
for="proctoringProvider"
|
||||
helpText="Select the proctoring provider you want to use for this course run."
|
||||
>
|
||||
<label htmlFor="proctoringProvider">Proctoring Provider</label>
|
||||
<Input
|
||||
type="select"
|
||||
id="proctoringProvider"
|
||||
name="proctoring_provider"
|
||||
options={getProctoringProviderOptions(availableProctoringProviders)}
|
||||
value={proctoringProvider}
|
||||
onChange={onProctoringProviderChange}
|
||||
/>
|
||||
</ValidationFormGroup>
|
||||
<ValidationFormGroup
|
||||
for="proctortrackEscalationEmail"
|
||||
helpText="Required if 'proctortrack' is selected as your proctoring provider. Enter an email address to be contacted by the support team whenever there are escalations (e.g. appeals, delayed reviews, etc.)."
|
||||
>
|
||||
<label htmlFor="proctortrackEscalationEmail">Proctortrack Escalation Email</label>
|
||||
<Input
|
||||
type="text"
|
||||
id="proctortrackEscalationEmail"
|
||||
name="proctortrack_escalation_email"
|
||||
value={proctortrackEscalationEmail}
|
||||
onChange={onProctortrackEscalationEmailChange}
|
||||
/>
|
||||
</ValidationFormGroup>
|
||||
<ValidationFormGroup
|
||||
for="createZendeskTickets"
|
||||
helpText="If checked, a Zendesk ticket will be created for suspicious attempts."
|
||||
>
|
||||
<CheckBox
|
||||
id="createZendeskTickets"
|
||||
name="create_zendesk_tickets"
|
||||
label="Create Zendesk Tickets For Suspicious Exam Attempts"
|
||||
checked={createZendeskTickets}
|
||||
onChange={onCreateZendeskTicketsChange}
|
||||
/>
|
||||
</ValidationFormGroup>
|
||||
<Button
|
||||
className="btn-primary mb-3"
|
||||
onClick={onButtonClick}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
ExamSettings.propTypes = {};
|
||||
|
||||
ExamSettings.defaultProps = {};
|
||||
|
||||
export default ExamSettings;
|
||||
Reference in New Issue
Block a user