#21 Using wouter for navigation and links. Register page works by sending request to register endpoint

This commit is contained in:
2023-03-19 16:34:44 -04:00
parent 0d4c4facd5
commit f6e2b445d7
7 changed files with 235 additions and 46 deletions

View File

@@ -2,17 +2,33 @@ import "./App.css";
import { Route } from "wouter"; import { Route } from "wouter";
import HomePage from "./pages/HomePage"; import HomePage from "./pages/HomePage";
import LoginPage from "./pages/LoginPage"; import LoginPage from "./pages/LoginPage";
import { UserContextProvider } from "./contexts/UserContext";
import LogoutPage from "./pages/LogoutPage"; import LogoutPage from "./pages/LogoutPage";
import RegisterPage from "./pages/RegisterPage";
import AuthenticatedRoute from "./components/AuthenticatedRoute";
function App() { function App() {
return ( return (
<div className="App"> <div className="App">
<UserContextProvider> <Route path="/login">
<Route path="/" component={HomePage} /> <AuthenticatedRoute isAuthenticated={false}>
<Route path="/login" component={LoginPage} /> <LoginPage />
<Route path="/logout" component={LogoutPage} /> </AuthenticatedRoute>
</UserContextProvider> </Route>
<Route path="/logout">
<AuthenticatedRoute isAuthenticated={false}>
<LogoutPage />
</AuthenticatedRoute>
</Route>
<Route path="/register">
<AuthenticatedRoute isAuthenticated={false}>
<RegisterPage />
</AuthenticatedRoute>
</Route>
<Route path="/">
<AuthenticatedRoute isAuthenticated={true}>
<HomePage />
</AuthenticatedRoute>
</Route>
</div> </div>
); );
} }

View File

@@ -0,0 +1,20 @@
import { useContext, useEffect } from "react";
import { useLocation } from "wouter";
import UserContext from "../contexts/UserContext";
const AuthenticatedRoute = ({ children, isAuthenticated }) => {
const { currentUser } = useContext(UserContext);
const [location, setLocation] = useLocation();
useEffect(() => {
if (isAuthenticated && !currentUser?.id) {
setLocation("/login");
} else if (!isAuthenticated && currentUser?.id) {
setLocation("/");
}
}, [currentUser]);
return children;
};
export default AuthenticatedRoute;

View File

@@ -1,24 +1,32 @@
import { useContext } from "react"; import React, { useContext } from "react";
import { Nav, Container, Navbar } from "react-bootstrap"; import { Nav, Container, Navbar } from "react-bootstrap";
import { Link } from "wouter";
import UserContext from "../contexts/UserContext"; import UserContext from "../contexts/UserContext";
const MyNavbar = () => { const MyNavbar = () => {
const { currentUser } = useContext(UserContext); const { currentUser } = useContext(UserContext);
const MyLink = ({ children, ...rest }) => {
return <Nav.Link as={Link} {...rest}>{children}</Nav.Link>;
};
return ( return (
<React.Fragment>
<Navbar variant="dark" bg="dark" expand="lg"> <Navbar variant="dark" bg="dark" expand="lg">
<Container> <Container>
<Navbar.Brand href="/">LearningTree</Navbar.Brand> <Navbar.Brand href="/">LearningTree</Navbar.Brand>
<Navbar.Toggle aria-controls="navbar-nav" /> <Navbar.Toggle aria-controls="navbar-nav" />
<Navbar.Collapse id="navbar-nav"> <Navbar.Collapse id="navbar-nav">
<Nav className="ms-auto"> <Nav className="ms-auto">
<Nav.Link href="/">Home</Nav.Link> <MyLink href="/">Home</MyLink>
{(currentUser?.id && ( {(currentUser?.id && (
<Nav.Link href="/logout">Logout</Nav.Link> <MyLink href="/logout">Logout</MyLink>
)) || <Nav.Link href="/login">Login</Nav.Link>} )) || <MyLink href="/login">Login</MyLink>}
</Nav> </Nav>
</Navbar.Collapse> </Navbar.Collapse>
</Container> </Container>
</Navbar> </Navbar>
</React.Fragment>
); );
}; };

View File

@@ -1,14 +1,17 @@
import React from 'react'; import React from "react";
import ReactDOM from 'react-dom/client'; import ReactDOM from "react-dom/client";
import './index.css'; import "./index.css";
import App from './App'; import App from "./App";
import reportWebVitals from './reportWebVitals'; import reportWebVitals from "./reportWebVitals";
import 'bootstrap/dist/css/bootstrap.min.css'; import "bootstrap/dist/css/bootstrap.min.css";
import { UserContextProvider } from "./contexts/UserContext";
const root = ReactDOM.createRoot(document.getElementById('root')); const root = ReactDOM.createRoot(document.getElementById("root"));
root.render( root.render(
<React.StrictMode> <React.StrictMode>
<UserContextProvider>
<App /> <App />
</UserContextProvider>
</React.StrictMode> </React.StrictMode>
); );

View File

