Compare commits
10 Commits
c15cbbf5ae
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
034dd8ddf2 | ||
|
|
2d121e4b0a | ||
|
|
92d4ace250 | ||
|
|
92f38e449d | ||
|
|
b94f5675a5 | ||
|
|
be9d1f6c41 | ||
|
|
e52b0b2a34 | ||
|
|
ae0cf1c2b4 | ||
|
|
de2ee07908 | ||
|
|
0a8421fad3 |
14691
package-lock.json
generated
14691
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@@ -6,7 +6,7 @@
|
|||||||
"react": "^16.5.0",
|
"react": "^16.5.0",
|
||||||
"react-dom": "^16.5.0",
|
"react-dom": "^16.5.0",
|
||||||
"react-router-dom": "^4.3.1",
|
"react-router-dom": "^4.3.1",
|
||||||
"react-scripts": "1.1.5"
|
"react-scripts": "^2.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"husky": "^0.14.3",
|
"husky": "^0.14.3",
|
||||||
@@ -25,5 +25,11 @@
|
|||||||
"prettier --single-quote --write",
|
"prettier --single-quote --write",
|
||||||
"git add"
|
"git add"
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
"browserslist": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not ie <= 11",
|
||||||
|
"not op_mini all"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,8 @@ export default function(props) {
|
|||||||
const cells = [...Array(props.numQuestions).keys()].map((question, index) => (
|
const cells = [...Array(props.numQuestions).keys()].map((question, index) => (
|
||||||
<td key={index}>
|
<td key={index}>
|
||||||
<input
|
<input
|
||||||
name={'answer_' + props.testNum + ',' + question}
|
|
||||||
type="text"
|
type="text"
|
||||||
onChange={props.handleInputChange}
|
onChange={event => props.onChange(event.target, question)}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -2,10 +2,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.answerkeyform__table input {
|
.answerkeyform__table input {
|
||||||
width: 4rem;
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.answerkeyform__table__questionrow td {
|
.answerkeyform__table__questionrow td {
|
||||||
padding: 1rem;
|
padding: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
import QuestionsRow from './QuestionsRow';
|
import QuestionsRow from './QuestionsRow';
|
||||||
|
import API from '../../lib/API';
|
||||||
|
|
||||||
class AnswerKeyForm extends React.Component {
|
class AnswerKeyForm extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -8,52 +9,100 @@ class AnswerKeyForm extends React.Component {
|
|||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
courseName: '',
|
courseName: '',
|
||||||
answers: Array(props.numberOfTests).fill(
|
numTests: 10,
|
||||||
Array(props.numberOfQuestionsPerTest)
|
numQuestionsPerTest: 10,
|
||||||
),
|
answers: Array(10 * 10).fill(null),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.handleInputChange = this.handleInputChange.bind(this);
|
this.handleInputChange = this.handleInputChange.bind(this);
|
||||||
console.log(this.state.answers);
|
this.handleAnswerChange = this.handleAnswerChange.bind(this);
|
||||||
|
this.submit = this.submit.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAnswerChange(test, question) {
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
// resize the answers array (and clear it) if
|
||||||
|
// the size parameters were changed
|
||||||
|
if (
|
||||||
|
this.state.numTests !== prevState.numTests ||
|
||||||
|
this.state.numQuestionsPerTest !== prevState.numQuestionsPerTest
|
||||||
|
) {
|
||||||
|
this.updateAnswersSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleAnswerChange(target, answerIndex) {
|
||||||
const answers = this.state.answers.slice();
|
const answers = this.state.answers.slice();
|
||||||
const testAnswers = answers[test];
|
answers[answerIndex] = target.value;
|
||||||
testAnswers[question] = value;
|
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
answers: answers,
|
answers: answers,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateAnswersSize() {
|
||||||
|
const newSize = this.state.numTests * this.state.numQuestionsPerTest;
|
||||||
|
const resizedAnswers = Array(newSize).fill(null);
|
||||||
|
const prevAnswers = this.state.answers.slice();
|
||||||
|
|
||||||
|
for (
|
||||||
|
let i = 0;
|
||||||
|
i < Math.min(resizedAnswers.length, prevAnswers.length);
|
||||||
|
i++
|
||||||
|
) {
|
||||||
|
resizedAnswers[i] = prevAnswers[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
answers: resizedAnswers,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
handleInputChange(event) {
|
handleInputChange(event) {
|
||||||
const target = event.target;
|
const target = event.target;
|
||||||
const value = target.value;
|
const value =
|
||||||
|
target.type === 'number' ? Number(target.value) : target.value;
|
||||||
const name = target.name;
|
const name = target.name;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
[name]: value,
|
[name]: value,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
console.log(this.state);
|
submit(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
name: this.state.courseName,
|
||||||
|
num_tests: this.state.numTests,
|
||||||
|
num_questions_per_test: this.state.numQuestionsPerTest,
|
||||||
|
answers: this.state.answers.slice(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = API.answerKeys.create(payload);
|
||||||
|
|
||||||
|
console.log(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const testRows = [...Array(this.props.numberOfTests).keys()].map(
|
const testRows = [...Array(this.state.numTests).keys()].map(
|
||||||
(test, index) => (
|
(test, index) => (
|
||||||
<QuestionsRow
|
<QuestionsRow
|
||||||
key={index}
|
key={index}
|
||||||
testNum={test + 1}
|
testNum={test + 1}
|
||||||
numQuestions={this.props.numberOfQuestionsPerTest}
|
numQuestions={this.state.numQuestionsPerTest}
|
||||||
handleInputChange={() => this.handleAnswerChange(testNum)}
|
onChange={(target, question) =>
|
||||||
|
this.handleAnswerChange(
|
||||||
|
target,
|
||||||
|
test * this.state.numQuestionsPerTest + question
|
||||||
|
)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="answerkeyform">
|
<div className="answerkeyform">
|
||||||
<h1>New Answer Key</h1>
|
<h2>New Answer Key</h2>
|
||||||
<form>
|
<form>
|
||||||
<label>Course name</label>
|
<label>Course name</label>
|
||||||
<input
|
<input
|
||||||
@@ -63,16 +112,43 @@ class AnswerKeyForm extends React.Component {
|
|||||||
onChange={this.handleInputChange}
|
onChange={this.handleInputChange}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div className="row">
|
||||||
|
<div className="six columns">
|
||||||
|
<label>Num. tests</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="numTests"
|
||||||
|
value={this.state.numTests}
|
||||||
|
onChange={this.handleInputChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="six columns">
|
||||||
|
<label>Num. questions per tests</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="numQuestionsPerTest"
|
||||||
|
value={this.state.numQuestionsPerTest}
|
||||||
|
onChange={this.handleInputChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<table className="u-full-width answerkeyform__table">
|
<table className="u-full-width answerkeyform__table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th colSpan={this.props.numberOfQuestionsPerTest + 1}>
|
<th colSpan={this.state.numQuestionsPerTest + 1}>
|
||||||
Test answers
|
Test answers
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>{testRows}</tbody>
|
<tbody>{testRows}</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<input
|
||||||
|
className="button-primary"
|
||||||
|
type="submit"
|
||||||
|
value="Save"
|
||||||
|
onClick={this.submit}
|
||||||
|
/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
57
src/components/AnswerKeyList/index.js
Normal file
57
src/components/AnswerKeyList/index.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import API from '../../lib/API';
|
||||||
|
import {Link} from 'react-router-dom';
|
||||||
|
|
||||||
|
class AnswerKeyList extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.api = new API();
|
||||||
|
this.state = {
|
||||||
|
courses: new Array(0),
|
||||||
|
};
|
||||||
|
|
||||||
|
this.api.answerKeys.list().then(r => this.setState({courses: r.courses}));
|
||||||
|
}
|
||||||
|
|
||||||
|
getCreatedAt(unixEpoch) {
|
||||||
|
if (!unixEpoch) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const date = new Date();
|
||||||
|
date.setTime(unixEpoch);
|
||||||
|
|
||||||
|
return date.getMonth() + '/' + date.getDate() + '/' + date.getFullYear();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const courseRows = this.state.courses.map((course, index) => (
|
||||||
|
<tr>
|
||||||
|
<td>{course.name}</td>
|
||||||
|
<td>{this.getCreatedAt(course.created_at)}</td>
|
||||||
|
<td>
|
||||||
|
<Link to="/answerkeys/edit/{course.name}">edit</Link>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="answerkeylist row">
|
||||||
|
<h2>Answer Keys</h2>
|
||||||
|
<table className="u-full-width">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Course name</th>
|
||||||
|
<th>Created</th>
|
||||||
|
<th> </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>{courseRows}</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AnswerKeyList;
|
||||||
@@ -1,16 +1,23 @@
|
|||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import NavBar from './NavBar';
|
import NavBar from './NavBar';
|
||||||
|
import Dashboard from './Dashboard';
|
||||||
import AnswerKeyForm from './AnswerKeyForm';
|
import AnswerKeyForm from './AnswerKeyForm';
|
||||||
|
import AnswerKeyList from './AnswerKeyList';
|
||||||
|
import {BrowserRouter as Router, Route} from 'react-router-dom';
|
||||||
|
|
||||||
class App extends Component {
|
class App extends Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
|
<Router>
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<NavBar />
|
<NavBar />
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<AnswerKeyForm numberOfTests={10} numberOfQuestionsPerTest={10} />
|
<Route path="/" exact component={Dashboard} />
|
||||||
|
<Route path="/answerkeys" exact component={AnswerKeyList} />
|
||||||
|
<Route path="/answerkeys/create" exact component={AnswerKeyForm} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Router>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
0
src/components/Dashboard/index.css
Normal file
0
src/components/Dashboard/index.css
Normal file
23
src/components/Dashboard/index.js
Normal file
23
src/components/Dashboard/index.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import './index.css';
|
||||||
|
import {Link} from 'react-router-dom';
|
||||||
|
|
||||||
|
class Dashboard extends React.Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="dashboard row">
|
||||||
|
<Link to="/grade" className="button four columns">
|
||||||
|
Grade a test
|
||||||
|
</Link>
|
||||||
|
<Link to="/answerkeys/create" className="button four columns">
|
||||||
|
Enter an answer key
|
||||||
|
</Link>
|
||||||
|
<Link to="/answerkeys" className="button four columns">
|
||||||
|
List answer keys
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Dashboard;
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
import {Link} from 'react-router-dom';
|
||||||
|
|
||||||
class Header extends Component {
|
class Header extends Component {
|
||||||
render() {
|
render() {
|
||||||
@@ -8,7 +9,9 @@ class Header extends Component {
|
|||||||
<nav className="header__nav container">
|
<nav className="header__nav container">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="three columns">
|
<div className="three columns">
|
||||||
<div className="header__sitename">ECS Grader</div>
|
<div className="header__sitename">
|
||||||
|
<Link to="/">ECS Grader</Link>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="nine columns" />
|
<div className="nine columns" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
40
src/lib/API.js
Normal file
40
src/lib/API.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
export default class API {
|
||||||
|
constructor() {
|
||||||
|
this.apiBase = 'https://bzwy0j344j.execute-api.us-east-2.amazonaws.com/dev';
|
||||||
|
this.resources = {
|
||||||
|
courses: {
|
||||||
|
list: '/courses',
|
||||||
|
create: '/courses',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
this.answerKeys = {
|
||||||
|
list: this.listAnswerKeys.bind(this),
|
||||||
|
create: this.createAnswerKey.bind(this),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
createAnswerKey(payload) {
|
||||||
|
const url = this.apiBase + this.resources.courses.create;
|
||||||
|
const result = fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
mode: 'cors',
|
||||||
|
cache: 'no-cache',
|
||||||
|
headers: {'Content-Type': 'application/json; charset=utf-8'},
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
}).then(res => res.json());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
listAnswerKeys() {
|
||||||
|
const url = this.apiBase + this.resources.courses.list;
|
||||||
|
const result = fetch(url, {
|
||||||
|
method: 'GET',
|
||||||
|
mode: 'cors',
|
||||||
|
cache: 'no-cache',
|
||||||
|
headers: {'Content-Type': 'application/json; charset=utf-8'},
|
||||||
|
}).then(res => res.json());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user