Skip to content

Commit f19259d

Browse files
feat: assessment view updates
1 parent 0a431cf commit f19259d

40 files changed

+21535
-1408
lines changed

.env.development

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
NODE_ENV='development'
2-
PORT=1993
2+
PORT=1992
33
ACCESS_TOKEN_COOKIE_NAME='edx-jwt-cookie-header-payload'
4-
BASE_URL='http://localhost:1993'
4+
BASE_URL='http://localhost:1992'
55
CREDENTIALS_BASE_URL='http://localhost:18150'
66
CSRF_TOKEN_API_PATH='/csrf/api/v1/token'
77
ECOMMERCE_BASE_URL='http://localhost:18130'

package-lock.json

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

package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,17 @@
3939
"@edx/frontend-platform": "4.5.1",
4040
"@edx/paragon": "^20.20.0",
4141
"@edx/react-unit-test-utils": "1.5.3",
42+
"@edx/tinymce-language-selector": "1.1.0",
4243
"@fortawesome/fontawesome-svg-core": "1.2.36",
4344
"@fortawesome/free-brands-svg-icons": "5.15.4",
4445
"@fortawesome/free-regular-svg-icons": "5.15.4",
4546
"@fortawesome/free-solid-svg-icons": "5.15.4",
4647
"@fortawesome/react-fontawesome": "0.2.0",
4748
"@tanstack/react-query": "^4.29.25",
49+
"@tinymce/tinymce-react": "3.8.4",
50+
"classnames": "^2.3.2",
4851
"core-js": "3.31.1",
52+
"filesize": "^8.0.6",
4953
"prop-types": "15.8.1",
5054
"query-string": "^8.1.0",
5155
"react": "16.14.0",
@@ -56,7 +60,8 @@
5660
"redux": "4.2.1",
5761
"redux-devtools-extension": "^2.13.9",
5862
"redux-logger": "^3.0.6",
59-
"regenerator-runtime": "0.13.11"
63+
"regenerator-runtime": "0.13.11",
64+
"tinymce": "5.10.7"
6065
},
6166
"devDependencies": {
6267
"@edx/browserslist-config": "^1.1.1",
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
import { FormattedMessage } from '@edx/frontend-platform/i18n';
5+
import filesize from 'filesize';
6+
7+
import messages from './messages';
8+
9+
const FileMetaDisplay = ({ name, description, size }) => (
10+
<>
11+
{console.log({ name, description, size })}
12+
<div className="file-meta-option">
13+
<strong><FormattedMessage {...messages.filePopoverNameTitle} /></strong>
14+
<br />
15+
{name}
16+
</div>
17+
<div className="file-meta-option">
18+
<strong><FormattedMessage {...messages.filePopoverDescriptionTitle} /></strong>
19+
<br />
20+
{description}
21+
</div>
22+
<div className="file-meta-option">
23+
<strong><FormattedMessage {...messages.fileSizeTitle} /></strong>
24+
<br />
25+
{typeof (size) === 'number' ? filesize(size) : 'Unknown'}
26+
</div>
27+
</>
28+
);
29+
30+
FileMetaDisplay.defaultProps = {
31+
description: '',
32+
size: null,
33+
};
34+
35+
FileMetaDisplay.propTypes = {
36+
name: PropTypes.string.isRequired,
37+
description: PropTypes.string,
38+
size: PropTypes.number,
39+
};
40+
41+
export default FileMetaDisplay;

src/components/FileUpload/index.jsx

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import React from 'react';
2+
import { DataTable, Dropzone } from '@edx/paragon';
3+
4+
import { useSubmissionResponse } from 'data/services/lms/hooks/selectors';
5+
6+
import { useIntl } from '@edx/frontend-platform/i18n';
7+
import filesize from 'filesize';
8+
9+
import messages from './messages';
10+
11+
const FileUpload = () => {
12+
const { uploadedFiles } = useSubmissionResponse();
13+
const { formatMessage } = useIntl();
14+
return (
15+
<div>
16+
<h3>File Upload</h3>
17+
{uploadedFiles && (
18+
<>
19+
<b>Uploaded Files</b>
20+
<DataTable
21+
itemCount={uploadedFiles.length}
22+
data={uploadedFiles.map(file => ({
23+
...file,
24+
size: typeof file.size === 'number' ? filesize(file.size) : 'Unknown',
25+
}))}
26+
columns={[
27+
{
28+
Header: formatMessage(messages.fileNameTitle),
29+
accessor: 'name',
30+
},
31+
{
32+
Header: formatMessage(messages.fileDescriptionTitle),
33+
accessor: 'description',
34+
},
35+
{
36+
Header: formatMessage(messages.fileSizeTitle),
37+
accessor: 'size',
38+
},
39+
]}
40+
/>
41+
</>
42+
)}
43+
<Dropzone />
44+
</div>
45+
);
46+
};
47+
48+
export default FileUpload;

src/components/FileUpload/messages.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { defineMessages } from '@edx/frontend-platform/i18n';
2+
3+
const messages = defineMessages({
4+
fileNameTitle: {
5+
id: 'ora-grading.FileContent.fileNameTitle',
6+
defaultMessage: 'File Name',
7+
description: ' title for file name',
8+
},
9+
fileDescriptionTitle: {
10+
id: 'ora-grading.FileCellContent.fileDescriptionTitle',
11+
defaultMessage: 'File Description',
12+
description: ' title for file description',
13+
},
14+
fileSizeTitle: {
15+
id: 'ora-grading.FileCellContent.fileSizeTitle',
16+
defaultMessage: 'File Size',
17+
description: ' title for file size',
18+
},
19+
});
20+
21+
export default messages;

src/components/InfoPopover/index.jsx

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
import {
5+
OverlayTrigger,
6+
Popover,
7+
Icon,
8+
IconButton,
9+
} from '@edx/paragon';
10+
import { InfoOutline } from '@edx/paragon/icons';
11+
import { useIntl } from '@edx/frontend-platform/i18n';
12+
13+
import { nullMethod } from 'hooks';
14+
15+
import messages from './messages';
16+
17+
/**
18+
* <InfoPopover />
19+
*/
20+
export const InfoPopover = ({ onClick, children }) => {
21+
const { formatMessage } = useIntl();
22+
return (
23+
<OverlayTrigger
24+
trigger="focus"
25+
placement="right-end"
26+
flip
27+
overlay={(
28+
<Popover id="info-popover" className="overlay-help-popover">
29+
<Popover.Content>{children}</Popover.Content>
30+
</Popover>
31+
)}
32+
>
33+
<IconButton
34+
className="esg-help-icon"
35+
src={InfoOutline}
36+
alt={formatMessage(messages.altText)}
37+
iconAs={Icon}
38+
onClick={onClick}
39+
/>
40+
</OverlayTrigger>
41+
);
42+
};
43+
44+
InfoPopover.defaultProps = {
45+
onClick: nullMethod,
46+
};
47+
InfoPopover.propTypes = {
48+
onClick: PropTypes.func,
49+
children: PropTypes.oneOfType([
50+
PropTypes.arrayOf(PropTypes.node),
51+
PropTypes.node,
52+
]).isRequired,
53+
};
54+
55+
export default InfoPopover;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { defineMessages } from '@edx/frontend-platform/i18n';
2+
3+
const messages = defineMessages({
4+
altText: {
5+
id: 'ora-grading.InfoPopover.alt-text',
6+
defaultMessage: 'Display more info',
7+
description: 'Info popover trigger element alt-text',
8+
},
9+
});
10+
11+
export default messages;

src/components/Prompt/index.jsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
import { useORAConfigData } from 'data/services/lms/hooks/selectors';
5+
6+
export const Prompt = ({ promptIndex }) => {
7+
const { prompts } = useORAConfigData();
8+
return (
9+
<div className="m-1 p-1">
10+
<h3>Prompt {promptIndex + 1}</h3>
11+
<div dangerouslySetInnerHTML={{ __html: prompts[promptIndex] }} />
12+
</div>
13+
);
14+
};
15+
16+
Prompt.propTypes = {
17+
promptIndex: PropTypes.number.isRequired,
18+
};
19+
20+
export default Prompt;
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
import { Form } from '@edx/paragon';
5+
import { useIntl } from '@edx/frontend-platform/i18n';
6+
import { StrictDict, useKeyedState } from '@edx/react-unit-test-utils';
7+
8+
import { feedbackRequirement } from 'data/services/lms/constants';
9+
import messages from './messages';
10+
11+
export const stateKeys = StrictDict({
12+
value: 'value',
13+
});
14+
15+
/**
16+
* <CriterionFeedback />
17+
*/
18+
const CriterionFeedback = ({
19+
criterion,
20+
}) => {
21+
const [value, setValue] = useKeyedState(stateKeys.value, '');
22+
const { formatMessage } = useIntl();
23+
24+
if (
25+
!criterion.feedbackEnabled
26+
|| criterion.feedbackRequired === feedbackRequirement.disabled
27+
) {
28+
return null;
29+
}
30+
31+
const onChange = ({ target }) => { setValue(target.value); };
32+
let commentMessage = formatMessage(messages.addComments);
33+
if (criterion.feedbackRequired === feedbackRequirement.optional) {
34+
commentMessage += ` ${formatMessage(messages.optional)}`;
35+
}
36+
37+
const isInvalid = value === '';
38+
39+
return (
40+
<Form.Group isInvalid={isInvalid}>
41+
<Form.Control
42+
as="textarea"
43+
className="criterion-feedback feedback-input"
44+
floatingLabel={commentMessage}
45+
value={value}
46+
onChange={onChange}
47+
/>
48+
{isInvalid && (
49+
<Form.Control.Feedback type="invalid" className="feedback-error-msg">
50+
{formatMessage(messages.criterionFeedbackError)}
51+
</Form.Control.Feedback>
52+
)}
53+
</Form.Group>
54+
);
55+
};
56+
57+
CriterionFeedback.propTypes = {
58+
criterion: PropTypes.shape({
59+
feedbackEnabled: PropTypes.bool,
60+
feedbackRequired: PropTypes.string,
61+
}).isRequired,
62+
};
63+
64+
export default CriterionFeedback;

0 commit comments

Comments
 (0)