Compare commits
10 Commits
c15cbbf5ae
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
034dd8ddf2 | ||
|
|
2d121e4b0a | ||
|
|
92d4ace250 | ||
|
|
92f38e449d | ||
|
|
b94f5675a5 | ||
|
|
be9d1f6c41 | ||
|
|
e52b0b2a34 | ||
|
|
ae0cf1c2b4 | ||
|
|
de2ee07908 | ||
|
|
0a8421fad3 |
14673
package-lock.json
generated
14673
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-dom": "^16.5.0",
|
||||
"react-router-dom": "^4.3.1",
|
||||
"react-scripts": "1.1.5"
|
||||
"react-scripts": "^2.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"husky": "^0.14.3",
|
||||
@@ -25,5 +25,11 @@
|
||||
"prettier --single-quote --write",
|
||||
"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) => (
|
||||
<td key={index}>
|
||||
<input
|
||||
name={'answer_' + props.testNum + ',' + question}
|
||||
type="text"
|
||||
onChange={props.handleInputChange}
|
||||
onChange={event => props.onChange(event.target, question)}
|
||||
/>
|
||||
</td>
|
||||
));
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
}
|
||||
|
||||
.answerkeyform__table input {
|
||||
width: 4rem;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
margin: 0;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.answerkeyform__table__questionrow td {
|
||||
padding: 1rem;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import './index.css';
|
||||
import QuestionsRow from './QuestionsRow';
|
||||
import API from '../../lib/API';
|
||||
|
||||
class AnswerKeyForm extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -8,52 +9,100 @@ class AnswerKeyForm extends React.Component {
|
||||
|
||||
this.state = {
|
||||
courseName: '',
|
||||
answers: Array(props.numberOfTests).fill(
|
||||
Array(props.numberOfQuestionsPerTest)
|
||||
),
|
||||
numTests: 10,
|
||||
numQuestionsPerTest: 10,
|
||||
answers: Array(10 * 10).fill(null),
|
||||
};
|
||||
|
||||
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 testAnswers = answers[test];
|
||||
testAnswers[question] = value;
|
||||
answers[answerIndex] = target.value;
|
||||
|
||||
this.setState({
|
||||
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) {
|
||||
const target = event.target;
|
||||
const value = target.value;
|
||||
const value =
|
||||
target.type === 'number' ? Number(target.value) : target.value;
|
||||
const name = target.name;
|
||||
|
||||
this.setState({
|
||||
[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() {
|
||||
const testRows = [...Array(this.props.numberOfTests).keys()].map(
|
||||
const testRows = [...Array(this.state.numTests).keys()].map(
|
||||
(test, index) => (
|
||||
<QuestionsRow
|
||||
key={index}
|
||||
testNum={test + 1}
|
||||
numQuestions={this.props.numberOfQuestionsPerTest}
|
||||
handleInputChange={() => this.handleAnswerChange(testNum)}
|
||||
numQuestions={this.state.numQuestionsPerTest}
|
||||
onChange={(target, question) =>
|
||||
this.handleAnswerChange(
|
||||
target,
|
||||
test * this.state.numQuestionsPerTest + question
|
||||
)
|
||||
}
|
||||
/>
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="answerkeyform">
|
||||
<h1>New Answer Key</h1>
|
||||
<h2>New Answer Key</h2>
|
||||
<form>
|
||||
<label>Course name</label>
|
||||
<input
|
||||
@@ -63,16 +112,43 @@ class AnswerKeyForm extends React.Component {
|
||||
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">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colSpan={this.props.numberOfQuestionsPerTest + 1}>
|
||||
<th colSpan={this.state.numQuestionsPerTest + 1}>
|
||||
Test answers
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>{testRows}</tbody>
|
||||
</table>
|
||||
<input
|
||||
className="button-primary"
|
||||
type="submit"
|
||||
value="Save"
|
||||
onClick={this.submit}
|
||||
/>
|
||||
</form>
|
||||
</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 NavBar from './NavBar';
|
||||
import Dashboard from './Dashboard';
|
||||
import AnswerKeyForm from './AnswerKeyForm';
|
||||
import AnswerKeyList from './AnswerKeyList';
|
||||
import {BrowserRouter as Router, Route} from 'react-router-dom';
|
||||
|
||||
class App extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="App">
|
||||
<NavBar />
|
||||
<div className="container">
|
||||
<AnswerKeyForm numberOfTests={10} numberOfQuestionsPerTest={10} />
|
||||
<Router>
|
||||
<div className="App">
|
||||
<NavBar />
|
||||
<div className="container">
|
||||
<Route path="/" exact component={Dashboard} />
|
||||
<Route path="/answerkeys" exact component={AnswerKeyList} />
|
||||
<Route path="/answerkeys/create" exact component={AnswerKeyForm} />
|
||||
</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 './index.css';
|
||||
import {Link} from 'react-router-dom';
|
||||
|
||||
class Header extends Component {
|
||||
render() {
|
||||
@@ -8,7 +9,9 @@ class Header extends Component {
|
||||
<nav className="header__nav container">
|
||||
<div className="row">
|
||||
<div className="three columns">
|
||||
<div className="header__sitename">ECS Grader</div>
|
||||
<div className="header__sitename">
|
||||
<Link to="/">ECS Grader</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="nine columns" />
|
||||
</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