Skip to content

Commit

Permalink
Merge branch 'develop/maple.1' into feature.maple/cgaber2045/estimate…
Browse files Browse the repository at this point in the history
…d-time
  • Loading branch information
cgaber2045 authored May 12, 2022
2 parents 751d24c + 5c6b77f commit 70f230f
Show file tree
Hide file tree
Showing 46 changed files with 3,534 additions and 42 deletions.
768 changes: 732 additions & 36 deletions package-lock.json

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,21 @@
"@reduxjs/toolkit": "1.6.2",
"classnames": "2.3.1",
"core-js": "3.18.3",
"iframe-resizer": "^4.3.2",
"js-cookie": "3.0.1",
"lodash.camelcase": "4.3.0",
"lodash.snakecase": "^4.1.1",
"mime-types": "^2.1.34",
"moment": "^2.29.1",
"moment-timezone": "^0.5.34",
"prop-types": "15.7.2",
"react": "17.0.2",
"react-break": "1.3.2",
"react-dom": "17.0.2",
"react-download-link": "^2.3.0",
"react-helmet": "6.1.0",
"react-markdown": "^8.0.0",
"react-moment": "^1.1.1",
"react-redux": "7.2.5",
"react-router": "5.2.1",
"react-router-dom": "5.3.0",
Expand All @@ -73,6 +81,7 @@
"axios-mock-adapter": "1.20.0",
"codecov": "3.8.3",
"es-check": "6.0.0",
"eslint-plugin-jsx-a11y": "^6.5.1",
"glob": "7.2.0",
"husky": "7.0.2",
"jest": "27.2.5",
Expand Down
49 changes: 49 additions & 0 deletions src/course-home/badges-tab/BadgeLeaderboardTab.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react';
// import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
// import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { useModel } from '../../generic/model-store';

import { BadgeTabsNavigation } from './badge-header';

// {
// intl
// }
function BadgeLeaderboardTab() {
const {
courseId,
} = useSelector(state => state.courseHome);

const { administrator, username } = getAuthenticatedUser();

const {
enrollmentMode,
} = useModel('courses', courseId);

const activeTabSlug = 'leaderboard';

return (
<>
<main className="d-flex flex-column">
<BadgeTabsNavigation className="mb-3 py-2" activeTabSlug={activeTabSlug} />
<div className="container-fluid">
<section>
<div className="mb-4">
the user is {username} and they are enrolled as an {enrollmentMode} learner
{administrator
&& <div><p>the user is admin</p></div>}
</div>
</section>
</div>
</main>
</>
);
}

// BadgeLeaderboardTab.propTypes = {
// intl: intlShape.isRequired,
// };

// export default injectIntl(BadgeLeaderboardTab);
export default BadgeLeaderboardTab;
142 changes: 142 additions & 0 deletions src/course-home/badges-tab/BadgeProgressTab.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import React, { useEffect, useState } from 'react';
// import PropTypes from 'prop-types';
import snakeCase from 'lodash.snakecase';
import { useSelector } from 'react-redux';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { StatusAlert } from '@edx/paragon';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faExclamationCircle } from '@fortawesome/free-solid-svg-icons';

import { useModel } from '../../generic/model-store';

import { BadgeTabsNavigation } from './badge-header';
import { BadgeProgressBanner, BadgeProgressCard, BadgeProgressCourseList } from './badge-progress';

import { headingMapper } from './utils';

function BadgeProgressTab({ intl }) { // eslint-disable-line no-unused-vars
const activeTabSlug = 'progress';

const {
courseId,
} = useSelector(state => state.courseHome);

const {
administrator,
username,
roles, // eslint-disable-line no-unused-vars
} = getAuthenticatedUser();

const hasInstructorStaffRights = () => administrator;

const [progress, setProgress] = useState([]);

const {
id,
...badgeProgressState
} = useModel('badge-progress', courseId);

const hasBadgeProgress = () => progress && progress.length > 0;
useEffect(() => {
let classProgressExists = 0;
const badgeProgress = Object.values(badgeProgressState);
if (hasInstructorStaffRights()) {
badgeProgress.forEach(student => {
if (student.progress.length) {
classProgressExists += 1;
}
});
if (classProgressExists) {
setProgress(badgeProgress);
}
} else {
setProgress(badgeProgress);
}
}, [courseId, administrator]);

const renderBadgeProgress = () => {
const defaultAssignmentFilter = 'All';

if (hasInstructorStaffRights()) {
return (
<>
<BadgeTabsNavigation className="mb-3 py-2" activeTabSlug={activeTabSlug} />
<BadgeProgressBanner
hasProgress={hasBadgeProgress()}
hasRights={hasInstructorStaffRights()}
/>
<BadgeProgressCourseList
data={progress}
headings={headingMapper(defaultAssignmentFilter, progress)(progress[0])}
/>
</>
);
}

/*
<section>
<div className="mb-4">
the user is {username} and they are enrolled as an {enrollmentMode} learner
{administrator
&& <div><p>the user is admin</p></div>}
</div>
</section>
*/
return (
<>
<div className="d-flex flex-column">
<BadgeTabsNavigation className="mb-3 py-2" activeTabSlug={activeTabSlug} />
<BadgeProgressBanner hasProgress={hasBadgeProgress()} hasRights={hasInstructorStaffRights()} />
<div className="container-fluid">
<section className="row">
<div className="col-sm-12 col-md-12 col-lg-12 col-xl-12">
{progress && (
<div className="row equal-col-height">
{progress.map(learnerProgress => {
const itemKey = snakeCase(`card ${learnerProgress.blockDisplayName} ${username}`);
return (
<BadgeProgressCard key={`${itemKey}`} data={learnerProgress} />
);
})}
</div>
)}
</div>
</section>
</div>
</div>
</>
);
};

const renderNoBadgeProgress = () => (
<StatusAlert
dialog={(
<>
<FontAwesomeIcon icon={faExclamationCircle} className="mr-2" />
There is no course badge progress to show.
</>
)}
alertType="info"
dismissible={false}
open
/>
);

return (
<>
{hasBadgeProgress() && (
renderBadgeProgress()
)}
{!hasBadgeProgress() && (
renderNoBadgeProgress()
)}
</>
);
}

