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 { Login } from "./pages/Login.tsx";
|
||||||
import { ContentLayout, SpaceBetween } from "@cloudscape-design/components";
|
import { ContentLayout, SpaceBetween } from "@cloudscape-design/components";
|
||||||
import icon from "./paella-icon.png";
|
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 { SignUp } from "./pages/SignUp.tsx";
|
||||||
import { Register } from "./pages/Register.tsx";
|
import { Register } from "./pages/Register.tsx";
|
||||||
import { Buy } from "./pages/Buy.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() {
|
function App() {
|
||||||
|
const [userId, setUserId] = useState<number | null>(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContentLayout defaultPadding maxContentWidth={500}>
|
<ContentLayout defaultPadding maxContentWidth={500}>
|
||||||
<SpaceBetween size="m">
|
<SpaceBetween size="m">
|
||||||
<img className="app-logo" src={icon} alt="App logo" />
|
<img className="app-logo" src={icon} alt="App logo" />
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Login />} />
|
<Route
|
||||||
<Route path="/signup" element={<SignUp />} />
|
element={
|
||||||
|
<PreLoginPageContext.Provider value={{ setUserId }}>
|
||||||
|
<Outlet />
|
||||||
|
</PreLoginPageContext.Provider>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Route path="/login" element={<Login />} />
|
||||||
<Route path="/register" element={<Register />} />
|
<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 path="/buy" element={<Buy />} />
|
||||||
|
</Route>
|
||||||
|
<Route path="*" element={<NotFound />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</SpaceBetween>
|
</SpaceBetween>
|
||||||
</ContentLayout>
|
</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) =>
|
export const login = (name: string, password: string) =>
|
||||||
axios.post("/access/login", {
|
axios.post("/access/login", {
|
||||||
name,
|
name,
|
||||||
password,
|
password,
|
||||||
});
|
}) as Promise<AxiosResponse<AccessResponse, unknown, {}>>;
|
||||||
|
|
||||||
export const registerUser = (name: string, password: string) =>
|
export const registerUser = (name: string, password: string) =>
|
||||||
axios.post("/access/register", {
|
axios.post("/access/register", {
|
||||||
name,
|
name,
|
||||||
password,
|
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,
|
Header,
|
||||||
SpaceBetween,
|
SpaceBetween,
|
||||||
} from "@cloudscape-design/components";
|
} from "@cloudscape-design/components";
|
||||||
import { useState } from "react";
|
import { useContext, useState } from "react";
|
||||||
import { login } from "../api/client";
|
import { login } from "../api/client";
|
||||||
|
import { Link, useNavigate } from "react-router";
|
||||||
|
import { PreLoginPageContext } from "../context/pre-login-page-context";
|
||||||
|
|
||||||
export const Login = () => {
|
export const Login = () => {
|
||||||
const [userName, setUserName] = useState("");
|
const [userName, setUserName] = useState("");
|
||||||
const [password, setPassword] = useState("");
|
const [password, setPassword] = useState("");
|
||||||
const [rememberMe, setRememberMe] = useState(false);
|
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 (
|
return (
|
||||||
<SpaceBetween size="s" alignItems={"center"}>
|
<SpaceBetween size="s" alignItems={"center"}>
|
||||||
<Header>Login</Header>
|
<Header>Login</Header>
|
||||||
|
|
@ -33,7 +44,12 @@ export const Login = () => {
|
||||||
>
|
>
|
||||||
Remember me
|
Remember me
|
||||||
</Checkbox>
|
</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>
|
</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 {
|
import {
|
||||||
Alert,
|
Alert,
|
||||||
Button,
|
Button,
|
||||||
|
|
@ -8,11 +8,15 @@ import {
|
||||||
} from "@cloudscape-design/components";
|
} from "@cloudscape-design/components";
|
||||||
import * as zod from "zod";
|
import * as zod from "zod";
|
||||||
import { registerUser } from "../api/client";
|
import { registerUser } from "../api/client";
|
||||||
|
import { Link, useNavigate } from "react-router";
|
||||||
|
import { PreLoginPageContext } from "../context/pre-login-page-context";
|
||||||
|
|
||||||
export const Register = () => {
|
export const Register = () => {
|
||||||
const [userName, setUserName] = useState("");
|
const [userName, setUserName] = useState("");
|
||||||
const [password, setPassword] = useState("");
|
const [password, setPassword] = useState("");
|
||||||
const [passwordConfirm, setPasswordConfirm] = useState("");
|
const [passwordConfirm, setPasswordConfirm] = useState("");
|
||||||
|
const { setUserId } = useContext(PreLoginPageContext)!;
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const PASSWORD_MAX_LENGTH = 64;
|
const PASSWORD_MAX_LENGTH = 64;
|
||||||
const PASSWORD_MIN_LENGTH = 8;
|
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 (
|
return (
|
||||||
<SpaceBetween size="s" alignItems={"center"}>
|
<SpaceBetween size="s" alignItems={"center"}>
|
||||||
<Header>Register</Header>
|
<Header>Register</Header>
|
||||||
|
|
@ -70,10 +81,15 @@ export const Register = () => {
|
||||||
))}
|
))}
|
||||||
<Button
|
<Button
|
||||||
disabled={passwordErrors.length !== 0}
|
disabled={passwordErrors.length !== 0}
|
||||||
onClick={() => registerUser(userName, password)}
|
onClick={handleRegisterFlow}
|
||||||
>
|
>
|
||||||
Register
|
Register
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button>
|
||||||
|
<Link to="/login" style={{ textDecoration: "none" }}>
|
||||||
|
Login with existing account
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
</SpaceBetween>
|
</SpaceBetween>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue