Skip to content

Commit

Permalink
feat: progress updates
Browse files Browse the repository at this point in the history
Bw/fake data
  • Loading branch information
Ben Warzeski authored Oct 10, 2023
2 parents 5363fae + d560277 commit 98ddeef
Show file tree
Hide file tree
Showing 54 changed files with 1,106 additions and 669 deletions.
5 changes: 3 additions & 2 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import { Spinner } from '@edx/paragon';

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

import AppContainer from 'views/AppContainer';
import ModalContainer from 'views/ModalContainer';
import PeerAssessmentView from 'views/PeerAssessmentView';
import SelfAssessmentView from 'views/SelfAssessmentView';
import StudentTrainingView from 'views/StudentTrainingView';
import SubmissionView from 'views/SubmissionView';
import XBlockView from 'views/XBlockView';

import AppContainer from 'components/AppContainer';
import ModalContainer from 'components/ModalContainer';
import PageDataProvider from 'components/PageDataProvider';

import messages from './messages';
Expand Down
File renamed without changes.
File renamed without changes.
78 changes: 78 additions & 0 deletions src/components/ProgressBar/ProgressStep.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import { StrictDict } from '@edx/react-unit-test-utils';
import { Nav, Icon } from '@edx/paragon';
import {
CheckCircle,
Edit,
Error,
Highlight,
Rule,
} from '@edx/paragon/icons';

import { stepNames } from 'data/services/lms/constants';
import { useProgressStepData } from './hooks';

export const stepIcons = StrictDict({
[stepNames.submission]: Edit,
[stepNames.studentTraining]: Highlight,
[stepNames.self]: Highlight,
[stepNames.peer]: Highlight,
[stepNames.myGrades]: Rule,
});

const ProgressStep = ({
step,
canRevisit,
label,
}) => {
const {
href,
isActive,
isEnabled,
isComplete,
isPastDue,
myGrade,
} = useProgressStepData({ step, canRevisit });
let iconSrc = stepIcons[step];
let subLabel = null;
let colorClass = null;
if (isPastDue) {
colorClass = 'text-danger-500';
iconSrc = Error;
subLabel = 'Past due!';
} else if (isComplete) {
iconSrc = CheckCircle;
if (step === stepNames.myGrades && myGrade) {
subLabel = `${myGrade.earned} / ${myGrade.possible}`;
}
}
return (
<Nav.Link
{...(!isActive && { href })}
disabled={!isEnabled}
className={classNames(
'ora-progress-nav',
'px-4',
{ 'is-active': isActive },
)}
>
<Icon className={classNames('nav-icon', colorClass)} src={iconSrc} />
<div className="d-inline-block">
{label}
{subLabel && (
<p className={classNames('x-small', colorClass)}>{subLabel}</p>
)}
</div>
</Nav.Link>
);
};
ProgressStep.propTypes = {
label: PropTypes.node.isRequired,
step: PropTypes.string.isRequired,
canRevisit: PropTypes.bool.isRequired,
};

export default ProgressStep;
40 changes: 40 additions & 0 deletions src/components/ProgressBar/hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useParams } from 'react-router-dom';
import { useActiveView, useIsEmbedded } from 'hooks';
import { useStepState, useEffectiveGrade } from 'data/services/lms/hooks/selectors';
import {
routeSteps,
stepRoutes,
stepStates,
} from 'data/services/lms/constants';

export const useProgressStepData = ({ step, canRevisit = false }) => {
const { xblockId } = useParams();
const isEmbedded = useIsEmbedded();
const activeView = useActiveView();
const viewStep = routeSteps[activeView];
const stepState = useStepState({ step });

const href = `/${stepRoutes[step]}${isEmbedded ? '/embedded' : ''}/${xblockId}`;
const isActive = viewStep === step;
const isEnabled = (
isActive
|| (stepState === stepStates.inProgress)
|| (canRevisit && stepState === stepStates.completed)
);
const myGrade = useEffectiveGrade()?.stepScore;

return {
href,
isEnabled,
isActive,
isComplete: stepState === stepStates.completed,
inProgress: stepState === stepStates.inProgress,
isPastDue: stepState === stepStates.closed,
myGrade,
// myGrade: { earned: 8, possible: 10 },
// isPastDue: step === 'self',

};
};

export default { useProgressStepData };
143 changes: 36 additions & 107 deletions src/components/ProgressBar/index.jsx
Original file line number Diff line number Diff line change
@@ -1,135 +1,64 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useLocation } from 'react-router-dom';
import classNames from 'classnames';

import { useIntl } from '@edx/frontend-platform/i18n';
import { Navbar, Nav, Icon } from '@edx/paragon';
import { Locked, CheckCircle, Error } from '@edx/paragon/icons';
import { Navbar } from '@edx/paragon';

