Large diffs are not rendered by default.

@@ -7,6 +7,7 @@
"cors": "^2.8.4",
"dotenv": "^6.0.0",
"express-jwt": "^5.3.1",
"hat": "0.0.3",
"lodash": "^4.17.10",
"mongodb": "^3.1.0-beta4",
"path": "^0.12.7",
@@ -29,6 +29,8 @@ router.get("/auth/:token", (req, res) => {
firstName: applicant.firstName,
lastName: applicant.lastName,
email: applicant.email,
companyId: applicant.companyId,
test: applicant.test,
completed: applicant.completed,
testTimestamp: applicant.testTimestamp
});
@@ -60,19 +62,15 @@ router.post("/test-results/:token", (req, res) => {
const db = req.app.locals.db;
const Applicants = db.collection("applicants");
const { token } = req.params;
const { applicantId, secondsElapsed, answer1, answer2 } = req.body;
const { applicantId, secondsElapsed, answers } = req.body;

Applicants.updateOne(
{ token },
{
$set: {
secondsElapsed,
answers,
completed: true
},
$push: {
results: {
$each: [answer1, answer2]
}
}
}
).then(success => {

Large diffs are not rendered by default.

@@ -0,0 +1,12 @@
module.exports: [
{
questionId,
type: "MULTIPLE_CHOICE",
choice: optionId
},
{
questionId,
type: "OPEN_RESPONSE",
body: "Here's your answer"
}
]
@@ -0,0 +1,24 @@
const shortid = require("shortid");

module.exports = {
name: "Customer Service",
id: shortid.generate(),
questions: [
{
id: shortid(),
body: "question 1",
type: "MULTIPLE_CHOICE",
options: [
{ id: shortid.generate(), answer: "some answer", correct: false },
{ id: shortid.generate(), answer: "another answer", correct: false },
{ id: shortid.generate(), answer: "the right answer", correct: true },
{ id: shortid.generate(), answer: "bleh", correct: false }
]
},
{
id: shortid.generate(),
body: "question 2",
type: "OPEN_RESPONSE"
}
]
};
@@ -6,7 +6,7 @@ import Company from './containers/Company/Company';
import Applicant from './containers/Applicant/Applicant';
import Finished from './containers/Applicant/Finished/Finished';
import FinalResults from './containers/Company/FinalResults/FinalResults';
import Questionaire from './containers/Company/Questionaire/Questionaire';
import TestEditor from './containers/Company/TestEditor/TestEditor';
import JobDescript from './containers/Applicant/JobDescript/JobDescript';
import ApplicantSignup from './containers/Applicant/ApplicantSignup/ApplicantSignup';

@@ -15,12 +15,12 @@ class App extends Component {
return (
<div className="App">
<Route path="/" exact component={Login} />
{/* <Route path="/applicant/signup/:companyId/:testId" component={ApplicantSignup} /> */}
<Route path="/applicant/signup/:companyId/:testId" component={ApplicantSignup} />
<Route path="/applicant/:token" component={Applicant} />
<Route path="/company" exact component={Company} />
<Route path="/company/:ApplicantId" component={FinalResults} />
<Route path="/company/test-editor" exact component={TestEditor} />
<Route path="/company/results/:ApplicantId" component={FinalResults} />
<Route path='/finished' component={Finished} />
<Route path='/questionaire' component={Questionaire} />
<Route path='/job-description' component={JobDescript} />
<Route path='/app-form' component={ApplicantSignup} />
</div>
@@ -0,0 +1,54 @@
import React from 'react';

const Question = props => {
let question = "";
switch (props.question.type) {
case "MULTIPLE_CHOICE":
question = (
<div>
<p style={{ color: 'purple' }}>Exercise {props.index + 1}</p>
<p>{props.question.body}</p>
{
props.question.options.map(x =>
<div key={x.id}>
<input
type="radio"
name={props.question.id}
value={x.answer}
onClick={props.handleChange}
/>
<label htmlFor={x.id}>{x.answer}</label>
</div>
)
}
</div>
);
break;
case "OPEN_RESPONSE":
question = (
<div>
<p style={{ color: 'purple' }}>Exercise {props.index + 1}</p>
<p>{props.question.body}</p>
<textarea
type="text"
name={props.question.id}
placeholder="Place response here"
onChange={props.handleChange}
rows='10'
cols='110'
/>
</div>
);
break;
default:
question = <p>Invalid question type</p>;
}

return (
<div>
{question}
</div>
);
};

export default Question;
@@ -5,16 +5,16 @@ const ActionButtons = (props) => {
return (
<div>
<strong><a className="SaveB" style={{padding: '7px', border: 'solid #3f3c87 3px', margin: '2px', color: '#3f3c87', cursor: 'pointer'}} onClick={props.onSaveClick}>SAVE</a></strong>
<strong><a className="CancelB" style={{padding: '7px', border: 'solid gray 3px', margin: '2px', color: 'gray', cursor: 'pointer'}} onClick={props.onCancelClick}>CANCEL</a></strong>
<strong><a className="CancelB" style={{padding: '7px', border: 'solid gray 3px', margin: '2px', color: 'gray', cursor: 'pointer'}} onClick={props.onCancel}>CANCEL</a></strong>
</div>
);
}
return (
<div>
<strong><a className="EditB" style={{padding: '7px', border: 'solid #af9121 3px', margin: '2px', color: '#af9121', cursor: 'pointer'}} onClick={props.editHandler}>EDIT</a></strong>
<strong><a className="DeleteB" style={{padding: '7px', border: 'solid #7c251d 3px', margin: '2px', color: '#7c251d', cursor: 'pointer'}} onClick={props.delete}>DELETE</a></strong>
<strong><a className="DeleteB" style={{padding: '7px', border: 'solid #7c251d 3px', margin: '2px', color: '#7c251d', cursor: 'pointer'}} onClick={props.deleteHandler}>DELETE</a></strong>
</div>
);
}

export default ActionButtons;
export default ActionButtons;
@@ -1,11 +1,7 @@
import React, { Component } from 'react';
//import Test from './Test/Test';

// const formattedSeconds = (sec) => {
// Math.floor(sec / 60) +
// ':' +
// ('0' + sec % 60).slice(-2)
// }
import Test from './Test/Test';
import WelcomePage from './WelcomePage/WelcomePage';

class Applicant extends Component {
constructor(props) {
@@ -15,11 +11,15 @@ class Applicant extends Component {
isCompleted: false,
isAuth: false,
isError: false,
testTaker: {},
buttonClicked: false,
secondsElapsed: 0
applicant: {},
secondsElapsed: 0,
test: {},
buttonClicked: false
}
this.incrementer = null;

this.startTest = this.startTest.bind(this);
this.propagateError = this.propagateError.bind(this);
this.redirectToFinished = this.redirectToFinished.bind(this);
this.token = this.props.match.params.token;
}

@@ -29,29 +29,33 @@ class Applicant extends Component {
return;
}

fetch(`https://decisiontime.herokuapp.com/api/applicant/auth/${this.token}`)
fetch(`http://localhost:4567/api/applicant/auth/${this.token}`)
.then(res => {
this.setState({ isLoading: false });
return res.status === 403 ?
Promise.reject("Auth denied") :
res.json()
}).then(data => {
if (data.completed) {
this.setState({
isLoading: false,
isAuth: true,
isCompleted: true
});
} else if (data.testTimestamp) {
const parsedDate = Date.parse(data.testTimestamp);
this.setState({
isLoading: false,
secondsElapsed: Math.floor((new Date() - parsedDate) / 1000),
isAuth: true,
testTaker: data
applicant: data,
test: data.test
}, this.changePageHandler);
} else {
this.setState({
isLoading: false,
isAuth: true,
testTaker: data
applicant: data,
test: data.test
});
}
}).catch(err => console.error(err));
@@ -63,7 +67,7 @@ class Applicant extends Component {
return;
}

fetch(`https://decisiontime.herokuapp.com/api/applicant/test-timestamp/${this.token}`)
fetch(`http://localhost:4567/api/applicant/test-timestamp/${this.token}`)
.then(res =>
res.status === 403 ?
Promise.reject("Auth denied") :
@@ -73,53 +77,32 @@ class Applicant extends Component {
}).catch(err => console.error(err));
}

redirectToFinished = () => {
this.props.history.push("/finished");
};

propagateError = () => {
this.setState({ isError: true });
};

changePageHandler = () => {
this.setState({ buttonClicked: !this.state.buttonClicked})
this.incrementer = setInterval( () =>
this.setState({
secondsElapsed: this.state.secondsElapsed + 1
})
, 1000);
this.setState({ buttonClicked: !this.state.buttonClicked});
}

handleSubmit = () => {
clearInterval(this.incrementer);
handleChange = e => {
this.setState({
lastClearedIncrementer: this.incrementer
});

const options = {
headers: {
"Content-Type": "application/json"
},
method: "POST",
body: JSON.stringify({
applicantId: this.state.testTaker.id,
secondsElapsed: this.state.secondsElapsed,
answer1: this.refs.editQuestion1.value,
answer2: this.refs.editQuestion2.value
})
};

fetch(`https://decisiontime.herokuapp.com/api/applicant/test-results/${this.token}`, options)
.then(res =>
res.status === 403 ?
Promise.reject("Auth denied") :
res.json()
).then(data => {
if (!data.success) {
return this.setState({ isError: true });
answers: {
...this.state.answers,
[e.target.id]: e.target.value
}

this.props.history.push("/finished");
}).catch(err => console.error(err));
}
});
};

formattedSeconds = (sec) => {
return (Math.floor(sec / 60) +
':' +
('0' + sec % 60).slice(-2));
}
}

render () {
if (this.state.isLoading) {
@@ -134,44 +117,25 @@ class Applicant extends Component {
return <p>You have already completed the test!</p>;
}

const welcomePage = <div style={{textAlign: 'center', margin: '200px 500px', padding: '10px 30px 35px 30px', backgroundColor: '#cfcfd1', boxShadow: '1px 1px 1px 0px rgba(0,0,0,0.75)'}}>
<div style={{paddingBottom: '10px'}}>
<p>Hey {this.state.testTaker.firstName}, thanks for showing iinterest in becoming a customer
service ninja. We think you have the potential to be a great fit
at our company. Before we can take this any further, we would like
you to respond to a couple questions. You only have 30 minutes
to finish all the questions upon the time you click “START TEST NOW”.</p>
<p style={{color: 'purple'}}><strong>Best of luck!</strong></p>
</div>
<a onClick={this.startTest.bind(this)} style={{backgroundColor: 'purple', textDecoration: 'none', color: 'white', padding: '10px', cursor: 'pointer', boxShadow: '2px 2px 1px 0px rgba(0,0,0,0.75)'}}>START TEST NOW</a>
</div>;
const test = <div style={{margin: '200px 300px', padding: '10px 30px 35px 30px', backgroundColor: '#cfcfd1', boxShadow: '1px 1px 1px 0px rgba(0,0,0,0.75)'}}>
<h1>BEGIN TESTING NOW</h1>
<h1 style={{color: 'red'}}>{this.formattedSeconds(this.state.secondsElapsed)}</h1>
<p style={{color: 'purple'}}><strong>Exercise A)</strong></p>

<p>Please write an email to the customer based on the following details:

The customer's order was 30 minutes late because the delivery driver got a flat tire on the way to the event. We were in contact with the customer throughout the process. The caterer is unwilling to offer any compensation to the customer because they feel that a flat tire is not their fault. In this case, ezCater would try to negotiate with both parties to reach an acceptable resolution. Please write an email apologizing to the customer and offering them some kind of compensation. Do not take more than 10-15 minutes and simply craft what you feel leaves the customer feeling that they received excellent, 5-star customer service. Do not try to figure out what we would do, we want to know what YOU think is right. The sky's the limit. Have fun and remember, we love our customers!</p>
<textarea type="text" placeholder="Place response here" ref='editQuestion1' rows='10' cols='110'/>
<p style={{color: 'purple'}}><strong>Exercise B)</strong></p>
<p>One of our core values is to be "Insanely Helpful". Below is a copy of a real email we received when trying to have a new account set up with one of our vendors:
Hi Sallie,
The request to add a new user profile is complete. It appears your name may be spelled incorrectly, however we processed the request as it was. Please let us know if you need this corrected.
To download the software on your computer go to:
sampledownloadxyz.com
Thank you
XYZ Company
More information and user guides can be found on our website. </p>

<p>1) What is wrong with this email?</p>
<p>2) How would you write it? </p>
<textarea type="text" placeholder="Place response here" ref='editQuestion2' rows='10' cols='110' />
<div style={{marginTop: '20px'}}>
<a onClick={this.handleSubmit.bind(this)} style={{backgroundColor: 'purple', textDecoration: 'none', color: 'white', padding: '10px', cursor: 'pointer', boxShadow: '2px 2px 1px 0px rgba(0,0,0,0.75)'}}>SUBMIT</a>
</div>
</div>;
let pageChoice = !this.state.buttonClicked ? welcomePage : test;
if (this.state.isError) {
return <p>There was an error.</p>
}

let pageChoice = !this.state.buttonClicked ?
<WelcomePage
applicant={this.state.applicant}
startTest={this.startTest}
/> :
<Test
token={this.token}
test={this.state.test}
secondsElapsed={this.state.secondsElapsed}
applicant={this.state.applicant}
propagateError={this.propagateError}
handleChange={this.handleChange}
redirectToFinished={this.redirectToFinished}
/>;

return (
<div>
{pageChoice}
@@ -1,13 +1,103 @@
import React from 'react';

const Test = () => {
return (<div style={{margin: '200px 500px', padding: '10px 30px 35px 30px', backgroundColor: '#cfcfd1', boxShadow: '1px 1px 1px 0px rgba(0,0,0,0.75)'}}>
import Question from '../../../components/Question';

class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
answers: {},
secondsElapsed: props.secondsElapsed
};

this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.incrementer = null;
}

componentDidMount() {
this.incrementer = setInterval(() =>
this.setState({
secondsElapsed: this.state.secondsElapsed + 1
})
, 1000);
}

handleChange = e => {
this.setState({
answers: {
...this.state.answers,
[e.target.name]: e.target.value
}
});
};

handleSubmit = () => {
clearInterval(this.incrementer);

const options = {
headers: {
"Content-Type": "application/json"
},
method: "POST",
body: JSON.stringify({
applicantId: this.props.applicant.id,
secondsElapsed: this.state.secondsElapsed,
answers: this.state.answers
})
};

fetch(`http://localhost:4567/api/applicant/test-results/${this.props.token}`, options)
.then(res =>
res.status === 403 ?
Promise.reject("Auth denied") :
res.json()
).then(data => {
if (!data.success) {
return this.props.propagateError();
}

this.props.redirectToFinished();
}).catch(err => console.error(err));
};

formattedSeconds = sec => {
return (Math.floor(sec / 60) +
':' +
('0' + sec % 60).slice(-2));
};

render() {
const style = {
submit: {
backgroundColor: 'purple',
textDecoration: 'none',
color: 'white',
padding: '10px',
cursor: 'pointer',
boxShadow: '2px 2px 1px 0px rgba(0,0,0,0.75)'
}
};

const questions = this.props.test.questions.map((x, i) =>
<div key={x.id}>
<Question
question={x}
index={i}
handleChange={this.handleChange}
/>
</div>
);

return (
<div style={{margin: '200px 500px', padding: '10px 30px 35px 30px', backgroundColor: '#cfcfd1', boxShadow: '1px 1px 1px 0px rgba(0,0,0,0.75)'}}>
<h1>BEGIN TESTING NOW</h1>
<p>Question 1</p>
<textarea style={{padding: '20px 100px', textAlign: 'left'}}/>
<p>Question 2</p>
<textarea style={{padding: '20px 100px', textAlign: 'left'}} />
</div>);
<h1 style={{color: 'red'}}>{this.formattedSeconds(this.state.secondsElapsed)}</h1>
{questions}
<a onClick={this.handleSubmit} style={style.submit}>SUBMIT</a>
</div>
);
}
}

