fix authentication
This commit is contained in:
parent
e6d963fc5a
commit
f98f0b4d2f
51
src/api/auth.ts
Normal file
51
src/api/auth.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { CreateUserRequest, User } from "@root/model/auth";
|
||||
import { authStore } from "@root/stores/makeRequest";
|
||||
|
||||
|
||||
const apiUrl = process.env.NODE_ENV === "production" ? "/user" : "https://hub.pena.digital/user";
|
||||
const authUrl = process.env.NODE_ENV === "production" ? "/auth" : "https://hub.pena.digital/auth";
|
||||
|
||||
const makeRequest = authStore.getState().makeRequest;
|
||||
|
||||
export async function getOrCreateUser({ userId, email, password }: {
|
||||
userId: string;
|
||||
email: string | null;
|
||||
password: string | null;
|
||||
}): Promise<User | null> {
|
||||
try {
|
||||
return await makeRequest<never, User>({
|
||||
url: `${apiUrl}/${userId}`,
|
||||
contentType: true,
|
||||
method: "GET",
|
||||
useToken: false,
|
||||
withCredentials: false,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("get user error", error);
|
||||
|
||||
if (!email || !password) return null;
|
||||
|
||||
return makeRequest<CreateUserRequest, User>({
|
||||
url: apiUrl,
|
||||
contentType: true,
|
||||
method: "POST",
|
||||
useToken: true,
|
||||
withCredentials: false,
|
||||
body: {
|
||||
email,
|
||||
login: email,
|
||||
password,
|
||||
phoneNumber: "-",
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function logout() {
|
||||
return makeRequest<never, void>({
|
||||
url: authUrl + "/logout",
|
||||
method: "POST",
|
||||
useToken: false,
|
||||
withCredentials: true,
|
||||
});
|
||||
}
|
13
src/components/Layout.tsx
Normal file
13
src/components/Layout.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import { Outlet } from "react-router-dom";
|
||||
import Navbar from "./Navbar/Navbar";
|
||||
|
||||
|
||||
export default function Layout() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar isLoggedIn={true} />
|
||||
<Outlet />
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,11 +1,15 @@
|
||||
import { useMediaQuery, useTheme } from "@mui/material";
|
||||
import NavbarCollapsed from "./NavbarCollapsed";
|
||||
import NavbarFull from "./NavbarFull";
|
||||
|
||||
interface Props {
|
||||
isLoggedIn: boolean;
|
||||
isCollapsed?: boolean;
|
||||
isCollapsed?: boolean;
|
||||
isLoggedIn: boolean;
|
||||
}
|
||||
|
||||
export default function Navbar({ isLoggedIn, isCollapsed = false }: Props) {
|
||||
return isCollapsed ? <NavbarCollapsed isLoggedIn={isLoggedIn} /> : <NavbarFull isLoggedIn={isLoggedIn} />;
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
|
||||
return upMd ? <NavbarFull isLoggedIn={isLoggedIn} /> : <NavbarCollapsed isLoggedIn={isLoggedIn} />;
|
||||
}
|
||||
|
@ -1,38 +1,41 @@
|
||||
import { IconButton, useTheme } from "@mui/material";
|
||||
import { Divider, IconButton, useTheme } from "@mui/material";
|
||||
import SectionWrapper from "../SectionWrapper";
|
||||
import MenuIcon from "@mui/icons-material/Menu";
|
||||
import PenaLogo from "../PenaLogo";
|
||||
|
||||
interface Props {
|
||||
isLoggedIn: boolean;
|
||||
isLoggedIn: boolean;
|
||||
}
|
||||
|
||||
export default function NavbarCollapsed({ isLoggedIn }: Props) {
|
||||
const theme = useTheme();
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<SectionWrapper
|
||||
component="nav"
|
||||
maxWidth="lg"
|
||||
outerContainerSx={{
|
||||
backgroundColor: theme.palette.navbarbg.main,
|
||||
position: "sticky",
|
||||
top: 0,
|
||||
zIndex: 1,
|
||||
// borderBottom: "1px solid #E3E3E3",
|
||||
}}
|
||||
sx={{
|
||||
height: "51px",
|
||||
py: "6px",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<PenaLogo width={100} />
|
||||
<IconButton sx={{ p: 0, width: "30px", color: theme.palette.primary.main }}>
|
||||
<MenuIcon sx={{ height: "30px", width: "30px" }} />
|
||||
</IconButton>
|
||||
</SectionWrapper>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<SectionWrapper
|
||||
component="nav"
|
||||
maxWidth="lg"
|
||||
outerContainerSx={{
|
||||
backgroundColor: theme.palette.navbarbg.main,
|
||||
position: "sticky",
|
||||
top: 0,
|
||||
zIndex: 1,
|
||||
// borderBottom: "1px solid #E3E3E3",
|
||||
}}
|
||||
sx={{
|
||||
height: "51px",
|
||||
py: "6px",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<PenaLogo width={100} />
|
||||
<IconButton sx={{ p: 0, width: "30px", color: theme.palette.primary.main }}>
|
||||
<MenuIcon sx={{ height: "30px", width: "30px" }} />
|
||||
</IconButton>
|
||||
</SectionWrapper>
|
||||
{!isLoggedIn && <Divider sx={{ bgcolor: "#E3E3E3", borderColor: "#00000000" }} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||
import { useEffect } from "react";
|
||||
import { Box, Button, Container, IconButton, Typography, useTheme } from "@mui/material";
|
||||
import { Box, Button, Container, Divider, IconButton, Typography, useTheme } from "@mui/material";
|
||||
|
||||
import SectionWrapper from "../SectionWrapper";
|
||||
import { basketStore } from "@stores/BasketStore";
|
||||
@ -12,6 +12,9 @@ import CustomAvatar from "./Avatar";
|
||||
import Drawers from "../Drawers";
|
||||
import PenaLogo from "../PenaLogo";
|
||||
import Menu from "../Menu";
|
||||
import { logout } from "@root/api/auth";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { clearUser, useUserStore } from "@root/stores/user";
|
||||
|
||||
interface Props {
|
||||
isLoggedIn: boolean;
|
||||
@ -21,6 +24,8 @@ export default function NavbarFull({ isLoggedIn }: Props) {
|
||||
const theme = useTheme();
|
||||
const { clearToken } = authStore();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const user = useUserStore(state => state.user);
|
||||
|
||||
const { open } = basketStore();
|
||||
|
||||
@ -31,7 +36,15 @@ export default function NavbarFull({ isLoggedIn }: Props) {
|
||||
}, [location.pathname, open]);
|
||||
|
||||
async function handleLogoutClick() {
|
||||
clearToken();
|
||||
try {
|
||||
await logout();
|
||||
clearToken();
|
||||
clearUser();
|
||||
navigate("/");
|
||||
} catch (error: any) {
|
||||
console.log("Logout error", error);
|
||||
enqueueSnackbar(error.response?.data?.message ?? error.message ?? "Logout error");
|
||||
}
|
||||
}
|
||||
|
||||
return isLoggedIn ? (
|
||||
@ -85,40 +98,43 @@ export default function NavbarFull({ isLoggedIn }: Props) {
|
||||
</Box>
|
||||
</Container>
|
||||
) : (
|
||||
<SectionWrapper
|
||||
component="nav"
|
||||
maxWidth="lg"
|
||||
outerContainerSx={{
|
||||
backgroundColor: theme.palette.lightPurple.main,
|
||||
// borderBottom: "1px solid #E3E3E3",
|
||||
}}
|
||||
sx={{
|
||||
px: "20px",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
height: "80px",
|
||||
alignItems: "center",
|
||||
gap: "50px",
|
||||
}}
|
||||
>
|
||||
<PenaLogo width={150} />
|
||||
<Menu />
|
||||
<Button
|
||||
component={Link}
|
||||
to="/signin"
|
||||
state={{ backgroundLocation: location }}
|
||||
variant="outlined"
|
||||
<>
|
||||
<SectionWrapper
|
||||
component="nav"
|
||||
maxWidth="lg"
|
||||
outerContainerSx={{
|
||||
backgroundColor: theme.palette.lightPurple.main,
|
||||
// borderBottom: "1px solid #E3E3E3",
|
||||
}}
|
||||
sx={{
|
||||
px: "18px",
|
||||
py: "10px",
|
||||
borderColor: "white",
|
||||
borderRadius: "8px",
|
||||
whiteSpace: "nowrap",
|
||||
minWidth: "180px",
|
||||
px: "20px",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
height: "80px",
|
||||
alignItems: "center",
|
||||
gap: "50px",
|
||||
}}
|
||||
>
|
||||
Личный кабинет
|
||||
</Button>
|
||||
</SectionWrapper>
|
||||
<PenaLogo width={150} />
|
||||
<Menu />
|
||||
<Button
|
||||
component={Link}
|
||||
to={user ? "/tariffs" : "/signin"}
|
||||
state={user ? undefined : { backgroundLocation: location }}
|
||||
variant="outlined"
|
||||
sx={{
|
||||
px: "18px",
|
||||
py: "10px",
|
||||
borderColor: "white",
|
||||
borderRadius: "8px",
|
||||
whiteSpace: "nowrap",
|
||||
minWidth: "180px",
|
||||
}}
|
||||
>
|
||||
Личный кабинет
|
||||
</Button>
|
||||
</SectionWrapper>
|
||||
<Divider sx={{ bgcolor: "#E3E3E3", borderColor: "#00000000" }} />
|
||||
</>
|
||||
);
|
||||
}
|
175
src/index.tsx
175
src/index.tsx
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { BrowserRouter, Route, Routes, useLocation } from "react-router-dom";
|
||||
import { CssBaseline, Divider, ThemeProvider, useMediaQuery } from "@mui/material";
|
||||
import { CssBaseline, ThemeProvider } from "@mui/material";
|
||||
import Faq from "./pages/Faq/Faq";
|
||||
import Wallet from "./pages/Wallet";
|
||||
import Payment from "./pages/Payment/Payment";
|
||||
@ -15,23 +15,34 @@ import SignupDialog from "./pages/auth/Signup";
|
||||
import PaymentHistory from "./pages/PaymentHistory/PaymentHistory";
|
||||
import Basket from "./pages/Basket/Basket";
|
||||
import TariffPage from "./pages/Tariffs/TariffsPage";
|
||||
import Footer from "@components/Footer";
|
||||
import Navbar from "@components/Navbar/Navbar";
|
||||
import darkTheme from "@utils/themes/dark";
|
||||
import lightTheme from "@utils/themes/light";
|
||||
import PublicRoute from "@utils/routes/publicRoute";
|
||||
import PrivateRoute from "@utils/routes/privateRoute";
|
||||
import reportWebVitals from "./reportWebVitals";
|
||||
import { SnackbarProvider } from "notistack";
|
||||
import { SnackbarProvider, enqueueSnackbar } from "notistack";
|
||||
import "./index.css";
|
||||
import Layout from "./components/Layout";
|
||||
import { getOrCreateUser } from "./api/auth";
|
||||
import { setUser, useUserStore } from "./stores/user";
|
||||
|
||||
|
||||
// TODO refactor routes
|
||||
const App = () => {
|
||||
const location = useLocation();
|
||||
const upMd = useMediaQuery(lightTheme.breakpoints.up("md"));
|
||||
const userId = useUserStore(state => state.userId);
|
||||
const email = useUserStore(state => state.email);
|
||||
const password = useUserStore(state => state.password);
|
||||
|
||||
const state = location.state as { backgroundLocation?: Location; };
|
||||
useEffect(function fetchUserData() {
|
||||
if (!userId) return;
|
||||
|
||||
getOrCreateUser({ userId, email, password }).then(result => {
|
||||
setUser(result);
|
||||
}).catch(error => {
|
||||
console.log("Error fetching user", error);
|
||||
enqueueSnackbar(error.response?.data?.message ?? error.message ?? "Error fetching user");
|
||||
});
|
||||
}, [email, password, userId]);
|
||||
|
||||
const state = (location.state as { backgroundLocation?: Location; });
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -42,133 +53,23 @@ const App = () => {
|
||||
</Routes>
|
||||
}
|
||||
<Routes location={state?.backgroundLocation || location}>
|
||||
<Route
|
||||
path="/"
|
||||
element={
|
||||
<ThemeProvider theme={darkTheme}>
|
||||
<CssBaseline />
|
||||
<Navbar isLoggedIn={false} isCollapsed={!upMd} />
|
||||
<Divider sx={{ bgcolor: "#E3E3E3", borderColor: "#00000000" }} />
|
||||
<Landing />
|
||||
<Footer />
|
||||
</ThemeProvider>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="tariffs"
|
||||
element={
|
||||
<>
|
||||
<Navbar isLoggedIn={true} isCollapsed={!upMd} />
|
||||
<Tariffs />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="tariffs/time"
|
||||
element={
|
||||
<>
|
||||
<Navbar isLoggedIn={true} isCollapsed={!upMd} />
|
||||
<TariffPage />
|
||||
</>
|
||||
} />
|
||||
<Route
|
||||
path="tariffs/volume"
|
||||
element={
|
||||
<>
|
||||
<Navbar isLoggedIn={true} isCollapsed={!upMd} />
|
||||
<TariffPage />
|
||||
</>
|
||||
} />
|
||||
|
||||
<Route
|
||||
path="/faq"
|
||||
element={
|
||||
<>
|
||||
<Navbar isLoggedIn={true} isCollapsed={!upMd} />
|
||||
<Faq />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/wallet"
|
||||
element={
|
||||
// <PrivateRoute>
|
||||
<>
|
||||
<Navbar isLoggedIn={true} isCollapsed={!upMd} />
|
||||
<Wallet />
|
||||
</>
|
||||
// </PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/payment"
|
||||
element={
|
||||
// <PrivateRoute>
|
||||
<>
|
||||
<Navbar isLoggedIn={true} isCollapsed={!upMd} />
|
||||
<Payment />
|
||||
</>
|
||||
// </PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/support"
|
||||
element={
|
||||
<>
|
||||
<Navbar isLoggedIn={true} isCollapsed={!upMd} />
|
||||
<Support />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/support/:ticketId"
|
||||
element={
|
||||
<>
|
||||
<Navbar isLoggedIn={true} isCollapsed={!upMd} />
|
||||
<Support />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/tariffconstructor"
|
||||
element={
|
||||
<>
|
||||
<Navbar isLoggedIn={true} isCollapsed={!upMd} />
|
||||
<CustomTariff />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/settings"
|
||||
element={
|
||||
// <PrivateRoute>
|
||||
<>
|
||||
<Navbar isLoggedIn={true} isCollapsed={!upMd} />
|
||||
<AccountSetup />
|
||||
</>
|
||||
// </PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/basket"
|
||||
element={
|
||||
<>
|
||||
<Navbar isLoggedIn={true} isCollapsed={!upMd} />
|
||||
<Basket />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/paymenthistory"
|
||||
element={
|
||||
// <PrivateRoute>
|
||||
<>
|
||||
<Navbar isLoggedIn={true} isCollapsed={!upMd} />
|
||||
<PaymentHistory />
|
||||
</>
|
||||
// </PrivateRoute>
|
||||
}
|
||||
/>
|
||||
<Route path="/" element={<Landing />} />
|
||||
<Route element={<Layout />}>
|
||||
<Route path="/tariffs" element={<Tariffs />} />
|
||||
<Route path="/tariffs/time" element={<TariffPage />} />
|
||||
<Route path="/tariffs/volume" element={<TariffPage />} />
|
||||
<Route path="/faq" element={<Faq />} />
|
||||
<Route path="/support" element={<Support />} />
|
||||
<Route path="/support/:ticketId" element={<Support />} />
|
||||
<Route path="/tariffconstructor" element={<CustomTariff />} />
|
||||
<Route path="/basket" element={<Basket />} />
|
||||
<Route element={<PrivateRoute />}>
|
||||
<Route path="/wallet" element={<Wallet />} />
|
||||
<Route path="/payment" element={<Payment />} />
|
||||
<Route path="/settings" element={<AccountSetup />} />
|
||||
<Route path="/paymenthistory" element={<PaymentHistory />} />
|
||||
</Route>
|
||||
</Route>
|
||||
</Routes>
|
||||
</>
|
||||
);
|
||||
|
42
src/model/auth.ts
Normal file
42
src/model/auth.ts
Normal file
@ -0,0 +1,42 @@
|
||||
|
||||
|
||||
export interface RegisterRequest {
|
||||
login: string;
|
||||
email: string;
|
||||
password: string;
|
||||
phoneNumber: string;
|
||||
};
|
||||
|
||||
export interface RegisterResponse {
|
||||
accessToken: string;
|
||||
login: string;
|
||||
email: string;
|
||||
phoneNumber: string;
|
||||
refreshToken: string;
|
||||
_id: string;
|
||||
}
|
||||
|
||||
export interface LoginRequest {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export type LoginResponse = RegisterResponse;
|
||||
|
||||
export interface CreateUserRequest {
|
||||
login: string;
|
||||
email: string;
|
||||
password: string;
|
||||
phoneNumber: string;
|
||||
};
|
||||
|
||||
export interface User {
|
||||
_id: string;
|
||||
login: string;
|
||||
email: string;
|
||||
phoneNumber: string;
|
||||
isDeleted: boolean;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
deletedAt?: string;
|
||||
}
|
@ -1,10 +1,13 @@
|
||||
import { Box } from "@mui/material";
|
||||
import { Box, CssBaseline, ThemeProvider } from "@mui/material";
|
||||
import Section1 from "./Section1";
|
||||
import Section2 from "./Section2";
|
||||
import Section3 from "./Section3";
|
||||
import Section4 from "./Section4";
|
||||
import Section5 from "./Section5";
|
||||
import FloatingSupportChat from "@root/components/FloatingSupportChat/FloatingSupportChat";
|
||||
import Footer from "@root/components/Footer";
|
||||
import darkTheme from "@root/utils/themes/dark";
|
||||
import Navbar from "@root/components/Navbar/Navbar";
|
||||
|
||||
interface Props {
|
||||
templaterOnly?: boolean;
|
||||
@ -13,15 +16,20 @@ interface Props {
|
||||
export default function Landing({ templaterOnly = false }: Props) {
|
||||
|
||||
return (
|
||||
<Box sx={{
|
||||
position: "relative",
|
||||
}}>
|
||||
<Section1 />
|
||||
<Section2 templaterOnly={templaterOnly}/>
|
||||
<Section3 />
|
||||
<Section4 />
|
||||
<Section5 />
|
||||
<FloatingSupportChat />
|
||||
</Box>
|
||||
<ThemeProvider theme={darkTheme}>
|
||||
<CssBaseline />
|
||||
<Box sx={{
|
||||
position: "relative",
|
||||
}}>
|
||||
<Navbar isLoggedIn={false} />
|
||||
<Section1 />
|
||||
<Section2 templaterOnly={templaterOnly} />
|
||||
<Section3 />
|
||||
<Section4 />
|
||||
<Section5 />
|
||||
<Footer />
|
||||
<FloatingSupportChat />
|
||||
</Box>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
@ -6,10 +6,12 @@ import { authStore } from "@stores/makeRequest";
|
||||
import CustomButton from "@components/CustomButton";
|
||||
import InputTextfield from "@components/InputTextfield";
|
||||
import PenaLogo from "@components/PenaLogo";
|
||||
import { useSnackbar } from "notistack";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
import { object, string } from "yup";
|
||||
import { useState } from "react";
|
||||
import { LoginRequest, LoginResponse } from "@root/model/auth";
|
||||
import { setUserId, setUserEmail, setUserPassword } from "@root/stores/user";
|
||||
|
||||
|
||||
interface Values {
|
||||
@ -27,10 +29,8 @@ const validationSchema = object({
|
||||
password: string().required("Поле обязательно"),
|
||||
});
|
||||
|
||||
// TODO remove useSnackbar anywhere
|
||||
export default function SigninDialog() {
|
||||
const [isDialogOpen, setIsDialogOpen] = useState<boolean>(true);
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const navigate = useNavigate();
|
||||
@ -39,18 +39,25 @@ export default function SigninDialog() {
|
||||
const formik = useFormik<Values>({
|
||||
initialValues,
|
||||
validationSchema,
|
||||
onSubmit: async (values: Values) => {
|
||||
makeRequest({
|
||||
onSubmit: (values, formikHelpers) => {
|
||||
makeRequest<LoginRequest, LoginResponse>({
|
||||
url: "https://hub.pena.digital/auth/login",
|
||||
body: {
|
||||
"email": values.email,
|
||||
"password": values.password
|
||||
email: values.email,
|
||||
password: values.password
|
||||
},
|
||||
useToken: false
|
||||
})
|
||||
.catch((e: any) => {
|
||||
enqueueSnackbar(e.message ? e.message : `Unknown error`);
|
||||
});
|
||||
useToken: false,
|
||||
withCredentials: true,
|
||||
}).then(result => {
|
||||
setUserId(result._id);
|
||||
setUserEmail(result.email);
|
||||
setUserPassword(values.password);
|
||||
navigate("/tariffs");
|
||||
}).catch((error: any) => {
|
||||
enqueueSnackbar(error.response?.data?.message ?? error.message ?? "Unknown error");
|
||||
}).finally(() => {
|
||||
formikHelpers.setSubmitting(false);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -5,11 +5,13 @@ import CloseIcon from "@mui/icons-material/Close";
|
||||
import CustomButton from "@components/CustomButton";
|
||||
import InputTextfield from "@components/InputTextfield";
|
||||
import PenaLogo from "@components/PenaLogo";
|
||||
import { useSnackbar } from "notistack";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { authStore } from "@stores/makeRequest";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
import { object, ref, string } from "yup";
|
||||
import { ChangeEvent, useState } from "react";
|
||||
import { RegisterRequest, RegisterResponse } from "@root/model/auth";
|
||||
import { setUserEmail, setUserPassword, setUserId } from "@root/stores/user";
|
||||
|
||||
|
||||
interface Values {
|
||||
@ -28,14 +30,13 @@ const initialValues: Values = {
|
||||
|
||||
const validationSchema = object({
|
||||
email: string().email("Введите email").required("Поле обязательно"),
|
||||
phoneNumber: string().matches(/^\+\d{5,}|\d{6,}$/, "Введите номер телефона").required("Поле обязательно"),
|
||||
password: string().min(8, "Минимум 8 символов").required("Поле обязательно"),
|
||||
phoneNumber: string().min(6, "Введите номер телефона").matches(/^\+\d+|\d+$/, "Введите номер телефона").required("Поле обязательно"),
|
||||
password: string().min(8, "Минимум 8 символов").matches(/^[.,:;-_+\d\w]+$/, "Некорректные символы").required("Поле обязательно"),
|
||||
repeatPassword: string().oneOf([ref("password"), undefined], "Пароли не совпадают"),
|
||||
});
|
||||
|
||||
export default function SignupDialog() {
|
||||
const [isDialogOpen, setIsDialogOpen] = useState<boolean>(true);
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const navigate = useNavigate();
|
||||
@ -44,21 +45,27 @@ export default function SignupDialog() {
|
||||
const formik = useFormik<Values>({
|
||||
initialValues,
|
||||
validationSchema,
|
||||
onSubmit: async (values: Values) => {
|
||||
makeRequest({
|
||||
onSubmit: (values, formikHelpers) => {
|
||||
makeRequest<RegisterRequest, RegisterResponse>({
|
||||
url: "https://hub.pena.digital/auth/register",
|
||||
body: {
|
||||
"login": values.email,
|
||||
"email": values.email,
|
||||
"password": values.repeatPassword,
|
||||
"phoneNumber": "--"
|
||||
login: values.email,
|
||||
email: values.email,
|
||||
password: values.repeatPassword,
|
||||
phoneNumber: values.phoneNumber,
|
||||
},
|
||||
useToken: false
|
||||
})
|
||||
.catch((e: any) => {
|
||||
console.log(e);
|
||||
enqueueSnackbar(e.response?.data?.message ?? `Unknown error`);
|
||||
});
|
||||
useToken: false,
|
||||
withCredentials: true,
|
||||
}).then(result => {
|
||||
setUserId(result._id);
|
||||
setUserEmail(result.email);
|
||||
setUserPassword(values.password);
|
||||
navigate("/tariffs");
|
||||
}).catch((error: any) => {
|
||||
enqueueSnackbar(error.response?.data?.message ?? error.message ?? "Unknown error");
|
||||
}).finally(() => {
|
||||
formikHelpers.setSubmitting(false);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
43
src/stores/user.ts
Normal file
43
src/stores/user.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { User } from "@root/model/auth";
|
||||
import { create } from "zustand";
|
||||
import { createJSONStorage, devtools, persist } from "zustand/middleware";
|
||||
|
||||
|
||||
interface UserStore {
|
||||
userId: string | null;
|
||||
email: string | null;
|
||||
password: string | null;
|
||||
user: User | null;
|
||||
}
|
||||
|
||||
const initialState: UserStore = {
|
||||
userId: null,
|
||||
email: null,
|
||||
password: null,
|
||||
user: null,
|
||||
};
|
||||
|
||||
export const useUserStore = create<UserStore>()(
|
||||
persist(
|
||||
devtools(
|
||||
(set, get) => initialState,
|
||||
{
|
||||
name: "User store",
|
||||
}
|
||||
),
|
||||
{
|
||||
name: "user",
|
||||
storage: createJSONStorage(() => localStorage),
|
||||
partialize: state => ({
|
||||
userId: state.userId,
|
||||
}),
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
export const setUserId = (userId: string | null) => useUserStore.setState({ userId });
|
||||
export const setUserEmail = (email: string | null) => useUserStore.setState({ email });
|
||||
export const setUserPassword = (password: string | null) => useUserStore.setState({ password });
|
||||
export const setUser = (user: User | null) => useUserStore.setState({ user });
|
||||
|
||||
export const clearUser = () => useUserStore.setState({ ...initialState });
|
@ -1,15 +1,9 @@
|
||||
import * as React from "react";
|
||||
import { useLocation, Navigate } from 'react-router-dom'
|
||||
import {authStore} from "@stores/makeRequest";
|
||||
import { Navigate, Outlet } from 'react-router-dom';
|
||||
import { useUserStore } from '@root/stores/user';
|
||||
|
||||
export default ({ children }: any) => {
|
||||
const location = useLocation()
|
||||
const { token } = authStore()
|
||||
console.log(token)
|
||||
//Если пользователь авторизован, перенаправляем его на нужный путь. Иначе выкидываем в регистрацию
|
||||
if (token) {
|
||||
return children
|
||||
}
|
||||
return <Navigate to="/signin" state={{from: location}} />
|
||||
|
||||
}
|
||||
export default function PrivateRoute() {
|
||||
const user = useUserStore(state => state.user);
|
||||
|
||||
return user ? <Outlet /> : <Navigate to="/" replace />;
|
||||
}
|
Loading…
Reference in New Issue
Block a user