incorporate leangseu rubric and navigation PR
This commit is contained in:
60
src/components/ResponseDisplay.jsx
Normal file
60
src/components/ResponseDisplay.jsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import {
|
||||
Card,
|
||||
} from '@edx/paragon';
|
||||
|
||||
import createDOMPurify from 'dompurify';
|
||||
|
||||
import parse from 'html-react-parser';
|
||||
|
||||
import selectors from 'data/selectors';
|
||||
|
||||
/**
|
||||
* <ResponseDisplay />
|
||||
*/
|
||||
export class ResponseDisplay extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.purify = createDOMPurify(window);
|
||||
}
|
||||
|
||||
get textContent() {
|
||||
return parse(this.purify.sanitize(this.props.response.text));
|
||||
}
|
||||
|
||||
get hasResponse() {
|
||||
return this.props.response !== undefined;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Card className="response-card">
|
||||
{this.hasResponse && (
|
||||
<Card.Body>
|
||||
{this.textContent}
|
||||
</Card.Body>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ResponseDisplay.defaultProps = {
|
||||
};
|
||||
ResponseDisplay.propTypes = {
|
||||
response: PropTypes.shape({
|
||||
text: PropTypes.string,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
export const mapStateToProps = (state) => ({
|
||||
response: selectors.grading.selectedResponse(state),
|
||||
});
|
||||
|
||||
export const mapDispatchToProps = {
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ResponseDisplay);
|
||||
@@ -85,7 +85,7 @@ export class ListView extends React.Component {
|
||||
},
|
||||
{
|
||||
Header: 'Grade',
|
||||
accessor: 'grade',
|
||||
accessor: 'score',
|
||||
Cell: this.formatGrade,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -6,33 +6,51 @@ import {
|
||||
ActionRow,
|
||||
Button,
|
||||
} from '@edx/paragon';
|
||||
import { Edit } from '@edx/paragon/icons';
|
||||
|
||||
import actions from 'data/actions';
|
||||
import selectors from 'data/selectors';
|
||||
|
||||
import StatusBadge from 'components/StatusBadge';
|
||||
import SubmissionNavigation from './SubmissionNavigation';
|
||||
import './ReviewModal.scss';
|
||||
|
||||
export const ReviewActions = ({ gradeStatus, username }) => (
|
||||
export const ReviewActions = ({
|
||||
gradeStatus,
|
||||
toggleShowRubric,
|
||||
showRubric,
|
||||
username,
|
||||
}) => (
|
||||
<div className="review-actions">
|
||||
<ActionRow>
|
||||
<span className="review-actions-username">{username}</span>
|
||||
<StatusBadge className="review-actions-status" status={gradeStatus} />
|
||||
<span className="review-actions-username">
|
||||
{username}
|
||||
<StatusBadge className="ml-3 review-actions-status" status={gradeStatus} />
|
||||
</span>
|
||||
<ActionRow.Spacer />
|
||||
<Button>Show Rubric</Button>
|
||||
<Button variant="outline-primary" onClick={toggleShowRubric}>
|
||||
{showRubric ? 'Hide' : 'Show'} Rubric
|
||||
</Button>
|
||||
<Button variant="primary" iconAfter={Edit}>Start Grading</Button>
|
||||
<SubmissionNavigation />
|
||||
</ActionRow>
|
||||
</div>
|
||||
);
|
||||
ReviewActions.propTypes = {
|
||||
gradeStatus: PropTypes.string.isRequired,
|
||||
username: PropTypes.string.isRequired,
|
||||
showRubric: PropTypes.bool.isRequired,
|
||||
toggleShowRubric: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export const mapStateToProps = (state) => ({
|
||||
username: selectors.grading.selected.username(state),
|
||||
gradeStatus: selectors.grading.selected.gradeStatus(state),
|
||||
showRubric: selectors.app.showRubric(state),
|
||||
});
|
||||
|
||||
export const mapDispatchToProps = {
|
||||
toggleShowRubric: actions.app.toggleShowRubric,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ReviewActions);
|
||||
|
||||
@@ -1,7 +1,84 @@
|
||||
@import "@edx/paragon/scss/core/core";
|
||||
.review-actions {
|
||||
padding: map_get($spacers, 3);
|
||||
|
||||
|
||||
@include media-breakpoint-down(md) {
|
||||
.review-actions {
|
||||
flex-direction: column;
|
||||
align-items: flex-start !important;
|
||||
|
||||
& > * {
|
||||
margin-bottom: map_get($spacers, 1) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ora-esg-list-view {
|
||||
padding: 20px;
|
||||
|
||||
.review-modal {
|
||||
background-color: $gray-300 !important;
|
||||
padding: inherit;
|
||||
|
||||
// action reviews
|
||||
.review-actions {
|
||||
padding: map_get($spacers, 3);
|
||||
flex-direction: row;
|
||||
background-color: $light-200;
|
||||
|
||||
.review-actions-username {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
// text response
|
||||
.response-card {
|
||||
padding: map-get($map: $spacers, $key: 0);
|
||||
max-width: map-get($map: $container-max-widths, $key: "sm");
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
}
|
||||
|
||||
.content-block {
|
||||
width: fit-content;
|
||||
margin: auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
.fullscreen-modal-body {
|
||||
padding: 0 !important;
|
||||
overflow-y: hidden !important;
|
||||
|
||||
& > div:nth-child(2) {
|
||||
height: 100%;
|
||||
|
||||
.row,
|
||||
.col {
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.grading-rubric-card {
|
||||
width: 320px;
|
||||
height: fit-content;
|
||||
max-height: 70vh;
|
||||
min-height: 320px;
|
||||
|
||||
.grading-rubric-header {
|
||||
box-shadow: 0 0 0.25rem rgba(0, 0, 0, 0.3) !important;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: map-get($map: $spacers, $key: 3);
|
||||
}
|
||||
|
||||
.grading-rubric-body {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.grading-rubric-footer {
|
||||
box-shadow: 0 0 0.25rem rgba(0, 0, 0, 0.3) !important;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: map-get($map: $spacers, $key: 3);
|
||||
}
|
||||
}
|
||||
|
||||
63
src/containers/ReviewModal/SubmissionNavigation.jsx
Normal file
63
src/containers/ReviewModal/SubmissionNavigation.jsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { Icon, IconButton } from '@edx/paragon';
|
||||
import { ChevronLeft, ChevronRight } from '@edx/paragon/icons';
|
||||
|
||||
import selectors from 'data/selectors';
|
||||
import thunkActions from 'data/thunkActions';
|
||||
|
||||
/**
|
||||
* <SubmissionNavigation />
|
||||
*/
|
||||
export const SubmissionNavigation = ({
|
||||
hasPrevSubmission,
|
||||
hasNextSubmission,
|
||||
loadPrev,
|
||||
loadNext,
|
||||
activeIndex,
|
||||
selectionLength,
|
||||
}) => (
|
||||
<>
|
||||
<IconButton
|
||||
disabled={!hasPrevSubmission}
|
||||
alt="Load previous submission"
|
||||
src={ChevronLeft}
|
||||
iconAs={Icon}
|
||||
onClick={loadPrev}
|
||||
/>
|
||||
<span>{activeIndex + 1} of {selectionLength}</span>
|
||||
<IconButton
|
||||
disabled={!hasNextSubmission}
|
||||
alt="Load next submission"
|
||||
src={ChevronRight}
|
||||
iconAs={Icon}
|
||||
onClick={loadNext}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
SubmissionNavigation.defaultProps = {
|
||||
};
|
||||
SubmissionNavigation.propTypes = {
|
||||
hasPrevSubmission: PropTypes.bool.isRequired,
|
||||
hasNextSubmission: PropTypes.bool.isRequired,
|
||||
loadPrev: PropTypes.func.isRequired,
|
||||
loadNext: PropTypes.func.isRequired,
|
||||
activeIndex: PropTypes.number.isRequired,
|
||||
selectionLength: PropTypes.number.isRequired,
|
||||
};
|
||||
|
||||
export const mapStateToProps = (state) => ({
|
||||
hasPrevSubmission: selectors.grading.hasPrevSubmission(state),
|
||||
hasNextSubmission: selectors.grading.hasNextSubmission(state),
|
||||
activeIndex: selectors.grading.activeIndex(state),
|
||||
selectionLength: selectors.grading.selectionLength(state),
|
||||
});
|
||||
|
||||
export const mapDispatchToProps = {
|
||||
loadPrev: thunkActions.grading.loadPrev,
|
||||
loadNext: thunkActions.grading.loadNext,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(SubmissionNavigation);
|
||||
@@ -4,16 +4,17 @@ import { connect } from 'react-redux';
|
||||
|
||||
import {
|
||||
FullscreenModal,
|
||||
Container,
|
||||
Row,
|
||||
Col,
|
||||
} from '@edx/paragon';
|
||||
|
||||
import createDOMPurify from 'dompurify';
|
||||
import parse from 'html-react-parser';
|
||||
|
||||
import selectors from 'data/selectors';
|
||||
import actions from 'data/actions';
|
||||
import thunkActions from 'data/thunkActions';
|
||||
|
||||
import ResponseDisplay from 'components/ResponseDisplay';
|
||||
import Rubric from 'containers/Rubric';
|
||||
|
||||
import ReviewActions from './ReviewActions';
|
||||
|
||||
import './ReviewModal.scss';
|
||||
@@ -24,16 +25,9 @@ import './ReviewModal.scss';
|
||||
export class ReviewModal extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
console.log('review modal');
|
||||
console.log({ props });
|
||||
this.purify = createDOMPurify(window);
|
||||
this.onClose = this.onClose.bind(this);
|
||||
}
|
||||
|
||||
get textContent() {
|
||||
return parse(this.purify.sanitize(this.props.response.text));
|
||||
}
|
||||
|
||||
onClose() {
|
||||
this.props.setShowReview(false);
|
||||
}
|
||||
@@ -48,10 +42,14 @@ export class ReviewModal extends React.Component {
|
||||
isOpen={this.props.isOpen}
|
||||
beforeBodyNode={<ReviewActions />}
|
||||
onClose={this.onClose}
|
||||
className="review-modal"
|
||||
>
|
||||
<Container size="md">
|
||||
{this.textContent}
|
||||
</Container>
|
||||
<div className="content-block">
|
||||
<Row>
|
||||
<Col><ResponseDisplay /></Col>
|
||||
{ this.props.showRubric && <Rubric /> }
|
||||
</Row>
|
||||
</div>
|
||||
</FullscreenModal>
|
||||
);
|
||||
}
|
||||
@@ -66,12 +64,14 @@ ReviewModal.propTypes = {
|
||||
text: PropTypes.node,
|
||||
}),
|
||||
setShowReview: PropTypes.func.isRequired,
|
||||
showRubric: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export const mapStateToProps = (state) => ({
|
||||
isOpen: selectors.app.showReview(state),
|
||||
oraName: selectors.app.oraName(state),
|
||||
response: selectors.grading.selected.response(state),
|
||||
showRubric: selectors.app.showRubric(state),
|
||||
});
|
||||
|
||||
export const mapDispatchToProps = {
|
||||
|
||||
93
src/containers/Rubric/GradingRubric.temp.jsx
Normal file
93
src/containers/Rubric/GradingRubric.temp.jsx
Normal file
@@ -0,0 +1,93 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import {
|
||||
Card,
|
||||
Button,
|
||||
Form,
|
||||
} from '@edx/paragon';
|
||||
|
||||
/**
|
||||
* <GradingRubric />
|
||||
*/
|
||||
export const GradingRubric = ({
|
||||
}) => {
|
||||
return (
|
||||
<Card className="grading-rubric-card">
|
||||
<Card.Body className="grading-rubric-body">
|
||||
<h3>Rubric</h3>
|
||||
<hr />
|
||||
<Form.Group>
|
||||
<Form.Label>Which Color?</Form.Label>
|
||||
<Form.RadioSet
|
||||
name="colors"
|
||||
>
|
||||
<Form.Radio value="red">Red</Form.Radio>
|
||||
<Form.Radio value="green">Green</Form.Radio>
|
||||
<Form.Radio value="blue">Blue</Form.Radio>
|
||||
<Form.Radio value="cyan" disabled>Cyan</Form.Radio>
|
||||
</Form.RadioSet>
|
||||
</Form.Group>
|
||||
<Form.Group>
|
||||
<Form.Control
|
||||
floatingLabel="Comments"
|
||||
/>
|
||||
</Form.Group>
|
||||
<Form.Group isInvalid>
|
||||
<Form.Label>Which Color?</Form.Label>
|
||||
<Form.RadioSet
|
||||
name="colors"
|
||||
>
|
||||
<Form.Radio value="red">Red</Form.Radio>
|
||||
<Form.Radio value="green">Green</Form.Radio>
|
||||
<Form.Radio value="blue">Blue</Form.Radio>
|
||||
<Form.Radio value="cyan" disabled>Cyan</Form.Radio>
|
||||
</Form.RadioSet>
|
||||
<Form.Control.Feedback type="invalid">
|
||||
Make a selection
|
||||
</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
<Form.Group isInvalid>
|
||||
<Form.Control
|
||||
floatingLabel="Comments"
|
||||
/>
|
||||
<Form.Control.Feedback type="invalid">
|
||||
Make a comment
|
||||
</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
<Form.Group>
|
||||
<Form.Label>Which Color?</Form.Label>
|
||||
<Form.RadioSet
|
||||
name="colors"
|
||||
>
|
||||
<Form.Radio value="red">Red</Form.Radio>
|
||||
<Form.Radio value="green">Green</Form.Radio>
|
||||
<Form.Radio value="blue">Blue</Form.Radio>
|
||||
<Form.Radio value="cyan" disabled>Cyan</Form.Radio>
|
||||
</Form.RadioSet>
|
||||
</Form.Group>
|
||||
<Form.Group>
|
||||
<Form.Control
|
||||
floatingLabel="Comments"
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
</Card.Body>
|
||||
<div className="grading-rubric-footer">
|
||||
<Button>Submit Grade</Button>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
GradingRubric.defaultProps = {};
|
||||
GradingRubric.propTypes = {};
|
||||
|
||||
export const mapStateToProps = (state) => ({
|
||||
});
|
||||
|
||||
export const mapDispatchToProps = {
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(GradingRubric);
|
||||
33
src/containers/Rubric/index.jsx
Normal file
33
src/containers/Rubric/index.jsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import {
|
||||
Card,
|
||||
Button,
|
||||
} from '@edx/paragon';
|
||||
|
||||
/**
|
||||
* <GradingRubric />
|
||||
*/
|
||||
export const Rubric = ({
|
||||
}) => {
|
||||
return (
|
||||
<Card className="grading-rubric-card">
|
||||
<Card.Body className="grading-rubric-body" />
|
||||
<div className="grading-rubric-footer">
|
||||
<Button>Submit Grade</Button>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Rubric.defaultProps = {};
|
||||
Rubric.propTypes = {};
|
||||
|
||||
export const mapStateToProps = (state) => ({
|
||||
});
|
||||
|
||||
export const mapDispatchToProps = {
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Rubric);
|
||||
@@ -8,10 +8,12 @@ export const loadCourseMetadata = createAction('loadCourseMetadata');
|
||||
export const loadOraMetadata = createAction('loadOraMetadata');
|
||||
export const setGrading = createAction('setGrading');
|
||||
export const setShowReview = createAction('setReview');
|
||||
export const toggleShowRubric = createAction('toggleShowRubric');
|
||||
|
||||
export default StrictDict({
|
||||
loadCourseMetadata,
|
||||
loadOraMetadata,
|
||||
setGrading,
|
||||
setShowReview,
|
||||
toggleShowRubric,
|
||||
});
|
||||
|
||||
@@ -10,7 +10,6 @@ const createAction = createActionFactory(dataKey);
|
||||
* @param {obj} submission data for the review/grading view
|
||||
* {
|
||||
* {obj} response - api response data
|
||||
* {obj} staticData - api submission static data
|
||||
* {obj} gradeData - api grade data
|
||||
* {str} status - api grade status
|
||||
* }
|
||||
@@ -20,14 +19,14 @@ const loadSubmission = createAction('loadSubmission');
|
||||
/**
|
||||
* Pre-load just the static info about the "next" submission in the review queue.
|
||||
* Load submission and the learner's response.
|
||||
* @param {obj} submission ({ staticData, response })
|
||||
* @param {obj} submission ({ response })
|
||||
*/
|
||||
const preloadNext = createAction('preloadNext');
|
||||
|
||||
/**
|
||||
* Pre-load just the static info about the "previous" submission in the review queue.
|
||||
* Load submission and the learner's response.
|
||||
* @param {obj} submission ({ staticData, response })
|
||||
* @param {obj} submission ({ response })
|
||||
*/
|
||||
const preloadPrev = createAction('preloadPrev');
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ const initialState = {
|
||||
org: '',
|
||||
},
|
||||
showReview: false,
|
||||
showRubric: false,
|
||||
grading: false,
|
||||
};
|
||||
|
||||
@@ -27,6 +28,8 @@ const app = (state = initialState, { type, payload }) => {
|
||||
return { ...state, showReview: payload };
|
||||
case actions.app.setGrading.toString():
|
||||
return { ...state, grading: payload };
|
||||
case actions.app.toggleShowRubric.toString():
|
||||
return { ...state, showRubric: !state.showRubric };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ const app = (state = initialState, { type, payload }) => {
|
||||
...state,
|
||||
prev: state.current,
|
||||
current: {
|
||||
response: state.next.response,
|
||||
...payload,
|
||||
},
|
||||
activeIndex: state.activeIndex + 1,
|
||||
@@ -85,6 +86,7 @@ const app = (state = initialState, { type, payload }) => {
|
||||
...state,
|
||||
next: state.current,
|
||||
current: {
|
||||
response: state.prev.response,
|
||||
...payload,
|
||||
},
|
||||
activeIndex: state.activeIndex - 1,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { StrictDict } from 'utils';
|
||||
|
||||
export const simpleSelectors = {
|
||||
showReview: state => state.app.showReview,
|
||||
showRubric: state => state.app.showRubric,
|
||||
grading: state => state.app.grading,
|
||||
courseMetadata: state => state.app.courseMetadata,
|
||||
oraName: state => state.app.oraMetadata.name,
|
||||
|
||||
@@ -10,6 +10,15 @@ export const simpleSelectors = {
|
||||
current: state => state.grading.current,
|
||||
};
|
||||
|
||||
/**
|
||||
* returns the length of the list of selected submissions
|
||||
* @return {number} selected submission list length
|
||||
*/
|
||||
export const selectionLength = createSelector(
|
||||
[module.simpleSelectors.selected],
|
||||
(selected) => selected.length,
|
||||
);
|
||||
|
||||
/**
|
||||
* returns the selected submission id
|
||||
* @return {string} selected submission id
|
||||
@@ -136,4 +145,6 @@ export default StrictDict({
|
||||
nextSubmissionId,
|
||||
prevSubmissionId,
|
||||
selected,
|
||||
selectedResponse,
|
||||
selectionLength,
|
||||
});
|
||||
|
||||
@@ -2,13 +2,9 @@ import _ from 'lodash';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
import { StrictDict } from 'utils';
|
||||
import * as module from './submissions';
|
||||
|
||||
export const simpleSelectors = {
|
||||
list: state => state.submissions.list,
|
||||
selected: state => state.submissions.selected,
|
||||
activeIndex: state => state.submissions.activeIndex,
|
||||
current: state => state.submissions.current,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -22,131 +18,7 @@ export const listData = createSelector(
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* returns the selected submission id
|
||||
* @return {string} selected submission id
|
||||
*/
|
||||
export const selectedSubmissionId = createSelector(
|
||||
[module.simpleSelectors.selected, module.simpleSelectors.activeIndex],
|
||||
(selected, index) => selected[index],
|
||||
);
|
||||
|
||||
/**
|
||||
* returns static data from the active selected submission
|
||||
* @return {obj} - staticData
|
||||
* { submissionId, username, teamName, dateSubmitted }
|
||||
*/
|
||||
export const selectedStaticData = createSelector(
|
||||
[module.selectedSubmissionId, module.simpleSelectors.list],
|
||||
(submissionId, list) => {
|
||||
const submission = list[submissionId];
|
||||
const { grade, gradeStatus, ...staticData } = submission;
|
||||
return staticData;
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns the username for the selected submission
|
||||
* @return {string} username
|
||||
*/
|
||||
export const selectedUsername = createSelector(
|
||||
[module.selectedStaticData],
|
||||
(staticData) => staticData.username,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns the grade status for the selected submission
|
||||
* @return {string} grade status
|
||||
*/
|
||||
export const selectedGradeStatus = createSelector(
|
||||
[module.simpleSelectors.current],
|
||||
(current) => current.gradeStatus,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns the grade data for the selected submission
|
||||
* @return {obj} grade data
|
||||
* { score, overallFeedback, criteria }
|
||||
*/
|
||||
export const selectedGradeData = createSelector(
|
||||
[module.simpleSelectors.current],
|
||||
(current) => current.gradeData,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns the response data for the selected submission
|
||||
* @return {obj} response
|
||||
* { text, files: [] }
|
||||
*/
|
||||
export const selectedResponse = createSelector(
|
||||
[module.simpleSelectors.current],
|
||||
(current) => current.response,
|
||||
);
|
||||
|
||||
export const selected = StrictDict({
|
||||
submissionId: module.selectedSubmissionId,
|
||||
staticData: module.selectedStaticData,
|
||||
username: module.selectedUsername,
|
||||
gradeStatus: module.selectedGradeStatus,
|
||||
gradeData: module.selectedGradeData,
|
||||
response: module.selectedResponse,
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns true iff there exists a selection previous to the current selection
|
||||
* in the queue.
|
||||
* @return {bool} has previous submission?
|
||||
*/
|
||||
export const hasPrevSubmission = createSelector(
|
||||
[simpleSelectors.activeIndex],
|
||||
(activeIndex) => activeIndex > 0,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns true iff there exists a selection after the current selection
|
||||
* in the queue.
|
||||
* @return {bool} has next submission?
|
||||
*/
|
||||
export const hasNextSubmission = createSelector(
|
||||
[simpleSelectors.selected, simpleSelectors.activeIndex],
|
||||
(list, activeIndex) => activeIndex < list.length - 1,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns the submissionId for the previous submission in the selection queu
|
||||
* @return {string} previous submission id (null if there isn't one)
|
||||
*/
|
||||
export const prevSubmissionId = createSelector(
|
||||
[simpleSelectors.selected, simpleSelectors.activeIndex],
|
||||
(list, activeIndex) => {
|
||||
if (activeIndex > 0) {
|
||||
return list[activeIndex - 1];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns the submissionId for the next submission in the selection queu
|
||||
* @return {string} next submission id (null if there isn't one)
|
||||
*/
|
||||
export const nextSubmissionId = createSelector(
|
||||
[simpleSelectors.selected, simpleSelectors.activeIndex],
|
||||
(list, activeIndex) => {
|
||||
if (activeIndex < list.length - 1) {
|
||||
return list[activeIndex + 1];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
export default StrictDict({
|
||||
...simpleSelectors,
|
||||
listData,
|
||||
selectedSubmissionId,
|
||||
hasPrevSubmission,
|
||||
hasNextSubmission,
|
||||
nextSubmissionId,
|
||||
prevSubmissionId,
|
||||
selected,
|
||||
});
|
||||
|
||||
@@ -72,9 +72,9 @@ const fetchSubmission = mockSuccess((submissionId) => (
|
||||
* },
|
||||
* }
|
||||
*/
|
||||
const fetchSubmissionStatus = mockSuccess((submissionId) => ({
|
||||
submissionData: fakeData.mockSubmission(submissionId),
|
||||
}));
|
||||
const fetchSubmissionStatus = mockSuccess((submissionId) => (
|
||||
fakeData.mockSubmissionStatus(submissionId)
|
||||
));
|
||||
|
||||
/**
|
||||
* Fetches only the learner response for a given submission. Used for pre-fetching response
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import submissions from './submissionList';
|
||||
import { mockSubmission } from './submissionFull';
|
||||
import { mockSubmission, mockSubmissionStatus } from './submissionFull';
|
||||
import oraMetadata from './ora';
|
||||
import courseMetadata from './course';
|
||||
import ids from './ids';
|
||||
@@ -9,5 +9,6 @@ export default {
|
||||
oraMetadata,
|
||||
courseMetadata,
|
||||
mockSubmission,
|
||||
mockSubmissionStatus,
|
||||
ids,
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ const rubricConfig = {
|
||||
comments: 'rubric-level comments',
|
||||
criteria: [
|
||||
{
|
||||
name: 'First Criterion',
|
||||
name: 'firstCriterion',
|
||||
orderNum: 0,
|
||||
prompt: 'A criterion prompt',
|
||||
feedback: 'optional',
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import submissionList from './submissionList';
|
||||
|
||||
const responseText = `<div><h1>Title</h1>
|
||||
const responseText = (submissionId) => `<div><h1>Title (${submissionId})</h1>
|
||||
Phasellus tempor eros aliquam ipsum molestie, vitae varius lectus tempus. Morbi iaculis, libero euismod vehicula rutrum, nisi leo volutpat diam, quis commodo ex nunc ut odio. Pellentesque condimentum feugiat erat ac vulputate. Pellentesque porta rutrum sagittis. Curabitur vulputate tempus accumsan. Fusce bibendum gravida metus a scelerisque. Mauris fringilla orci non lobortis commodo. Quisque iaculis, quam a tincidunt vehicula, erat nisi accumsan quam, eu cursus ligula magna id odio. Nulla porttitor, lorem gravida vehicula tristique, sapien metus tristique ex, id tincidunt sapien justo nec sapien. Maecenas luctus, nisl vestibulum scelerisque pharetra, ligula orci vulputate turpis, in ultrices mauris dolor eu enim. Suspendisse quis nibh nec augue semper maximus. Morbi maximus eleifend magna.
|
||||
|
||||
Phasellus porttitor vel magna et auctor. Nulla porttitor convallis aliquam. Donec cursus, ipsum ut egestas bibendum, purus metus dignissim est, ac condimentum leo felis eget diam. In magna mi, tincidunt id sapien id, fermentum vestibulum quam. Quisque et dui sed urna convallis rutrum pellentesque quis sapien. Cras non lectus velit. Praesent semper eros id risus mollis, quis interdum quam imperdiet. Sed nec vulputate tortor, at tristique tortor.
|
||||
</div>`;
|
||||
|
||||
// eslint-disable-next-line
|
||||
export const mockSubmission = (submissionId) => {
|
||||
console.log({ submissionId, submission: submissionList[submissionId] });
|
||||
return {
|
||||
response: {
|
||||
text: responseText,
|
||||
files: [],
|
||||
},
|
||||
gradeStatus: submissionList[submissionId].gradeStatus,
|
||||
gradeData: submissionList[submissionId].grade,
|
||||
};
|
||||
};
|
||||
export const mockSubmission = (submissionId) => ({
|
||||
response: {
|
||||
text: responseText(submissionId),
|
||||
files: [],
|
||||
},
|
||||
gradeStatus: submissionList[submissionId].gradeStatus,
|
||||
score: submissionList[submissionId].score,
|
||||
});
|
||||
|
||||
export const mockSubmissionStatus = (submissionId) => ({
|
||||
gradeData: submissionList[submissionId].gradeData,
|
||||
gradeStatus: submissionList[submissionId].gradeStatus,
|
||||
});
|
||||
|
||||
@@ -22,17 +22,27 @@ const day = 86400000;
|
||||
const submissions = {};
|
||||
let lastIndex = 0;
|
||||
|
||||
const createSubmission = (grade, gradeStatus) => {
|
||||
const createSubmission = (score, gradeStatus) => {
|
||||
const index = lastIndex;
|
||||
lastIndex += 1;
|
||||
const submissionId = ids.submissionId(index);
|
||||
const gradeData = score === null ? null : {
|
||||
score,
|
||||
overallFeedback: 'was okay',
|
||||
criteria: [{
|
||||
name: 'firstCriterion',
|
||||
feedback: 'did alright',
|
||||
selectedOption: 'good'
|
||||
}],
|
||||
};
|
||||
submissions[submissionId] = {
|
||||
submissionId,
|
||||
username: ids.username(index),
|
||||
// teamName: '',
|
||||
dateSubmitted: date0 + (day * index),
|
||||
score,
|
||||
gradeData,
|
||||
gradeStatus,
|
||||
grade,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { StrictDict } from 'utils';
|
||||
import actions from 'data/actions';
|
||||
import selectors from 'data/selectors';
|
||||
import api from 'data/services/lms/api';
|
||||
import * as module from './grading';
|
||||
|
||||
/**
|
||||
* Prefetch the "next" submission in the selected queue. Only fetches the response info.
|
||||
@@ -11,7 +12,7 @@ export const prefetchNext = () => (dispatch, getState) => (
|
||||
api.fetchSubmissionResponse(
|
||||
selectors.grading.nextSubmissionId(getState()),
|
||||
).then((response) => {
|
||||
dispatch(actions.grading.preloadNext(response.submission));
|
||||
dispatch(actions.grading.preloadNext(response));
|
||||
})
|
||||
);
|
||||
|
||||
@@ -22,7 +23,7 @@ export const prefetchPrev = () => (dispatch, getState) => (
|
||||
api.fetchSubmissionResponse(
|
||||
selectors.grading.prevSubmissionId(getState()),
|
||||
).then((response) => {
|
||||
dispatch(actions.grading.preloadPrev(response.submissionStatus));
|
||||
dispatch(actions.grading.preloadPrev(response));
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user