export default Test;
export default Test;
@@ -1,17 +1,39 @@
import React from 'react';

const welcomePage = (props) => {
return (<div style={{margin: '200px 500px', padding: '10px 30px 35px 30px', backgroundColor: '#cfcfd1', boxShadow: '1px 1px 1px 0px rgba(0,0,0,0.75)'}}>
<div style={{paddingBottom: '10px'}}>
<p>Hey {this.props.name}, thanks for showing iinterest in becoming a customer
service ninja. We think you have the potential to be a great fit
at our company. Before we can take this any further, we would like
you to respond to a couple questions. You only have 30 minutes
to finish all the questions upon the time you click “START TEST NOW”.</p>
<p><strong>Best of luck!</strong></p>
</div>
<a onClick={this.changePageHandler} style={{backgroundColor: '#6d6dc4', textDecoration: 'none', color: 'white', padding: '10px', cursor: 'pointer', boxShadow: '2px 2px 1px 0px rgba(0,0,0,0.75)'}}>START TEST NOW</a>
</div>);
}
const WelcomePage = (props) => {
const style = {
outer: {
margin: '200px 500px',
padding: '10px 30px 35px 30px',
backgroundColor: '#cfcfd1',
boxShadow: '1px 1px 1px 0px rgba(0,0,0,0.75)'
},
inner: {
paddingBottom: '10px'
},
startBtn: {
backgroundColor: '#6d6dc4',
textDecoration: 'none',
color: 'white',
padding: '10px',
cursor: 'pointer',
boxShadow: '2px 2px 1px 0px rgba(0,0,0,0.75)'
}
};

export default welcomePage;
return (
<div style={style.outer}>
<div style={style.inner}>
<p>Hey {props.applicant.firstName}, thanks for showing iinterest in becoming a customer
service ninja. We think you have the potential to be a great fit
at our company. Before we can take this any further, we would like
you to respond to a couple questions. You only have 30 minutes
to finish all the questions upon the time you click “START TEST NOW”.</p>
<p><strong>Best of luck!</strong></p>
</div>
<a onClick={props.startTest} style={style.startBtn}>START TEST NOW</a>
</div>
);
};

