Set up login/register navigation and API integration
parent
21f2232723
commit
7f2624b75a
29
src/App.tsx
29
src/App.tsx
|
|
@ -2,21 +2,44 @@ import "./App.css";
|
|||
import { Login } from "./pages/Login.tsx";
|
||||
import { ContentLayout, SpaceBetween } from "@cloudscape-design/components";
|
||||
import icon from "./paella-icon.png";
|
||||
import { Route, Routes } from "react-router";
|
||||
import { Outlet, Route, Routes } from "react-router";
|
||||
import { SignUp } from "./pages/SignUp.tsx";
|
||||
import { Register } from "./pages/Register.tsx";
|
||||
import { Buy } from "./pages/Buy.tsx";
|
||||
import { useState } from "react";
|
||||
import { PreLoginPageContext } from "./context/pre-login-page-context.ts";
|
||||
import { LoggedInPageContext } from "./context/logged-in-page-context.ts";
|
||||
import { NotFound } from "./pages/NotFound.tsx";
|
||||
|
||||
function App() {
|
||||
const [userId, setUserId] = useState<number | null>(null);
|
||||
|
||||
return (
|
||||
<ContentLayout defaultPadding maxContentWidth={500}>
|
||||
<SpaceBetween size="m">
|
||||
<img className="app-logo" src={icon} alt="App logo" />
|
||||
<Routes>
|
||||
<Route path="/" element={<Login />} />
|
||||
<Route path="/signup" element={<SignUp />} />
|
||||
<Route
|
||||
element={
|
||||
<PreLoginPageContext.Provider value={{ setUserId }}>
|
||||
<Outlet />
|
||||
</PreLoginPageContext.Provider>
|
||||
}
|
||||
>
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/register" element={<Register />} />
|
||||
</Route>
|
||||
<Route
|
||||
element={
|
||||
<LoggedInPageContext.Provider value={{ userId: userId! }}>
|
||||
<Outlet />
|
||||
</LoggedInPageContext.Provider>
|
||||
}
|
||||
>
|
||||
<Route path="/signup" element={<SignUp />} />
|
||||
<Route path="/buy" element={<Buy />} />
|
||||
</Route>
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Routes>
|
||||
</SpaceBetween>
|
||||
</ContentLayout>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,20 @@
|
|||
import axios from "axios";
|
||||
import axios, { type AxiosResponse } from "axios";
|
||||
|
||||
interface AccessResponse {
|
||||
userId: number;
|
||||
}
|
||||
|
||||
export const login = (name: string, password: string) =>
|
||||
axios.post("/access/login", {
|
||||
name,
|
||||
password,
|
||||
});
|
||||
}) as Promise<AxiosResponse<AccessResponse, unknown, {}>>;
|
||||
|
||||
export const registerUser = (name: string, password: string) =>
|
||||
axios.post("/access/register", {
|
||||
name,
|
||||
password,
|
||||
});
|
||||
}) as Promise<AxiosResponse<AccessResponse, unknown, {}>>;
|
||||
|
||||
export const startBuyFlow = (userId: string) =>
|
||||
axios.post("/buy/start", { userId });
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
import { createContext } from "react";
|
||||
|
||||
interface LoggedInPageContextProps {
|
||||
userId: number;
|
||||
}
|
||||
|
||||
export const LoggedInPageContext =
|
||||
createContext<LoggedInPageContextProps | null>(null);
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import { createContext, type Dispatch, type SetStateAction } from "react";
|
||||
|
||||
interface PreLoginPageContextProps {
|
||||
setUserId: Dispatch<SetStateAction<number | null>>;
|
||||
}
|
||||
|
||||
export const PreLoginPageContext =
|
||||
createContext<PreLoginPageContextProps | null>(null);
|
||||
|
|
@ -5,14 +5,25 @@ import {
|
|||
Header,
|
||||
SpaceBetween,
|
||||
} from "@cloudscape-design/components";
|
||||
import { useState } from "react";
|
||||
import { useContext, useState } from "react";
|
||||
import { login } from "../api/client";
|
||||
import { Link, useNavigate } from "react-router";
|
||||
import { PreLoginPageContext } from "../context/pre-login-page-context";
|
||||
|
||||
export const Login = () => {
|
||||
const [userName, setUserName] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [rememberMe, setRememberMe] = useState(false);
|
||||
|
||||
const { setUserId } = useContext(PreLoginPageContext)!;
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleLoginFlow = () =>
|
||||
login(userName, password).then((response) => {
|
||||
setUserId(response.data.userId);
|
||||
navigate("/signup");
|
||||
});
|
||||
|
||||
return (
|
||||
<SpaceBetween size="s" alignItems={"center"}>
|
||||
<Header>Login</Header>
|
||||
|
|
@ -33,7 +44,12 @@ export const Login = () => {
|
|||
>
|
||||
Remember me
|
||||
</Checkbox>
|
||||
<Button onClick={() => login(userName, password)}>Login</Button>
|
||||
<Button onClick={handleLoginFlow}>Login</Button>
|
||||
<Button>
|
||||
<Link to="/register" style={{ textDecoration: "none" }}>
|
||||
Create an account
|
||||
</Link>
|
||||
</Button>
|
||||
</SpaceBetween>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
import { useEffect } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
|
||||
export const NotFound = () => {
|
||||
const navigate = useNavigate();
|
||||
useEffect(() => {
|
||||
navigate("/login");
|
||||
});
|
||||
return <div />;
|
||||
};
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState } from "react";
|
||||
import { useContext, useState } from "react";
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
|
|
@ -8,11 +8,15 @@ import {
|
|||
} from "@cloudscape-design/components";
|
||||
import * as zod from "zod";
|
||||
import { registerUser } from "../api/client";
|
||||
import { Link, useNavigate } from "react-router";
|
||||
import { PreLoginPageContext } from "../context/pre-login-page-context";
|
||||
|
||||
export const Register = () => {
|
||||
const [userName, setUserName] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [passwordConfirm, setPasswordConfirm] = useState("");
|
||||
const { setUserId } = useContext(PreLoginPageContext)!;
|
||||
const navigate = useNavigate();
|
||||
|
||||
const PASSWORD_MAX_LENGTH = 64;
|
||||
const PASSWORD_MIN_LENGTH = 8;
|
||||
|
|
@ -45,6 +49,13 @@ export const Register = () => {
|
|||
}
|
||||
}
|
||||
|
||||
const handleRegisterFlow = () =>
|
||||
registerUser(userName, password).then((response) => {
|
||||
console.log(response);
|
||||
setUserId(response.data.userId);
|
||||
navigate("/signup");
|
||||
});
|
||||
|
||||
return (
|
||||
<SpaceBetween size="s" alignItems={"center"}>
|
||||
<Header>Register</Header>
|
||||
|
|
@ -70,10 +81,15 @@ export const Register = () => {
|
|||
))}
|
||||
<Button
|
||||
disabled={passwordErrors.length !== 0}
|
||||
onClick={() => registerUser(userName, password)}
|
||||
onClick={handleRegisterFlow}
|
||||
>
|
||||
Register
|
||||
</Button>
|
||||
<Button>
|
||||
<Link to="/login" style={{ textDecoration: "none" }}>
|
||||
Login with existing account
|
||||
</Link>
|
||||
</Button>
|
||||
</SpaceBetween>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue