Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,17 @@ app.use(morgan('dev'));
app.use('/student', Student);
app.use('/test', Test);

// for any static files, refer to the public folder
app.use(express.static(path.join(__dirname, 'public')));

app.use(function(err, req, res, next) {
app.use(function (err, req, res, next) {
console.error(err.stack);
res.status(500).send('Something broke!');
});

db.sync()
.then(() =>
app.listen(3000, function() {
app.listen(3000, function () {
console.log('Server is listening on port 3000!');
})
)
Expand Down
120 changes: 67 additions & 53 deletions browser/components/Main.js
Original file line number Diff line number Diff line change
@@ -1,55 +1,69 @@
import React, {Component} from 'react';
import axios from 'axios';

import StudentList from './StudentList.js'
import SingleStudent from './SingleStudent.js'

export default class Main extends Component {
constructor(props){
super(props)
this.state = {
students: [],
selectedStudent : {}
import React, { Component } from 'react';
import { connect } from 'react-redux';
import StudentList from './StudentList.js'
import SingleStudent from './SingleStudent.js'
import NewStudentForm from './NewStudentForm.js';
import { toggleFormThunk, getStudentsThunk } from '../store';

class Main extends Component {
constructor() {
super(); // state is outside of component unless it's
// the local state; props (properties of component)
// need to be passed down.
// both state and props can be passed down,
// but you can pass state down as props
// avoid passing state down as 'state' because
// it's confusing; we'd have props.state.
// you should pass the information on state down as props
this.handleClick = this.handleClick.bind(this);
}

componentDidMount() {
this.props.getStudentsOnMount();
}

handleClick(e) {
this.props.toggleAddStudentForm();
}

render() { // only for class components
// variable declaration, object destructuring

return ( // always has return
<div style={{ position: 'absolute', top: '10%', left: '30%', marginRight: '-30%' }}>
<h1>Students</h1>
<button onClick={this.handleClick}>Add a student</button>
{/* bootstrap for passing down CSS as props */}
{this.props.showForm ? (
<NewStudentForm />
) : null}
{/* conditionally rendered based on Redux's state.showForm */}
<table>
<thead>
<tr>
<th>Name</th>
<th>Tests</th>
</tr>
</thead>
< StudentList />
</table>
{
this.props.studentDetails.id ? <SingleStudent /> : null
}
</div>
)
}
};

const mapStateToProps = (state) => {
return state;
};

const mapDispatchToProps = (dispatch) => {
return {
toggleAddStudentForm: () => dispatch(toggleFormThunk()),
getStudentsOnMount: () => dispatch(getStudentsThunk())
};
};

this.selectStudent = this.selectStudent.bind(this)
}

componentDidMount(){
this.getStudents()
}

getStudents(){
console.log("fetching")
axios.get('/student')
.then(res => this.setState({students: res.data}))
.catch(console.error)
}

selectStudent(student) {
return this.setState({
selectedStudent : student
})
}

render(){
return (
<div>
<h1>Students</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Tests</th>
</tr>
</thead>
< StudentList students={this.state.students} selectStudent={this.selectStudent} />
</table>
{
this.state.selectedStudent.id ? <SingleStudent student={this.state.selectedStudent} /> : null
}

</div>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Main);
81 changes: 81 additions & 0 deletions browser/components/NewStudentForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { submitStudentThunk } from '../store';

class NewStudentForm extends Component {
constructor(props) {
super(props);
this.state = {
firstName: '',
lastName: '',
email: '',
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}

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

handleSubmit(e) {
e.preventDefault();
this.props.submitStudent(this.state);
this.setState({
firstName: '',
lastName: '',
email: ''
});
}

render() {
return (
<form onSubmit={this.handleSubmit} style={{ marginLeft: '-30%' }}>
<label>First Name:
<input type="text" name="firstName" onChange={this.handleChange} value={this.state.firstName} />
</label>
<br></br>
<label>
Last Name:
<input type="text" name="lastName" onChange={this.handleChange} value={this.state.lastName} />
</label>
<br></br>
<label>
Email:
<input type="email" name="email" onChange={this.handleChange} value={this.state.email} />
</label>

<button type="submit">Submit</button>
</form>
);
}
}
// // convention
// const varname = (something) => {
// return {
// submitStudent: (student) => something(submitStudentThunk(student)),
// }
// }

const mapDispatchToProps = (dispatch) => {
return {
// On line 26: basically passing in this.state as the student argument.

submitStudent: (student) => dispatch(submitStudentThunk(student)),
};
}; // redux store is listening to the payload of that dispatch
// (whether it's a function / object)

// if a thunk were a package, and a normal action were the
// letter, dispatch would be the mailbox; it delivers whatever
// you pass in to the redux store.


// export default connect(null, mapDispatchToProps)(NewStudentForm);
export default connect(null, mapDispatchToProps)(NewStudentForm);

// for regular exports, the names have to match because it's not default

36 changes: 21 additions & 15 deletions browser/components/SingleStudent.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import React from 'react';
import { connect } from 'react-redux';

const avgGrade = tests => {
return (
Math.round(
tests.map(test => test.grade)
.reduce((x, y) => x + y) / tests.length
))
tests.map(test => test.grade)
.reduce((x, y) => x + y) / tests.length
))
}

const SingleStudent = (props) => {
console.log('ppp', props)
return ( <div>
return (<div>
<h3>{props.student.fullName}</h3>
<em><b>Email:</b> {props.student.email}</em>
<h3>Average grade: {avgGrade(props.student.tests)}%</h3>
<div>
<table>
Expand All @@ -22,22 +23,27 @@ const SingleStudent = (props) => {
</tr>
</thead>
<tbody>
{
props.student.tests.map((test) => {
return (
<tr key={test.id}>
<td>{test.subject}</td>
<td>{test.grade}%</td>
</tr>
{
props.student.tests.map((test) => {
return (
<tr key={test.id}>
<td>{test.subject}</td>
<td>{test.grade}%</td>
</tr>
)
}
)
}
)
}
</tbody>
</table>
</div>
</div>)
}

const mapStateToProps = (state) => {
return {
student: state.studentDetails,
};
};

export default SingleStudent
export default connect(mapStateToProps)(SingleStudent);
45 changes: 29 additions & 16 deletions browser/components/StudentList.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,39 @@
import React from 'react';
import { connect } from 'react-redux';
import { studentDetailsThunk } from '../store';

const StudentList = (props) => {
console.log("p", props)
return (
<tbody>
{
props.students
.map(student =>
(
<tr key={student.id}>
<td>
{student.fullName}
{
props.students
.map(student =>
(
<tr key={student.id}>
<td>
{student.fullName}
</td>
<td onClick={() => props.studentDetails(student)}>
Details
</td>
<td onClick= {() => props.selectStudent(student)}>
Details
</td>
</tr>
)
)
}
</tr>
)
)
}
</tbody>
)
}

export default StudentList
const mapStateToProps = (state) => {
return {
students: state.students,
};
};

const mapDispatchToProps = (dispatch) => {
return {
studentDetails: (student) => dispatch(studentDetailsThunk(student)),
};
};

export default connect(mapStateToProps, mapDispatchToProps)(StudentList);
16 changes: 14 additions & 2 deletions browser/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
import React from 'react';
import ReactDOM from 'react-dom';
import Main from './components/Main';
import store from './store';

import { Provider } from 'react-redux';
// provides your main component access to the entire store;
// ultimately giving access to all components


ReactDOM.render(
<Main />,
<Provider store={store}>
<Main />
</Provider>, // first arg is component, second arg is the DOM element where it goes
document.getElementById('app')
)
// replace that element with the Main component
// default is index.html, since we haven't specified the name of document. How would you specify the name of the specific document?
);

// we're not exporting anything
Loading