export default WelcomePage;
@@ -1,4 +1,6 @@
import React, { Component } from 'react';
import { Link } from 'react-router-dom'

import Aux from '../../hoc/Aux/Aux';
import NewApplicant from './NewApplicant';
import ApplicantList from './ApplicantList';
@@ -9,7 +11,7 @@ class Company extends Component{
this.state = {
isLoading: true,
applicants: [],
// viewing: false,
companyName: "",
viewableApplicant: null,
search: ""
};
@@ -22,7 +24,7 @@ class Company extends Component{
refreshApplicantList = () => {
const token = localStorage.getItem("token");
if (token === null) {
this.props.history.push("/company-login");
this.props.history.push("/");
return;
}

@@ -32,15 +34,16 @@ class Company extends Component{
}
};

fetch("https://decisiontime.herokuapp.com/api/company/applicants", options)
fetch("http://localhost:4567/api/company/applicants", options)
.then(res =>
res.status === 200 ?
res.json() :
Promise.reject("Auth denied")
).then(data => {
this.setState({
isLoading: false,
applicants: data,
applicants: data.applicants,
companyName: data.companyName,
viewableApplicant: data[0]
})
}).catch(err => console.error(err));
@@ -65,7 +68,7 @@ class Company extends Component{
})
};

fetch("https://decisiontime.herokuapp.com/api/company/remove-applicant", options)
fetch("http://localhost:4567/api/company/remove-applicant", options)
.then(res =>
res.status === 403 ?
Promise.reject("Auth denied") :
@@ -75,14 +78,6 @@ class Company extends Component{
}).catch(err => console.error(err));
}