import {
useAssessmentStepConfig,
useProgressData,
useAssessmentStepOrder,
useIsPageDataLoaded,
} from 'data/services/lms/hooks/selectors';
import { stepNames } from 'data/services/lms/constants';

import ProgressStep from './ProgressStep';

import messages from './messages';
import './index.scss';

export const ProgressStep = ({
children,
href,
isActive,
isEnabled,
isComplete,
isError,
number,
}) => {
let icon = <Icon className="nav-icon" src={Locked} />;
if (isComplete) {
icon = <Icon className="nav-icon" src={CheckCircle} />;
} else if (isError) {
icon = <Icon className="nav-icon text-danger-300" src={Error} />;
} else if (number) {
icon = <span className="nav-icon number-icon">{number}</span>;
}
return (
<Nav.Link
href={href}
disabled={!isEnabled}
className={classNames(
'ora-progress-nav',
'px-4',
{ 'is-active': isActive },
)}
>
{icon}
{children}
</Nav.Link>
);
};
ProgressStep.defaultProps = {
href: '#',
isActive: false,
isEnabled: false,
isComplete: false,
isError: false,
number: null,
};
ProgressStep.propTypes = {
children: PropTypes.node.isRequired,
href: PropTypes.string,
isActive: PropTypes.bool,
isEnabled: PropTypes.bool,
isError: PropTypes.bool,
isComplete: PropTypes.bool,
number: PropTypes.number,
export const stepLabels = {
[stepNames.submission]: messages.createSubmission,
[stepNames.peer]: messages.peerAssess,
[stepNames.studentTraining]: messages.studentTraining,
[stepNames.self]: messages.selfAssess,
[stepNames.myGrades]: messages.myGrade,
};

export const SubmissionStep = () => {
const { formatMessage } = useIntl();
return (
<ProgressStep>{formatMessage(messages.createSubmission)}</ProgressStep>
);
export const stepCanRevisit = {
[stepNames.submission]: true,
[stepNames.peer]: true,
[stepNames.studentTraining]: false,
[stepNames.self]: false,
[stepNames.myGrades]: false,
};

export const TrainingStep = () => {
const { formatMessage } = useIntl();
return (
<ProgressStep>{formatMessage(messages.practice)}</ProgressStep>
);
};

export const SelfAssessStep = () => {
const { formatMessage } = useIntl();
return (
<ProgressStep>{formatMessage(messages.selfAssess)}</ProgressStep>
);
};

export const PeerAssessStep = () => {
const { formatMessage } = useIntl();
return (
<ProgressStep>{formatMessage(messages.peerAssess)}</ProgressStep>
);
};
export const ProgressBar = () => {
const isLoaded = useIsPageDataLoaded();

export const MyGradeStep = () => {
const stepOrder = useAssessmentStepOrder();
const { formatMessage } = useIntl();
return (
<ProgressStep>{formatMessage(messages.myGrade)}</ProgressStep>
);
};

export const ProgressBar = () => {
const stepConfig = useAssessmentStepConfig();
const progress = useProgressData();
const isLoaded = useIsPageDataLoaded();
const location = useLocation();
if (!isLoaded) {
return null;
}
console.log({
stepConfig, progress, location,
});

const stepEl = (step) => (
stepLabels[step]
? (
<ProgressStep
step={step}
key={step}
label={formatMessage(stepLabels[step])}
canRevisit={stepCanRevisit[step]}
/>
) : null
);

return (
<Navbar>
<Navbar.Collapse className="ora-progress-nav-group">
<hr className="ora-progress-divider" />
<SubmissionStep />
{stepConfig.order.map(step => {
if (step === 'peer') {
return <PeerAssessStep key="peer" />;
}
if (step === 'training') {
return <TrainingStep key="training" />;
}
if (step === 'self') {
return <SelfAssessStep key="self" />;
}
return null;
})}
<MyGradeStep />
{stepEl(stepNames.submission)}
{stepOrder.map(stepEl)}
{stepEl(stepNames.myGrades)}
</Navbar.Collapse>
</Navbar>
);
Expand Down
3 changes: 3 additions & 0 deletions src/components/ProgressBar/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,7 @@
color: white;
border-radius: 100%;
}
.active {
border-bottom: 2px solid black;
}
}
4 changes: 2 additions & 2 deletions src/components/ProgressBar/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ const messages = defineMessages({
defaultMessage: 'Create your response',
description: 'Create response progress indicator',
},
practice: {
id: 'ora-grading.ProgressBar.practice',
studentTraining: {
id: 'ora-grading.ProgressBar.studentTraining',
defaultMessage: 'Practice grading',
description: 'Student training step progress indicator',
},
Expand Down
60 changes: 0 additions & 60 deletions src/components/StatefulStatus/index.jsx

This file was deleted.

Loading

0 comments on commit 98ddeef

Please sign in to comment.