@@ -6,18 +6,12 @@ import UserContext from "../contexts/UserContext";
const HomePage = () => { const HomePage = () => {
const { currentUser } = useContext(UserContext); const { currentUser } = useContext(UserContext);
useEffect(() => {
if (!currentUser?.id) {
window.location.replace("/login");
}
}, [currentUser]);
return ( return (
<div> <div>
<MyNavbar /> <MyNavbar />
<Container> <Container className="p-5">
<div> <div>
<h1>This is the home page</h1> <h1>Welcome back {currentUser?.username}!</h1>
</div> </div>
</Container> </Container>
</div> </div>

View File

@@ -1,5 +1,6 @@
import React, { useContext, useEffect, useState } from "react"; import React, { useContext, useEffect, useState } from "react";
import { Button, Col, Container, Form, Row, Alert } from "react-bootstrap"; import { Button, Col, Container, Form, Row, Alert } from "react-bootstrap";
import { Link } from "wouter";
import MyNavbar from "../components/MyNavbar"; import MyNavbar from "../components/MyNavbar";
import UserContext from "../contexts/UserContext"; import UserContext from "../contexts/UserContext";
import { makeRequest } from "../utils.ts"; import { makeRequest } from "../utils.ts";
@@ -12,7 +13,6 @@ const LoginPage = () => {
const { currentUser, setCurrentUser } = useContext(UserContext); const { currentUser, setCurrentUser } = useContext(UserContext);
useEffect(() => { useEffect(() => {
console.log(currentUser);
if (currentUser?.id) { if (currentUser?.id) {
gotoHome(); gotoHome();
} }
@@ -42,14 +42,16 @@ const LoginPage = () => {
return ( return (
<React.Fragment> <React.Fragment>
<MyNavbar /> <MyNavbar />
<Container className="p-5">
{error && ( {error && (
<Container> <Container>
<Alert variant="danger" className="m-4 mx-auto w-50 text-center"> <Alert variant="danger" className="mx-auto w-50 text-center">
{error.message} {error.message}
</Alert> </Alert>
</Container> </Container>
)} )}
<Container className="p-5"> <h2>Login</h2>
<br />
<Form onSubmit={sendLoginRequest}> <Form onSubmit={sendLoginRequest}>
<Form.Group as={Row} className="mb-3" controlId="username"> <Form.Group as={Row} className="mb-3" controlId="username">
<Form.Label column sm={2} className="me-2"> <Form.Label column sm={2} className="me-2">
@@ -87,7 +89,7 @@ const LoginPage = () => {
</Form.Group> </Form.Group>
<Form.Group as={Row}> <Form.Group as={Row}>
<p> <p>
Don't have an account? <a href="/register">Register here</a> Don't have an account? <Link href="/register">Register here</Link>
</p> </p>
</Form.Group> </Form.Group>
</Form> </Form>

View File

@@ -0,0 +1,146 @@
import React, { useState } from "react";
import { Button, Col, Container, Form, Row, Alert } from "react-bootstrap";
import { Link } from "wouter";
import MyNavbar from "../components/MyNavbar";
import { makeRequest } from "../utils.ts";
const RegisterPage = () => {
const [role, setRole] = useState("student");
const [username, setUsername] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [password2, setPassword2] = useState("");
const [error, setError] = useState(null);
const sendRegisterRequest = (e) => {
e?.preventDefault();
makeRequest({
url: "http://localhost:5000/register",
method: "POST",
body: {
role,
email,
username,
password,
password2,
},
})
.then((resp) => resp.json())
.then((data) => {
if (data.error) {
setError(data);
return;
}
window.location.href = "/login";
});
};
return (
<React.Fragment>
<MyNavbar />
<Container className="p-5">
{error && (
<Container>
<Alert variant="danger" className="mx-auto w-50 text-center">
{error.message}
</Alert>
</Container>
)}
<h2>Register</h2>
<br />
<Form onSubmit={sendRegisterRequest}>
<Form.Group as={Row} className="mb-3" controlId="role">
<Form.Label column sm={2} className="me-2">
Role
</Form.Label>
<Col sm={9}>
<Form.Select
onChange={(e) => {
setRole(e.target.value);
}}
>
<option value="student">Student</option>
<option value="teacher">Teacher</option>
</Form.Select>
</Col>
</Form.Group>
<Form.Group as={Row} className="mb-3" controlId="username">
<Form.Label column sm={2} className="me-2">
Username
</Form.Label>
<Col sm={9}>
<Form.Control
type="username"
placeholder="username"
onChange={(e) => {
setUsername(e.target.value);
}}
/>
</Col>
</Form.Group>
<Form.Group as={Row} className="mb-3" controlId="email">
<Form.Label column sm={2} className="me-2">
Email
</Form.Label>
<Col sm={9}>
<Form.Control
type="email"
placeholder="email"
onChange={(e) => {
setEmail(e.target.value);
}}
/>
</Col>
</Form.Group>
<Form.Group as={Row} className="mb-3" controlId="password">
<Form.Label column sm={2} className="me-2">
Password
</Form.Label>
<Col sm={9}>
<Form.Control
type="password"
placeholder="password"
onChange={(e) => {
setPassword(e.target.value);
}}
/>
</Col>
</Form.Group>
<Form.Group as={Row} className="mb-3" controlId="password2">
<Form.Label column sm={2} className="me-2">
Confirm Password
</Form.Label>
<Col sm={9}>
<Form.Control
type="password"
placeholder="password"
onChange={(e) => {
setPassword2(e.target.value);
}}
/>
</Col>
</Form.Group>
<Form.Group as={Row} className="mb-3" controlId="submit">
<Col sm={2}></Col>
<Col sm={2}>
<Button
type="submit"
disabled={!password || password !== password2}
>
Submit
</Button>
</Col>
</Form.Group>
<Form.Group as={Row}>
<p>
Already have an account? <Link href="/login">Login here</Link>
</p>
</Form.Group>
</Form>
</Container>
</React.Fragment>
);
};
export default RegisterPage;