generateKeyId = () => {
let Id = this.state.keyId;
Id += 1;
this.setState({keyId: Id});
return Id;
}


generateTokenHandler = () => {
var length = 8,
charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
@@ -113,7 +108,7 @@ class Company extends Component{
})
};

fetch("https://decisiontime.herokuapp.com/api/company/create-applicant", options)
fetch("http://localhost:4567/api/company/create-applicant", options)
.then(res =>
res.status === 403 ?
Promise.reject("Auth denied") :
@@ -145,7 +140,8 @@ class Company extends Component{
<Aux>
<header style={{textAlign: 'right', padding: '0px 40px 20px 40px', color: 'purple', cursor: 'pointer', marginTop: '15px'}}><a onClick={this.logOut}>Logout</a></header>
<div style={{backgroundColor: '#d8d8d8', margin: '0px 0px 0px 0px', padding: '20px 0px', boxShadow: '1px 1px 1px 0px rgba(0,0,0,0.75)'}}>
<h1 style={{color: 'purple'}}>All Potential Applicants</h1>
<Link to="/company/test-editor">Test Editor</Link>
<h1 style={{color: 'purple'}}>All Potential Applicants for {this.state.companyName}</h1>
<h4 style={{color: 'purple'}}>Create New Applicant</h4>
<NewApplicant createApplicant={this.createApplicant.bind(this)} />
<div style={{borderTopStyle: 'solid', margin: '20px 60px', borderColor: 'purple'}}>
@@ -162,7 +158,6 @@ class Company extends Component{
applicants={this.state.applicants}
deleteApplicantsHandler={this.deleteApplicantsHandler.bind(this)}
refreshApplicantList={this.refreshApplicantList.bind(this)}
//viewable={this.viewHandler.bind(this)}
searchedApplicant={this.state.search} />
</div>
</div>
@@ -29,7 +29,7 @@ class FinalResults extends React.Component {
}
};

fetch(`https://decisiontime.herokuapp.com/api/company/test-results/${this.ApplicantId}`, options)
fetch(`http://localhost:4567/api/company/test-results/${this.ApplicantId}`, options)
.then(res =>
res.status === 403 ?
Promise.reject("Auth denied") :
@@ -54,17 +54,27 @@ class FinalResults extends React.Component {
return <p>Loading...</p>;
}

let answers = this.state.data.answers;
let content = [];
let exerciseCounter = 1;
for (let k in answers) {
content.push(
<div key={k}>
<p>Exercise {exerciseCounter}:</p>
<p><em>{answers[k]}</em></p>
</div>
);
exerciseCounter++;
}

return (
<Aux>
<div style={{padding: '20px', textAlign: 'left'}}>
<Link to='/company' style={{textDecoration: 'none', color: 'white', padding: '10px', cursor: 'pointer', boxShadow: '2px 2px 1px 0px rgba(0,0,0,0.75)', backgroundColor: 'purple'}}>BACK</Link>
</div>
<h3 style={{color: 'purple'}}>Results for {this.state.data.firstName} {this.state.data.lastName}</h3>
<h4 style={{color: 'purple'}}>Total amount of time taken is <span style={{color: 'red', textDecoration: 'underline'}}>{this.formattedSeconds(this.state.data.secondsElapsed)}</span></h4>
<p>Exercise A:</p>
<p><em>{this.state.data.results[0]}</em></p>
<p>Exercise B:</p>
<p><em>{this.state.data.results[1]}</em></p>
{content}
</Aux>
);
}
@@ -65,7 +65,7 @@ class IndividualApplicant extends Component{
})
};

