Skip to content

Commit 03f1b2f

Browse files
author
Ben Warzeski
authored
Merge pull request #42 from openedx/bw/routes
Bw/routes
2 parents a254965 + c4504c0 commit 03f1b2f

File tree

19 files changed

+1105
-868
lines changed

19 files changed

+1105
-868
lines changed

package-lock.json

Lines changed: 767 additions & 787 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
"@edx/paragon": "^20.20.0",
4242
"@edx/react-unit-test-utils": "1.7.0",
4343
"@edx/tinymce-language-selector": "1.1.0",
44-
"@edx/typescript-config": "^1.0.1",
4544
"@fortawesome/fontawesome-svg-core": "1.2.36",
4645
"@fortawesome/free-brands-svg-icons": "5.15.4",
4746
"@fortawesome/free-regular-svg-icons": "5.15.4",

src/App.jsx

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,41 @@ import { Spinner } from '@edx/paragon';
55

66
import { useIsORAConfigLoaded, useIsPageDataLoaded } from 'data/services/lms/hooks/selectors';
77

8+
import AppContainer from 'views/AppContainer';
9+
import ModalContainer from 'views/ModalContainer';
810
import PeerAssessmentView from 'views/PeerAssessmentView';
911
import SelfAssessmentView from 'views/SelfAssessmentView';
1012
import StudentTrainingView from 'views/StudentTrainingView';
1113
import SubmissionView from 'views/SubmissionView';
14+
import XBlockView from 'views/XBlockView';
1215
import messages from './messages';
1316
import routes from './routes';
1417

