diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index ff1abe5..526c137 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -23,7 +23,7 @@
"@types/node": "^16.11.45",
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6",
- "axios": "^1.5.0",
+ "axios": "^0.27.2",
"date-fns": "^2.29.1",
"formik": "^2.2.9",
"react": "^18.2.0",
@@ -5417,13 +5417,12 @@
}
},
"node_modules/axios": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz",
- "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+ "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
"dependencies": {
- "follow-redirects": "^1.15.0",
- "form-data": "^4.0.0",
- "proxy-from-env": "^1.1.0"
+ "follow-redirects": "^1.14.9",
+ "form-data": "^4.0.0"
}
},
"node_modules/axios/node_modules/form-data": {
@@ -13498,11 +13497,6 @@
"node": ">= 0.10"
}
},
- "node_modules/proxy-from-env": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
- "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
- },
"node_modules/psl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 78998e4..4c674c1 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -18,7 +18,7 @@
"@types/node": "^16.11.45",
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6",
- "axios": "^1.5.0",
+ "axios": "^0.27.2",
"date-fns": "^2.29.1",
"formik": "^2.2.9",
"react": "^18.2.0",
diff --git a/frontend/src/Router.tsx b/frontend/src/Router.tsx
index 3082277..7cf53d6 100644
--- a/frontend/src/Router.tsx
+++ b/frontend/src/Router.tsx
@@ -1,8 +1,16 @@
-import { BrowserRouter, Routes } from "react-router-dom";
+import { BrowserRouter, Route, Routes } from "react-router-dom";
+
+import ScrollToTop from "./components/ScrollToTop";
+import TopBar from "./components/TopBar";
+import Register from "./pages/Register";
const Router = () => (
- {}
+
+
+
+ } />
+
);
diff --git a/frontend/src/components/AccountMenu.tsx b/frontend/src/components/AccountMenu.tsx
new file mode 100644
index 0000000..d52828b
--- /dev/null
+++ b/frontend/src/components/AccountMenu.tsx
@@ -0,0 +1,67 @@
+import axios from "axios";
+import Divider from "@mui/material/Divider";
+import IconButton from "@mui/material/IconButton";
+import Menu from "@mui/material/Menu";
+import MenuItem from "@mui/material/MenuItem";
+import AccountCircle from "@mui/icons-material/AccountCircle";
+import { useQueryClient } from "@tanstack/react-query";
+import React, { useContext, useState } from "react";
+import { Link } from "react-router-dom";
+
+import { AuthContext } from "src/AuthContext";
+import { useMutation } from "src/query";
+
+const useLogout = () => {
+ const { setAuthenticated } = useContext(AuthContext);
+ const queryClient = useQueryClient();
+ const { mutate: logout } = useMutation(
+ async () => await axios.delete("/sessions/"),
+ {
+ onSuccess: () => {
+ setAuthenticated(false);
+ queryClient.clear();
+ },
+ },
+ );
+ return logout;
+};
+
+const AccountMenu = () => {
+ const logout = useLogout();
+ const [anchorEl, setAnchorEl] = useState(null);
+
+ const onMenuOpen = (event: React.MouseEvent) =>
+ setAnchorEl(event.currentTarget);
+ const onMenuClose = () => setAnchorEl(null);
+
+ return (
+ <>
+
+
+
+
+ >
+ );
+};
+
+export default AccountMenu;
diff --git a/frontend/src/components/TopBar.tsx b/frontend/src/components/TopBar.tsx
new file mode 100644
index 0000000..51e4482
--- /dev/null
+++ b/frontend/src/components/TopBar.tsx
@@ -0,0 +1,37 @@
+import AppBar from "@mui/material/AppBar";
+import Box from "@mui/material/Box";
+import Button from "@mui/material/Button";
+import Toolbar from "@mui/material/Toolbar";
+import React, { useContext } from "react";
+import { Link } from "react-router-dom";
+
+import { AuthContext } from "src/AuthContext";
+import AccountMenu from "src/components/AccountMenu";
+
+const sxToolbar = {
+ paddingLeft: "env(safe-area-inset-left)",
+ paddingRight: "env(safe-area-inset-right)",
+ paddingTop: "env(safe-area-inset-top)",
+};
+
+const TopBar = () => {
+ const { authenticated } = useContext(AuthContext);
+
+ return (
+ <>
+
+
+
+
+
+ {authenticated ? : null}
+
+
+
+ >
+ );
+};
+
+export default TopBar;
diff --git a/frontend/src/pages/Register.tsx b/frontend/src/pages/Register.tsx
new file mode 100644
index 0000000..d776085
--- /dev/null
+++ b/frontend/src/pages/Register.tsx
@@ -0,0 +1,99 @@
+import axios from "axios";
+import { Form, Formik, FormikHelpers } from "formik";
+import { useContext } from "react";
+import { useNavigate } from "react-router";
+import { useLocation } from "react-router-dom";
+import * as yup from "yup";
+
+import EmailField from "../components/EmailField";
+import FormActions from "../components/FormActions";
+import LazyPasswordWithStrengthField from "../components/LazyPasswordWithStrengthField";
+import Title from "../components/Title";
+import { ToastContext } from "../ToastContext";
+import { useMutation } from "../query";
+
+interface IForm {
+ email: string;
+ password: string;
+}
+
+const useRegister = () => {
+ const navigate = useNavigate();
+ const { addToast } = useContext(ToastContext);
+ const { mutateAsync: register } = useMutation(
+ async (data: IForm) => await axios.post("/members/", data),
+ );
+
+ return async (data: IForm, { setFieldError }: FormikHelpers) => {
+ try {
+ await register(data);
+ addToast("Registered", "success");
+ navigate("/login/", { state: { email: data.email } });
+ } catch (error: any) {
+ if (
+ error.response?.status === 400 &&
+ error.response?.data.code === "WEAK_PASSWORD"
+ ) {
+ setFieldError("password", "Password is too weak");
+ } else {
+ addToast("Try again", "error");
+ }
+ }
+ };
+};
+
+const validationSchema = yup.object({
+ email: yup.string().email("Email invalid").required("Required"),
+ password: yup.string().required("Required"),
+});
+
+const Register = () => {
+ const location = useLocation();
+ const onSubmit = useRegister();
+
+ return (
+ <>
+
+
+ initialValues={{
+ email: (location.state as any)?.email ?? "",
+ password: "",
+ }}
+ onSubmit={onSubmit}
+ validationSchema={validationSchema}
+ >
+ {({ dirty, isSubmitting, values }) => (
+
+ )}
+
+ >
+ );
+};
+
+export default Register;