fetch("https://decisiontime.herokuapp.com/api/company/edit-applicant", options)
fetch("http://localhost:4567/api/company/edit-applicant", options)
.then(res =>
res.status === 403 ?
Promise.reject("Auth denied") :
@@ -85,14 +85,14 @@ class IndividualApplicant extends Component{
}

viewApplicantResults = () => {
return <div>{this.props.applicant.completed ?<strong><Link style={{cursor: 'pointer', color: 'blue', textDecoration: 'underline'}} to={`/company/${this.props.applicant.id}`}>VIEW</Link></strong>: null}</div>;
return <div>{this.props.applicant.completed ?<strong><Link style={{cursor: 'pointer', color: 'blue', textDecoration: 'underline'}} to={`/company/results/${this.props.applicant.id}`}>VIEW</Link></strong>: null}</div>;
}

copyURLHandler = () => {
let URL = `https://decisiontime.herokuapp.com/applicant/${this.props.applicant.token}`
let URL = `http://localhost:3000/applicant/${this.props.applicant.token}`
return (<CopyToClipboard text={URL}>
<button>Click to Copy URL</button>
</CopyToClipboard>);
</CopyToClipboard>);
}

render() {

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

@@ -0,0 +1,83 @@
import React, {Component} from 'react';

class DeleteTestForm extends Component {
constructor(props) {
super(props);
this.state = {
nameConfirmation: "",
isError: false
};

this.handleChange = this.handleChange.bind(this);
this.handleDelete = this.handleDelete.bind(this);
}

handleDelete() {
if (this.state.nameConfirmation !== this.props.name) {
return this.setState({
isError: true
});
}

const options = {
headers: {
"Authorization": `Bearer ${this.props.token}`,
"Content-Type": "application/json"
},
method: "POST",
body: JSON.stringify({
id: this.props.id
})
};

fetch("http://localhost:4567/api/company/delete-test", options)
.then(res => res.json())
.then(data => {
console.log(data);
this.props.refreshTestData(this.props.firstTestId);
this.props.toggleDeleteForm();
}).catch(err => {
console.error(err);
})
}

handleChange(e) {
this.setState({
[e.target.name]: e.target.value
});
}

render() {
let errorMsg = "";
if (this.state.isError) {
errorMsg = (
<p style={{ color: 'red' }}>
The name you entered does not match
</p>
)
}

return (
<div>
<h3 style={{ color: 'red '}}>
Enter name of test to delete:
</h3>
<input
type="text"
name="nameConfirmation"
onChange={this.handleChange}
/>
<button type="button"
style={{ backgroundColor: 'red' }}
onClick={this.handleDelete}
>Delete</button>
<button type="button"
onClick={this.props.toggleDeleteForm}
>Cancel</button>
{errorMsg}
</div>
);
}
}

export default DeleteTestForm;
@@ -0,0 +1,150 @@
import React, {Component} from 'react';
import shortid from 'shortid';

import ActionButtons from '../../../components/UI/Buttons/ActionButtons';

class EditForm extends Component {
constructor(props) {
super(props);
this.state = {
questionType: props.question.type,
options: props.question.options,
correctAnswerId: props.question.type === "MULTIPLE_CHOICE" ?
props.question.options.find(x => x.correct) ?
props.question.options.find(x => x.correct).id : null
: null
};

this.onSaveClick = this.onSaveClick.bind(this);
this.setCorrectAnswer = this.setCorrectAnswer.bind(this);
this.removeOption = this.removeOption.bind(this);
this.addOption = this.addOption.bind(this);
}

onSaveClick = event => {
event.preventDefault();

const options = {
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${this.props.token}`
},
method: "POST"
};

const body = {
questionType: this.props.question.type,
body: this.refs.body.value,
questionId: this.props.question.id,
testId: this.props.testId
};

if (this.props.question.type === "MULTIPLE_CHOICE") {
options.body = JSON.stringify({
...body,
options: this.state.options.filter(x =>
this.refs[x.id].value.length > 0
).map(x => ({
...x,
answer: this.refs[x.id].value,
correct: x.id === this.state.correctAnswerId ? true : false
}))
});
} else {
options.body = JSON.stringify(body);
}

fetch("http://localhost:4567/api/company/edit-question", options)
.then(res => res.json())
.then(data => {
console.log(data);
this.props.refreshTestData();
this.props.toggleEditForm();
}).catch(err => console.error(err));
}

setCorrectAnswer(e) {
this.setState({
correctAnswerId: e.target.value
});
}

addOption() {
this.setState(prevState => ({
options: prevState.options.concat({
id: shortid.generate(),
answer: "",
correct: false
})
}));
}

removeOption(id) {
this.setState(prevState => ({
options: prevState.options.length > 2 ?
prevState.options.filter(x => x.id !== id) :
prevState.options
}));
}

render() {
let options = "";
let addOptionBtn = "";
if (this.props.question.type === "MULTIPLE_CHOICE") {
options = this.state.options.map(x =>
<div key={x.id}>
<input
type="radio"
name="options"
value={x.id}
onClick={this.setCorrectAnswer}
defaultChecked={x.id === this.state.correctAnswerId ? true : false}
/>
<input
type="text"
style={{padding: '5px 10px'}}
ref={x.id}
defaultValue={x.answer}
/>
<input
type="button"
onClick={() => this.removeOption(x.id)}
value="Delete"
/>
<br/><br/>
</div>
);

addOptionBtn = (
<div style={{ padding: '5px 0px 20px 0px' }}>
<button
type="button"
onClick={this.addOption}
>Add</button>
</div>
);
}

return (
<div style={{padding: '10px 0px'}}>
<textarea
rows='5'
cols='50'
defaultValue={this.props.question.body}
ref='body'
/>
{options}
{addOptionBtn}
<div style={{padding: '20px'}}>
<ActionButtons
isEditing={true}
onCancel={this.props.toggleEditForm}
onSaveClick={this.onSaveClick}
/>
</div>
</div>
);
}
}

export default EditForm;
@@ -0,0 +1,169 @@
import React, {Component} from 'react';
import shortid from 'shortid';

class QuestionForm extends Component {
constructor(props) {
super(props);
this.state = {
body: "",
type: "OPEN_RESPONSE",
options: [
{
id: shortid.generate(),
answer: "",
correct: true
},
{
id: shortid.generate(),
answer: "",
correct: false
}
],
correctAnswerId: null
};

this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.removeOption = this.removeOption.bind(this);
this.addOption = this.addOption.bind(this);
}

handleChange(e) {
this.setState({
[e.target.name]: e.target.value
});
}

removeOption(id) {
this.setState(prevState => ({
options: prevState.options.length > 2 ?
prevState.options.filter(x => x.id !== id) :
prevState.options
}));
}

addOption() {
this.setState(prevState => ({
options: prevState.options.concat({
id: shortid.generate(),
answer: "",
correct: false,
removable: true
})
}));
}

handleSubmit(e) {
e.preventDefault();

const options = {
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${this.props.token}`
},
method: "POST"
};

const body = {
questionId: shortid.generate(),
testId: this.props.testId,
body: this.state.body,
type: this.state.type
};

switch (this.state.type) {
case "OPEN_RESPONSE":
options.body = JSON.stringify(body);
break;
case "MULTIPLE_CHOICE":
options.body = JSON.stringify({
...body,
options: this.state.options.filter(x =>
this.refs[x.id].value.length > 0
).map(x => ({
...x,
answer: this.refs[x.id].value,
correct: x.id === this.state.correctAnswerId ? true : false
}))
});
break;
default:
console.error("Submitted invalid question type");
}

fetch("http://localhost:4567/api/company/create-question", options)
.then(res => res.json())
.then(data => {
console.log(data);
this.props.refreshTestData();
this.props.toggleQuestionForm();
}).catch(err => console.error(err));
}

render() {
let options = "";
let addOptionBtn = "";
if (this.state.type === "MULTIPLE_CHOICE") {
options = this.state.options.map(x =>
<div key={x.id}>
<input
type="radio"
name="correctAnswerId"
value={x.id}
onClick={this.handleChange}
/>
<input
type="text"
style={{padding: '5px 10px'}}
ref={x.id}
defaultValue={x.answer}
/>
<input
type="button"
onClick={() => this.removeOption(x.id)}
value="Delete"
/>
<br/><br/>
</div>
);

addOptionBtn = (
<button
type="button"
onClick={this.addOption}
>Add Option</button>
);
}

return (
<div>
<form>
<h3>Question Type:</h3>
<select name="type" value={this.state.type} onChange={this.handleChange}>
<option value="OPEN_RESPONSE">Open Response</option>
<option value="MULTIPLE_CHOICE">Multiple Choice</option>
</select>
<br/>
<h3>Question Body:</h3>
<textarea
cols="100"
rows="5"
name="body"
onChange={this.handleChange}
placeholder="write question here"
/>
<br />
{options}
{addOptionBtn}
<br/><br/>
<span>
<button type="button" onClick={this.handleSubmit}>Create</button>
<button type="button" onClick={this.props.toggleQuestionForm}>Cancel</button>
</span>
</form>
</div>
);
}
}

