Compare commits

...

10 Commits

Author SHA1 Message Date
benjaminramey
034dd8ddf2 Answer key list 2018-12-09 20:21:44 -06:00
benjaminramey
2d121e4b0a Router, dashboard 2018-12-09 19:42:00 -06:00
benjaminramey
92d4ace250 Package.json browserList 2018-12-07 07:09:38 -06:00
benjaminramey
92f38e449d Remove console.logs 2018-12-07 07:08:43 -06:00
benjaminramey
b94f5675a5 Update packages 2018-12-06 21:26:57 -06:00
Ben Ramey
be9d1f6c41 Working answer key submission 2018-09-19 06:58:37 -05:00
Ben Ramey
e52b0b2a34 resizable answers table 2018-09-17 06:59:04 -05:00
Ben Ramey
ae0cf1c2b4 Add save button 2018-09-17 06:17:16 -05:00
Ben Ramey
de2ee07908 Remove duplicate title 2018-09-17 06:15:04 -05:00
Ben Ramey
0a8421fad3 Working answer key form (saving to state at least) 2018-09-17 06:14:09 -05:00
11 changed files with 9972 additions and 4966 deletions

14673
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -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"
]
}

View File

@@ -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>
));

View File

@@ -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;
}

View File

@@ -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>
);

View 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>&nbsp;</th>
</tr>
</thead>
<tbody>{courseRows}</tbody>
</table>
</div>
);
}
}
export default AnswerKeyList;

View File

@@ -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>
);
}
}

View File

View 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;

View File

@@ -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
View 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;
}
}