BadgeProgressTab.propTypes = {
intl: intlShape.isRequired,
};

export default injectIntl(BadgeProgressTab);
83 changes: 83 additions & 0 deletions src/course-home/badges-tab/BadgeProgressTab.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/* eslint-disable */
// TODO Need to complete these tests.

// import React from 'react';
// import { Route } from 'react-router';
// import MockAdapter from 'axios-mock-adapter';
// import { Factory } from 'rosie';
// import { getConfig, history } from '@edx/frontend-platform';
// import { sendTrackEvent } from '@edx/frontend-platform/analytics';
// import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
// import { AppProvider } from '@edx/frontend-platform/react';
// import { waitForElementToBeRemoved } from '@testing-library/dom';
// import { render, screen, within } from '@testing-library/react'; // eslint-disable-line no-unused-vars
// import userEvent from '@testing-library/user-event';

// import BadgeProgressTab from './BadgeProgressTab';
// import { fetchBadgeProgressTab } from '../data';
import { fireEvent, initializeMockApp, waitFor } from '../../setupTest'; // eslint-disable-line no-unused-vars
import initializeStore from '../../store';
// import { TabContainer } from '../../tab-page';
// import { appendBrowserTimezoneToUrl } from '../../utils';
// import { UserMessagesProvider } from '../../generic/user-messages';

initializeMockApp();
jest.mock('@edx/frontend-platform/analytics');

describe('BadgeProgressTab', () => {
let axiosMock;
let component;

const store = initializeStore();

// beforeEach(() => {
// axiosMock = new MockAdapter(getAuthenticatedHttpClient());
// store = initializeStore();
// // component = (
// // <AppProvider store={store}>
// // <UserMessagesProvider>
// // <Route path="/course/:courseId/badges/progress">
// // <TabContainer tab="badge-progress" fetch={fetchBadgeProgressTab} slice="courseHome">
// // <BadgeProgressTab />
// // </TabContainer>
// // </Route>
// // </UserMessagesProvider>
// // </AppProvider>
// // );
// });

// const badgeProgressTabData = Factory.build('badgeProgressTabData');
// let courseMetadata = Factory.build('courseHomeMetadata', { user_timezone: 'America/New_York' });
// const { id: courseId } = courseMetadata;

// const badgeProgressUrl = `${getConfig().LMS_BASE_URL}/api/badges/v1/progress/${courseId}`;
// let courseMetadataUrl = `${getConfig().LMS_BASE_URL}/api/course_home/course_metadata/${courseId}`;
// courseMetadataUrl = appendBrowserTimezoneToUrl(courseMetadataUrl);

// function setMetadata(attributes, options) { // eslint-disable-line no-unused-vars
// courseMetadata = Factory.build('courseHomeMetadata', { id: courseId, ...attributes }, options);
// axiosMock.onGet(courseMetadataUrl).reply(200, courseMetadata);
// }

it('Todo: Need Test', () => {

});

// describe('when receiving a full set of dates data', () => {
// beforeEach(() => {
// axiosMock.onGet(courseMetadataUrl).reply(200, courseMetadata);
// axiosMock.onGet(badgeProgressUrl).reply(200, badgeProgressTabData);
// history.push(`/course/${courseId}/badges/progress`); // so tab can pull course id from url

// render(component);
// });

// it('handles unreleased & complete', async () => {
// // const { header } = await getDay('Sun, May 3, 2020');
// // const badges = within(header).getAllByTestId('dates-badge');
// // expect(badges).toHaveLength(2);
// // expect(badges[0]).toHaveTextContent('Completed');
// // expect(badges[1]).toHaveTextContent('Not yet released');
// });
// });
});
Loading

0 comments on commit 70f230f

Please sign in to comment.