Compare commits
12 Commits
v1.4.4
...
robrap/foo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a284c286f5 | ||
|
|
bea36fb387 | ||
|
|
1408b0ae7e | ||
|
|
7b817a4234 | ||
|
|
a762c47d77 | ||
|
|
aecb93c252 | ||
|
|
5a489b1bd5 | ||
|
|
5c642a1be5 | ||
|
|
9a0e0e0ece | ||
|
|
7486a342e2 | ||
|
|
fd807c54f8 | ||
|
|
9b894b502f |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
.DS_Store
|
||||
.eslintcache
|
||||
.idea
|
||||
node_modules
|
||||
npm-debug.log
|
||||
coverage
|
||||
|
||||
@@ -23,7 +23,7 @@ npm i --save @edx/gradebook
|
||||
|
||||
After cloning the repository, run `make up-detached` in the `gradebook` directory - this will build and start the `gradebook` web application in a docker container.
|
||||
|
||||
The web application runs on port **1991**, so when you go to `http://localhost:1991` you should see the UI.
|
||||
The web application runs on port **1991**, so when you go to `http://localhost:1991/course-v1:edX+DemoX+Demo_Course` you should see the UI (assuming you have such a Demo Course in your devstack). Note that you always have to provide a course id to actually see a gradebook.
|
||||
|
||||
If you don't, you can see the log messages for the docker container by executing `make logs` in the `gradebook` directory.
|
||||
|
||||
|
||||
BIN
assets/edx-footer.png
Normal file
BIN
assets/edx-footer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
@@ -47,6 +47,7 @@ module.exports = Merge.smart(commonConfig, {
|
||||
minimize: true,
|
||||
},
|
||||
},
|
||||
'postcss-loader',
|
||||
{
|
||||
loader: 'sass-loader', // compiles Sass to CSS
|
||||
options: {
|
||||
|
||||
10512
package-lock.json
generated
10512
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -33,7 +33,7 @@
|
||||
"font-awesome": "^4.7.0",
|
||||
"history": "^4.7.2",
|
||||
"prop-types": "^15.5.10",
|
||||
"query-string": "^6.2.0",
|
||||
"query-string": "^5.1.1",
|
||||
"react": "^16.2.0",
|
||||
"react-dom": "^16.2.0",
|
||||
"react-redux": "^5.0.7",
|
||||
@@ -47,6 +47,7 @@
|
||||
"whatwg-fetch": "^2.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^9.4.2",
|
||||
"axios-mock-adapter": "^1.15.0",
|
||||
"babel-cli": "^6.26.0",
|
||||
"babel-eslint": "^8.2.2",
|
||||
@@ -72,6 +73,7 @@
|
||||
"jest": "^22.4.0",
|
||||
"mini-css-extract-plugin": "^0.4.0",
|
||||
"node-sass": "^4.7.2",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"react-dev-utils": "^5.0.0",
|
||||
"react-test-renderer": "^16.2.0",
|
||||
"redux-mock-store": "^1.5.1",
|
||||
|
||||
@@ -7,3 +7,4 @@ $fa-font-path: "~font-awesome/fonts";
|
||||
@import "~@edx/paragon/src/SearchField/SearchField";
|
||||
|
||||
@import "./components/Gradebook/gradebook";
|
||||
@import "./components/Gradebook/footer";
|
||||
|
||||
122
src/components/Gradebook/footer.jsx
Normal file
122
src/components/Gradebook/footer.jsx
Normal file
@@ -0,0 +1,122 @@
|
||||
import React from 'react';
|
||||
import { Hyperlink, Icon } from '@edx/paragon';
|
||||
|
||||
import EdXFooterLogo from '../../../assets/edx-footer.png';
|
||||
|
||||
export default function Footer() {
|
||||
function renderLogo() {
|
||||
return (
|
||||
<img src={EdXFooterLogo} alt="edX logo" />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<footer
|
||||
role="contentinfo"
|
||||
aria-label="Page Footer"
|
||||
className="footer d-flex justify-content-center border-top py-3 px-4"
|
||||
>
|
||||
<div className="max-width-1180 d-grid">
|
||||
<div className="area-1">
|
||||
<Hyperlink destination="https://www.edx.org/" content={renderLogo()} />
|
||||
</div>
|
||||
<div className="area-2">
|
||||
<h2>edx</h2>
|
||||
<ul className="list-unstyled p-0 m-0">
|
||||
<li><a href="https://www.edx.org/about-us">About</a></li>
|
||||
<li><a href="https://www.edx.org/enterprise">edX for Business</a></li>
|
||||
<li><a href="https://www.edx.org/affiliate-program">Affiliates</a></li>
|
||||
<li><a href="http://open.edx.org">Open edX</a></li>
|
||||
<li><a href="https://www.edx.org/careers">Careers</a></li>
|
||||
<li><a href="https://www.edx.org/news-announcements">News</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="area-3">
|
||||
<h2>Legal</h2>
|
||||
<ul className="list-unstyled p-0 m-0">
|
||||
<li><a href="https://www.edx.org/edx-terms-service">Terms of Service & Honor Code</a></li>
|
||||
<li><a href="https://www.edx.org/edx-privacy-policy">Privacy Policy</a></li>
|
||||
<li><a href="https://www.edx.org/accessibility">Accessibility Policy</a></li>
|
||||
<li><a href="https://www.edx.org/trademarks">Trademark Policy</a></li>
|
||||
<li><a href="https://www.edx.org/sitemap">Sitemap</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="area-4">
|
||||
<h2>Connect</h2>
|
||||
<ul className="list-unstyled p-0 m-0">
|
||||
<li><a href="https://www.edx.org/blog">Blog</a></li>
|
||||
<li><a href="https://courses.edx.org/support/contact_us">Contact Us</a></li>
|
||||
<li><a href="https://support.edx.org">Help Center</a></li>
|
||||
<li><a href="https://www.edx.org/media-kit">Media Kit</a></li>
|
||||
<li><a href="https://www.edx.org/donate">Donate</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="area-5">
|
||||
<ul
|
||||
className="d-flex flex-row justify-content-between list-unstyled max-width-222 p-0 mb-4"
|
||||
>
|
||||
{/* TODO: Use Paragon HyperLink with Icon. */}
|
||||
{/* Would need to add rel to paragon if we still need it. */}
|
||||
<li>
|
||||
<a href="http://www.facebook.com/EdxOnline" title="Facebook" rel="noopener noreferrer" target="_blank">
|
||||
<Icon className={['fa', 'fa-facebook-square', 'fa-2x']} screenReaderText="Like edX on Facebook" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://twitter.com/edXOnline" title="Twitter" rel="noopener noreferrer" target="_blank">
|
||||
<Icon className={['fa', 'fa-twitter-square', 'fa-2x']} screenReaderText="Follow edX on Twitter" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://www.youtube.com/user/edxonline" title="Youtube" rel="noopener noreferrer" target="_blank">
|
||||
<Icon className={['fa', 'fa-youtube-square', 'fa-2x']} screenReaderText="Subscribe to the edX YouTube channel" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://www.linkedin.com/company/edx" title="LinkedIn" rel="noopener noreferrer" target="_blank">
|
||||
<Icon className={['fa', 'fa-linkedin-square', 'fa-2x']} screenReaderText="Follow edX on LinkedIn" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://plus.google.com/+edXOnline" title="Google+" rel="noopener noreferrer" target="_blank">
|
||||
<Icon className={['fa', 'fa-google-plus-square', 'fa-2x']} screenReaderText="Follow edX on Google+" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://www.reddit.com/r/edx" title="Reddit" rel="noopener noreferrer" target="_blank">
|
||||
<Icon className={['fa', 'fa-reddit-square', 'fa-2x']} screenReaderText="Subscribe to the edX subreddit" />
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul className="d-flex flex-row justify-content-between list-unstyled max-width-264 p-0 mb-5">
|
||||
<li>
|
||||
<a href="https://itunes.apple.com/us/app/edx/id945480667?mt=8" rel="noopener noreferrer" target="_blank">
|
||||
<img
|
||||
className="max-height-39"
|
||||
alt="Download the edX mobile app from the Apple App Store"
|
||||
src="https://prod-edxapp.edx-cdn.org/static/images/app/app_store_badge_135x40.d0558d910630.svg"
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://play.google.com/store/apps/details?id=org.edx.mobile" rel="noopener noreferrer" target="_blank">
|
||||
<img
|
||||
className="max-height-39"
|
||||
alt="Download the edX mobile app from Google Play"
|
||||
src="https://prod-edxapp.edx-cdn.org/static/images/app/google_play_badge_45.6ea466e328da.png"
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
© 2012–{(new Date().getFullYear())} edX Inc.
|
||||
<br />
|
||||
EdX, Open edX, and MicroMasters are registered trademarks of edX Inc.
|
||||
| 粤ICP备17044299号-2
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
||||
165
src/components/Gradebook/footer.scss
Normal file
165
src/components/Gradebook/footer.scss
Normal file
@@ -0,0 +1,165 @@
|
||||
.max-width-222 {
|
||||
max-width: 222px;
|
||||
}
|
||||
|
||||
.max-width-264 {
|
||||
max-width: 264px;
|
||||
}
|
||||
|
||||
.max-width-1180 {
|
||||
max-width: 1180px;
|
||||
}
|
||||
|
||||
.max-height-39 {
|
||||
max-height: 39px;
|
||||
}
|
||||
|
||||
.d-grid {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
$gray-footer: #fcfcfc;
|
||||
$border-1: 1px solid $gray-200;
|
||||
|
||||
.footer {
|
||||
background-color: $gray-footer;
|
||||
|
||||
.area-1 {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
border-bottom: $border-1;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.area-2 {
|
||||
grid-column: 1;
|
||||
grid-row: 2;
|
||||
border-bottom: $border-1;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.area-3 {
|
||||
grid-column: 1;
|
||||
grid-row: 3;
|
||||
border-bottom: $border-1;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.area-4 {
|
||||
grid-column: 1;
|
||||
grid-row: 4;
|
||||
border-bottom: $border-1;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.area-5 {
|
||||
grid-column: 1;
|
||||
grid-row: 5;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 717px) {
|
||||
.area-1 {
|
||||
grid-column: 1 / span 2;
|
||||
grid-row: 1;
|
||||
border-bottom: none;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.area-2 {
|
||||
grid-column: 1;
|
||||
grid-row: 2;
|
||||
}
|
||||
|
||||
.area-3 {
|
||||
grid-column: 1;
|
||||
grid-row: 3;
|
||||
}
|
||||
|
||||
.area-4 {
|
||||
grid-column: 1;
|
||||
grid-row: 4;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.area-5 {
|
||||
grid-column: 2;
|
||||
grid-row: 2 / span 3;
|
||||
border-left: $border-1;
|
||||
padding-left: 1rem;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 870px) {
|
||||
.area-1 {
|
||||
grid-column: 1;
|
||||
grid-row: 1 / span 3;
|
||||
border-right: $border-1;
|
||||
padding-right: 1rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.area-2 {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
border-bottom: none;
|
||||
border-right: $border-1;
|
||||
padding-right: 1rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.area-3 {
|
||||
grid-column: 3;
|
||||
grid-row: 1;
|
||||
border-bottom: none;
|
||||
border-right: $border-1;
|
||||
padding-right: 1rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.area-4 {
|
||||
grid-column: 4;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.area-5 {
|
||||
grid-column: 2 / span 3;
|
||||
grid-row: 2;
|
||||
border: none;
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1188px) {
|
||||
.area-1 {
|
||||
grid-column: 1 / span 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.area-2 {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.area-3 {
|
||||
grid-column: 3;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.area-4 {
|
||||
grid-column: 4;
|
||||
grid-row: 1;
|
||||
border-right: $border-1;
|
||||
padding-right: 1rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.area-5 {
|
||||
grid-column: 5 / span 1;
|
||||
grid-row: 1;
|
||||
max-width: 372px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,17 +39,23 @@ export default class Gradebook extends React.Component {
|
||||
}
|
||||
|
||||
setNewModalState = (userEntry, subsection) => {
|
||||
let adjustedGradePossible = '';
|
||||
let currentGradePossible = '';
|
||||
if (subsection.attempted) {
|
||||
adjustedGradePossible = ` / ${subsection.score_possible}`;
|
||||
currentGradePossible = `/${subsection.score_possible}`;
|
||||
}
|
||||
this.setState({
|
||||
modalModel: [{
|
||||
username: userEntry.username,
|
||||
currentGrade: `${subsection.score_earned}/${subsection.score_possible}`,
|
||||
currentGrade: `${subsection.score_earned}${currentGradePossible}`,
|
||||
adjustedGrade: (
|
||||
<span>
|
||||
<input
|
||||
style={{ width: '25px' }}
|
||||
type="text"
|
||||
onChange={event => this.setState({ updateVal: event.target.value })}
|
||||
/> / {subsection.score_possible}
|
||||
/>{adjustedGradePossible}
|
||||
</span>
|
||||
),
|
||||
assignmentName: `${subsection.subsection_name}`,
|
||||
@@ -206,16 +212,23 @@ export default class Gradebook extends React.Component {
|
||||
const assignments = entry.section_breakdown
|
||||
.filter(section => section.is_graded)
|
||||
.reduce((acc, subsection) => {
|
||||
const scoreEarned = this.roundGrade(subsection.score_earned);
|
||||
const scorePossible = this.roundGrade(subsection.score_possible);
|
||||
let label = `${scoreEarned}`;
|
||||
if (subsection.attempted) {
|
||||
label = `${scoreEarned}/${scorePossible}`;
|
||||
}
|
||||
if (areGradesFrozen) {
|
||||
acc[subsection.label] = `${this.roundGrade(subsection.score_earned)}/${this.roundGrade(subsection.score_possible)}`;
|
||||
acc[subsection.label] = label;
|
||||
} else {
|
||||
acc[subsection.label] = (
|
||||
<button
|
||||
className="btn btn-header link-style"
|
||||
onClick={() => this.setNewModalState(entry, subsection)}
|
||||
>
|
||||
{this.roundGrade(subsection.score_earned)}/{this.roundGrade(subsection.score_possible)}
|
||||
</button>);
|
||||
{label}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
@@ -242,7 +255,7 @@ export default class Gradebook extends React.Component {
|
||||
<h1>Gradebook</h1>
|
||||
<h3> {this.props.match.params.courseId}</h3>
|
||||
{ this.props.areGradesFrozen &&
|
||||
<div className="alert alert-danger" role="alert" >
|
||||
<div className="alert alert-warning" role="alert" >
|
||||
The grades for this course are now frozen. Editing of grades is no longer allowed.
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import apiClient from './data/apiClient';
|
||||
import Footer from './components/Gradebook/footer';
|
||||
import GradebookPage from './containers/GradebookPage';
|
||||
import Header from './components/Header';
|
||||
import store from './data/store';
|
||||
@@ -20,6 +21,7 @@ const App = () => (
|
||||
<Route exact path="/:courseId" component={GradebookPage} />
|
||||
</Switch>
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
</Router>
|
||||
</Provider>
|
||||
|
||||
7
src/postcss.config.js
Normal file
7
src/postcss.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
/* I'm here to allow autoprefixing in webpack.prod.config.js */
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require('autoprefixer')({ grid: true, browsers: ['>1%'] }),
|
||||
],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user