Last active
June 14, 2020 23:52
-
-
Save brlafreniere/687332297ab26c078503cf6c39ce9aca to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import './App.css'; | |
import { BrowserRouter } from "react-router-dom" | |
import AppContext from "./AppContext" | |
import IssueBrowser from "./components/IssueBrowser" | |
import ErrorMessages from "./components/ErrorMessages" | |
import LoaderWidget from "./components/LoaderWidget" | |
class App extends React.Component { | |
// For errors in the format of: | |
// [{message: "first_name missing..."}, {message: "Email address in use..."}] | |
setErrorMessages = (errorMessages) => { | |
this.setState({errorMessages}) } | |
// Other messages not in the format above. Can set "alert-warning" or | |
// "alert-success" type messages. | |
setStatusMessage = (statusType, statusMessage) => { | |
this.setState({statusType, statusMessage}) } | |
clearAllMessages = () => { | |
this.setState({errorMessages: [], statusType: null, statusMessage: null}) } | |
setAuthToken = () => {} | |
setLoaded = (val) => { this.setState({loaded: val}) } | |
state = { | |
user: {}, | |
loaded: false, | |
errorMessages: [], | |
statusType: null, | |
statusMessage: null, | |
setErrorMessages: this.setErrorMessages, | |
clearAllMessages: this.clearAllMessages, | |
setStatusMessage: this.setStatusMessage, | |
setLoading: this.setLoading, | |
setLoaded: this.setLoaded, | |
setAuthToken: this.setAuthToken, | |
} | |
// componentDidMount() { | |
// this.setState({loading: false}) | |
// } | |
componentDidUpdate(prevProps, prevState, snapshot) { | |
if (prevState.errorMessages.length > 0) { | |
this.setState({errorMessages: []}) | |
} | |
if (prevState.statusType !== null) { | |
this.setState({errorMessages: null}) | |
} | |
if (prevState.statusMessage !== null) { | |
this.setState({statusMessage: null}) | |
} | |
} | |
render() { | |
return ( | |
<div className="App container"> | |
<React.StrictMode> | |
<AppContext.Provider value={this.state}> | |
<BrowserRouter> | |
{this.state.statusMessage ? | |
<div className={"alert alert-" + this.state.statusType}>{this.state.statusMessage}</div> : null} | |
<ErrorMessages errorMessages={this.state.errorMessages} /> | |
{!this.state.loaded ? <LoaderWidget /> : null } | |
<IssueBrowser /> | |
</BrowserRouter> | |
</AppContext.Provider> | |
</React.StrictMode> | |
</div> | |
); | |
} | |
} | |
export default App |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from "react"; | |
import Issues from "../modules/issues"; | |
import { | |
Switch, | |
Route, | |
NavLink, | |
Link, | |
useParams, | |
useHistory | |
} from "react-router-dom"; | |
import "./IssueBrowser.css"; | |
import UserRegistration from "./UserRegistration" | |
import AppContext from "../AppContext" | |
export default function IssueBrowserWithHistory(props) { | |
let history = useHistory() | |
return (<IssueBrowser history={history} />) | |
} | |
class IssueBrowser extends React.Component { | |
static contextType = AppContext | |
state = { | |
issues: [], | |
noResponse: false, | |
loaded: false, | |
} | |
render() { | |
const NewIssueForm = (props) => { | |
return ( | |
<form onSubmit={this.createIssue}> | |
<div className="form-group"> | |
<label htmlFor="title">Title</label> | |
<input className="form-control" name="title" type="text" /> | |
</div> | |
<div className="form-group"> | |
<label htmlFor="body">Body</label> | |
<textarea name="body" className="form-control" rows="10"></textarea> | |
</div> | |
<input type="submit" className="btn btn-primary" /> | |
</form> | |
) | |
} | |
const IssueDetail = (props) => { | |
let {id} = useParams() | |
let issue = this.state.issues.find(issue => issue.id === id) | |
if (issue) { | |
return ( | |
<div> | |
<h1 className="card-title">{issue.title}</h1> | |
<p className="card-text">{issue.body}</p> | |
<button className="btn btn-primary" onClick={(e) => this.deleteIssue(issue.id)}>Delete</button> | |
</div> | |
) | |
} else { | |
return null | |
} | |
} | |
const IssueListItem = (props) => { | |
return ( | |
<li className="list-group-item" key={props.issue.id}> | |
<Link to={"/issues/" + props.issue.id}>{props.issue.title}</Link> | |
</li> | |
) | |
} | |
const IssueList = (props) => { | |
if (this.state.issues.length > 0) { | |
return ( | |
<ul className="list-group list-group-flush"> | |
{this.state.issues.map(issue => <IssueListItem key={issue.id} issue={issue} />)} | |
</ul> | |
) | |
} else if (!this.state.noResponse) { | |
return ( | |
<div> | |
There are no issues. Aren't you lucky? | |
</div> | |
) | |
} | |
return null; | |
} | |
const MainSwitch = (props) => { | |
return ( | |
<Switch> | |
<Route exact path="/users/registration"> | |
<UserRegistration /> | |
</Route> | |
<Route exact path="/issues/create/"> | |
<NewIssueForm /> | |
</Route> | |
<Route exact path="/issues/:id"> | |
<IssueDetail /> | |
</Route> | |
<Route exact path="/"> | |
<IssueList /> | |
</Route> | |
</Switch> | |
) | |
} | |
return ( | |
<div className="mt-5"> | |
<this.Navigation /> | |
<this.NoResponseFromServer noResponse={this.state.noResponse} /> | |
<div className="p-3 border border-top-0 rounded-bottom"> | |
<MainSwitch /> | |
</div> | |
</div> | |
) | |
} | |
deleteIssue = (issue_id) => { | |
this.context.setLoaded(false) | |
Issues.delete(issue_id) | |
.then(response => { | |
this.refreshIssues() | |
this.props.history.push('/') | |
}) | |
} | |
createIssue = (event) => { | |
event.preventDefault() | |
this.context.setLoaded(false) | |
let payload = { | |
title: event.target.title.value, | |
body: event.target.body.value } | |
Issues.create(payload) | |
.then(response => { | |
this.refreshIssues() | |
this.props.history.push('/') | |
}) | |
.catch(error => { console.log(error) }) | |
} | |
refreshIssues = () => { | |
Issues.getAll() | |
.then(issues => { | |
this.setState({issues}) | |
this.context.setLoaded(true) | |
}) | |
.catch(error => { | |
if (!error.response) { | |
this.setState({noResponse: true}) | |
} | |
this.context.setLoaded(true) | |
}) | |
} | |
componentDidMount() { | |
this.refreshIssues() | |
} | |
NoResponseFromServer = (props) => { | |
if (this.state.noResponse) { | |
return ( | |
<div className="alert alert-danger"> | |
No response received from server. Is it running? | |
</div> | |
) | |
} else { | |
return null; | |
} | |
} | |
Navigation = (props) => { | |
return ( | |
<nav className="nav nav-tabs"> | |
<li className="nav-item"><NavLink to="/" exact={true} className="nav-link" activeClassName="active">All</NavLink></li> | |
<li className="nav-item"><NavLink to="/issues/create/" className="nav-link" activeClassName="active">New</NavLink></li> | |
<li className="nav-item"><NavLink to="/users/registration/" className="nav-link" activeClassName="active">Register</NavLink></li> | |
</nav> | |
) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from "react" | |
import { Redirect, useHistory } from "react-router-dom" | |
import Users from "../modules/user" | |
import AppContext from "../AppContext" | |
export default function UserRegistrationWithHistory(props) { | |
let history = useHistory(); | |
return (<UserRegistration history={history} />) | |
} | |
class UserRegistration extends React.Component { | |
static contextType = AppContext | |
registerUser = (event) => { | |
event.preventDefault() | |
this.context.setLoaded(false) | |
// TODO: check if password matches confirmation, display error message | |
// if it doesn't | |
let payload = { | |
first_name: event.target.first_name.value, | |
last_name: event.target.last_name.value, | |
email_address: event.target.email_address.value, | |
password: event.target.password.value } | |
Users.create(payload) | |
.then(response => { | |
this.context.setStatusMessage("success", "User successfully registered.") | |
this.context.setLoaded(true) | |
}) | |
.catch(error => { | |
if (error.response.data.messages) { this.context.setErrorMessages(error.response.data.messages) } | |
this.context.setLoaded(true) | |
}) | |
this.setState({redirect: true}) | |
} | |
componentWillUnmount() { | |
this.context.clearAllMessages() | |
} | |
render() { | |
return ( | |
<form onSubmit={this.registerUser}> | |
<div className="form-group"> | |
<label htmlFor="first_name">First Name:</label> | |
<input name="first_name" type="text" className="form-control" /> | |
</div> | |
<div className="form-group"> | |
<label htmlFor="last_name">Last Name:</label> | |
<input name="last_name" type="text" className="form-control" /> | |
</div> | |
<div className="form-group"> | |
<label htmlFor="email_address">Email Address:</label> | |
<input name="email_address" type="text" className="form-control" /> | |
</div> | |
<div className="form-group"> | |
<label htmlFor="password">Password:</label> | |
<input name="password" type="password" className="form-control" /> | |
</div> | |
<div className="form-group"> | |
<label htmlFor="password">Confirm Password:</label> | |
<input name="password" type="password" className="form-control" /> | |
</div> | |
<div className="form-group"> | |
<input className="btn btn-primary form-control" type="submit" value="Register" /> | |
</div> | |
</form> | |
) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment