diff --git a/src/components/Assessment/ReadonlyAssessment/AssessmentCriterion.jsx b/src/components/Assessment/ReadonlyAssessment/AssessmentCriterion.jsx
new file mode 100644
index 00000000..bdd19cc5
--- /dev/null
+++ b/src/components/Assessment/ReadonlyAssessment/AssessmentCriterion.jsx
@@ -0,0 +1,52 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import { useIntl } from '@edx/frontend-platform/i18n';
+import Feedback from './Feedback';
+import messages from './messages';
+import { useORAConfigData } from 'data/services/lms/hooks/selectors';
+
+const AssessmentCriterion = ({
+ criteria,
+ overallFeedback,
+ stepLabel,
+}) => {
+ const { formatMessage } = useIntl();
+ const { rubricConfig } = useORAConfigData();
+ return (
+ <>
+ {rubricConfig.criteria.map((criterion, i) => {
+ const assessmentCriterion = criteria[i];
+ const option = criterion.options[assessmentCriterion.selectedOption];
+ return (
+
+ );
+ })}
+
+ >
+ );
+};
+AssessmentCriterion.defaultProps = {};
+AssessmentCriterion.propTypes = {
+ criteria: PropTypes.arrayOf(PropTypes.shape({
+ selectedOption: PropTypes.number,
+ // selectedPoints: PropTypes.number,
+ feedback: PropTypes.string,
+ })),
+ overallFeedback: PropTypes.string,
+ stepLabel: PropTypes.string.isRequired,
+};
+
+export default AssessmentCriterion;
diff --git a/src/components/Assessment/ReadonlyAssessment/Feedback.jsx b/src/components/Assessment/ReadonlyAssessment/Feedback.jsx
index a624a2c5..14ed9cbe 100644
--- a/src/components/Assessment/ReadonlyAssessment/Feedback.jsx
+++ b/src/components/Assessment/ReadonlyAssessment/Feedback.jsx
@@ -33,7 +33,7 @@ const Feedback = ({
{criterionName}
{criterionDescription && (
-
{}}>
+
{criterionDescription}
)}
diff --git a/src/components/FileUpload/index.jsx b/src/components/FileUpload/index.jsx
index a531876d..7c02e241 100644
--- a/src/components/FileUpload/index.jsx
+++ b/src/components/FileUpload/index.jsx
@@ -6,6 +6,7 @@ import { DataTable, Dropzone } from '@edx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';
import { nullMethod } from 'hooks';
+import { useFileUploadEnabled } from 'data/services/lms/hooks/selectors';
import UploadConfirmModal from './UploadConfirmModal';
import ActionCell from './ActionCell';
@@ -28,6 +29,9 @@ const FileUpload = ({
defaultCollapsePreview,
}) => {
const { formatMessage } = useIntl();
+ if ( !useFileUploadEnabled() ) {
+ return null;
+ }
const {
confirmUpload,
diff --git a/src/components/ProgressBar/hooks.js b/src/components/ProgressBar/hooks.js
index 649af80e..9c26ed62 100644
--- a/src/components/ProgressBar/hooks.js
+++ b/src/components/ProgressBar/hooks.js
@@ -20,14 +20,14 @@ export const useProgressStepData = ({ step, canRevisit = false }) => {
const isEnabled = (
isActive
|| (stepState === stepStates.inProgress)
- || (canRevisit && stepState === stepStates.completed)
+ || (canRevisit && stepState === stepStates.done)
);
const myGrade = useEffectiveGrade()?.stepScore;
return {
href,
isEnabled,
isActive,
- isComplete: stepState === stepStates.completed,
+ isComplete: stepState === stepStates.done,
inProgress: stepState === stepStates.inProgress,
isPastDue: stepState === stepStates.closed,
myGrade,
diff --git a/src/components/ProgressBar/index.jsx b/src/components/ProgressBar/index.jsx
index cc0abb7a..7607d397 100644
--- a/src/components/ProgressBar/index.jsx
+++ b/src/components/ProgressBar/index.jsx
@@ -7,7 +7,9 @@ import { Navbar } from '@edx/paragon';
import {
useAssessmentStepOrder,
+ useHasReceivedFinalGrade,
useIsPageDataLoaded,
+ useStepInfo,
} from 'data/services/lms/hooks/selectors';
import { stepNames } from 'data/services/lms/constants';
@@ -34,33 +36,35 @@ export const stepCanRevisit = {
export const ProgressBar = ({ className }) => {
const isLoaded = useIsPageDataLoaded();
+ const stepInfo = useStepInfo();
+ const hasReceivedFinalGrade = useHasReceivedFinalGrade();
- const stepOrder = useAssessmentStepOrder();
+ const stepOrders = [
+ stepNames.submission,
+ ...useAssessmentStepOrder(),
+ stepNames.done,
+ ];
const { formatMessage } = useIntl();
if (!isLoaded) {
return null;
}
- const stepEl = (step) => (
- stepLabels[step]
- ? (
-
- ) : null
- );
+ const stepEl = (curStep) =>
+ stepLabels[curStep] ? (
+
+ ) : null;
return (
-
-
- {stepEl(stepNames.submission)}
- {stepOrder.map(stepEl)}
- {stepEl(stepNames.done)}
+
+
+ {stepOrders.map(stepEl)}
);
diff --git a/src/components/StatusAlert/index.jsx b/src/components/StatusAlert/index.jsx
index 3bba6e2f..d0c052ac 100644
--- a/src/components/StatusAlert/index.jsx
+++ b/src/components/StatusAlert/index.jsx
@@ -11,6 +11,9 @@ const StatusAlert = ({
step,
showTrainingError,
}) => {
+ if ( step === null ) {
+ return null;
+ }
const {
variant,
icon,
diff --git a/src/components/StatusAlert/useStatusAlert.js b/src/components/StatusAlert/useStatusAlert.js
new file mode 100644
index 00000000..d3134f9d
--- /dev/null
+++ b/src/components/StatusAlert/useStatusAlert.js
@@ -0,0 +1,92 @@
+import { useIntl } from '@edx/frontend-platform/i18n';
+import { CheckCircle, Info, WarningFilled } from '@edx/paragon/icons';
+import { StrictDict } from '@edx/react-unit-test-utils';
+
+import {
+ stepNames,
+ stepStates,
+} from 'data/services/lms/constants';
+import { useGlobalState } from 'data/services/lms/hooks/selectors';
+import messages from './messages';
+import alertMessages from './alertMessages';
+import headingMessages from './alertHeadingMessages';
+
+export const alertMap = {
+ [stepStates.done]: {
+ variant: 'success',
+ icon: CheckCircle,
+ },
+ [stepStates.closed]: {
+ variant: 'danger',
+ icon: Info,
+ },
+ [stepStates.teamAlreadySubmitted]: {
+ variant: 'warning',
+ icon: WarningFilled,
+ },
+ [stepStates.cancelled]: {
+ variant: 'warning',
+ icon: WarningFilled,
+ },
+ [stepStates.inProgress]: {
+ variant: 'dark',
+ icon: null,
+ },
+};
+
+const useStatusAlertMessages = (step = null) => {
+ const { formatMessage } = useIntl();
+ const {
+ activeStepName,
+ stepState,
+ cancellationInfo,
+ } = useGlobalState({ step });
+ const stepName = step || activeStepName;
+ const isRevisit = stepName !== activeStepName;
+ if (cancellationInfo.hasCancelled) {
+ const { cancelledBy, cancelledAt } = cancellationInfo;
+ if (cancelledBy) {
+ return {
+ message: formatMessage(
+ alertMessages.submission.cancelledBy,
+ { cancelledBy, cancelledAt },
+ ),
+ heading: formatMessage(headingMessages.submission.cancelledBy),
+ };
+ }
+ return {
+ message: formatMessage(alertMessages.submission.cancelledAt, { cancelledAt }),
+ heading: formatMessage(headingMessages.submission.cancelledAt),
+ };
+ }
+ if (stepName === stepNames.submission && isRevisit) {
+ return {
+ message: formatMessage(alertMessages.submission.finished),
+ heading: formatMessage(headingMessages.submission.finished),
+ };
+ }
+ if (stepName === stepNames.peer && isRevisit && stepState !== stepStates.waiting) {
+ return {
+ message: formatMessage(alertMessages.peer.finished),
+ heading: formatMessage(headingMessages.peer.finished),
+ };
+ }
+ return {
+ message: formatMessage(alertMessages[stepName][stepState]),
+ heading: formatMessage(headingMessages[stepName][stepState]),
+ };
+};
+
+const useStatusAlert = (step = null) => {
+ const { stepState } = useGlobalState({ step });
+ const { variant, icon } = alertMap[stepState];
+ const { message, heading } = useStatusAlertMessages(step);
+ return {
+ variant,
+ icon,
+ message,
+ heading,
+ };
+};
+
+export default useStatusAlert;
diff --git a/src/components/StatusAlert/useStatusAlertData.jsx b/src/components/StatusAlert/useStatusAlertData.jsx
index e6021a40..745eb8aa 100644
--- a/src/components/StatusAlert/useStatusAlertData.jsx
+++ b/src/components/StatusAlert/useStatusAlertData.jsx
@@ -15,7 +15,7 @@ import alertMessages from './alertMessages';
import headingMessages from './alertHeadingMessages';
export const alertMap = {
- [stepStates.completed]: {
+ [stepStates.done]: {
variant: 'success',
icon: CheckCircle,
},
diff --git a/src/data/services/lms/constants.js b/src/data/services/lms/constants.js
index da3fb68c..5a02d624 100644
--- a/src/data/services/lms/constants.js
+++ b/src/data/services/lms/constants.js
@@ -20,7 +20,7 @@ export const MutationStatus = StrictDict({
export const stepStates = StrictDict({
inProgress: 'inProgress',
- completed: 'completed',
+ done: 'done',
cancelled: 'cancelled',
closed: 'closed',
notAvailable: 'notAvailable',
diff --git a/src/data/services/lms/fakeData/dataStates.js b/src/data/services/lms/fakeData/dataStates.js
index d467c19a..2aa5867c 100644
--- a/src/data/services/lms/fakeData/dataStates.js
+++ b/src/data/services/lms/fakeData/dataStates.js
@@ -31,7 +31,7 @@ export const loadState = (opts) => {
const state = {
progress: pageData.getProgressState({ progressKey, stepConfig, viewStep }),
response: pageData.getResponseState({ progressKey, isTeam }),
- assessments: pageData.getAssessmentState({ progressKey, stepConfig }),
+ assessment: pageData.getAssessmentState({ progressKey, stepConfig }),
};
console.log({
opts, progressKey, state, isTeam,
diff --git a/src/data/services/lms/hooks/data.ts b/src/data/services/lms/hooks/data.ts
index 8a2cb484..ac5efde9 100644
--- a/src/data/services/lms/hooks/data.ts
+++ b/src/data/services/lms/hooks/data.ts
@@ -17,15 +17,9 @@ export const useORAConfig = (): types.QueryData => {
return useQuery({
queryKey: [queryKeys.oraConfig],
queryFn: () => {
- /*
return getAuthenticatedHttpClient().post(oraConfigUrl, {}).then(
({ data }) => camelCaseObject(data)
);
- */
- console.log({ oraConfig: camelCaseObject(fakeData.oraConfig.assessmentTinyMCE) });
- return Promise.resolve(
- camelCaseObject(fakeData.oraConfig.assessmentTinyMCE)
- );
},
});
};
@@ -39,12 +33,9 @@ export const usePageData = (): types.QueryData => {
return useQuery({
queryKey: [queryKeys.pageData],
queryFn: () => {
- /*
return getAuthenticatedHttpClient().post(pageDataUrl, {}).then(
({ data }) => camelCaseObject(data)
);
- */
- return Promise.resolve(camelCaseObject(loadState({ view, progressKey })));
},
});
};
diff --git a/src/data/services/lms/hooks/selectors/index.ts b/src/data/services/lms/hooks/selectors/index.ts
index bb9b87e9..b8cca2f3 100644
--- a/src/data/services/lms/hooks/selectors/index.ts
+++ b/src/data/services/lms/hooks/selectors/index.ts
@@ -30,7 +30,7 @@ export const useStepState = ({ step = null } = {}) => {
const stepIndex = selectors.useStepIndex({ step: stepName });
const subState = selectors.useSubmissionState();
if (hasReceivedFinalGrade) {
- return stepStates.completed;
+ return stepStates.done;
}
if (step === stepNames.submission) {
@@ -41,14 +41,14 @@ export const useStepState = ({ step = null } = {}) => {
if (hasCancelled) { return stepStates.cancelled; }
if (step === stepNames.done) {
- return hasReceivedFinalGrade ? stepStates.completed : stepStates.notAvailable;
+ return hasReceivedFinalGrade ? stepStates.done : stepStates.notAvailable;
}
if (step === stepNames.peer && stepInfo?.peer?.isWaitingForSubmissions) {
return stepStates.waiting;
}
// For Assessment steps
- if (stepIndex < activeStepIndex) { return stepStates.completed; }
+ if (stepIndex < activeStepIndex) { return stepStates.done; }
if (stepIndex > activeStepIndex) { return stepStates.notAvailable; }
// only check for closed or not-available on active step
diff --git a/src/data/services/lms/hooks/selectors/oraConfig.ts b/src/data/services/lms/hooks/selectors/oraConfig.ts
index e9fc5607..eb81e80b 100644
--- a/src/data/services/lms/hooks/selectors/oraConfig.ts
+++ b/src/data/services/lms/hooks/selectors/oraConfig.ts
@@ -36,6 +36,8 @@ export const useAssessmentStepConfig = (): types.AssessmentStepConfig => (
useORAConfigData().assessmentSteps
);
+export const useFileUploadEnabled = (): boolean => useSubmissionConfig().fileResponseConfig.enabled;
+
export const useAssessmentStepOrder = (): string[] => useAssessmentStepConfig()?.order;
export const useStepIndex = ({ step }): number => useAssessmentStepOrder().indexOf(step);
diff --git a/src/data/services/lms/hooks/selectors/pageData.ts b/src/data/services/lms/hooks/selectors/pageData.ts
index 572a2cd1..b4c71a52 100644
--- a/src/data/services/lms/hooks/selectors/pageData.ts
+++ b/src/data/services/lms/hooks/selectors/pageData.ts
@@ -54,7 +54,7 @@ export const useSubmissionState = () => {
}
if (subStatus.hasSubmitted) {
- return stepStates.completed;
+ return stepStates.done;
}
if (subStatus.isClosed) {
if (subStatus.closedReason === closedReasons.pastDue) {
@@ -68,13 +68,10 @@ export const useSubmissionState = () => {
return stepStates.inProgress;
};
-// Assessments
-export const useAssessmentsData = (): types.AssessmentsData => {
- console.log({ pageData: usePageData() });
- return usePageData().assessments;
-};
-export const useHasReceivedFinalGrade = (): boolean => useAssessmentsData() !== null;
+// Assessment
+export const useAssessmentData = (): types.AssessmentsData => usePageData().assessment;
+export const useHasReceivedFinalGrade = (): boolean => useAssessmentData() !== null;
export const useEffectiveGrade = () => {
- const assessments = useAssessmentsData();
- return assessments ? assessments[assessments.effectiveAssessmentType] : null;
+ const assessment = useAssessmentData();
+ return assessment ? assessment[assessment.effectiveAssessmentType] : null;
};
diff --git a/src/data/services/lms/types/pageData.ts b/src/data/services/lms/types/pageData.ts
index 14d2b6f3..d81a82d1 100644
--- a/src/data/services/lms/types/pageData.ts
+++ b/src/data/services/lms/types/pageData.ts
@@ -76,7 +76,7 @@ export interface ResponseData {
// Assessments Data
export interface AssessmentData {
- assessmentCriterions: {
+ criteria: {
selectedOption: number | null,
feedback: string,
}[],
@@ -106,5 +106,5 @@ export interface AssessmentsData {
export interface PageData {
progress: ProgressData,
response: ResponseData,
- assessments: AssessmentsData
+ assessment: AssessmentsData
}
diff --git a/src/data/services/lms/urls.js b/src/data/services/lms/urls.js
index c5fe9839..5b74c4ac 100644
--- a/src/data/services/lms/urls.js
+++ b/src/data/services/lms/urls.js
@@ -22,10 +22,10 @@ export const useViewUrl = () => {
export const usePageDataUrl = (step) => {
const baseUrl = useBaseUrl();
- if ([stepNames.submission, stepNames.xblock].includes(step)) {
- return `${baseUrl}/get_block_learner_submission_data`;
+ if ( [stepNames.submission, stepNames.peer].includes(step) ) {
+ return `${baseUrl}/get_learner_data/${step}`;
}
- return `${baseUrl}/get_block_learner_assessment_data/${step}`;
+ return `${baseUrl}/get_learner_data`;
};
export default StrictDict({
diff --git a/src/index.jsx b/src/index.jsx
index 899fba76..cefb6bbc 100644
--- a/src/index.jsx
+++ b/src/index.jsx
@@ -27,7 +27,7 @@ subscribe(APP_READY, () => {
// This is a hack to prevent the Paragon Modal overlay stop query devtools from clickable
rootEl.removeAttribute('data-focus-on-hidden');
rootEl.removeAttribute('aria-hidden');
- }, 1000);
+ }, 3000);
}
ReactDOM.render(
diff --git a/src/views/GradeView/FinalGrade.jsx b/src/views/GradeView/FinalGrade.jsx
index 780c5a8c..e7a3afc8 100644
--- a/src/views/GradeView/FinalGrade.jsx
+++ b/src/views/GradeView/FinalGrade.jsx
@@ -2,14 +2,18 @@ import React from 'react';
import { useIntl } from '@edx/frontend-platform/i18n';
-import { useAssessmentsData } from 'data/services/lms/hooks/selectors';
+import {
+ useAssessmentData,
+ useStepInfo,
+} from 'data/services/lms/hooks/selectors';
import InfoPopover from 'components/InfoPopover';
import ReadOnlyAssessment from 'components/Assessment/ReadonlyAssessment';
import messages, { labelMessages } from './messages';
const FinalGrade = () => {
const { formatMessage } = useIntl();
- const { effectiveAssessmentType, ...assessments } = useAssessmentsData();
+ const { effectiveAssessmentType, ...assessments } = useAssessmentData();
+ const stepInfo = useStepInfo();
const loadStepData = (step) => ({
...assessments[step],
@@ -22,7 +26,7 @@ const FinalGrade = () => {
const finalStepScore = effectiveAssessment?.stepScore;
const extraGrades = Object.keys(assessments)
- .filter(type => type !== effectiveAssessmentType)
+ .filter((type) => !!stepInfo[type] && type !== effectiveAssessmentType)
.map(loadStepData);
const renderAssessment = (stepData, defaultOpen = false) => (
@@ -33,23 +37,27 @@ const FinalGrade = () => {
{formatMessage(messages.yourFinalGrade, finalStepScore)}
- {}}>
+
{effectiveAssessmentType === 'peer'
? formatMessage(messages.peerAsFinalGradeInfo)
- : formatMessage(messages.finalGradeInfo, { step: effectiveAssessmentType })}
+ : formatMessage(messages.finalGradeInfo, {
+ step: effectiveAssessmentType,
+ })}
{renderAssessment(effectiveAssessment, true)}
-
-
- {formatMessage(messages.unweightedGrades)}
- {}}>
- {formatMessage(messages.unweightedGradesInfo)}
-
-
- {extraGrades.map(assessment => renderAssessment(assessment, false))}
+
+ {extraGrades.length > 0 && (
+
+ {formatMessage(messages.unweightedGrades)}
+
+ {formatMessage(messages.unweightedGradesInfo)}
+
+
+ )}
+ {extraGrades.map((assessment) => renderAssessment(assessment, false))}
);
};
diff --git a/src/views/SubmissionView/index.jsx b/src/views/SubmissionView/index.jsx
index e61f9e28..60b0741e 100644
--- a/src/views/SubmissionView/index.jsx
+++ b/src/views/SubmissionView/index.jsx
@@ -33,7 +33,7 @@ export const SubmissionView = () => {
} = useSubmissionViewData();
const stepState = useStepState({ step: stepNames.submission });
- const isReadOnly = stepState === stepStates.completed;
+ const isReadOnly = stepState === stepStates.done;
const { formatMessage } = useIntl();
const draftIndicator = (!isReadOnly && isDraftSaved) && (