1518
const RouterRoot = () => {
1619
const { formatMessage } = useIntl();
20+
const appRoute = (route, Component) => (
21+
<Route path={route} element={<AppContainer Component={Component} />} />
22+
);
23+
const modalRoute = (route, Component, title) => (
24+
<Route path={route} element={<ModalContainer {...{ title, Component }} />} />
25+
);
26+
27+
const embeddedRoutes = [
28+
<Route path={routes.embedded.xblock} element={<XBlockView />} />,
29+
modalRoute(routes.embedded.peerAssessment, PeerAssessmentView, 'ORA Peer Assessment'),
30+
modalRoute(routes.embedded.selfAssessment, SelfAssessmentView, 'ORA Self Assessment'),
31+
modalRoute(routes.embedded.studentTraining, StudentTrainingView, 'ORA Student Training'),
32+
modalRoute(routes.embedded.submission, SubmissionView, 'ORA Submission'),
33+
<Route path={routes.embedded.root} element={<ErrorPage message={formatMessage(messages.error404Message)} />} />,
34+
];
35+
const baseRoutes = [
36+
appRoute(routes.xblock, PeerAssessmentView),
37+
appRoute(routes.peerAssessment, PeerAssessmentView),
38+
appRoute(routes.selfAssessment, SelfAssessmentView),
39+
appRoute(routes.studentTraining, StudentTrainingView),
40+
appRoute(routes.submission, SubmissionView),
41+
<Route path={routes.root} element={<ErrorPage message={formatMessage(messages.error404Message)} />} />,
42+
];
1743

1844
const isConfigLoaded = useIsORAConfigLoaded();
1945
const isPageLoaded = useIsPageDataLoaded();
@@ -33,11 +59,8 @@ const RouterRoot = () => {
3359

3460
return (
3561
<Routes>
36-
<Route path={routes.peerAssessment} element={<PeerAssessmentView />} />
37-
<Route path={routes.selfAssessment} element={<SelfAssessmentView />} />
38-
<Route path={routes.studentTraining} element={<StudentTrainingView />} />
39-
<Route path={routes.submission} element={<SubmissionView />} />
40-
<Route path={routes.root} element={<ErrorPage message={formatMessage(messages.error404Message)} />} />
62+
{embeddedRoutes}
63+
{baseRoutes}
4164
</Routes>
4265
);
4366
};

src/components/ProgressBar/index.jsx

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { useLocation } from 'react-router-dom';
4+
import classNames from 'classnames';
5+
6+
import { useIntl } from '@edx/frontend-platform/i18n';
7+
import { Navbar, Nav, Icon } from '@edx/paragon';
8+
import { Locked, CheckCircle, Error } from '@edx/paragon/icons';
9+
10+
import {
11+
useAssessmentStepConfig,
12+
useProgressData,
13+
useIsPageDataLoaded,
14+
} from 'data/services/lms/hooks/selectors';
15+
16+
import messages from './messages';
17+
import './index.scss';
18+
19+
export const ProgressStep = ({
20+
children,
21+
href,
22+
isActive,
23+
isEnabled,
24+
isComplete,
25+
isError,
26+
number,
27+
}) => {
28+
let icon = <Icon className="nav-icon" src={Locked} />;
29+
if (isComplete) {
30+
icon = <Icon className="nav-icon" src={CheckCircle} />;
31+
} else if (isError) {
32+
icon = <Icon className="nav-icon text-danger-300" src={Error} />;
33+
} else if (number) {
34+
icon = <span className="nav-icon number-icon">{number}</span>;
35+
}
36+
return (
37+
<Nav.Link
38+
href={href}
39+
disabled={!isEnabled}
40+
className={classNames(
41+
'ora-progress-nav',
42+
'px-4',
43+
{ 'is-active': isActive },
44+
)}
45+
>
46+
{icon}
47+
{children}
48+
</Nav.Link>
49+
);
50+
};
51+
ProgressStep.defaultProps = {
52+
href: '#',
53+
isActive: false,
54+
isEnabled: false,
55+
isComplete: false,
56+
isError: false,
57+
number: null,
58+
};
59+
ProgressStep.propTypes = {
60+
children: PropTypes.node.isRequired,
61+
href: PropTypes.string,
62+
isActive: PropTypes.bool,
63+
isEnabled: PropTypes.bool,
64+
isError: PropTypes.bool,
65+
isComplete: PropTypes.bool,
66+
number: PropTypes.number,
67+
};
68+
69+
export const SubmissionStep = () => {
70+
const { formatMessage } = useIntl();
71+
return (
72+
<ProgressStep>{formatMessage(messages.createSubmission)}</ProgressStep>
73+
);
74+
};
75+
76+
export const TrainingStep = () => {
77+
const { formatMessage } = useIntl();
78+
return (
79+
<ProgressStep>{formatMessage(messages.practice)}</ProgressStep>
80+
);
81+
};
82+
83+
export const SelfAssessStep = () => {
84+
const { formatMessage } = useIntl();
85+
return (
86+
<ProgressStep>{formatMessage(messages.selfAssess)}</ProgressStep>
87+
);
88+
};
89+
90+
export const PeerAssessStep = () => {
91+
const { formatMessage } = useIntl();
92+
return (
93+
<ProgressStep>{formatMessage(messages.peerAssess)}</ProgressStep>
94+
);
95+
};
96+
97+
export const MyGradeStep = () => {
98+
const { formatMessage } = useIntl();
99+
return (
100+
<ProgressStep>{formatMessage(messages.myGrade)}</ProgressStep>
101+
);
102+
};
103+
104+
export const ProgressBar = () => {
105+
const stepConfig = useAssessmentStepConfig();
106+
const progress = useProgressData();
107+
const isLoaded = useIsPageDataLoaded();
108+
const location = useLocation();
109+
if (!isLoaded) {
110+
return null;
111+
}
112+
console.log({
113+
stepConfig, progress, location,
114+
});
115+
return (
116+
<Navbar>
117+
<Navbar.Collapse className="ora-progress-nav-group">
118+
<hr className="ora-progress-divider" />
119+
<SubmissionStep />
120+
{stepConfig.order.map(step => {
121+
if (step === 'peer') {
122+
return <PeerAssessStep />;
123+
}
124+
if (step === 'training') {
125+
return <TrainingStep />;
126+
}
127+
if (step === 'self') {
128+
return <SelfAssessStep />;
129+
}
130+
return null;
131+
})}
132+
<MyGradeStep />
133+
</Navbar.Collapse>
134+
</Navbar>
135+
);
136+
};
137+
138+
export default ProgressBar;

src/components/ProgressBar/index.scss

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
.ora-progress-nav-group {
2+
display: flex;
3+
justify-content: space-between;
4+
.ora-progress-divider {
5+
position: absolute;
6+
width: 95%;
7+
}
8+
.ora-progress-nav {
9+
background-color: white;
10+
z-index: 5;
11+
font-size: 1.125rem;
12+
&.is-active {
13+
border-bottom: 2px solid black;
14+
}
15+
}
16+
.nav-icon {
17+
display: inline-block;
18+
margin-inline-end: 0.5rem;
19+
margin-left: -0.25rem;
20+
}
21+
.number-icon {
22+
background-color: black;
23+
color: white;
24+
border-radius: 100%;
25+
}
26+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { defineMessages } from '@edx/frontend-platform/i18n';
2+
3+
const messages = defineMessages({
4+
createSubmission: {
5+
id: 'ora-grading.ProgressBar.createSubmission',
6+
defaultMessage: 'Create your response',
7+
description: 'Create response progress indicator',
8+
},
9+
practice: {
10+
id: 'ora-grading.ProgressBar.practice',
11+
defaultMessage: 'Practice grading',
12+
description: 'Student training step progress indicator',
13+
},
14+
selfAssess: {
15+
id: 'ora-grading.ProgressBar.selfAssess',
16+
defaultMessage: 'Grade yourself',
17+
description: 'Self assessment step progress indicator',
18+
},
19+
peerAssess: {
20+
id: 'ora-grading.ProgressBar.peerAssess',
21+
defaultMessage: 'Grade peers',
22+
description: 'Peer assessment step progress indicator',
23+
},
24+
myGrade: {
25+
id: 'ora-grading.ProgressBar.myGrade',
26+
defaultMessage: 'My grade',
27+
description: 'My Grade step progress indicator',
28+
},
29+
});
30+
31+
export default messages;

src/data/services/lms/fakeData/oraConfig.js

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -135,15 +135,17 @@ export const createORAConfig = ({
135135
leaderboard_config,
136136
});
137137

138+
export const tinyMCEConfig = {
139+
submission_config: {
140+
...submissionConfig,
141+
text_response_config: {
142+
...submissionConfig.text_response_config,
143+
editor_type: 'tinymce',
144+
},
145+
},
146+
};
147+
138148
export default {
139149
assessmentText: createORAConfig(),
140-
assessmentTinyMCE: createORAConfig({
141-
submission_config: {
142-
...submissionConfig,
143-
text_response_config: {
144-
...submissionConfig.text_response_config,
145-
editor_type: 'tinymce',
146-
},
147-
},
148-
}),
150+
assessmentTinyMCE: createORAConfig(tinyMCEConfig),
149151
};

src/data/services/lms/fakeData/pageData/submission.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export default {
6262
has_received_grade: false,
6363
}),
6464
response: createSubmissionResponse({
65-
text_responses: ['', ''],
65+
text_responses: ['response 1', 'response 2'],
6666
uploaded_files: [],
6767
}),
6868
}),

