Dev
This commit is contained in:
parent
1df8502813
commit
24399db30b
11
cypress.config.ts
Normal file
11
cypress.config.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { defineConfig } from "cypress";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
e2e: {
|
||||||
|
viewportWidth: 1200,
|
||||||
|
viewportHeight: 800,
|
||||||
|
fixturesFolder: "tests/e2e/fixtures",
|
||||||
|
supportFile: false,
|
||||||
|
defaultCommandTimeout: 100,
|
||||||
|
},
|
||||||
|
});
|
125
cypress/e2e/access.cy.ts
Normal file
125
cypress/e2e/access.cy.ts
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
describe("Форма Входа", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit("http://localhost:3000");
|
||||||
|
cy.wait(1000);
|
||||||
|
cy.contains("Личный кабинет").click();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("должна успешно входить с правильными учетными данными", () => {
|
||||||
|
const login = "valid_user@example.com";
|
||||||
|
const password = "valid_password";
|
||||||
|
|
||||||
|
cy.get("#login").type(login);
|
||||||
|
cy.get("#password").type(password);
|
||||||
|
cy.get('button[type="submit"]').click();
|
||||||
|
|
||||||
|
cy.wait(2000);
|
||||||
|
cy.url().should("include", "http://localhost:3000/tariffs");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("должна отображать два сообщение об ошибке при отсутствии полей", () => {
|
||||||
|
cy.get('button[type="submit"]').click();
|
||||||
|
|
||||||
|
cy.wait(2000);
|
||||||
|
cy.get("#password-helper-text").should("contain", "Поле обязательно");
|
||||||
|
cy.get("#login-helper-text").should("contain", "Поле обязательно");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("должна отображать сообщение об ошибке при отсутствии пароля", () => {
|
||||||
|
cy.get("#login").type("valid_email@example.com");
|
||||||
|
cy.get('button[type="submit"]').click();
|
||||||
|
|
||||||
|
cy.get("#password-helper-text").should("contain", "Поле обязательно");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("должна отображать сообщение об ошибке при отсутствии Логина", () => {
|
||||||
|
cy.get("#password").type("valid_password");
|
||||||
|
cy.get('button[type="submit"]').click();
|
||||||
|
|
||||||
|
cy.get("#login-helper-text").should("contain", "Поле обязательно");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Форма регистрации", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit("http://localhost:3000");
|
||||||
|
cy.wait(1000);
|
||||||
|
cy.contains("Личный кабинет").click();
|
||||||
|
cy.contains("Регистрация").click();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("должна регистрировать нового пользователя с правильными данными", () => {
|
||||||
|
const login = Cypress._.random(1000) + "@example.com";
|
||||||
|
const password = "valid_password";
|
||||||
|
|
||||||
|
cy.get("#login").type(login);
|
||||||
|
cy.get("#password").type(password);
|
||||||
|
cy.get("#repeatPassword").type(password);
|
||||||
|
|
||||||
|
cy.get('button[type="submit"]').click();
|
||||||
|
cy.wait(5000);
|
||||||
|
|
||||||
|
cy.url().should("include", "http://localhost:3000/tariffs");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("должна отображать ошибку при отсутсвии логина", () => {
|
||||||
|
cy.get("#password").type("valid_password");
|
||||||
|
cy.get("#repeatPassword").type("valid_password");
|
||||||
|
|
||||||
|
cy.get('button[type="submit"]').click();
|
||||||
|
|
||||||
|
cy.get("#login-helper-text").should("contain", "Поле обязательно");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("должна отображать ошибку при отсутствии пароля", () => {
|
||||||
|
cy.get("#login").type("valid_login");
|
||||||
|
|
||||||
|
cy.get('button[type="submit"]').click();
|
||||||
|
|
||||||
|
cy.get("#password-helper-text").should("contain", "Поле обязательно");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("должна отображать ошибку при отсутствии поля Повторения пароля", () => {
|
||||||
|
cy.get("#login").type("valid_login");
|
||||||
|
cy.get("#password").type("valid_password");
|
||||||
|
|
||||||
|
cy.get('button[type="submit"]').click();
|
||||||
|
|
||||||
|
cy.get("#repeatPassword-helper-text").should("contain", "Повторите пароль");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("должна отображать ошибку при некоректном пароле", () => {
|
||||||
|
cy.get("#login").type("valid_log");
|
||||||
|
cy.get("#password").type("valid@12_-_@@password");
|
||||||
|
|
||||||
|
cy.get('button[type="submit"]').click();
|
||||||
|
|
||||||
|
cy.get("#password-helper-text").should("contain", "Некорректные символы");
|
||||||
|
|
||||||
|
cy.get("#repeatPassword-helper-text").should("contain", "Повторите пароль");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("должна отображать ошибку при несовпадении паролей", () => {
|
||||||
|
cy.get("#login").type("valid_login");
|
||||||
|
cy.get("#password").type("valid_password");
|
||||||
|
cy.get("#repeatPassword").type("invalidPassword");
|
||||||
|
|
||||||
|
cy.get('button[type="submit"]').click();
|
||||||
|
|
||||||
|
cy.get("#repeatPassword-helper-text").should("contain", "Пароли не совпадают");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("попытка отправки запроса при уже зарегистрированном пользователе", () => {
|
||||||
|
const login = "valid_user@example.com";
|
||||||
|
const password = "valid_password";
|
||||||
|
|
||||||
|
cy.get("#login").type(login);
|
||||||
|
cy.get("#password").type(password);
|
||||||
|
cy.get("#repeatPassword").type(password);
|
||||||
|
|
||||||
|
cy.get('button[type="submit"]').click();
|
||||||
|
|
||||||
|
cy.wait(5000);
|
||||||
|
cy.contains("user with this login is exist");
|
||||||
|
});
|
||||||
|
});
|
@ -7,7 +7,9 @@
|
|||||||
"build": "craco build",
|
"build": "craco build",
|
||||||
"test": "craco test --env=node --transformIgnorePatterns \"node_modules/(?!@frontend)/\"",
|
"test": "craco test --env=node --transformIgnorePatterns \"node_modules/(?!@frontend)/\"",
|
||||||
"test:cart": "craco test src/utils/calcCart --transformIgnorePatterns \"node_modules/(?!@frontend)/\"",
|
"test:cart": "craco test src/utils/calcCart --transformIgnorePatterns \"node_modules/(?!@frontend)/\"",
|
||||||
"eject": "craco eject"
|
"eject": "craco eject",
|
||||||
|
"test:cypress": "start-server-and-test start http://localhost:3000 cypress",
|
||||||
|
"cypress": "cypress open"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.10.5",
|
"@emotion/react": "^11.10.5",
|
||||||
@ -19,6 +21,7 @@
|
|||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
|
"cypress": "^12.17.3",
|
||||||
"formik": "^2.2.9",
|
"formik": "^2.2.9",
|
||||||
"immer": "^10.0.2",
|
"immer": "^10.0.2",
|
||||||
"isomorphic-fetch": "^3.0.0",
|
"isomorphic-fetch": "^3.0.0",
|
||||||
|
@ -51,9 +51,7 @@ export default function Drawers() {
|
|||||||
(state) => state.summaryPriceAfterDiscountsMap
|
(state) => state.summaryPriceAfterDiscountsMap
|
||||||
);
|
);
|
||||||
const userAccount = useUserStore((state) => state.userAccount);
|
const userAccount = useUserStore((state) => state.userAccount);
|
||||||
const { tickets, ticketCount, apiPage, ticketsPerPage } = useTicketStore(
|
const { tickets, apiPage, ticketsPerPage } = useTicketStore((state) => state);
|
||||||
(state) => state
|
|
||||||
);
|
|
||||||
|
|
||||||
useTickets({
|
useTickets({
|
||||||
url: "https://hub.pena.digital/heruvym/getTickets",
|
url: "https://hub.pena.digital/heruvym/getTickets",
|
||||||
@ -75,6 +73,10 @@ export default function Drawers() {
|
|||||||
0
|
0
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const notificationsCount = tickets.filter(
|
||||||
|
({ user, top_message }) => user !== top_message.user_id
|
||||||
|
).length;
|
||||||
|
|
||||||
const totalPriceBeforeDiscounts = cart.priceBeforeDiscounts + basePrice;
|
const totalPriceBeforeDiscounts = cart.priceBeforeDiscounts + basePrice;
|
||||||
const totalPriceAfterDiscounts = cart.priceAfterDiscounts + discountedPrice;
|
const totalPriceAfterDiscounts = cart.priceAfterDiscounts + discountedPrice;
|
||||||
|
|
||||||
@ -119,10 +121,10 @@ export default function Drawers() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Badge
|
<Badge
|
||||||
badgeContent={ticketCount}
|
badgeContent={notificationsCount}
|
||||||
sx={{
|
sx={{
|
||||||
"& .MuiBadge-badge": {
|
"& .MuiBadge-badge": {
|
||||||
display: ticketCount ? "flex" : "none",
|
display: notificationsCount ? "flex" : "none",
|
||||||
color: "#FFFFFF",
|
color: "#FFFFFF",
|
||||||
background: theme.palette.brightPurple.main,
|
background: theme.palette.brightPurple.main,
|
||||||
transform: "scale(0.8) translate(50%, -50%)",
|
transform: "scale(0.8) translate(50%, -50%)",
|
||||||
@ -136,12 +138,13 @@ export default function Drawers() {
|
|||||||
</Badge>
|
</Badge>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<NotificationsModal
|
<NotificationsModal
|
||||||
open={openNotificationsModal}
|
open={notificationsCount ? openNotificationsModal : false}
|
||||||
setOpen={setOpenNotificationsModal}
|
setOpen={setOpenNotificationsModal}
|
||||||
anchorElement={bellRef.current}
|
anchorElement={bellRef.current}
|
||||||
notifications={tickets.map((ticket) => ({
|
notifications={tickets.map((ticket) => ({
|
||||||
text: "У вас новое сообщение от техподдержки",
|
text: "У вас новое сообщение от техподдержки",
|
||||||
date: new Date(ticket.updated_at).toLocaleDateString(),
|
date: new Date(ticket.updated_at).toLocaleDateString(),
|
||||||
|
url: `/support/${ticket.id}`,
|
||||||
watched: ticket.user === ticket.top_message.user_id,
|
watched: ticket.user === ticket.top_message.user_id,
|
||||||
}))}
|
}))}
|
||||||
/>
|
/>
|
||||||
|
@ -12,6 +12,7 @@ import {
|
|||||||
|
|
||||||
import { useUserStore } from "@root/stores/user";
|
import { useUserStore } from "@root/stores/user";
|
||||||
import { currencyFormatter } from "@root/utils/currencyFormatter";
|
import { currencyFormatter } from "@root/utils/currencyFormatter";
|
||||||
|
import { cardShadow } from "@root/utils/themes/shadow";
|
||||||
|
|
||||||
import CustomAvatar from "./Avatar";
|
import CustomAvatar from "./Avatar";
|
||||||
|
|
||||||
@ -45,6 +46,7 @@ export default function DialogMenu({ handleClose }: DialogMenuProps) {
|
|||||||
const [activeSubMenuIndex, setActiveSubMenuIndex] = useState<number>(-1);
|
const [activeSubMenuIndex, setActiveSubMenuIndex] = useState<number>(-1);
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
const isTablet = useMediaQuery(theme.breakpoints.down(900));
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||||
const user = useUserStore((state) => state.user);
|
const user = useUserStore((state) => state.user);
|
||||||
const cash = useUserStore((state) => state.userAccount?.wallet.cash) ?? 0;
|
const cash = useUserStore((state) => state.userAccount?.wallet.cash) ?? 0;
|
||||||
@ -118,12 +120,13 @@ export default function DialogMenu({ handleClose }: DialogMenuProps) {
|
|||||||
sx={{
|
sx={{
|
||||||
backgroundColor: theme.palette.background.paper,
|
backgroundColor: theme.palette.background.paper,
|
||||||
width: "100%",
|
width: "100%",
|
||||||
|
boxShadow: !isTablet ? cardShadow : null,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{index === activeSubMenuIndex &&
|
{index === activeSubMenuIndex &&
|
||||||
subMenu.map(({ name, url }) => (
|
subMenu.map(({ name, url }) => (
|
||||||
<Link
|
<Link
|
||||||
key={url}
|
key={name + url}
|
||||||
style={{
|
style={{
|
||||||
paddingLeft: "30px",
|
paddingLeft: "30px",
|
||||||
display: "block",
|
display: "block",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useMediaQuery, useTheme } from "@mui/material";
|
import { Box, useMediaQuery, useTheme } from "@mui/material";
|
||||||
import NavbarCollapsed from "./NavbarCollapsed";
|
import NavbarCollapsed from "./NavbarCollapsed";
|
||||||
import NavbarFull from "./NavbarFull";
|
import NavbarFull from "./NavbarFull";
|
||||||
|
|
||||||
@ -14,12 +14,17 @@ export default function Navbar({ isLoggedIn, children }: Props) {
|
|||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Box
|
||||||
|
sx={{
|
||||||
|
paddingTop: upMd ? "80px" : 0,
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
{upMd ? (
|
{upMd ? (
|
||||||
<NavbarFull isLoggedIn={isLoggedIn}>{children}</NavbarFull>
|
<NavbarFull isLoggedIn={isLoggedIn}>{children}</NavbarFull>
|
||||||
) : (
|
) : (
|
||||||
<NavbarCollapsed isLoggedIn={isLoggedIn}>{children}</NavbarCollapsed>
|
<NavbarCollapsed isLoggedIn={isLoggedIn}>{children}</NavbarCollapsed>
|
||||||
)}
|
)}
|
||||||
</>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -34,9 +34,7 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) {
|
|||||||
useState<boolean>(false);
|
useState<boolean>(false);
|
||||||
const bellRef = useRef<HTMLButtonElement | null>(null);
|
const bellRef = useRef<HTMLButtonElement | null>(null);
|
||||||
const userAccount = useUserStore((state) => state.userAccount);
|
const userAccount = useUserStore((state) => state.userAccount);
|
||||||
const { ticketCount, tickets, apiPage, ticketsPerPage } = useTicketStore(
|
const { tickets, apiPage, ticketsPerPage } = useTicketStore((state) => state);
|
||||||
(state) => state
|
|
||||||
);
|
|
||||||
|
|
||||||
useTickets({
|
useTickets({
|
||||||
url: "https://hub.pena.digital/heruvym/getTickets",
|
url: "https://hub.pena.digital/heruvym/getTickets",
|
||||||
@ -55,6 +53,10 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) {
|
|||||||
setOpen(false);
|
setOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const notificationsCount = tickets.filter(
|
||||||
|
({ user, top_message }) => user !== top_message.user_id
|
||||||
|
).length;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open) {
|
if (open) {
|
||||||
document.body.style.overflow = "hidden";
|
document.body.style.overflow = "hidden";
|
||||||
@ -71,18 +73,23 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) {
|
|||||||
maxWidth="lg"
|
maxWidth="lg"
|
||||||
outerContainerSx={{
|
outerContainerSx={{
|
||||||
backgroundColor: theme.palette.navbarbg.main,
|
backgroundColor: theme.palette.navbarbg.main,
|
||||||
position: "sticky",
|
|
||||||
top: 0,
|
|
||||||
}}
|
}}
|
||||||
sx={{ height: "51px", padding: "0" }}
|
sx={{ height: "51px", padding: "0" }}
|
||||||
>
|
>
|
||||||
|
<Box sx={{ height: "100%" }}>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
zIndex: 2,
|
||||||
|
position: "fixed",
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
width: "100%",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
columnGap: "10px",
|
columnGap: "10px",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
height: "100%",
|
height: "51px",
|
||||||
padding: "0 18px",
|
padding: "0 18px",
|
||||||
|
background: "#FFFFFF",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
@ -158,10 +165,10 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Badge
|
<Badge
|
||||||
badgeContent={ticketCount}
|
badgeContent={notificationsCount}
|
||||||
sx={{
|
sx={{
|
||||||
"& .MuiBadge-badge": {
|
"& .MuiBadge-badge": {
|
||||||
display: ticketCount ? "flex" : "none",
|
display: notificationsCount ? "flex" : "none",
|
||||||
color: "#FFFFFF",
|
color: "#FFFFFF",
|
||||||
background: theme.palette.brightPurple.main,
|
background: theme.palette.brightPurple.main,
|
||||||
transform: "scale(0.7) translate(50%, -50%)",
|
transform: "scale(0.7) translate(50%, -50%)",
|
||||||
@ -175,12 +182,13 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) {
|
|||||||
</Badge>
|
</Badge>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<NotificationsModal
|
<NotificationsModal
|
||||||
open={openNotificationsModal}
|
open={notificationsCount ? openNotificationsModal : false}
|
||||||
setOpen={setOpenNotificationsModal}
|
setOpen={setOpenNotificationsModal}
|
||||||
anchorElement={bellRef.current}
|
anchorElement={bellRef.current}
|
||||||
notifications={tickets.map((ticket) => ({
|
notifications={tickets.map((ticket) => ({
|
||||||
text: "У вас новое сообщение от техподдержки",
|
text: "У вас новое сообщение от техподдержки",
|
||||||
date: new Date(ticket.updated_at).toLocaleDateString(),
|
date: new Date(ticket.updated_at).toLocaleDateString(),
|
||||||
|
url: `/support/${ticket.id}`,
|
||||||
watched: ticket.user === ticket.top_message.user_id,
|
watched: ticket.user === ticket.top_message.user_id,
|
||||||
}))}
|
}))}
|
||||||
/>
|
/>
|
||||||
@ -188,6 +196,7 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) {
|
|||||||
<PenaLogo width={100} />
|
<PenaLogo width={100} />
|
||||||
</Link>
|
</Link>
|
||||||
</Box>
|
</Box>
|
||||||
|
</Box>
|
||||||
<Box sx={{ display: "flex", overflow: open ? "hidden" : "unset" }}>
|
<Box sx={{ display: "flex", overflow: open ? "hidden" : "unset" }}>
|
||||||
<Drawer
|
<Drawer
|
||||||
sx={{
|
sx={{
|
||||||
@ -195,10 +204,11 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) {
|
|||||||
position: "relative",
|
position: "relative",
|
||||||
zIndex: open ? "none" : "-1",
|
zIndex: open ? "none" : "-1",
|
||||||
"& .MuiDrawer-paper": {
|
"& .MuiDrawer-paper": {
|
||||||
position: "absolute",
|
position: "fixed",
|
||||||
top: "0",
|
top: "0",
|
||||||
width: 210,
|
width: 210,
|
||||||
height: "100%",
|
height: "100%",
|
||||||
|
marginTop: "51px",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
variant="persistent"
|
variant="persistent"
|
||||||
|
@ -58,6 +58,8 @@ export default function NavbarFull({ isLoggedIn, children }: Props) {
|
|||||||
disableGutters
|
disableGutters
|
||||||
maxWidth={false}
|
maxWidth={false}
|
||||||
sx={{
|
sx={{
|
||||||
|
position: "fixed",
|
||||||
|
top: "0",
|
||||||
px: "16px",
|
px: "16px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
height: "80px",
|
height: "80px",
|
||||||
|
@ -3,18 +3,14 @@ import { TransitionProps } from "@mui/material/transitions";
|
|||||||
import logotip from "../../assets/Icons/logoPenaHab.svg";
|
import logotip from "../../assets/Icons/logoPenaHab.svg";
|
||||||
import logotipBlack from "../../assets/Icons/black_logo_PenaHab.svg";
|
import logotipBlack from "../../assets/Icons/black_logo_PenaHab.svg";
|
||||||
import CustomAvatar from "./Avatar";
|
import CustomAvatar from "./Avatar";
|
||||||
import CloseIcon from "../icons/CloseIcons";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {
|
import {
|
||||||
AppBar,
|
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Dialog,
|
Dialog,
|
||||||
IconButton,
|
|
||||||
List,
|
List,
|
||||||
ListItem,
|
ListItem,
|
||||||
Slide,
|
Slide,
|
||||||
Toolbar,
|
|
||||||
Typography,
|
Typography,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme,
|
useTheme,
|
||||||
@ -55,45 +51,44 @@ interface DialogMenuProps {
|
|||||||
export default function DialogMenu({ open, handleClose }: DialogMenuProps) {
|
export default function DialogMenu({ open, handleClose }: DialogMenuProps) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
const isTablet = useMediaQuery(theme.breakpoints.down(900));
|
||||||
const user = useUserStore((state) => state.user);
|
const user = useUserStore((state) => state.user);
|
||||||
const cash = useUserStore(state => state.userAccount?.wallet.cash) ?? 0;
|
const cash = useUserStore((state) => state.userAccount?.wallet.cash) ?? 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
fullScreen
|
fullScreen
|
||||||
sx={{ width: isMobile ? "100%" : "320px", ml: "auto", height: "100%" }}
|
sx={{
|
||||||
|
width: isTablet ? "100%" : "320px",
|
||||||
|
ml: "auto",
|
||||||
|
mt: "50px",
|
||||||
|
height: "100%",
|
||||||
|
".MuiBackdrop-root.MuiModal-backdrop": {
|
||||||
|
background: "transparent",
|
||||||
|
},
|
||||||
|
".MuiPaper-root.MuiPaper-rounded": {
|
||||||
|
background: "#333647",
|
||||||
|
},
|
||||||
|
}}
|
||||||
open={open}
|
open={open}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
TransitionComponent={Transition}
|
TransitionComponent={Transition}
|
||||||
>
|
>
|
||||||
<AppBar
|
<List
|
||||||
sx={{
|
sx={{
|
||||||
position: "relative",
|
|
||||||
background: location.pathname === "/" ? "#333647" : "#FFFFFF",
|
background: location.pathname === "/" ? "#333647" : "#FFFFFF",
|
||||||
boxShadow: "none",
|
height: "100vh",
|
||||||
height: isMobile ? "66px" : "100px",
|
p: "0",
|
||||||
|
paddingTop: "20px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Toolbar
|
<ListItem
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
pl: "40px",
|
||||||
justifyContent: "space-between",
|
flexDirection: "column",
|
||||||
svg: { color: "#000000" },
|
alignItems: isTablet ? "start" : "end",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isMobile && (
|
|
||||||
<Box sx={{ mt: "6px" }}>
|
|
||||||
<img src={location.pathname === "/" ? logotip : logotipBlack} alt="icon" />
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
<IconButton sx={{ ml: "auto" }} edge="start" color="inherit" onClick={handleClose} aria-label="close">
|
|
||||||
<CloseIcon />
|
|
||||||
</IconButton>
|
|
||||||
</Toolbar>
|
|
||||||
</AppBar>
|
|
||||||
<List sx={{ background: location.pathname === "/" ? "#333647" : "#FFFFFF", height: "100vh", p: "0" }}>
|
|
||||||
<ListItem sx={{ pl: "40px", flexDirection: "column", alignItems: isMobile ? "start" : "end" }}>
|
|
||||||
{arrayMenu.map(({ name, url }, index) => (
|
{arrayMenu.map(({ name, url }, index) => (
|
||||||
<Button
|
<Button
|
||||||
key={index}
|
key={index}
|
||||||
@ -105,7 +100,12 @@ export default function DialogMenu({ open, handleClose }: DialogMenuProps) {
|
|||||||
variant="text"
|
variant="text"
|
||||||
sx={{
|
sx={{
|
||||||
fontWeight: "500",
|
fontWeight: "500",
|
||||||
color: location.pathname === url ? "#7E2AEA" : location.pathname === "/" ? "white" : "black",
|
color:
|
||||||
|
location.pathname === url
|
||||||
|
? "#7E2AEA"
|
||||||
|
: location.pathname === "/"
|
||||||
|
? "white"
|
||||||
|
: "black",
|
||||||
height: "20px",
|
height: "20px",
|
||||||
textTransform: "none",
|
textTransform: "none",
|
||||||
marginBottom: "25px",
|
marginBottom: "25px",
|
||||||
@ -120,7 +120,7 @@ export default function DialogMenu({ open, handleClose }: DialogMenuProps) {
|
|||||||
</Button>
|
</Button>
|
||||||
))}
|
))}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
{isMobile ? (
|
{isTablet ? (
|
||||||
location.pathname === "/" ? (
|
location.pathname === "/" ? (
|
||||||
<Button
|
<Button
|
||||||
component={Link}
|
component={Link}
|
||||||
@ -166,7 +166,10 @@ export default function DialogMenu({ open, handleClose }: DialogMenuProps) {
|
|||||||
>
|
>
|
||||||
Мой баланс
|
Мой баланс
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2" color={theme.palette.brightPurple.main}>
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
color={theme.palette.brightPurple.main}
|
||||||
|
>
|
||||||
{currencyFormatter.format(cash / 100)}
|
{currencyFormatter.format(cash / 100)}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
@ -201,7 +204,10 @@ export default function DialogMenu({ open, handleClose }: DialogMenuProps) {
|
|||||||
bottom: "60px",
|
bottom: "60px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img src={location.pathname === "/" ? logotip : logotipBlack} alt="icon" />
|
<img
|
||||||
|
src={location.pathname === "/" ? logotip : logotipBlack}
|
||||||
|
alt="icon"
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { IconButton, useTheme } from "@mui/material";
|
import { IconButton, useTheme } from "@mui/material";
|
||||||
import MenuIcon from "@mui/icons-material/Menu";
|
import MenuIcon from "@mui/icons-material/Menu";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import SectionWrapper from "../SectionWrapper";
|
import SectionWrapper from "../SectionWrapper";
|
||||||
|
|
||||||
import PenaLogo from "../PenaLogo";
|
import PenaLogo from "../PenaLogo";
|
||||||
import DialogMenu from "./DialogMenu";
|
import DialogMenu from "./DialogMenu";
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isLoggedIn: boolean;
|
isLoggedIn: boolean;
|
||||||
@ -30,11 +30,10 @@ export default function NavbarCollapsed({ isLoggedIn }: Props) {
|
|||||||
component="nav"
|
component="nav"
|
||||||
maxWidth="lg"
|
maxWidth="lg"
|
||||||
outerContainerSx={{
|
outerContainerSx={{
|
||||||
|
position: "fixed",
|
||||||
|
top: "0",
|
||||||
backgroundColor: theme.palette.navbarbg.main,
|
backgroundColor: theme.palette.navbarbg.main,
|
||||||
position: "sticky",
|
borderBottom: "1px solid #E3E3E3",
|
||||||
top: 0,
|
|
||||||
zIndex: 1,
|
|
||||||
// borderBottom: "1px solid #E3E3E3",
|
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
height: "51px",
|
height: "51px",
|
||||||
@ -44,9 +43,14 @@ export default function NavbarCollapsed({ isLoggedIn }: Props) {
|
|||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Link to="/"><PenaLogo width={100} /></Link>
|
<Link to="/">
|
||||||
|
<PenaLogo width={100} />
|
||||||
|
</Link>
|
||||||
|
|
||||||
<IconButton onClick={handleClickOpen} sx={{ p: 0, width: "30px", color: theme.palette.primary.main }}>
|
<IconButton
|
||||||
|
onClick={handleClickOpen}
|
||||||
|
sx={{ p: 0, width: "30px", color: theme.palette.primary.main }}
|
||||||
|
>
|
||||||
<MenuIcon sx={{ height: "30px", width: "30px" }} />
|
<MenuIcon sx={{ height: "30px", width: "30px" }} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<DialogMenu open={open} handleClose={handleClose} />
|
<DialogMenu open={open} handleClose={handleClose} />
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||||
import { Box, Button, Container, IconButton, Typography, useTheme } from "@mui/material";
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Container,
|
||||||
|
IconButton,
|
||||||
|
Typography,
|
||||||
|
useTheme,
|
||||||
|
} from "@mui/material";
|
||||||
import SectionWrapper from "../SectionWrapper";
|
import SectionWrapper from "../SectionWrapper";
|
||||||
import LogoutIcon from "../icons/LogoutIcon";
|
import LogoutIcon from "../icons/LogoutIcon";
|
||||||
import WalletIcon from "../icons/WalletIcon";
|
import WalletIcon from "../icons/WalletIcon";
|
||||||
@ -23,7 +30,7 @@ export default function NavbarFull({ isLoggedIn }: Props) {
|
|||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const user = useUserStore((state) => state.user);
|
const user = useUserStore((state) => state.user);
|
||||||
const cash = useUserStore(state => state.userAccount?.wallet.cash) ?? 0;
|
const cash = useUserStore((state) => state.userAccount?.wallet.cash) ?? 0;
|
||||||
|
|
||||||
async function handleLogoutClick() {
|
async function handleLogoutClick() {
|
||||||
try {
|
try {
|
||||||
@ -53,7 +60,9 @@ export default function NavbarFull({ isLoggedIn }: Props) {
|
|||||||
borderBottom: "1px solid #E3E3E3",
|
borderBottom: "1px solid #E3E3E3",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Link to="/"><PenaLogo width={124} /></Link>
|
<Link to="/">
|
||||||
|
<PenaLogo width={124} />
|
||||||
|
</Link>
|
||||||
<Menu />
|
<Menu />
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -85,7 +94,13 @@ export default function NavbarFull({ isLoggedIn }: Props) {
|
|||||||
<CustomAvatar />
|
<CustomAvatar />
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={handleLogoutClick}
|
onClick={handleLogoutClick}
|
||||||
sx={{ ml: "20px", bgcolor: "#F2F3F7", borderRadius: "6px", height: "36px", width: "36px" }}
|
sx={{
|
||||||
|
ml: "20px",
|
||||||
|
bgcolor: "#F2F3F7",
|
||||||
|
borderRadius: "6px",
|
||||||
|
height: "36px",
|
||||||
|
width: "36px",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<LogoutIcon />
|
<LogoutIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
@ -97,6 +112,8 @@ export default function NavbarFull({ isLoggedIn }: Props) {
|
|||||||
component="nav"
|
component="nav"
|
||||||
maxWidth="lg"
|
maxWidth="lg"
|
||||||
outerContainerSx={{
|
outerContainerSx={{
|
||||||
|
position: "fixed",
|
||||||
|
top: "0",
|
||||||
backgroundColor: theme.palette.lightPurple.main,
|
backgroundColor: theme.palette.lightPurple.main,
|
||||||
borderBottom: "1px solid #E3E3E3",
|
borderBottom: "1px solid #E3E3E3",
|
||||||
}}
|
}}
|
||||||
|
@ -6,10 +6,12 @@ import {
|
|||||||
useTheme,
|
useTheme,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
type Notification = {
|
type Notification = {
|
||||||
text: string;
|
text: string;
|
||||||
date: string;
|
date: string;
|
||||||
|
url: string;
|
||||||
watched?: boolean;
|
watched?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -59,8 +61,17 @@ export const NotificationsModal = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<List sx={{ width: "100%", padding: "5px" }}>
|
<List sx={{ width: "100%", padding: "5px" }}>
|
||||||
{notifications.map(({ text, date, watched = true }) => (
|
{notifications.map(({ text, date, url, watched = true }) => (
|
||||||
|
<Link
|
||||||
|
to={url}
|
||||||
|
onClick={() => setOpen(false)}
|
||||||
|
style={{
|
||||||
|
textDecoration: "none",
|
||||||
|
color: "inherit",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<ListItem
|
<ListItem
|
||||||
|
key={text + date}
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: isMobile ? "normal" : "center",
|
alignItems: isMobile ? "normal" : "center",
|
||||||
@ -111,6 +122,7 @@ export const NotificationsModal = ({
|
|||||||
{date}
|
{date}
|
||||||
</Typography>
|
</Typography>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
</Link>
|
||||||
))}
|
))}
|
||||||
</List>
|
</List>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
@ -39,7 +39,7 @@ export const Select = ({
|
|||||||
<Box>
|
<Box>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
zIndex: 1500,
|
zIndex: 1,
|
||||||
position: "relative",
|
position: "relative",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "56px",
|
height: "56px",
|
||||||
@ -81,6 +81,7 @@ export const Select = ({
|
|||||||
<MuiSelect
|
<MuiSelect
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className="select"
|
className="select"
|
||||||
|
value=""
|
||||||
open={opened}
|
open={opened}
|
||||||
MenuProps={{ disablePortal: true }}
|
MenuProps={{ disablePortal: true }}
|
||||||
sx={{ width: "100%" }}
|
sx={{ width: "100%" }}
|
||||||
|
@ -26,3 +26,7 @@
|
|||||||
.MuiInputBase-root.MuiOutlinedInput-root .MuiSelect-icon {
|
.MuiInputBase-root.MuiOutlinedInput-root .MuiSelect-icon {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.MuiMenu-root.MuiModal-root {
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
@ -22,9 +22,7 @@ interface Props {
|
|||||||
color?: string;
|
color?: string;
|
||||||
FormInputSx?: SxProps<Theme>;
|
FormInputSx?: SxProps<Theme>;
|
||||||
TextfieldProps: TextFieldProps;
|
TextfieldProps: TextFieldProps;
|
||||||
onChange: (
|
onChange: (e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => void;
|
||||||
e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>
|
|
||||||
) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PasswordInput({
|
export default function PasswordInput({
|
||||||
@ -46,17 +44,13 @@ export default function PasswordInput({
|
|||||||
: { ...theme.typography.body1, fontWeight: 500 }
|
: { ...theme.typography.body1, fontWeight: 500 }
|
||||||
: theme.typography.body2;
|
: theme.typography.body2;
|
||||||
|
|
||||||
const placeholderFont = upMd
|
const placeholderFont = upMd ? undefined : { fontWeight: 400, fontSize: "16px", lineHeight: "19px" };
|
||||||
? undefined
|
|
||||||
: { fontWeight: 400, fontSize: "16px", lineHeight: "19px" };
|
|
||||||
|
|
||||||
const [showPassword, setShowPassword] = React.useState(false);
|
const [showPassword, setShowPassword] = React.useState(false);
|
||||||
|
|
||||||
const handleClickShowPassword = () => setShowPassword((show) => !show);
|
const handleClickShowPassword = () => setShowPassword((show) => !show);
|
||||||
|
|
||||||
const handleMouseDownPassword = (
|
const handleMouseDownPassword = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
event: React.MouseEvent<HTMLButtonElement>
|
|
||||||
) => {
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ export default function Landing({ templaterOnly = false }: Props) {
|
|||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
position: "relative",
|
position: "relative",
|
||||||
|
paddingTop: "80px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Navbar isLoggedIn={false} />
|
<Navbar isLoggedIn={false} />
|
||||||
|
@ -46,6 +46,10 @@
|
|||||||
right: -5px;
|
right: -5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.slider .slick-arrow::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.slider .slick-dots {
|
.slider .slick-dots {
|
||||||
bottom: -15px;
|
bottom: -15px;
|
||||||
}
|
}
|
||||||
|
@ -12,19 +12,23 @@ import "slick-carousel/slick/slick-theme.css";
|
|||||||
import "./slider.css";
|
import "./slider.css";
|
||||||
|
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
import type { CustomArrowProps } from "react-slick";
|
||||||
|
|
||||||
type SliderProps = {
|
type SliderProps = {
|
||||||
items: ReactNode[];
|
items: ReactNode[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type ArrowProps = CustomArrowProps & {
|
||||||
|
icon: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
export const Slider = ({ items }: SliderProps) => {
|
export const Slider = ({ items }: SliderProps) => {
|
||||||
const [range, setRange] = useState<number>(3);
|
const [range, setRange] = useState<number>(3);
|
||||||
const [activeRange, setActiveRange] = useState<number[]>([0, 1, 2]);
|
const [activeRange, setActiveRange] = useState<number[]>([0, 1, 2]);
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isMiddle = useMediaQuery(
|
const isMiddle = useMediaQuery(theme.breakpoints.down(1200));
|
||||||
theme.breakpoints.down(1200) && theme.breakpoints.up(830)
|
|
||||||
);
|
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(830));
|
const isTablet = useMediaQuery(theme.breakpoints.down(830));
|
||||||
|
const isMobileHeader = useMediaQuery(theme.breakpoints.down(900));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isTablet) {
|
if (isTablet) {
|
||||||
@ -64,10 +68,14 @@ export const Slider = ({ items }: SliderProps) => {
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Arrow = ({ currentSlide, slideCount, icon, ...props }: ArrowProps) => (
|
||||||
|
<Box {...props}>{icon}</Box>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box className="slider-wrapper">
|
<Box className="slider-wrapper">
|
||||||
{(items.length < 4 && !isMiddle && !isTablet) ||
|
{(items.length < 4 && !isMiddle && !isTablet) ||
|
||||||
(items.length < 3 && isMiddle) ||
|
(items.length < 3 && isMiddle && !isTablet) ||
|
||||||
(items.length < 1 && isTablet) ? (
|
(items.length < 1 && isTablet) ? (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -87,14 +95,15 @@ export const Slider = ({ items }: SliderProps) => {
|
|||||||
dots
|
dots
|
||||||
infinite
|
infinite
|
||||||
variableWidth
|
variableWidth
|
||||||
|
lazyLoad={isMobileHeader ? "progressive" : undefined}
|
||||||
slidesToShow={range}
|
slidesToShow={range}
|
||||||
prevArrow={<img src={arrowLeftIcon} alt="prev" />}
|
prevArrow={<Arrow icon={<img src={arrowLeftIcon} alt="prev" />} />}
|
||||||
nextArrow={<img src={arrowRightIcon} alt="next" />}
|
nextArrow={<Arrow icon={<img src={arrowRightIcon} alt="next" />} />}
|
||||||
beforeChange={(_, active) =>
|
beforeChange={(_, active) =>
|
||||||
setActiveRange(calculateRange(active, items.length))
|
setActiveRange(calculateRange(active, items.length))
|
||||||
}
|
}
|
||||||
customPaging={(slideNumber) => (
|
customPaging={(slideNumber) => (
|
||||||
<li
|
<Box
|
||||||
className={classNames("dot", {
|
className={classNames("dot", {
|
||||||
active: activeRange.includes(slideNumber),
|
active: activeRange.includes(slideNumber),
|
||||||
})}
|
})}
|
||||||
|
@ -84,14 +84,14 @@ export default function SigninDialog() {
|
|||||||
sx: {
|
sx: {
|
||||||
width: "600px",
|
width: "600px",
|
||||||
maxWidth: "600px",
|
maxWidth: "600px",
|
||||||
}
|
},
|
||||||
}}
|
}}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
backdrop: {
|
backdrop: {
|
||||||
style: {
|
style: {
|
||||||
backgroundColor: "rgb(0 0 0 / 0.7)",
|
backgroundColor: "rgb(0 0 0 / 0.7)",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
|
@ -16,7 +16,6 @@ import { makeRequest } from "@frontend/kitui";
|
|||||||
import { cardShadow } from "@root/utils/themes/shadow";
|
import { cardShadow } from "@root/utils/themes/shadow";
|
||||||
import PasswordInput from "@root/components/passwordInput";
|
import PasswordInput from "@root/components/passwordInput";
|
||||||
|
|
||||||
|
|
||||||
interface Values {
|
interface Values {
|
||||||
login: string;
|
login: string;
|
||||||
password: string;
|
password: string;
|
||||||
@ -31,13 +30,18 @@ const initialValues: Values = {
|
|||||||
|
|
||||||
const validationSchema = object({
|
const validationSchema = object({
|
||||||
login: string().required("Поле обязательно"),
|
login: string().required("Поле обязательно"),
|
||||||
password: string().min(8, "Минимум 8 символов").matches(/^[.,:;-_+\d\w]+$/, "Некорректные символы").required("Поле обязательно"),
|
password: string()
|
||||||
repeatPassword: string().oneOf([ref("password"), undefined], "Пароли не совпадают"),
|
.min(8, "Минимум 8 символов")
|
||||||
|
.matches(/^[.,:;-_+\d\w]+$/, "Некорректные символы")
|
||||||
|
.required("Поле обязательно"),
|
||||||
|
repeatPassword: string()
|
||||||
|
.oneOf([ref("password"), undefined], "Пароли не совпадают")
|
||||||
|
.required("Повторите пароль"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function SignupDialog() {
|
export default function SignupDialog() {
|
||||||
const [isDialogOpen, setIsDialogOpen] = useState<boolean>(true);
|
const [isDialogOpen, setIsDialogOpen] = useState<boolean>(true);
|
||||||
const user = useUserStore(state => state.user);
|
const user = useUserStore((state) => state.user);
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -55,20 +59,26 @@ export default function SignupDialog() {
|
|||||||
},
|
},
|
||||||
useToken: false,
|
useToken: false,
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
}).then(result => {
|
})
|
||||||
|
.then((result) => {
|
||||||
setUserId(result._id);
|
setUserId(result._id);
|
||||||
}).catch((error: any) => {
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
const errorMessage = getMessageFromFetchError(error);
|
const errorMessage = getMessageFromFetchError(error);
|
||||||
if (errorMessage) enqueueSnackbar(errorMessage);
|
if (errorMessage) enqueueSnackbar(errorMessage);
|
||||||
}).finally(() => {
|
})
|
||||||
|
.finally(() => {
|
||||||
formikHelpers.setSubmitting(false);
|
formikHelpers.setSubmitting(false);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(function redirectIfSignedIn() {
|
useEffect(
|
||||||
|
function redirectIfSignedIn() {
|
||||||
if (user) navigate("/tariffs", { replace: true });
|
if (user) navigate("/tariffs", { replace: true });
|
||||||
}, [navigate, user]);
|
},
|
||||||
|
[navigate, user]
|
||||||
|
);
|
||||||
|
|
||||||
function handleClose() {
|
function handleClose() {
|
||||||
setIsDialogOpen(false);
|
setIsDialogOpen(false);
|
||||||
@ -83,14 +93,14 @@ export default function SignupDialog() {
|
|||||||
sx: {
|
sx: {
|
||||||
width: "600px",
|
width: "600px",
|
||||||
maxWidth: "600px",
|
maxWidth: "600px",
|
||||||
}
|
},
|
||||||
}}
|
}}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
backdrop: {
|
backdrop: {
|
||||||
style: {
|
style: {
|
||||||
backgroundColor: "rgb(0 0 0 / 0.7)",
|
backgroundColor: "rgb(0 0 0 / 0.7)",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
@ -153,7 +163,7 @@ export default function SignupDialog() {
|
|||||||
onBlur: formik.handleBlur,
|
onBlur: formik.handleBlur,
|
||||||
error: formik.touched.password && Boolean(formik.errors.password),
|
error: formik.touched.password && Boolean(formik.errors.password),
|
||||||
helperText: formik.touched.password && formik.errors.password,
|
helperText: formik.touched.password && formik.errors.password,
|
||||||
autoComplete: "new-password"
|
autoComplete: "new-password",
|
||||||
}}
|
}}
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
color="#F2F3F7"
|
color="#F2F3F7"
|
||||||
@ -168,7 +178,7 @@ export default function SignupDialog() {
|
|||||||
onBlur: formik.handleBlur,
|
onBlur: formik.handleBlur,
|
||||||
error: formik.touched.repeatPassword && Boolean(formik.errors.repeatPassword),
|
error: formik.touched.repeatPassword && Boolean(formik.errors.repeatPassword),
|
||||||
helperText: formik.touched.repeatPassword && formik.errors.repeatPassword,
|
helperText: formik.touched.repeatPassword && formik.errors.repeatPassword,
|
||||||
autoComplete: "new-password"
|
autoComplete: "new-password",
|
||||||
}}
|
}}
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
color="#F2F3F7"
|
color="#F2F3F7"
|
||||||
|
@ -1,8 +1 @@
|
|||||||
export const cardShadow = `
|
export const cardShadow = "0px 15px 80px rgb(210 208 225 / 70%)";
|
||||||
0px 100px 309px rgba(210, 208, 225, 0.24),
|
|
||||||
0px 41.7776px 129.093px rgba(210, 208, 225, 0.172525),
|
|
||||||
0px 22.3363px 69.0192px rgba(210, 208, 225, 0.143066),
|
|
||||||
0px 12.5216px 38.6916px rgba(210, 208, 225, 0.12),
|
|
||||||
0px 6.6501px 20.5488px rgba(210, 208, 225, 0.0969343),
|
|
||||||
0px 2.76726px 8.55082px rgba(210, 208, 225, 0.0674749)
|
|
||||||
`;
|
|
||||||
|
@ -2,11 +2,8 @@
|
|||||||
"extends": "./tsconfig.extend.json",
|
"extends": "./tsconfig.extend.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es5",
|
"target": "es5",
|
||||||
"lib": [
|
"lib": ["es5", "dom", "dom.iterable", "esnext"],
|
||||||
"dom",
|
"types": ["node"],
|
||||||
"dom.iterable",
|
|
||||||
"esnext"
|
|
||||||
],
|
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
@ -21,7 +18,5 @@
|
|||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react-jsx"
|
"jsx": "react-jsx"
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["src", "**/*.ts"]
|
||||||
"src"
|
|
||||||
]
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user