export default QuestionForm;
@@ -0,0 +1,117 @@
import React, {Component} from 'react';

import EditForm from '../../EditForm';
import ActionButtons from '../../../../../components/UI/Buttons/ActionButtons';

class IndividualQuestion extends Component {
constructor(props) {
super(props);

this.state = {
editFormMounted: false
}

this.toggleEditForm = this.toggleEditForm.bind(this);
this.deleteQuestion = this.deleteQuestion.bind(this);
}

toggleEditForm = () => {
this.setState(prevState => ({
editFormMounted: !prevState.editFormMounted
}));
}

deleteQuestion = id => {
const options = {
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${this.props.token}`
},
method: "POST",
body: JSON.stringify({
questionId: this.props.question.id,
testId: this.props.testId
})
};

fetch("http://localhost:4567/api/company/delete-question", options)
.then(res => res.json())
.then(data => {
console.log(data);
this.props.refreshTestData();
}).catch(err => console.error(err));
}

render() {
let question = "";
switch (this.props.question.type) {
case "OPEN_RESPONSE":
question = (
<div>
<h3>{this.props.index + 1}. {this.props.question.body}</h3>
</div>
);
break;
case "MULTIPLE_CHOICE":
question = (
<div>
<h3>{this.props.index + 1}. {this.props.question.body}</h3>
{
this.props.question.options.map(x => {
let highlighter = {};
if (x.correct) {
highlighter = {
color: 'green'
};
}
return (
<div key={x.id}>
<h4 style={highlighter}>{x.answer}</h4>
</div>
)
})
}
</div>
);
break;
default:
console.error("Invalid question type");
return <p>Error</p>
}

let editForm = "";
let actionButtons = "";
if (this.state.editFormMounted) {
editForm = (
<EditForm
question={this.props.question}
toggleEditForm={this.toggleEditForm.bind(this)}
testId={this.props.testId}
refreshTestData={this.props.refreshTestData}
token={this.props.token}
delete={this.props.delete}
/>
);
} else {
actionButtons = (
<ActionButtons
isEditing={false}
editHandler={this.toggleEditForm}
deleteHandler={this.deleteQuestion}
/>
)
}

return (
<div style={{paddingBottom: '20px', border: 'solid #cccdce 2px', margin: "20px 300px", backgroundColor: '#cccdce', boxShadow: '1px 1px 1px 0px rgba(0,0,0,0.75)'}}>
<span>
{question}
{editForm}
{actionButtons}
</span>
</div>
);
}
}

export default IndividualQuestion;
@@ -7,15 +7,18 @@ class QuestionList extends Component {
let questions = this.props.questions;
const props = _.omit(this.props, 'questions');
return questions
.map((question) =>
.map((question, i) =>
<IndividualQuestion question={question}
key={question.key}
delete={() => props.deleteQuestionHandler(question)} />
key={question.id}
index={i}
token={props.token}
refreshTestData={props.refreshTestData}
testId={props.testId}/>



);
}

render() {
return (
<div>
@@ -25,4 +28,4 @@ class QuestionList extends Component {
}
}

export default QuestionList;
export default QuestionList;
@@ -0,0 +1,238 @@
import React, {Component} from 'react';
import { Link } from 'react-router-dom';

import TestForm from './TestForm';
import QuestionList from './QuestionList/QuestionList';
import QuestionForm from './QuestionForm';
import DeleteTestForm from './DeleteTestForm';

class TestEditor extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
isError: false,
tests: [],
editingTestId: null,
testFormMounted: false,
testName: "",
questions: [],
questionFormMounted: false,
deleteTestForm: false
};

this.token = localStorage.getItem("token");

this.toggleQuestionForm = this.toggleQuestionForm.bind(this);
this.toggleTestForm = this.toggleTestForm.bind(this);
this.toggleDeleteForm = this.toggleDeleteForm.bind(this);
this.refreshTestData = this.refreshTestData.bind(this);
}

componentDidMount() {
if (this.token === null) {
return this.props.history.push("/");
}

const options = {
headers: {
"Authorization": `Bearer ${this.token}`
}
};

fetch("http://localhost:4567/api/company/tests/", options)
.then(res => res.json())
.then(data => {
if (!data.success) {
return this.setState({
isError: true,
isLoading: false
});
}

console.log("TESTS", data.tests);

this.setState({
isLoading: false,
tests: data.tests,
editingTestId: data.tests[0].id
});
}).catch(err => console.error(err));
}

refreshTestData = (editingTestId = this.state.editingTestId) => {
this.setState({
isLoading: true
}, () => {
if (this.token === null) {
return this.props.history.push("/");
}

const options = {
headers: {
"Authorization": `Bearer ${this.token}`
}
};

fetch("http://localhost:4567/api/company/tests/", options)
.then(res => res.json())
.then(data => {
if (!data.success) {
return this.setState({
isError: true,
isLoading: false
});
}

this.setState({
editingTestId,
tests: data.tests,
isLoading: false
});
}).catch(err => console.error(err));
});
}

toggleQuestionForm = () => {
this.setState(prevState => ({
questionFormMounted: !prevState.questionFormMounted
}));
}

toggleTestForm = () => {
this.setState(prevState => ({
testFormMounted: !prevState.testFormMounted
}));
}

toggleDeleteForm = () => {
this.setState(prevState => ({
deleteFormMounted: !prevState.deleteFormMounted
}));
}

setEditingTestId = (id) => {
this.setState({
editingTestId: id
});
}

render() {
const style = {
header: {
padding: '50px'
},
li: {
cursor: 'pointer',
border: 'solid',
padding: '10px'
}
};

if (this.state.isLoading) {
return <p>Loading...</p>
}

let header = this.state.tests.map(x =>
<span key={x.id}
style={style.li}
onClick={() => this.setEditingTestId(x.id)}
>{x.name}</span>
);

let test = this.state.tests.find(x =>
x.id === this.state.editingTestId
);

let newTestBtn = "";
let newTestForm = "";
if (!this.state.testFormMounted) {
newTestBtn = (
<button type="button"
onClick={this.toggleTestForm}
>New Test</button>
);
} else {
newTestForm = (
<div>
<TestForm
refreshTestData={this.refreshTestData}
toggleTestForm={this.toggleTestForm}
token={this.token}
setEditingTestId={this.setEditingTestId}
/>
</div>
);
}

let addQuestionBtn = "";
let questionForm = "";
if (!this.state.questionFormMounted) {
addQuestionBtn = (
<button
type="button"
onClick={this.toggleQuestionForm}
>Add New Question</button>
);
} else {
questionForm = (
<QuestionForm
testId={test.id}
toggleQuestionForm={this.toggleQuestionForm}
refreshTestData={this.refreshTestData}
token={this.token}
/>
);
}

let deleteBtn = "";
let deleteForm = "";
if (this.state.deleteFormMounted) {
deleteForm = (
<DeleteTestForm
toggleDeleteForm={this.toggleDeleteForm}
id={this.state.editingTestId}
refreshTestData={this.refreshTestData}
name={test.name}
token={this.token}
firstTestId={this.state.tests[0].id}
/>
);
} else {
deleteBtn = (
<button type="button"
onClick={this.toggleDeleteForm}
>Delete</button>
);
}

return (
<div>
<div style={{padding: '20px', textAlign: 'left'}}>
<Link to='/company' style={{textDecoration: 'none', color: 'white', padding: '10px', cursor: 'pointer', boxShadow: '2px 2px 1px 0px rgba(0,0,0,0.75)', backgroundColor: 'purple'}}>BACK</Link>
</div>
<div>
{newTestBtn}
{newTestForm}
</div>
<div style={style.header}>
{header}
</div>
<h1>{test.name}</h1>
{addQuestionBtn}
{questionForm}
<QuestionList
questions={test.questions}
testId={test.id}
inTestForm={false}
refreshTestData={this.refreshTestData}
token={this.token}
/>
{deleteBtn}
{deleteForm}
</div>
);
};
};

export default TestEditor;
@@ -0,0 +1,66 @@
import React, { Component } from 'react';

class TestForm extends Component {
constructor(props) {
super(props);
this.state = {
name: ""
};

this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}

handleSubmit(e) {
e.preventDefault();

const options = {
headers: {
"Authorization": `Bearer ${this.props.token}`,
"Content-Type": "application/json"
},
method: "POST",
body: JSON.stringify({
name: this.state.name
})
};

fetch("http://localhost:4567/api/company/create-test", options)
.then(res => res.json())
.then(data => {
console.log(data);
this.props.refreshTestData(data.createdTestId);
this.props.toggleTestForm();
}).catch(err => console.error(err));
}

handleChange(e) {
this.setState({
[e.target.name]: e.target.value
});
}

render() {
return (
<div>
<div>
<label htmlFor="name">Name:</label>
<input
type="text"
name="name"
onChange={this.handleChange}
placeholder="name"
/>
<button type="button"
onClick={this.handleSubmit}
>Submit</button>
<button type="button"
onClick={this.props.toggleTestForm}
>Cancel</button>
</div>
</div>
);
}
}

export default TestForm;
@@ -26,7 +26,7 @@ class Login extends Component {
}
};

fetch("https://decisiontime.herokuapp.com/api/company/auth", options)
fetch("http://localhost:4567/api/company/auth", options)
.then(res => {
if (res.status === 403) {
return Promise.reject(new Error("Auth denied"));
@@ -57,7 +57,7 @@ class Login extends Component {
})
};

fetch(`https://decisiontime.herokuapp.com/api/company/login`, options)
fetch(`http://localhost:4567/api/company/login`, options)
.then(res => {
if (res.status === 403) {
this.setState({ denied: true });
@@ -0,0 +1,14 @@
- Pull state up from Questions to Test so that we can send all answers on submit.
- Pass handleChange as prop

<QuestionList
questions={this.state.questions}
inTestForm={true}
token={this.token}
/>
{addQuestionBtn}
{questionForm}
<button
type="button"
onClick={this.handleSubmit}
>Submit</button>