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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.env
__pycache__/
*.py[cod]
.vscode/*
72 changes: 52 additions & 20 deletions client/src/App.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,62 @@
import React from 'react';
import React, {useEffect, useState} from 'react';
import { BrowserRouter, Switch, Route, NavLink } from 'react-router-dom';

import UserList from './components/UsersList';

import UserForm from './components/UserForm';
import AuthContext from './auth'

function App() {
const [fetchWithCSRF, setFetchWithCSRF] = useState(() => fetch);
const authContextValue = {
fetchWithCSRF,
};
useEffect(() => {
async function restoreCSRF() {
const response = await fetch('/api/csrf/restore', {
method: 'GET',
credentials: 'include'
});
if (response.ok) {
const authData = await response.json();
setFetchWithCSRF(() => {
return (resource, init) => {
if (init.headers) {
init.headers['X-CSRFToken'] = authData.csrf_token;
} else {
init.headers = {
'X-CSRFToken': authData.csrf_token
}
}
return fetch(resource, init);
}
});
}
}
restoreCSRF();
}, []);

return (
<BrowserRouter>
<nav>
<ul>
<li><NavLink to="/" activeclass="active">Home</NavLink></li>
<li><NavLink to="/users" activeclass="active">Users</NavLink></li>
</ul>
</nav>
<Switch>
<Route path="/users">
<UserList />
</Route>

<Route path="/">
<h1>My Home Page</h1>
</Route>
</Switch>
</BrowserRouter>
<AuthContext.Provider value={authContextValue}>
<BrowserRouter>
<nav>
<ul>
<li><NavLink to="/" activeclass="active">Home</NavLink></li>
<li><NavLink to="/login" activeclass="active">Login</NavLink></li>
<li><NavLink to="/users" activeclass="active">Users</NavLink></li>
</ul>
</nav>
<Switch>
<Route path="/users" exact={true}>
<UserList />
</Route>
<Route path="/users/:id/edit" component={UserForm} />
<Route path="/">
<h1>My Home Page</h1>
</Route>
</Switch>
</BrowserRouter>
</AuthContext.Provider>
);
}

export default App;
export default App;
5 changes: 5 additions & 0 deletions client/src/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react';

const AuthContext = React.createContext({});

export default AuthContext;
10 changes: 8 additions & 2 deletions client/src/components/User.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import React from 'react';

import React, {} from 'react';
import { Link } from 'react-router-dom';

function User(props) {
return (
<>
<strong>Username:</strong> {props.user.username}<br />
<strong>Email:</strong> {props.user.email}<br />
<Link to={{
pathname: `/users/${props.user.id}/edit`,
state: {
user: props.user
}
}}> Edit </Link>
<hr />
</>
);
Expand Down
56 changes: 56 additions & 0 deletions client/src/components/UserForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React, {useState, useContext} from 'react';
import AuthContext from '../auth'

function UserForm(props) {
const { id } = props.location.state.user;
console.log(id)
const [username, setUsername] = useState(props.location.state.user.username);
const [email, setEmail] = useState(props.location.state.user.email);

const [errors, setErrors] = useState([]);
const {fetchWithCSRF} = useContext(AuthContext);

const submitForm = (e) => {
e.preventDefault();

async function updateUser() {
const response = await fetchWithCSRF(`/api/users/${id}`, {
method: 'POST',
headers: {
"Content-Type": "application/json",
},
credentials: 'include',
body: JSON.stringify({
username,
email
})
});

const responseData = await response.json();
if (!response.ok) {
setErrors(responseData.errors);
}
}
updateUser();
}
return (
<form onSubmit={submitForm}>
{errors.length ? errors.map((err) => <li key={err} >{err}</li>) : ''}
<div className="field">
<label>Email: </label>
<div className="control">
<input className="input" type="text" value={email} onChange={(e) => setEmail(e.target.value)} name="email" />
</div>
</div>
<div className="field">
<label>Username: </label>
<div className="control">
<input className="input" type="text" value={username} onChange={(e) => setUsername(e.target.value)} name="username" />
</div>
</div>

<button>Update</button>
</form>
);
}
export default UserForm;
3 changes: 1 addition & 2 deletions client/src/components/UsersList.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, createContext } from 'react';

import User from './User';

Expand All @@ -13,7 +13,6 @@ function UsersList (props) {
}
fetchData();
}, []);

const userComponents = users.map((user) => <User key={user.id} user={user} />)
return (
<>
Expand Down
4 changes: 4 additions & 0 deletions starter_app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
app = Flask(__name__)

app.config.from_object(Config)
CSRFProtect(app)
app.register_blueprint(user_routes, url_prefix='/api/users')
db.init_app(app)

Expand All @@ -34,3 +35,6 @@ def react_root(path):
if path == 'favicon.ico':
return app.send_static_file('favicon.ico')
return app.send_static_file('index.html')
@app.route('/api/csrf/restore')
def restore_csrf():
return {'csrf_token': generate_csrf()}
10 changes: 8 additions & 2 deletions starter_app/api/user_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@

user_routes = Blueprint('users', __name__)


@user_routes.route('/')
def index():
response = User.query.all()
return { "users": [user.to_dict() for user in response]}
response = User.query.all()
return {"users": [user.to_dict() for user in response]}


@user_routes.route('/<int:id>', methods=['GET', 'POST'])
def user_detail(id):
return {}