src/data/services/lms/hooks/data.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ describe('lms data hooks', () => {
3535
});
3636

3737
const mockUseQueryForORA = (hasData) => {
38-
console.log({ useQuery });
3938
when(useQuery)
4039
.calledWith(expect.objectContaining({ queryKey: [queryKeys.oraConfig] }))
4140
.mockImplementationOnce(mockUseQuery(hasData));

src/data/services/lms/hooks/data.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useQuery } from '@tanstack/react-query';
1+
import { useQuery, useMutation } from '@tanstack/react-query';
22

33
import { useMatch } from 'react-router-dom';
44
import { camelCaseObject } from '@edx/frontend-platform';
@@ -40,12 +40,11 @@ export const usePageData = (): types.QueryData<types.PageData> => {
4040
const returnData = assessmentData ? {
4141
...camelCaseObject(assessmentData),
4242
rubric: {
43-
optionsSelected: {...assessmentData.rubric.options_selected},
44-
criterionFeedback: {...assessmentData.rubric.criterion_feedback},
43+
optionsSelected: { ...assessmentData.rubric.options_selected },
44+
criterionFeedback: { ...assessmentData.rubric.criterion_feedback },
4545
overallFeedback: assessmentData.rubric.overall_feedback,
4646
},
47-
}: {};
48-
47+
} : {};
4948
return Promise.resolve(returnData as any);
5049
},
5150
});
@@ -54,3 +53,10 @@ export const usePageData = (): types.QueryData<types.PageData> => {
5453
data,
5554
};
5655
};
56+
57+
export const useSubmitResponse = () => useMutation({
58+
mutationFn: (response) => {
59+
console.log({ submit: response });
60+
return Promise.resolve();
61+
},
62+
});

src/data/services/lms/hooks/selectors.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,11 @@ export const useIsPageDataLoaded = (): boolean => (
4747
data.usePageData().status === 'success'
4848
);
4949

50-
export const usePageData = (): types.PageData => data.usePageData().data;
50+
export const usePageData = (): types.PageData => data.usePageData()?.data;
5151

52-
export const useSubmissionTeamInfo = (): types.SubmissionTeamData => usePageData().submission.teamInfo;
52+
export const useProgressData = (): types.ProgressData => usePageData()?.progress;
53+
54+
export const useSubmissionTeamInfo = (): types.SubmissionTeamData => usePageData()?.submission.teamInfo;
5355

5456
export const useSubmissionStatus = (): types.SubmissionStatusData => {
5557
const {

src/routes.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
11
export default {
2+
embedded: {
3+
xblock: '/embedded/xblock/:id',
4+
peerAssessment: '/embedded/peer_assessment/:id',
5+
selfAssessment: '/embedded/self_assessment/:id',
6+
studentTraining: '/embedded/student_training/:id',
7+
submission: '/embedded/submission/:id',
8+
root: '/embedded/*',
9+
},
10+
xblock: '/xblock/:id',
211
peerAssessment: '/peer_assessment/:id',
312
selfAssessment: '/self_assessment/:id',
413
studentTraining: '/student_training/:id',

src/views/AppContainer.jsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
/* The purpose of this component is to wrap views with a header/footer for situations
5+
* where we need to run non-embedded
6+
*/
7+
8+
const AppContainer = ({ Component }) => (
9+
<div>
10+
<Component />
11+
</div>
12+
);
13+
AppContainer.propTypes = {
14+
Component: PropTypes.elementType.isRequired,
15+
};
16+
17+
export default AppContainer;

0 commit comments

Comments
 (0)