This commit is contained in:
Ben Warzeski
2020-08-21 16:51:09 -04:00
parent da60ff9f1d
commit dabd923b10
27 changed files with 389 additions and 853 deletions

View File

@@ -14,11 +14,6 @@ export default class Drawer extends React.Component {
};
}
deferToNextRepaint(callback) {
window.requestAnimationFrame(() =>
window.setTimeout(callback, 0));
}
close = () => {
if (this.state.open) {
this.toggleOpen();
@@ -39,6 +34,10 @@ export default class Drawer extends React.Component {
}
};
deferToNextRepaint(callback) {
window.requestAnimationFrame(() => window.setTimeout(callback, 0));
}
render() {
return (
<div className="d-flex drawer-container">

View File

@@ -1,4 +1,3 @@
import { connect } from 'react-redux';
import React from 'react';
@@ -27,27 +26,43 @@ function RangeFilterBadge({
filterValue2,
handleBadgeClose,
}) {
return ((filterValue1 !== initialFilters[filterName1]) ||
(filterValue2 !== initialFilters[filterName2]))
&&
return ((filterValue1 !== initialFilters[filterName1])
|| (filterValue2 !== initialFilters[filterName2]))
&& (
<FilterBadge
name={displayName}
value={`${filterValue1} - ${filterValue2}`}
onClick={handleBadgeClose}
/>;
/>
);
}
RangeFilterBadge.propTypes = {
displayName: PropTypes.string.isRequired,
filterName1: PropTypes.string.isRequired,
filterValue1: PropTypes.string.isRequired,
filterName2: PropTypes.string.isRequired,
filterValue2: PropTypes.string.isRequired,
handleBadgeClose: PropTypes.func.isRequired,
};
function SingleValueFilterBadge({
displayName, filterName, filterValue, handleBadgeClose,
}) {
return (filterValue !== initialFilters[filterName]) &&
return (filterValue !== initialFilters[filterName])
&& (
<FilterBadge
name={displayName}
value={filterValue}
onClick={handleBadgeClose}
/>;
/>
);
}
SingleValueFilterBadge.propTypes = {
displayName: PropTypes.string.isRequired,
filterName: PropTypes.string.isRequired,
filterValue: PropTypes.string.isRequired,
handleBadgeClose: PropTypes.func.isRequired,
};
function FilterBadges({
assignment,
@@ -150,4 +165,3 @@ FilterBadges.propTypes = {
courseGradeMax: PropTypes.string,
handleFilterBadgeClose: PropTypes.func.isRequired,
};

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/sort-comp, react/button-has-type */
import React from 'react';
import PropTypes from 'prop-types';
import {
@@ -24,7 +25,6 @@ import { formatDateForDisplay } from '../../data/actions/utils';
import initialFilters from '../../data/constants/filters';
import ConnectedFilterBadges from '../FilterBadges';
const DECIMAL_PRECISION = 2;
const GRADE_OVERRIDE_HISTORY_COLUMNS = [{ label: 'Date', key: 'date' }, { label: 'Grader', key: 'grader' },
{ label: 'Reason', key: 'reason' },
@@ -166,8 +166,7 @@ export default class Gradebook extends React.Component {
};
handleAssignmentFilterChange = (assignment) => {
const selectedFilterOption = this.props.assignmentFilterOptions.find(assig =>
assig.label === assignment);
const selectedFilterOption = this.props.assignmentFilterOptions.find(assig => assig.label === assignment);
const { type, id } = selectedFilterOption || {};
const typedValue = { label: assignment, type, id };
this.props.updateAssignmentFilter(typedValue);
@@ -396,7 +395,8 @@ export default class Gradebook extends React.Component {
onClick={() => this.setNewModalState(entry, subsection)}
>
{this.roundGrade(subsection.percent * 100)}%
</button>);
</button>
);
}
return acc;
}, {});
@@ -550,47 +550,45 @@ export default class Gradebook extends React.Component {
href={this.lmsInstructorDashboardUrl(this.props.courseId)}
className="mb-3"
>
<span aria-hidden="true">{'<< '}</span> {'Back to Dashboard'}
<span aria-hidden="true">{'<< '}</span> Back to Dashboard
</a>
<h1>Gradebook</h1>
<h3> {this.props.courseId}</h3>
{this.props.areGradesFrozen &&
<div className="alert alert-warning" role="alert" >
{this.props.areGradesFrozen
&& (
<div className="alert alert-warning" role="alert">
The grades for this course are now frozen. Editing of grades is no longer allowed.
</div>
}
{(this.props.canUserViewGradebook === false) &&
<div className="alert alert-warning" role="alert" >
)}
{(this.props.canUserViewGradebook === false)
&& (
<div className="alert alert-warning" role="alert">
You are not authorized to view the gradebook for this course.
</div>
}
)}
<Tabs defaultActiveKey="grades">
<Tab eventKey="grades" title="Grades">
<h4>Step 1: Filter the Grade Report</h4>
<div className="d-flex justify-content-between" >
<div className="d-flex justify-content-between">
{this.props.showSpinner && <div className="spinner-overlay"><Icon className="fa fa-spinner fa-spin fa-5x color-black" /></div>}
<Button className="btn-primary align-self-start" onClick={toggleFilterDrawer}><FontAwesomeIcon icon={faFilter} /> Edit Filters</Button>
<div>
<SearchField
onSubmit={value =>
this.props.searchForUser(
this.props.courseId,
value,
this.props.selectedCohort,
this.props.selectedTrack,
this.props.selectedAssignmentType,
)
}
onSubmit={value => this.props.searchForUser(
this.props.courseId,
value,
this.props.selectedCohort,
this.props.selectedTrack,
this.props.selectedAssignmentType,
)}
inputLabel="Search for a learner"
onChange={filterValue => this.setState({ filterValue })}
onClear={() =>
this.props.getUserGrades(
this.props.courseId,
this.props.selectedCohort,
this.props.selectedTrack,
this.props.selectedAssignmentType,
)
}
onClear={() => this.props.getUserGrades(
this.props.courseId,
this.props.selectedCohort,
this.props.selectedTrack,
this.props.selectedAssignmentType,
)}
value={this.state.filterValue}
/>
<small className="form-text text-muted search-help-text">Search by username, email, or student key</small>
@@ -610,21 +608,22 @@ export default class Gradebook extends React.Component {
dialog={this.getCourseGradeFilterAlertDialog()}
dismissible={false}
open={
!this.state.isMinCourseGradeFilterValid ||
!this.state.isMaxCourseGradeFilterValid
!this.state.isMinCourseGradeFilterValid
|| !this.state.isMaxCourseGradeFilterValid
}
/>
<h4>Step 2: View or Modify Individual Grades</h4>
{this.props.totalUsersCount ?
<div>
Showing
<span className="font-weight-bold"> {this.props.filteredUsersCount} </span>
of
<span className="font-weight-bold"> {this.props.totalUsersCount} </span>
total learners
</div> :
null
}
{this.props.totalUsersCount
? (
<div>
Showing
<span className="font-weight-bold"> {this.props.filteredUsersCount} </span>
of
<span className="font-weight-bold"> {this.props.totalUsersCount} </span>
total learners
</div>
)
: null}
<div className="d-flex justify-content-between align-items-center mb-2">
<InputSelect
label="Score View:"
@@ -727,10 +726,11 @@ export default class Gradebook extends React.Component {
|| this.props.gradeOriginalPossibleGraded}
</span>),
}]}
/>)}
/>
)}
<div>Showing most recent actions (max 5). To see more, please contact
support.
support.
</div>
<div>Note: Once you save, your changes will be visible to students.</div>
</div>
@@ -814,16 +814,17 @@ export default class Gradebook extends React.Component {
]}
className="table-striped"
/>
</Tab>)}
</Tab>
)}
</Tabs>
</div>
)}
initiallyOpen={false}
title={
<React.Fragment>
title={(
<>
<FontAwesomeIcon icon={faFilter} /> Filter By...
</React.Fragment>
}
</>
)}
>
<Collapsible title="Assignments" open className="filter-group mb-3">
<div>

View File

@@ -2,7 +2,6 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Button } from '@edx/paragon';
export default function PageButtons({
prevPage, nextPage, selectedTrack, selectedCohort, selectedAssignmentType,
getPrevNextGrades, match,
@@ -16,14 +15,13 @@ export default function PageButtons({
style={{ margin: '20px' }}
variant="outline-primary"
disabled={!prevPage}
onClick={() =>
getPrevNextGrades(
prevPage,
match.params.courseId,
selectedCohort,
selectedTrack,
selectedAssignmentType,
)}
onClick={() => getPrevNextGrades(
prevPage,
match.params.courseId,
selectedCohort,
selectedTrack,
selectedAssignmentType,
)}
>
Previous Page
</Button>
@@ -31,14 +29,13 @@ export default function PageButtons({
style={{ margin: '20px' }}
variant="outline-primary"
disabled={!nextPage}
onClick={() =>
getPrevNextGrades(
nextPage,
match.params.courseId,
selectedCohort,
selectedTrack,
selectedAssignmentType,
)}
onClick={() => getPrevNextGrades(
nextPage,
match.params.courseId,
selectedCohort,
selectedTrack,
selectedAssignmentType,
)}
>
Next Page
</Button>
@@ -76,4 +73,3 @@ PageButtons.propTypes = {
name: PropTypes.string,
}),
};

View File

@@ -18,7 +18,9 @@ import {
} from '../../data/actions/grades';
import { fetchCohorts } from '../../data/actions/cohorts';
import { fetchTracks } from '../../data/actions/tracks';
import { initializeFilters, resetFilters, updateAssignmentFilter, updateAssignmentLimits, updateCourseGradeFilter } from '../../data/actions/filters';
import {
initializeFilters, resetFilters, updateAssignmentFilter, updateAssignmentLimits, updateCourseGradeFilter,
} from '../../data/actions/filters';
import stateHasMastersTrack from '../../data/selectors/tracks';
import {
getBulkManagementHistory,
@@ -37,7 +39,7 @@ import LmsApiService from '../../data/services/LmsApiService';
function shouldShowSpinner(state) {
if (state.roles.canUserViewGradebook === true) {
return state.grades.showSpinner;
} else if (state.roles.canUserViewGradebook === false) {
} if (state.roles.canUserViewGradebook === false) {
return false;
} // canUserViewGradebook === null
return true;
@@ -104,12 +106,12 @@ const mapStateToProps = (state, ownProps) => (
courseGradeMin: formatMinCourseGrade(state.filters.courseGradeMin),
courseGradeMax: formatMaxCourseGrade(state.filters.courseGradeMax),
}),
bulkImportError: state.grades.bulkManagement &&
state.grades.bulkManagement.errorMessages ?
`Errors while processing: ${state.grades.bulkManagement.errorMessages.join(', ')}` :
'',
uploadSuccess: !!(state.grades.bulkManagement &&
state.grades.bulkManagement.uploadSuccess),
bulkImportError: state.grades.bulkManagement
&& state.grades.bulkManagement.errorMessages
? `Errors while processing: ${state.grades.bulkManagement.errorMessages.join(', ')}`
: '',
uploadSuccess: !!(state.grades.bulkManagement
&& state.grades.bulkManagement.uploadSuccess),
showBulkManagement: stateHasMastersTrack(state) && state.config.bulkManagementAvailable,
bulkManagementHistory: getBulkManagementHistory(state),
totalUsersCount: state.grades.totalUsersCount,

View File

@@ -38,4 +38,3 @@ export {
gotAssignmentTypes,
errorFetchingAssignmentTypes,
};

View File

@@ -1,5 +1,7 @@
import initialFilters from '../constants/filters';
import { INITIALIZE_FILTERS, RESET_FILTERS, UPDATE_ASSIGNMENT_FILTER, UPDATE_ASSIGNMENT_LIMITS, UPDATE_COURSE_GRADE_LIMITS } from '../constants/actionTypes/filters';
import {
INITIALIZE_FILTERS, RESET_FILTERS, UPDATE_ASSIGNMENT_FILTER, UPDATE_ASSIGNMENT_LIMITS, UPDATE_COURSE_GRADE_LIMITS,
} from '../constants/actionTypes/filters';
const initializeFilters = ({
assignment = initialFilters.assignment,

View File

@@ -26,7 +26,9 @@ import {
} from '../constants/actionTypes/grades';
import LmsApiService from '../services/LmsApiService';
import { sortAlphaAsc, formatDateForDisplay } from './utils';
import { formatMaxAssignmentGrade, formatMinAssignmentGrade, formatMaxCourseGrade, formatMinCourseGrade } from '../selectors/grades';
import {
formatMaxAssignmentGrade, formatMinAssignmentGrade, formatMaxCourseGrade, formatMinCourseGrade,
} from '../selectors/grades';
import { getFilters } from '../selectors/filters';
const defaultAssignmentFilter = 'All';
@@ -108,7 +110,6 @@ const uploadOverrideFailure = (courseId, error) => ({
payload: { error },
});
const toggleGradeFormat = formatType => ({ type: TOGGLE_GRADE_FORMAT, formatType });
const filterAssignmentType = filterType => (
@@ -191,27 +192,26 @@ const doneViewingAssignment = () => dispatch => dispatch({
type: DONE_VIEWING_ASSIGNMENT,
});
const fetchGradeOverrideHistory = (subsectionId, userId) => (
dispatch =>
LmsApiService.fetchGradeOverrideHistory(subsectionId, userId)
.then(response => response.data)
.then((data) => {
dispatch(gotGradeOverrideHistory({
overrideHistory: formatGradeOverrideForDisplay(data.history),
currentEarnedAllOverride: data.override ? data.override.earned_all_override : null,
currentPossibleAllOverride: data.override ? data.override.possible_all_override : null,
currentEarnedGradedOverride: data.override ? data.override.earned_graded_override : null,
currentPossibleGradedOverride: data.override ?
data.override.possible_graded_override : null,
originalGradeEarnedAll: data.original_grade ? data.original_grade.earned_all : null,
originalGradePossibleAll: data.original_grade ? data.original_grade.possible_all : null,
originalGradeEarnedGraded: data.original_grade ? data.original_grade.earned_graded : null,
originalGradePossibleGraded: data.original_grade ?
data.original_grade.possible_graded : null,
}));
})
.catch(() => {
dispatch(errorFetchingGradeOverrideHistory());
})
dispatch => LmsApiService.fetchGradeOverrideHistory(subsectionId, userId)
.then(response => response.data)
.then((data) => {
dispatch(gotGradeOverrideHistory({
overrideHistory: formatGradeOverrideForDisplay(data.history),
currentEarnedAllOverride: data.override ? data.override.earned_all_override : null,
currentPossibleAllOverride: data.override ? data.override.possible_all_override : null,
currentEarnedGradedOverride: data.override ? data.override.earned_graded_override : null,
currentPossibleGradedOverride: data.override
? data.override.possible_graded_override : null,
originalGradeEarnedAll: data.original_grade ? data.original_grade.earned_all : null,
originalGradePossibleAll: data.original_grade ? data.original_grade.possible_all : null,
originalGradeEarnedGraded: data.original_grade ? data.original_grade.earned_graded : null,
originalGradePossibleGraded: data.original_grade
? data.original_grade.possible_graded : null,
}));
})
.catch(() => {
dispatch(errorFetchingGradeOverrideHistory());
})
);
const fetchMatchingUserGrades = (
@@ -293,11 +293,10 @@ const submitFileUploadFormData = (courseId, formData) => (
);
const fetchBulkUpgradeHistory = courseId => (
dispatch =>
// todo add loading effect
LmsApiService.fetchGradeBulkOperationHistory(courseId).then((response) => {
dispatch(gotBulkHistory(response));
}).catch(() => dispatch(bulkHistoryError()))
// todo add loading effect
dispatch => LmsApiService.fetchGradeBulkOperationHistory(courseId).then(
(response) => { dispatch(gotBulkHistory(response)); },
).catch(() => dispatch(bulkHistoryError()))
);
const updateGradesIfAssignmentGradeFiltersSet = (

View File

@@ -14,7 +14,6 @@ import {
} from '../constants/actionTypes/grades';
import { sortAlphaAsc } from './utils';
const mockStore = configureMockStore([thunk]);
jest.mock('@edx/frontend-platform/auth');

View File

@@ -15,7 +15,6 @@ import { STARTED_FETCHING_TRACKS } from '../constants/actionTypes/tracks';
import { STARTED_FETCHING_COHORTS } from '../constants/actionTypes/cohorts';
import { STARTED_FETCHING_ASSIGNMENT_TYPES } from '../constants/actionTypes/assignmentTypes';
const mockStore = configureMockStore([thunk]);
jest.mock('@edx/frontend-platform/auth');

View File

@@ -27,4 +27,3 @@ const sortAlphaAsc = (gradeRowA, gradeRowB) => {
};
export { sortAlphaAsc, formatDateForDisplay };

View File

@@ -9,4 +9,3 @@ export {
ERROR_FETCHING_ASSIGNMENT_TYPES,
GOT_ARE_GRADES_FROZEN,
};

View File

@@ -11,7 +11,6 @@ const initialState = {
errorFetching: false,
};
const assignmentTypes = (state = initialState, action) => {
switch (action.type) {
case GOT_ASSIGNMENT_TYPES:
@@ -45,4 +44,3 @@ const assignmentTypes = (state = initialState, action) => {
};
export default assignmentTypes;

View File

@@ -10,7 +10,6 @@ const initialState = {
errorFetching: false,
};
const cohorts = (state = initialState, action) => {
switch (action.type) {
case GOT_COHORTS:
@@ -37,4 +36,3 @@ const cohorts = (state = initialState, action) => {
};
export default cohorts;

View File

@@ -1,6 +1,8 @@
import { GOT_GRADES, FILTER_BY_ASSIGNMENT_TYPE } from '../constants/actionTypes/grades';
import { INITIALIZE_FILTERS, UPDATE_ASSIGNMENT_FILTER, UPDATE_ASSIGNMENT_LIMITS, UPDATE_COURSE_GRADE_LIMITS, RESET_FILTERS } from '../constants/actionTypes/filters';
import {
INITIALIZE_FILTERS, UPDATE_ASSIGNMENT_FILTER, UPDATE_ASSIGNMENT_LIMITS, UPDATE_COURSE_GRADE_LIMITS, RESET_FILTERS,
} from '../constants/actionTypes/filters';
import initialFilters from '../constants/filters';
@@ -15,8 +17,8 @@ const reducer = (state = initialState, action) => {
...state,
assignmentType: action.filterType,
assignment: (
action.filterType !== '' &&
(state.assignment || {}).type !== action.filterType)
action.filterType !== ''
&& (state.assignment || {}).type !== action.filterType)
? '' : state.assignment,
};
case INITIALIZE_FILTERS:

View File

@@ -10,7 +10,6 @@ const initialState = {
errorFetching: false,
};
const tracks = (state = initialState, action) => {
switch (action.type) {
case GOT_TRACKS:
@@ -37,4 +36,3 @@ const tracks = (state = initialState, action) => {
};
export default tracks;

View File

@@ -1,4 +1,3 @@
const getCohorts = state => state.cohorts.results || [];
const getCohortById = (state, selectedCohortId) => {
@@ -6,7 +5,6 @@ const getCohortById = (state, selectedCohortId) => {
return cohort;
};
const getCohortNameById = (state, selectedCohortId) =>
(getCohortById(state, selectedCohortId) || {}).name;
const getCohortNameById = (state, selectedCohortId) => (getCohortById(state, selectedCohortId) || {}).name;
export { getCohortById, getCohortNameById, getCohorts };

View File

@@ -1,7 +1,6 @@
const getFilters = state => state.filters || {};
const getAssignmentsFromResultsSubstate = results =>
(results[0] || {}).section_breakdown || [];
const getAssignmentsFromResultsSubstate = results => (results[0] || {}).section_breakdown || [];
const selectableAssignments = (state) => {
const selectedAssignmentType = getFilters(state).assignmentType;
@@ -20,14 +19,12 @@ const chooseRelevantAssignmentData = assignment => ({
id: assignment.module_id,
});
const selectableAssignmentLabels = state =>
selectableAssignments(state).map(chooseRelevantAssignmentData);
const selectableAssignmentLabels = state => selectableAssignments(state).map(chooseRelevantAssignmentData);
const typeOfSelectedAssignment = (state) => {
const selectedAssignmentLabel = getFilters(state).assignment;
const sectionBreakdown = (state.grades.results[0] || {}).section_breakdown || [];
const selectedAssignment = sectionBreakdown.find(section =>
section.label === selectedAssignmentLabel);
const selectedAssignment = sectionBreakdown.find(section => section.label === selectedAssignmentLabel);
return selectedAssignment && selectedAssignment.category;
};

View File

@@ -33,10 +33,8 @@ const transformHistoryEntry = (historyRow) => {
...rest,
};
};
const getBulkManagementHistoryFromState = state =>
state.grades.bulkManagement.history || [];
const getBulkManagementHistory = state =>
getBulkManagementHistoryFromState(state).map(transformHistoryEntry);
const getBulkManagementHistoryFromState = state => state.grades.bulkManagement.history || [];
const getBulkManagementHistory = state => getBulkManagementHistoryFromState(state).map(transformHistoryEntry);
const headingMapper = (category, label = 'All') => {
const filters = {
@@ -82,13 +80,12 @@ const getHeadings = (state) => {
return headingMapper(type, assignment)(assignments);
};
const composeFilters = (...predicates) => (percentGrade, options = {}) =>
predicates.reduce((accum, predicate) => {
if (predicate(percentGrade, options)) {
return null;
}
return accum;
}, percentGrade);
const composeFilters = (...predicates) => (percentGrade, options = {}) => predicates.reduce((accum, predicate) => {
if (predicate(percentGrade, options)) {
return null;
}
return accum;
}, percentGrade);
const percentGradeIsMax = percentGrade => (
percentGrade === '100'

View File

@@ -1,7 +1,6 @@
const compose = (...fns) => {
const [firstFunc, ...rest] = fns.reverse();
return (...args) =>
rest.reduce((accum, fn) => fn(accum), firstFunc(...args));
return (...args) => rest.reduce((accum, fn) => fn(accum), firstFunc(...args));
};
const getTracks = state => state.tracks.results || [];

View File

@@ -1,8 +1,9 @@
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { configuration } from '../../config';
class LmsApiService {
static baseUrl = configuration.LMS_BASE_URL;
static pageSize = 25
static fetchGradebookData(courseId, searchText, cohort, track, options = {}) {
@@ -96,8 +97,8 @@ class LmsApiService {
static getGradeExportCsvUrl(courseId, options = {}) {
const queryParams = ['track', 'cohort', 'assignment', 'assignmentType', 'assignmentGradeMax',
'assignmentGradeMin', 'courseGradeMin', 'courseGradeMax']
.filter(opt => options[opt] &&
options[opt] !== 'All')
.filter(opt => options[opt]
&& options[opt] !== 'All')
.map(opt => `${opt}=${encodeURIComponent(options[opt])}`)
.join('&');
return `${LmsApiService.baseUrl}/api/bulk_grades/course/${courseId}/?${queryParams}`;
@@ -106,8 +107,8 @@ class LmsApiService {
static getInterventionExportCsvUrl(courseId, options = {}) {
const queryParams = ['track', 'cohort', 'assignment', 'assignmentType', 'assignmentGradeMax',
'assignmentGradeMin', 'courseGradeMin', 'courseGradeMax']
.filter(opt => options[opt] &&
options[opt] !== 'All')
.filter(opt => options[opt]
&& options[opt] !== 'All')
.map(opt => `${opt}=${encodeURIComponent(options[opt])}`)
.join('&');
return `${LmsApiService.baseUrl}/api/bulk_grades/course/${courseId}/intervention?${queryParams}`;

View File

@@ -90,7 +90,6 @@ const eventsMap = {
const segmentMiddleware = createMiddleware(eventsMap, Segment());
const store = createStore(
reducers,
composeWithDevTools(applyMiddleware(thunkMiddleware, loggerMiddleware, segmentMiddleware)),

View File

@@ -1,5 +1,3 @@
console.log("index.jsx");
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
@@ -49,51 +47,47 @@ const socialLinks = [
},
];
const App = () => {
console.log("App");
return (
<IntlProvider locale='en'>
<Provider store={store}>
<Router>
{ console.log("App Render") }
<div>
<Header />
<main>
<Switch>
<Route exact path="/:courseId" component={GradebookPage} />
</Switch>
</main>
<SiteFooter
siteName={process.env.SITE_NAME}
siteLogo={FooterLogo}
marketingSiteBaseUrl={process.env.MARKETING_SITE_BASE_URL}
supportUrl={process.env.SUPPORT_URL}
contactUrl={process.env.CONTACT_URL}
openSourceUrl={process.env.OPEN_SOURCE_URL}
termsOfServiceUrl={process.env.TERMS_OF_SERVICE_URL}
privacyPolicyUrl={process.env.PRIVACY_POLICY_URL}
appleAppStoreUrl={process.env.APPLE_APP_STORE_URL}
googlePlayUrl={process.env.GOOGLE_PLAY_URL}
socialLinks={socialLinks}
enterpriseMarketingLink={{
url: process.env.ENTERPRISE_MARKETING_URL,
queryParams: {
utm_source: process.env.ENTERPRISE_MARKETING_UTM_SOURCE,
utm_campaign: process.env.ENTERPRISE_MARKETING_UTM_CAMPAIGN,
utm_medium: process.env.ENTERPRISE_MARKETING_FOOTER_UTM_MEDIUM,
},
}}
/>
</div>
</Router>
</Provider>
</IntlProvider>
);
}
const App = () => (
<IntlProvider locale="en">
<Provider store={store}>
<Router>
<div>
<Header />
<main>
<Switch>
<Route exact path="/:courseId" component={GradebookPage} />
</Switch>
</main>
<SiteFooter
siteName={process.env.SITE_NAME}
siteLogo={FooterLogo}
marketingSiteBaseUrl={process.env.MARKETING_SITE_BASE_URL}
supportUrl={process.env.SUPPORT_URL}
contactUrl={process.env.CONTACT_URL}
openSourceUrl={process.env.OPEN_SOURCE_URL}
termsOfServiceUrl={process.env.TERMS_OF_SERVICE_URL}
privacyPolicyUrl={process.env.PRIVACY_POLICY_URL}
appleAppStoreUrl={process.env.APPLE_APP_STORE_URL}
googlePlayUrl={process.env.GOOGLE_PLAY_URL}
socialLinks={socialLinks}
enterpriseMarketingLink={{
url: process.env.ENTERPRISE_MARKETING_URL,
queryParams: {
utm_source: process.env.ENTERPRISE_MARKETING_UTM_SOURCE,
utm_campaign: process.env.ENTERPRISE_MARKETING_UTM_CAMPAIGN,
utm_medium: process.env.ENTERPRISE_MARKETING_FOOTER_UTM_MEDIUM,
},
}}
/>
</div>
</Router>
</Provider>
</IntlProvider>
);
subscribe(APP_READY, () => {
ReactDOM.render(<App />, document.getElementById('root'));
})
});
initialize({
messages: [
@@ -101,5 +95,3 @@ initialize({
],
requireAuthenticatedUser: true,
});
console.log("end of index.jsx");