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");
|
||||
});
|
||||
});
|
139
package.json
139
package.json
@ -1,70 +1,73 @@
|
||||
{
|
||||
"name": "hub_frontend",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "craco start",
|
||||
"build": "craco build",
|
||||
"test": "craco test --env=node --transformIgnorePatterns \"node_modules/(?!@frontend)/\"",
|
||||
"test:cart": "craco test src/utils/calcCart --transformIgnorePatterns \"node_modules/(?!@frontend)/\"",
|
||||
"eject": "craco eject"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.10.5",
|
||||
"@emotion/styled": "^11.10.5",
|
||||
"@frontend/kitui": "^1.0.17",
|
||||
"@mui/icons-material": "^5.10.14",
|
||||
"@mui/material": "^5.10.14",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"axios": "^1.4.0",
|
||||
"buffer": "^6.0.3",
|
||||
"classnames": "^2.3.2",
|
||||
"formik": "^2.2.9",
|
||||
"immer": "^10.0.2",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"notistack": "^3.0.1",
|
||||
"pdfjs-dist": "3.6.172",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-pdf": "^7.1.2",
|
||||
"react-router-dom": "^6.4.3",
|
||||
"react-slick": "^0.29.0",
|
||||
"slick-carousel": "^1.8.1",
|
||||
"web-vitals": "^2.1.0",
|
||||
"yup": "^1.1.1",
|
||||
"zustand": "^4.3.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@craco/craco": "^7.1.0",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^13.0.0",
|
||||
"@testing-library/user-event": "^13.2.1",
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/node": "^16.7.13",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"@types/react-slick": "^0.23.10",
|
||||
"craco-alias": "^3.0.1",
|
||||
"jest": "^29.5.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"typescript": "^4.9.3"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
"name": "hub_frontend",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "craco start",
|
||||
"build": "craco build",
|
||||
"test": "craco test --env=node --transformIgnorePatterns \"node_modules/(?!@frontend)/\"",
|
||||
"test:cart": "craco test src/utils/calcCart --transformIgnorePatterns \"node_modules/(?!@frontend)/\"",
|
||||
"eject": "craco eject",
|
||||
"test:cypress": "start-server-and-test start http://localhost:3000 cypress",
|
||||
"cypress": "cypress open"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.10.5",
|
||||
"@emotion/styled": "^11.10.5",
|
||||
"@frontend/kitui": "^1.0.17",
|
||||
"@mui/icons-material": "^5.10.14",
|
||||
"@mui/material": "^5.10.14",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"axios": "^1.4.0",
|
||||
"buffer": "^6.0.3",
|
||||
"classnames": "^2.3.2",
|
||||
"cypress": "^12.17.3",
|
||||
"formik": "^2.2.9",
|
||||
"immer": "^10.0.2",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"notistack": "^3.0.1",
|
||||
"pdfjs-dist": "3.6.172",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-pdf": "^7.1.2",
|
||||
"react-router-dom": "^6.4.3",
|
||||
"react-slick": "^0.29.0",
|
||||
"slick-carousel": "^1.8.1",
|
||||
"web-vitals": "^2.1.0",
|
||||
"yup": "^1.1.1",
|
||||
"zustand": "^4.3.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@craco/craco": "^7.1.0",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^13.0.0",
|
||||
"@testing-library/user-event": "^13.2.1",
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/node": "^16.7.13",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"@types/react-slick": "^0.23.10",
|
||||
"craco-alias": "^3.0.1",
|
||||
"jest": "^29.5.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"typescript": "^4.9.3"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -51,9 +51,7 @@ export default function Drawers() {
|
||||
(state) => state.summaryPriceAfterDiscountsMap
|
||||
);
|
||||
const userAccount = useUserStore((state) => state.userAccount);
|
||||
const { tickets, ticketCount, apiPage, ticketsPerPage } = useTicketStore(
|
||||
(state) => state
|
||||
);
|
||||
const { tickets, apiPage, ticketsPerPage } = useTicketStore((state) => state);
|
||||
|
||||
useTickets({
|
||||
url: "https://hub.pena.digital/heruvym/getTickets",
|
||||
@ -75,6 +73,10 @@ export default function Drawers() {
|
||||
0
|
||||
);
|
||||
|
||||
const notificationsCount = tickets.filter(
|
||||
({ user, top_message }) => user !== top_message.user_id
|
||||
).length;
|
||||
|
||||
const totalPriceBeforeDiscounts = cart.priceBeforeDiscounts + basePrice;
|
||||
const totalPriceAfterDiscounts = cart.priceAfterDiscounts + discountedPrice;
|
||||
|
||||
@ -119,10 +121,10 @@ export default function Drawers() {
|
||||
}}
|
||||
>
|
||||
<Badge
|
||||
badgeContent={ticketCount}
|
||||
badgeContent={notificationsCount}
|
||||
sx={{
|
||||
"& .MuiBadge-badge": {
|
||||
display: ticketCount ? "flex" : "none",
|
||||
display: notificationsCount ? "flex" : "none",
|
||||
color: "#FFFFFF",
|
||||
background: theme.palette.brightPurple.main,
|
||||
transform: "scale(0.8) translate(50%, -50%)",
|
||||
@ -136,12 +138,13 @@ export default function Drawers() {
|
||||
</Badge>
|
||||
</IconButton>
|
||||
<NotificationsModal
|
||||
open={openNotificationsModal}
|
||||
open={notificationsCount ? openNotificationsModal : false}
|
||||
setOpen={setOpenNotificationsModal}
|
||||
anchorElement={bellRef.current}
|
||||
notifications={tickets.map((ticket) => ({
|
||||
text: "У вас новое сообщение от техподдержки",
|
||||
date: new Date(ticket.updated_at).toLocaleDateString(),
|
||||
url: `/support/${ticket.id}`,
|
||||
watched: ticket.user === ticket.top_message.user_id,
|
||||
}))}
|
||||
/>
|
||||
|
@ -12,6 +12,7 @@ import {
|
||||
|
||||
import { useUserStore } from "@root/stores/user";
|
||||
import { currencyFormatter } from "@root/utils/currencyFormatter";
|
||||
import { cardShadow } from "@root/utils/themes/shadow";
|
||||
|
||||
import CustomAvatar from "./Avatar";
|
||||
|
||||
@ -45,6 +46,7 @@ export default function DialogMenu({ handleClose }: DialogMenuProps) {
|
||||
const [activeSubMenuIndex, setActiveSubMenuIndex] = useState<number>(-1);
|
||||
const theme = useTheme();
|
||||
const location = useLocation();
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(900));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
const user = useUserStore((state) => state.user);
|
||||
const cash = useUserStore((state) => state.userAccount?.wallet.cash) ?? 0;
|
||||
@ -118,12 +120,13 @@ export default function DialogMenu({ handleClose }: DialogMenuProps) {
|
||||
sx={{
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
width: "100%",
|
||||
boxShadow: !isTablet ? cardShadow : null,
|
||||
}}
|
||||
>
|
||||
{index === activeSubMenuIndex &&
|
||||
subMenu.map(({ name, url }) => (
|
||||
<Link
|
||||
key={url}
|
||||
key={name + url}
|
||||
style={{
|
||||
paddingLeft: "30px",
|
||||
display: "block",
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useMediaQuery, useTheme } from "@mui/material";
|
||||
import { Box, useMediaQuery, useTheme } from "@mui/material";
|
||||
import NavbarCollapsed from "./NavbarCollapsed";
|
||||
import NavbarFull from "./NavbarFull";
|
||||
|
||||
@ -14,12 +14,17 @@ export default function Navbar({ isLoggedIn, children }: Props) {
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
paddingTop: upMd ? "80px" : 0,
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
{upMd ? (
|
||||
<NavbarFull isLoggedIn={isLoggedIn}>{children}</NavbarFull>
|
||||
) : (
|
||||
<NavbarCollapsed isLoggedIn={isLoggedIn}>{children}</NavbarCollapsed>
|
||||
)}
|
||||
</>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
@ -34,9 +34,7 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) {
|
||||
useState<boolean>(false);
|
||||
const bellRef = useRef<HTMLButtonElement | null>(null);
|
||||
const userAccount = useUserStore((state) => state.userAccount);
|
||||
const { ticketCount, tickets, apiPage, ticketsPerPage } = useTicketStore(
|
||||
(state) => state
|
||||
);
|
||||
const { tickets, apiPage, ticketsPerPage } = useTicketStore((state) => state);
|
||||
|
||||
useTickets({
|
||||
url: "https://hub.pena.digital/heruvym/getTickets",
|
||||
@ -55,6 +53,10 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) {
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const notificationsCount = tickets.filter(
|
||||
({ user, top_message }) => user !== top_message.user_id
|
||||
).length;
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
document.body.style.overflow = "hidden";
|
||||
@ -71,36 +73,80 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) {
|
||||
maxWidth="lg"
|
||||
outerContainerSx={{
|
||||
backgroundColor: theme.palette.navbarbg.main,
|
||||
position: "sticky",
|
||||
top: 0,
|
||||
}}
|
||||
sx={{ height: "51px", padding: "0" }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
columnGap: "10px",
|
||||
alignItems: "center",
|
||||
height: "100%",
|
||||
padding: "0 18px",
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
onClick={() => setOpen((isOpened) => !isOpened)}
|
||||
<Box sx={{ height: "100%" }}>
|
||||
<Box
|
||||
sx={{
|
||||
p: 0,
|
||||
width: "30px",
|
||||
color: theme.palette.primary.main,
|
||||
zIndex: 2,
|
||||
position: "fixed",
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
columnGap: "10px",
|
||||
alignItems: "center",
|
||||
height: "51px",
|
||||
padding: "0 18px",
|
||||
background: "#FFFFFF",
|
||||
}}
|
||||
>
|
||||
{open ? (
|
||||
<CloseIcon />
|
||||
) : (
|
||||
<MenuIcon sx={{ height: "30px", width: "30px" }} />
|
||||
)}
|
||||
</IconButton>
|
||||
<Link to="/cart">
|
||||
<IconButton
|
||||
onClick={() => setOpen((isOpened) => !isOpened)}
|
||||
sx={{
|
||||
p: 0,
|
||||
width: "30px",
|
||||
color: theme.palette.primary.main,
|
||||
}}
|
||||
>
|
||||
{open ? (
|
||||
<CloseIcon />
|
||||
) : (
|
||||
<MenuIcon sx={{ height: "30px", width: "30px" }} />
|
||||
)}
|
||||
</IconButton>
|
||||
<Link to="/cart">
|
||||
<IconButton
|
||||
aria-label="cart"
|
||||
sx={{
|
||||
width: "30px",
|
||||
height: "30px",
|
||||
background: theme.palette.background.default,
|
||||
borderRadius: "6px",
|
||||
"&:hover": {
|
||||
background: theme.palette.brightPurple.main,
|
||||
"& .MuiBadge-badge": {
|
||||
background: theme.palette.background.default,
|
||||
color: theme.palette.brightPurple.main,
|
||||
},
|
||||
"& svg > path:nth-child(1)": { fill: "#FFFFFF" },
|
||||
"& svg > path:nth-child(2)": { fill: "#FFFFFF" },
|
||||
"& svg > path:nth-child(3)": { stroke: "#FFFFFF" },
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Badge
|
||||
badgeContent={userAccount?.cart.length}
|
||||
sx={{
|
||||
"& .MuiBadge-badge": {
|
||||
display: userAccount?.cart.length ? "flex" : "none",
|
||||
color: "#FFFFFF",
|
||||
background: theme.palette.brightPurple.main,
|
||||
transform: "scale(0.7) translate(50%, -50%)",
|
||||
top: "2px",
|
||||
right: "3px",
|
||||
fontWeight: 400,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CartIcon />
|
||||
</Badge>
|
||||
</IconButton>
|
||||
</Link>
|
||||
<IconButton
|
||||
ref={bellRef}
|
||||
onClick={() => setOpenNotificationsModal((isOpened) => !isOpened)}
|
||||
aria-label="cart"
|
||||
sx={{
|
||||
width: "30px",
|
||||
@ -113,80 +159,43 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) {
|
||||
background: theme.palette.background.default,
|
||||
color: theme.palette.brightPurple.main,
|
||||
},
|
||||
"& svg > path:nth-child(1)": { fill: "#FFFFFF" },
|
||||
"& svg > path:nth-child(2)": { fill: "#FFFFFF" },
|
||||
"& svg > path:nth-child(3)": { stroke: "#FFFFFF" },
|
||||
"& svg > path:first-child": { fill: "#FFFFFF" },
|
||||
"& svg > path:last-child": { stroke: "#FFFFFF" },
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Badge
|
||||
badgeContent={userAccount?.cart.length}
|
||||
badgeContent={notificationsCount}
|
||||
sx={{
|
||||
"& .MuiBadge-badge": {
|
||||
display: userAccount?.cart.length ? "flex" : "none",
|
||||
display: notificationsCount ? "flex" : "none",
|
||||
color: "#FFFFFF",
|
||||
background: theme.palette.brightPurple.main,
|
||||
transform: "scale(0.7) translate(50%, -50%)",
|
||||
top: "2px",
|
||||
top: "3px",
|
||||
right: "3px",
|
||||
fontWeight: 400,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CartIcon />
|
||||
<BellIcon />
|
||||
</Badge>
|
||||
</IconButton>
|
||||
</Link>
|
||||
<IconButton
|
||||
ref={bellRef}
|
||||
onClick={() => setOpenNotificationsModal((isOpened) => !isOpened)}
|
||||
aria-label="cart"
|
||||
sx={{
|
||||
width: "30px",
|
||||
height: "30px",
|
||||
background: theme.palette.background.default,
|
||||
borderRadius: "6px",
|
||||
"&:hover": {
|
||||
background: theme.palette.brightPurple.main,
|
||||
"& .MuiBadge-badge": {
|
||||
background: theme.palette.background.default,
|
||||
color: theme.palette.brightPurple.main,
|
||||
},
|
||||
"& svg > path:first-child": { fill: "#FFFFFF" },
|
||||
"& svg > path:last-child": { stroke: "#FFFFFF" },
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Badge
|
||||
badgeContent={ticketCount}
|
||||
sx={{
|
||||
"& .MuiBadge-badge": {
|
||||
display: ticketCount ? "flex" : "none",
|
||||
color: "#FFFFFF",
|
||||
background: theme.palette.brightPurple.main,
|
||||
transform: "scale(0.7) translate(50%, -50%)",
|
||||
top: "3px",
|
||||
right: "3px",
|
||||
fontWeight: 400,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<BellIcon />
|
||||
</Badge>
|
||||
</IconButton>
|
||||
<NotificationsModal
|
||||
open={openNotificationsModal}
|
||||
setOpen={setOpenNotificationsModal}
|
||||
anchorElement={bellRef.current}
|
||||
notifications={tickets.map((ticket) => ({
|
||||
text: "У вас новое сообщение от техподдержки",
|
||||
date: new Date(ticket.updated_at).toLocaleDateString(),
|
||||
watched: ticket.user === ticket.top_message.user_id,
|
||||
}))}
|
||||
/>
|
||||
<Link to="/" style={{ marginLeft: "auto" }}>
|
||||
<PenaLogo width={100} />
|
||||
</Link>
|
||||
<NotificationsModal
|
||||
open={notificationsCount ? openNotificationsModal : false}
|
||||
setOpen={setOpenNotificationsModal}
|
||||
anchorElement={bellRef.current}
|
||||
notifications={tickets.map((ticket) => ({
|
||||
text: "У вас новое сообщение от техподдержки",
|
||||
date: new Date(ticket.updated_at).toLocaleDateString(),
|
||||
url: `/support/${ticket.id}`,
|
||||
watched: ticket.user === ticket.top_message.user_id,
|
||||
}))}
|
||||
/>
|
||||
<Link to="/" style={{ marginLeft: "auto" }}>
|
||||
<PenaLogo width={100} />
|
||||
</Link>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{ display: "flex", overflow: open ? "hidden" : "unset" }}>
|
||||
<Drawer
|
||||
@ -195,10 +204,11 @@ export default function NavbarCollapsed({ isLoggedIn, children }: Props) {
|
||||
position: "relative",
|
||||
zIndex: open ? "none" : "-1",
|
||||
"& .MuiDrawer-paper": {
|
||||
position: "absolute",
|
||||
position: "fixed",
|
||||
top: "0",
|
||||
width: 210,
|
||||
height: "100%",
|
||||
marginTop: "51px",
|
||||
},
|
||||
}}
|
||||
variant="persistent"
|
||||
|
@ -58,6 +58,8 @@ export default function NavbarFull({ isLoggedIn, children }: Props) {
|
||||
disableGutters
|
||||
maxWidth={false}
|
||||
sx={{
|
||||
position: "fixed",
|
||||
top: "0",
|
||||
px: "16px",
|
||||
display: "flex",
|
||||
height: "80px",
|
||||
|
@ -3,209 +3,215 @@ import { TransitionProps } from "@mui/material/transitions";
|
||||
import logotip from "../../assets/Icons/logoPenaHab.svg";
|
||||
import logotipBlack from "../../assets/Icons/black_logo_PenaHab.svg";
|
||||
import CustomAvatar from "./Avatar";
|
||||
import CloseIcon from "../icons/CloseIcons";
|
||||
import React from "react";
|
||||
import {
|
||||
AppBar,
|
||||
Box,
|
||||
Button,
|
||||
Dialog,
|
||||
IconButton,
|
||||
List,
|
||||
ListItem,
|
||||
Slide,
|
||||
Toolbar,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
Box,
|
||||
Button,
|
||||
Dialog,
|
||||
List,
|
||||
ListItem,
|
||||
Slide,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
import { useUserStore } from "@root/stores/user";
|
||||
import { currencyFormatter } from "@root/utils/currencyFormatter";
|
||||
|
||||
const arrayMenu = [
|
||||
{ name: "Главная", url: "/" },
|
||||
{ name: "Тарифы", url: "/tariffs" },
|
||||
{ name: "Тарифы на время", url: "/tariffs/time" },
|
||||
{ name: "Тарифы на объём", url: "/tariffs/volume" },
|
||||
{ name: "Кастомный тариф", url: "/tariffconstructor" },
|
||||
{ name: "Вопросы и ответы", url: "/faq" },
|
||||
{ name: "История", url: "/history" },
|
||||
{ name: "Оплата", url: "/payment" },
|
||||
{ name: "Поддержка", url: "/support" },
|
||||
{ name: "Оплата", url: "/wallet" },
|
||||
{ name: "Корзина", url: "/cart" },
|
||||
{ name: "Главная", url: "/" },
|
||||
{ name: "Тарифы", url: "/tariffs" },
|
||||
{ name: "Тарифы на время", url: "/tariffs/time" },
|
||||
{ name: "Тарифы на объём", url: "/tariffs/volume" },
|
||||
{ name: "Кастомный тариф", url: "/tariffconstructor" },
|
||||
{ name: "Вопросы и ответы", url: "/faq" },
|
||||
{ name: "История", url: "/history" },
|
||||
{ name: "Оплата", url: "/payment" },
|
||||
{ name: "Поддержка", url: "/support" },
|
||||
{ name: "Оплата", url: "/wallet" },
|
||||
{ name: "Корзина", url: "/cart" },
|
||||
];
|
||||
|
||||
const Transition = React.forwardRef(function Transition(
|
||||
props: TransitionProps & {
|
||||
children: React.ReactElement;
|
||||
},
|
||||
props: TransitionProps & {
|
||||
children: React.ReactElement;
|
||||
},
|
||||
|
||||
ref: React.Ref<null>
|
||||
ref: React.Ref<null>
|
||||
) {
|
||||
return <Slide direction={"left"} ref={ref} {...props} />;
|
||||
return <Slide direction={"left"} ref={ref} {...props} />;
|
||||
});
|
||||
|
||||
interface DialogMenuProps {
|
||||
open: boolean;
|
||||
handleClose: () => void;
|
||||
open: boolean;
|
||||
handleClose: () => void;
|
||||
}
|
||||
|
||||
export default function DialogMenu({ open, handleClose }: DialogMenuProps) {
|
||||
const theme = useTheme();
|
||||
const location = useLocation();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
const user = useUserStore((state) => state.user);
|
||||
const cash = useUserStore(state => state.userAccount?.wallet.cash) ?? 0;
|
||||
const theme = useTheme();
|
||||
const location = useLocation();
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(900));
|
||||
const user = useUserStore((state) => state.user);
|
||||
const cash = useUserStore((state) => state.userAccount?.wallet.cash) ?? 0;
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
fullScreen
|
||||
sx={{ width: isMobile ? "100%" : "320px", ml: "auto", height: "100%" }}
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
TransitionComponent={Transition}
|
||||
return (
|
||||
<Dialog
|
||||
fullScreen
|
||||
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}
|
||||
onClose={handleClose}
|
||||
TransitionComponent={Transition}
|
||||
>
|
||||
<List
|
||||
sx={{
|
||||
background: location.pathname === "/" ? "#333647" : "#FFFFFF",
|
||||
height: "100vh",
|
||||
p: "0",
|
||||
paddingTop: "20px",
|
||||
}}
|
||||
>
|
||||
<ListItem
|
||||
sx={{
|
||||
pl: "40px",
|
||||
flexDirection: "column",
|
||||
alignItems: isTablet ? "start" : "end",
|
||||
}}
|
||||
>
|
||||
<AppBar
|
||||
sx={{
|
||||
position: "relative",
|
||||
background: location.pathname === "/" ? "#333647" : "#FFFFFF",
|
||||
boxShadow: "none",
|
||||
height: isMobile ? "66px" : "100px",
|
||||
}}
|
||||
{arrayMenu.map(({ name, url }, index) => (
|
||||
<Button
|
||||
key={index}
|
||||
component={Link}
|
||||
to={url}
|
||||
onClick={handleClose}
|
||||
state={user ? undefined : { backgroundLocation: location }}
|
||||
disableRipple
|
||||
variant="text"
|
||||
sx={{
|
||||
fontWeight: "500",
|
||||
color:
|
||||
location.pathname === url
|
||||
? "#7E2AEA"
|
||||
: location.pathname === "/"
|
||||
? "white"
|
||||
: "black",
|
||||
height: "20px",
|
||||
textTransform: "none",
|
||||
marginBottom: "25px",
|
||||
fontSize: "16px",
|
||||
"&:hover": {
|
||||
background: "none",
|
||||
color: "#7E2AEA",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Toolbar
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
svg: { color: "#000000" },
|
||||
}}
|
||||
{name}
|
||||
</Button>
|
||||
))}
|
||||
</ListItem>
|
||||
{isTablet ? (
|
||||
location.pathname === "/" ? (
|
||||
<Button
|
||||
component={Link}
|
||||
to={user ? "/tariffs" : "/signin"}
|
||||
state={user ? undefined : { backgroundLocation: location }}
|
||||
variant="outlined"
|
||||
sx={{
|
||||
width: "188px",
|
||||
color: "white",
|
||||
border: "1px solid white",
|
||||
ml: "40px",
|
||||
mt: "35px",
|
||||
textTransform: "none",
|
||||
fontWeight: "400",
|
||||
fontSize: "18px",
|
||||
lineHeight: "24px",
|
||||
borderRadius: "8px",
|
||||
padding: "8px 17px",
|
||||
}}
|
||||
>
|
||||
Личный кабинет
|
||||
</Button>
|
||||
) : (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "72px",
|
||||
background: "#F2F3F7",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
position: "absolute",
|
||||
bottom: "0",
|
||||
}}
|
||||
>
|
||||
<CustomAvatar />
|
||||
<Box sx={{ ml: "8px", whiteSpace: "nowrap" }}>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "12px",
|
||||
lineHeight: "14px",
|
||||
color: theme.palette.grey3.main,
|
||||
}}
|
||||
>
|
||||
{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) => (
|
||||
<Button
|
||||
key={index}
|
||||
component={Link}
|
||||
to={url}
|
||||
onClick={handleClose}
|
||||
state={user ? undefined : { backgroundLocation: location }}
|
||||
disableRipple
|
||||
variant="text"
|
||||
sx={{
|
||||
fontWeight: "500",
|
||||
color: location.pathname === url ? "#7E2AEA" : location.pathname === "/" ? "white" : "black",
|
||||
height: "20px",
|
||||
textTransform: "none",
|
||||
marginBottom: "25px",
|
||||
fontSize: "16px",
|
||||
"&:hover": {
|
||||
background: "none",
|
||||
color: "#7E2AEA",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
</Button>
|
||||
))}
|
||||
</ListItem>
|
||||
{isMobile ? (
|
||||
location.pathname === "/" ? (
|
||||
<Button
|
||||
component={Link}
|
||||
to={user ? "/tariffs" : "/signin"}
|
||||
state={user ? undefined : { backgroundLocation: location }}
|
||||
variant="outlined"
|
||||
sx={{
|
||||
width: "188px",
|
||||
color: "white",
|
||||
border: "1px solid white",
|
||||
ml: "40px",
|
||||
mt: "35px",
|
||||
textTransform: "none",
|
||||
fontWeight: "400",
|
||||
fontSize: "18px",
|
||||
lineHeight: "24px",
|
||||
borderRadius: "8px",
|
||||
padding: "8px 17px",
|
||||
}}
|
||||
>
|
||||
Личный кабинет
|
||||
</Button>
|
||||
) : (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "72px",
|
||||
background: "#F2F3F7",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
position: "absolute",
|
||||
bottom: "0",
|
||||
}}
|
||||
>
|
||||
<CustomAvatar />
|
||||
<Box sx={{ ml: "8px", whiteSpace: "nowrap" }}>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "12px",
|
||||
lineHeight: "14px",
|
||||
color: theme.palette.grey3.main,
|
||||
}}
|
||||
>
|
||||
Мой баланс
|
||||
</Typography>
|
||||
<Typography variant="body2" color={theme.palette.brightPurple.main}>
|
||||
{currencyFormatter.format(cash / 100)}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
component={Link}
|
||||
to={user ? "/tariffs" : "/signin"}
|
||||
state={user ? undefined : { backgroundLocation: location }}
|
||||
variant="outlined"
|
||||
sx={{
|
||||
width: "188px",
|
||||
color: "white",
|
||||
border: "1px solid white",
|
||||
margin: "35px 0 0 126px",
|
||||
textTransform: "none",
|
||||
fontWeight: "400",
|
||||
fontSize: "18px",
|
||||
lineHeight: "24px",
|
||||
borderRadius: "8px",
|
||||
padding: "8px 17px",
|
||||
}}
|
||||
>
|
||||
Личный кабинет
|
||||
</Button>
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
right: "40px",
|
||||
bottom: "60px",
|
||||
}}
|
||||
>
|
||||
<img src={location.pathname === "/" ? logotip : logotipBlack} alt="icon" />
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</List>
|
||||
</Dialog>
|
||||
);
|
||||
Мой баланс
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
color={theme.palette.brightPurple.main}
|
||||
>
|
||||
{currencyFormatter.format(cash / 100)}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
component={Link}
|
||||
to={user ? "/tariffs" : "/signin"}
|
||||
state={user ? undefined : { backgroundLocation: location }}
|
||||
variant="outlined"
|
||||
sx={{
|
||||
width: "188px",
|
||||
color: "white",
|
||||
border: "1px solid white",
|
||||
margin: "35px 0 0 126px",
|
||||
textTransform: "none",
|
||||
fontWeight: "400",
|
||||
fontSize: "18px",
|
||||
lineHeight: "24px",
|
||||
borderRadius: "8px",
|
||||
padding: "8px 17px",
|
||||
}}
|
||||
>
|
||||
Личный кабинет
|
||||
</Button>
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
right: "40px",
|
||||
bottom: "60px",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={location.pathname === "/" ? logotip : logotipBlack}
|
||||
alt="icon"
|
||||
/>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</List>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { useState } from "react";
|
||||
import { IconButton, useTheme } from "@mui/material";
|
||||
import MenuIcon from "@mui/icons-material/Menu";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import SectionWrapper from "../SectionWrapper";
|
||||
|
||||
import PenaLogo from "../PenaLogo";
|
||||
import DialogMenu from "./DialogMenu";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
interface Props {
|
||||
isLoggedIn: boolean;
|
||||
@ -30,11 +30,10 @@ export default function NavbarCollapsed({ isLoggedIn }: Props) {
|
||||
component="nav"
|
||||
maxWidth="lg"
|
||||
outerContainerSx={{
|
||||
position: "fixed",
|
||||
top: "0",
|
||||
backgroundColor: theme.palette.navbarbg.main,
|
||||
position: "sticky",
|
||||
top: 0,
|
||||
zIndex: 1,
|
||||
// borderBottom: "1px solid #E3E3E3",
|
||||
borderBottom: "1px solid #E3E3E3",
|
||||
}}
|
||||
sx={{
|
||||
height: "51px",
|
||||
@ -44,9 +43,14 @@ export default function NavbarCollapsed({ isLoggedIn }: Props) {
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Link to="/"><PenaLogo width={100} /></Link>
|
||||
|
||||
<IconButton onClick={handleClickOpen} sx={{ p: 0, width: "30px", color: theme.palette.primary.main }}>
|
||||
<Link to="/">
|
||||
<PenaLogo width={100} />
|
||||
</Link>
|
||||
|
||||
<IconButton
|
||||
onClick={handleClickOpen}
|
||||
sx={{ p: 0, width: "30px", color: theme.palette.primary.main }}
|
||||
>
|
||||
<MenuIcon sx={{ height: "30px", width: "30px" }} />
|
||||
</IconButton>
|
||||
<DialogMenu open={open} handleClose={handleClose} />
|
||||
|
@ -1,5 +1,12 @@
|
||||
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 LogoutIcon from "../icons/LogoutIcon";
|
||||
import WalletIcon from "../icons/WalletIcon";
|
||||
@ -15,119 +22,129 @@ import { clearCustomTariffs } from "@root/stores/customTariffs";
|
||||
import { currencyFormatter } from "@root/utils/currencyFormatter";
|
||||
|
||||
interface Props {
|
||||
isLoggedIn: boolean;
|
||||
isLoggedIn: boolean;
|
||||
}
|
||||
|
||||
export default function NavbarFull({ isLoggedIn }: Props) {
|
||||
const theme = useTheme();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const user = useUserStore((state) => state.user);
|
||||
const cash = useUserStore(state => state.userAccount?.wallet.cash) ?? 0;
|
||||
const theme = useTheme();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const user = useUserStore((state) => state.user);
|
||||
const cash = useUserStore((state) => state.userAccount?.wallet.cash) ?? 0;
|
||||
|
||||
async function handleLogoutClick() {
|
||||
try {
|
||||
await logout();
|
||||
clearAuthToken();
|
||||
clearUserData();
|
||||
clearCustomTariffs();
|
||||
navigate("/");
|
||||
} catch (error: any) {
|
||||
const message = getMessageFromFetchError(error, "Не удалось выйти");
|
||||
if (message) enqueueSnackbar(message);
|
||||
}
|
||||
async function handleLogoutClick() {
|
||||
try {
|
||||
await logout();
|
||||
clearAuthToken();
|
||||
clearUserData();
|
||||
clearCustomTariffs();
|
||||
navigate("/");
|
||||
} catch (error: any) {
|
||||
const message = getMessageFromFetchError(error, "Не удалось выйти");
|
||||
if (message) enqueueSnackbar(message);
|
||||
}
|
||||
}
|
||||
|
||||
return isLoggedIn ? (
|
||||
<Container
|
||||
component="nav"
|
||||
disableGutters
|
||||
maxWidth={false}
|
||||
sx={{
|
||||
px: "16px",
|
||||
display: "flex",
|
||||
height: "80px",
|
||||
alignItems: "center",
|
||||
gap: "60px",
|
||||
bgcolor: "white",
|
||||
borderBottom: "1px solid #E3E3E3",
|
||||
}}
|
||||
return isLoggedIn ? (
|
||||
<Container
|
||||
component="nav"
|
||||
disableGutters
|
||||
maxWidth={false}
|
||||
sx={{
|
||||
px: "16px",
|
||||
display: "flex",
|
||||
height: "80px",
|
||||
alignItems: "center",
|
||||
gap: "60px",
|
||||
bgcolor: "white",
|
||||
borderBottom: "1px solid #E3E3E3",
|
||||
}}
|
||||
>
|
||||
<Link to="/">
|
||||
<PenaLogo width={124} />
|
||||
</Link>
|
||||
<Menu />
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
ml: "auto",
|
||||
}}
|
||||
>
|
||||
<Drawers />
|
||||
<IconButton
|
||||
sx={{ p: 0, ml: "8px" }}
|
||||
onClick={() => navigate("/wallet")}
|
||||
>
|
||||
<Link to="/"><PenaLogo width={124} /></Link>
|
||||
<Menu />
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
ml: "auto",
|
||||
}}
|
||||
>
|
||||
<Drawers />
|
||||
<IconButton
|
||||
sx={{ p: 0, ml: "8px" }}
|
||||
onClick={() => navigate("/wallet")}
|
||||
>
|
||||
<WalletIcon color={theme.palette.grey2.main} bgcolor="#F2F3F7" />
|
||||
</IconButton>
|
||||
<Box sx={{ ml: "8px", whiteSpace: "nowrap" }}>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "12px",
|
||||
lineHeight: "14px",
|
||||
color: theme.palette.grey3.main,
|
||||
}}
|
||||
>
|
||||
Мой баланс
|
||||
</Typography>
|
||||
<Typography variant="body2" color={theme.palette.brightPurple.main}>
|
||||
{currencyFormatter.format(cash / 100)}
|
||||
</Typography>
|
||||
</Box>
|
||||
<CustomAvatar />
|
||||
<IconButton
|
||||
onClick={handleLogoutClick}
|
||||
sx={{ ml: "20px", bgcolor: "#F2F3F7", borderRadius: "6px", height: "36px", width: "36px" }}
|
||||
>
|
||||
<LogoutIcon />
|
||||
</IconButton>
|
||||
</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={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>
|
||||
</>
|
||||
);
|
||||
<WalletIcon color={theme.palette.grey2.main} bgcolor="#F2F3F7" />
|
||||
</IconButton>
|
||||
<Box sx={{ ml: "8px", whiteSpace: "nowrap" }}>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "12px",
|
||||
lineHeight: "14px",
|
||||
color: theme.palette.grey3.main,
|
||||
}}
|
||||
>
|
||||
Мой баланс
|
||||
</Typography>
|
||||
<Typography variant="body2" color={theme.palette.brightPurple.main}>
|
||||
{currencyFormatter.format(cash / 100)}
|
||||
</Typography>
|
||||
</Box>
|
||||
<CustomAvatar />
|
||||
<IconButton
|
||||
onClick={handleLogoutClick}
|
||||
sx={{
|
||||
ml: "20px",
|
||||
bgcolor: "#F2F3F7",
|
||||
borderRadius: "6px",
|
||||
height: "36px",
|
||||
width: "36px",
|
||||
}}
|
||||
>
|
||||
<LogoutIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
</Container>
|
||||
) : (
|
||||
<>
|
||||
<SectionWrapper
|
||||
component="nav"
|
||||
maxWidth="lg"
|
||||
outerContainerSx={{
|
||||
position: "fixed",
|
||||
top: "0",
|
||||
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={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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -6,10 +6,12 @@ import {
|
||||
useTheme,
|
||||
useMediaQuery,
|
||||
} from "@mui/material";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
type Notification = {
|
||||
text: string;
|
||||
date: string;
|
||||
url: string;
|
||||
watched?: boolean;
|
||||
};
|
||||
|
||||
@ -59,58 +61,68 @@ export const NotificationsModal = ({
|
||||
}}
|
||||
>
|
||||
<List sx={{ width: "100%", padding: "5px" }}>
|
||||
{notifications.map(({ text, date, watched = true }) => (
|
||||
<ListItem
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: isMobile ? "normal" : "center",
|
||||
justifyContent: "space-between",
|
||||
flexDirection: isMobile ? "column-reverse" : "unset",
|
||||
borderBottom: "1px solid #F2F3F7",
|
||||
padding: "20px 10px",
|
||||
background: watched ? "none" : "#F0F0F6",
|
||||
borderRadius: watched ? "0" : "8px",
|
||||
"&:first-child": {
|
||||
borderTop: "1px solid #F2F3F7",
|
||||
},
|
||||
{notifications.map(({ text, date, url, watched = true }) => (
|
||||
<Link
|
||||
to={url}
|
||||
onClick={() => setOpen(false)}
|
||||
style={{
|
||||
textDecoration: "none",
|
||||
color: "inherit",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
<ListItem
|
||||
key={text + date}
|
||||
sx={{
|
||||
position: "relative",
|
||||
fontSize: "16px",
|
||||
lineHeight: "19px",
|
||||
paddingLeft: watched ? "0" : "35px",
|
||||
fontWeight: watched ? "normal" : "bold",
|
||||
"&::before": {
|
||||
content: '""',
|
||||
display: watched ? "none" : "block",
|
||||
position: "absolute",
|
||||
left: "10px",
|
||||
top: isMobile ? "5px" : "50%",
|
||||
transform: isMobile ? "none" : "translateY(-50%)",
|
||||
height: "8px",
|
||||
width: "8px",
|
||||
borderRadius: "50%",
|
||||
background: "#7E2AEA",
|
||||
display: "flex",
|
||||
alignItems: isMobile ? "normal" : "center",
|
||||
justifyContent: "space-between",
|
||||
flexDirection: isMobile ? "column-reverse" : "unset",
|
||||
borderBottom: "1px solid #F2F3F7",
|
||||
padding: "20px 10px",
|
||||
background: watched ? "none" : "#F0F0F6",
|
||||
borderRadius: watched ? "0" : "8px",
|
||||
"&:first-child": {
|
||||
borderTop: "1px solid #F2F3F7",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "14px",
|
||||
lineHeight: "19px",
|
||||
color: "#9A9AAF",
|
||||
fontWeight: watched ? "normal" : "bold",
|
||||
paddingLeft: isMobile ? (watched ? "0" : "35px") : "0",
|
||||
marginBottom: isMobile ? "5px" : "0",
|
||||
}}
|
||||
>
|
||||
{date}
|
||||
</Typography>
|
||||
</ListItem>
|
||||
<Typography
|
||||
sx={{
|
||||
position: "relative",
|
||||
fontSize: "16px",
|
||||
lineHeight: "19px",
|
||||
paddingLeft: watched ? "0" : "35px",
|
||||
fontWeight: watched ? "normal" : "bold",
|
||||
"&::before": {
|
||||
content: '""',
|
||||
display: watched ? "none" : "block",
|
||||
position: "absolute",
|
||||
left: "10px",
|
||||
top: isMobile ? "5px" : "50%",
|
||||
transform: isMobile ? "none" : "translateY(-50%)",
|
||||
height: "8px",
|
||||
width: "8px",
|
||||
borderRadius: "50%",
|
||||
background: "#7E2AEA",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "14px",
|
||||
lineHeight: "19px",
|
||||
color: "#9A9AAF",
|
||||
fontWeight: watched ? "normal" : "bold",
|
||||
paddingLeft: isMobile ? (watched ? "0" : "35px") : "0",
|
||||
marginBottom: isMobile ? "5px" : "0",
|
||||
}}
|
||||
>
|
||||
{date}
|
||||
</Typography>
|
||||
</ListItem>
|
||||
</Link>
|
||||
))}
|
||||
</List>
|
||||
</Popover>
|
||||
|
@ -39,7 +39,7 @@ export const Select = ({
|
||||
<Box>
|
||||
<Box
|
||||
sx={{
|
||||
zIndex: 1500,
|
||||
zIndex: 1,
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
height: "56px",
|
||||
@ -81,6 +81,7 @@ export const Select = ({
|
||||
<MuiSelect
|
||||
ref={ref}
|
||||
className="select"
|
||||
value=""
|
||||
open={opened}
|
||||
MenuProps={{ disablePortal: true }}
|
||||
sx={{ width: "100%" }}
|
||||
|
@ -26,3 +26,7 @@
|
||||
.MuiInputBase-root.MuiOutlinedInput-root .MuiSelect-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.MuiMenu-root.MuiModal-root {
|
||||
z-index: 0;
|
||||
}
|
||||
|
@ -22,9 +22,7 @@ interface Props {
|
||||
color?: string;
|
||||
FormInputSx?: SxProps<Theme>;
|
||||
TextfieldProps: TextFieldProps;
|
||||
onChange: (
|
||||
e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>
|
||||
) => void;
|
||||
onChange: (e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => void;
|
||||
}
|
||||
|
||||
export default function PasswordInput({
|
||||
@ -46,17 +44,13 @@ export default function PasswordInput({
|
||||
: { ...theme.typography.body1, fontWeight: 500 }
|
||||
: theme.typography.body2;
|
||||
|
||||
const placeholderFont = upMd
|
||||
? undefined
|
||||
: { fontWeight: 400, fontSize: "16px", lineHeight: "19px" };
|
||||
const placeholderFont = upMd ? undefined : { fontWeight: 400, fontSize: "16px", lineHeight: "19px" };
|
||||
|
||||
const [showPassword, setShowPassword] = React.useState(false);
|
||||
|
||||
const handleClickShowPassword = () => setShowPassword((show) => !show);
|
||||
|
||||
const handleMouseDownPassword = (
|
||||
event: React.MouseEvent<HTMLButtonElement>
|
||||
) => {
|
||||
const handleMouseDownPassword = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
|
@ -20,6 +20,7 @@ export default function Landing({ templaterOnly = false }: Props) {
|
||||
<Box
|
||||
sx={{
|
||||
position: "relative",
|
||||
paddingTop: "80px",
|
||||
}}
|
||||
>
|
||||
<Navbar isLoggedIn={false} />
|
||||
|
@ -46,6 +46,10 @@
|
||||
right: -5px;
|
||||
}
|
||||
|
||||
.slider .slick-arrow::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.slider .slick-dots {
|
||||
bottom: -15px;
|
||||
}
|
||||
|
@ -12,19 +12,23 @@ import "slick-carousel/slick/slick-theme.css";
|
||||
import "./slider.css";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import type { CustomArrowProps } from "react-slick";
|
||||
|
||||
type SliderProps = {
|
||||
items: ReactNode[];
|
||||
};
|
||||
|
||||
type ArrowProps = CustomArrowProps & {
|
||||
icon: ReactNode;
|
||||
};
|
||||
|
||||
export const Slider = ({ items }: SliderProps) => {
|
||||
const [range, setRange] = useState<number>(3);
|
||||
const [activeRange, setActiveRange] = useState<number[]>([0, 1, 2]);
|
||||
const theme = useTheme();
|
||||
const isMiddle = useMediaQuery(
|
||||
theme.breakpoints.down(1200) && theme.breakpoints.up(830)
|
||||
);
|
||||
const isMiddle = useMediaQuery(theme.breakpoints.down(1200));
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(830));
|
||||
const isMobileHeader = useMediaQuery(theme.breakpoints.down(900));
|
||||
|
||||
useEffect(() => {
|
||||
if (isTablet) {
|
||||
@ -64,10 +68,14 @@ export const Slider = ({ items }: SliderProps) => {
|
||||
];
|
||||
};
|
||||
|
||||
const Arrow = ({ currentSlide, slideCount, icon, ...props }: ArrowProps) => (
|
||||
<Box {...props}>{icon}</Box>
|
||||
);
|
||||
|
||||
return (
|
||||
<Box className="slider-wrapper">
|
||||
{(items.length < 4 && !isMiddle && !isTablet) ||
|
||||
(items.length < 3 && isMiddle) ||
|
||||
(items.length < 3 && isMiddle && !isTablet) ||
|
||||
(items.length < 1 && isTablet) ? (
|
||||
<Box
|
||||
sx={{
|
||||
@ -87,14 +95,15 @@ export const Slider = ({ items }: SliderProps) => {
|
||||
dots
|
||||
infinite
|
||||
variableWidth
|
||||
lazyLoad={isMobileHeader ? "progressive" : undefined}
|
||||
slidesToShow={range}
|
||||
prevArrow={<img src={arrowLeftIcon} alt="prev" />}
|
||||
nextArrow={<img src={arrowRightIcon} alt="next" />}
|
||||
prevArrow={<Arrow icon={<img src={arrowLeftIcon} alt="prev" />} />}
|
||||
nextArrow={<Arrow icon={<img src={arrowRightIcon} alt="next" />} />}
|
||||
beforeChange={(_, active) =>
|
||||
setActiveRange(calculateRange(active, items.length))
|
||||
}
|
||||
customPaging={(slideNumber) => (
|
||||
<li
|
||||
<Box
|
||||
className={classNames("dot", {
|
||||
active: activeRange.includes(slideNumber),
|
||||
})}
|
||||
|
@ -76,39 +76,39 @@ export default function SigninDialog() {
|
||||
setTimeout(() => navigate("/"), theme.transitions.duration.leavingScreen);
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={isDialogOpen}
|
||||
onClose={handleClose}
|
||||
PaperProps={{
|
||||
sx: {
|
||||
width: "600px",
|
||||
maxWidth: "600px",
|
||||
}
|
||||
}}
|
||||
slotProps={{
|
||||
backdrop: {
|
||||
style: {
|
||||
backgroundColor: "rgb(0 0 0 / 0.7)",
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
component="form"
|
||||
onSubmit={formik.handleSubmit}
|
||||
noValidate
|
||||
sx={{
|
||||
position: "relative",
|
||||
backgroundColor: "white",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
p: upMd ? "50px" : "18px",
|
||||
pb: upMd ? "40px" : "30px",
|
||||
gap: "15px",
|
||||
borderRadius: "12px",
|
||||
boxShadow: cardShadow,
|
||||
return (
|
||||
<Dialog
|
||||
open={isDialogOpen}
|
||||
onClose={handleClose}
|
||||
PaperProps={{
|
||||
sx: {
|
||||
width: "600px",
|
||||
maxWidth: "600px",
|
||||
},
|
||||
}}
|
||||
slotProps={{
|
||||
backdrop: {
|
||||
style: {
|
||||
backgroundColor: "rgb(0 0 0 / 0.7)",
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
component="form"
|
||||
onSubmit={formik.handleSubmit}
|
||||
noValidate
|
||||
sx={{
|
||||
position: "relative",
|
||||
backgroundColor: "white",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
p: upMd ? "50px" : "18px",
|
||||
pb: upMd ? "40px" : "30px",
|
||||
gap: "15px",
|
||||
borderRadius: "12px",
|
||||
boxShadow: cardShadow,
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
|
@ -16,193 +16,203 @@ import { makeRequest } from "@frontend/kitui";
|
||||
import { cardShadow } from "@root/utils/themes/shadow";
|
||||
import PasswordInput from "@root/components/passwordInput";
|
||||
|
||||
|
||||
interface Values {
|
||||
login: string;
|
||||
password: string;
|
||||
repeatPassword: string;
|
||||
login: string;
|
||||
password: string;
|
||||
repeatPassword: string;
|
||||
}
|
||||
|
||||
const initialValues: Values = {
|
||||
login: "",
|
||||
password: "",
|
||||
repeatPassword: "",
|
||||
login: "",
|
||||
password: "",
|
||||
repeatPassword: "",
|
||||
};
|
||||
|
||||
const validationSchema = object({
|
||||
login: string().required("Поле обязательно"),
|
||||
password: string().min(8, "Минимум 8 символов").matches(/^[.,:;-_+\d\w]+$/, "Некорректные символы").required("Поле обязательно"),
|
||||
repeatPassword: string().oneOf([ref("password"), undefined], "Пароли не совпадают"),
|
||||
login: string().required("Поле обязательно"),
|
||||
password: string()
|
||||
.min(8, "Минимум 8 символов")
|
||||
.matches(/^[.,:;-_+\d\w]+$/, "Некорректные символы")
|
||||
.required("Поле обязательно"),
|
||||
repeatPassword: string()
|
||||
.oneOf([ref("password"), undefined], "Пароли не совпадают")
|
||||
.required("Повторите пароль"),
|
||||
});
|
||||
|
||||
export default function SignupDialog() {
|
||||
const [isDialogOpen, setIsDialogOpen] = useState<boolean>(true);
|
||||
const user = useUserStore(state => state.user);
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const formik = useFormik<Values>({
|
||||
initialValues,
|
||||
validationSchema,
|
||||
onSubmit: (values, formikHelpers) => {
|
||||
makeRequest<RegisterRequest, RegisterResponse>({
|
||||
url: "https://hub.pena.digital/auth/register",
|
||||
body: {
|
||||
login: values.login.trim(),
|
||||
password: values.password.trim(),
|
||||
phoneNumber: "+7",
|
||||
},
|
||||
useToken: false,
|
||||
withCredentials: true,
|
||||
}).then(result => {
|
||||
setUserId(result._id);
|
||||
}).catch((error: any) => {
|
||||
const errorMessage = getMessageFromFetchError(error);
|
||||
if (errorMessage) enqueueSnackbar(errorMessage);
|
||||
}).finally(() => {
|
||||
formikHelpers.setSubmitting(false);
|
||||
});
|
||||
const [isDialogOpen, setIsDialogOpen] = useState<boolean>(true);
|
||||
const user = useUserStore((state) => state.user);
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const formik = useFormik<Values>({
|
||||
initialValues,
|
||||
validationSchema,
|
||||
onSubmit: (values, formikHelpers) => {
|
||||
makeRequest<RegisterRequest, RegisterResponse>({
|
||||
url: "https://hub.pena.digital/auth/register",
|
||||
body: {
|
||||
login: values.login.trim(),
|
||||
password: values.password.trim(),
|
||||
phoneNumber: "+7",
|
||||
},
|
||||
});
|
||||
useToken: false,
|
||||
withCredentials: true,
|
||||
})
|
||||
.then((result) => {
|
||||
setUserId(result._id);
|
||||
})
|
||||
.catch((error: any) => {
|
||||
const errorMessage = getMessageFromFetchError(error);
|
||||
if (errorMessage) enqueueSnackbar(errorMessage);
|
||||
})
|
||||
.finally(() => {
|
||||
formikHelpers.setSubmitting(false);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(function redirectIfSignedIn() {
|
||||
if (user) navigate("/tariffs", { replace: true });
|
||||
}, [navigate, user]);
|
||||
useEffect(
|
||||
function redirectIfSignedIn() {
|
||||
if (user) navigate("/tariffs", { replace: true });
|
||||
},
|
||||
[navigate, user]
|
||||
);
|
||||
|
||||
function handleClose() {
|
||||
setIsDialogOpen(false);
|
||||
setTimeout(() => navigate("/"), theme.transitions.duration.leavingScreen);
|
||||
}
|
||||
function handleClose() {
|
||||
setIsDialogOpen(false);
|
||||
setTimeout(() => navigate("/"), theme.transitions.duration.leavingScreen);
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={isDialogOpen}
|
||||
onClose={handleClose}
|
||||
PaperProps={{
|
||||
sx: {
|
||||
width: "600px",
|
||||
maxWidth: "600px",
|
||||
}
|
||||
}}
|
||||
slotProps={{
|
||||
backdrop: {
|
||||
style: {
|
||||
backgroundColor: "rgb(0 0 0 / 0.7)",
|
||||
}
|
||||
}
|
||||
}}
|
||||
return (
|
||||
<Dialog
|
||||
open={isDialogOpen}
|
||||
onClose={handleClose}
|
||||
PaperProps={{
|
||||
sx: {
|
||||
width: "600px",
|
||||
maxWidth: "600px",
|
||||
},
|
||||
}}
|
||||
slotProps={{
|
||||
backdrop: {
|
||||
style: {
|
||||
backgroundColor: "rgb(0 0 0 / 0.7)",
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
component="form"
|
||||
onSubmit={formik.handleSubmit}
|
||||
noValidate
|
||||
sx={{
|
||||
position: "relative",
|
||||
backgroundColor: "white",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
p: upMd ? "50px" : "18px",
|
||||
pb: upMd ? "40px" : "30px",
|
||||
gap: "15px",
|
||||
borderRadius: "12px",
|
||||
boxShadow: cardShadow,
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
onClick={handleClose}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
right: "7px",
|
||||
top: "7px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
component="form"
|
||||
onSubmit={formik.handleSubmit}
|
||||
noValidate
|
||||
sx={{
|
||||
position: "relative",
|
||||
backgroundColor: "white",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
p: upMd ? "50px" : "18px",
|
||||
pb: upMd ? "40px" : "30px",
|
||||
gap: "15px",
|
||||
borderRadius: "12px",
|
||||
boxShadow: cardShadow,
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
onClick={handleClose}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
right: "7px",
|
||||
top: "7px",
|
||||
}}
|
||||
>
|
||||
<CloseIcon sx={{ transform: "scale(1.5)" }} />
|
||||
</IconButton>
|
||||
<Box sx={{ mt: upMd ? undefined : "62px" }}>
|
||||
<PenaLogo width={upMd ? 233 : 196} />
|
||||
</Box>
|
||||
<Typography
|
||||
sx={{
|
||||
color: theme.palette.grey3.main,
|
||||
mt: "5px",
|
||||
mb: upMd ? "35px" : "33px",
|
||||
}}
|
||||
>
|
||||
Регистрация
|
||||
</Typography>
|
||||
<InputTextfield
|
||||
TextfieldProps={{
|
||||
value: formik.values.login,
|
||||
placeholder: "username",
|
||||
onBlur: formik.handleBlur,
|
||||
error: formik.touched.login && Boolean(formik.errors.login),
|
||||
helperText: formik.touched.login && formik.errors.login,
|
||||
}}
|
||||
onChange={formik.handleChange}
|
||||
color="#F2F3F7"
|
||||
id="login"
|
||||
label="Логин"
|
||||
gap={upMd ? "10px" : "10px"}
|
||||
/>
|
||||
<PasswordInput
|
||||
TextfieldProps={{
|
||||
value: formik.values.password,
|
||||
placeholder: "Не менее 8 символов",
|
||||
onBlur: formik.handleBlur,
|
||||
error: formik.touched.password && Boolean(formik.errors.password),
|
||||
helperText: formik.touched.password && formik.errors.password,
|
||||
autoComplete: "new-password"
|
||||
}}
|
||||
onChange={formik.handleChange}
|
||||
color="#F2F3F7"
|
||||
id="password"
|
||||
label="Пароль"
|
||||
gap={upMd ? "10px" : "10px"}
|
||||
/>
|
||||
<PasswordInput
|
||||
TextfieldProps={{
|
||||
value: formik.values.repeatPassword,
|
||||
placeholder: "Не менее 8 символов",
|
||||
onBlur: formik.handleBlur,
|
||||
error: formik.touched.repeatPassword && Boolean(formik.errors.repeatPassword),
|
||||
helperText: formik.touched.repeatPassword && formik.errors.repeatPassword,
|
||||
autoComplete: "new-password"
|
||||
}}
|
||||
onChange={formik.handleChange}
|
||||
color="#F2F3F7"
|
||||
id="repeatPassword"
|
||||
label="Повторить пароль"
|
||||
gap={upMd ? "10px" : "10px"}
|
||||
/>
|
||||
<CustomButton
|
||||
fullWidth
|
||||
variant="contained"
|
||||
sx={{
|
||||
backgroundColor: theme.palette.brightPurple.main,
|
||||
textColor: "white",
|
||||
width: "100%",
|
||||
py: "12px",
|
||||
mt: upMd ? undefined : "10px",
|
||||
}}
|
||||
type="submit"
|
||||
disabled={formik.isSubmitting}
|
||||
>
|
||||
Зарегистрироваться
|
||||
</CustomButton>
|
||||
<Link
|
||||
component={RouterLink}
|
||||
to="/signin"
|
||||
state={{ backgroundLocation: location.state.backgroundLocation }}
|
||||
sx={{
|
||||
color: theme.palette.brightPurple.main,
|
||||
mt: "auto",
|
||||
}}
|
||||
>
|
||||
Вход в личный кабинет
|
||||
</Link>
|
||||
</Box>
|
||||
</Dialog>
|
||||
);
|
||||
<CloseIcon sx={{ transform: "scale(1.5)" }} />
|
||||
</IconButton>
|
||||
<Box sx={{ mt: upMd ? undefined : "62px" }}>
|
||||
<PenaLogo width={upMd ? 233 : 196} />
|
||||
</Box>
|
||||
<Typography
|
||||
sx={{
|
||||
color: theme.palette.grey3.main,
|
||||
mt: "5px",
|
||||
mb: upMd ? "35px" : "33px",
|
||||
}}
|
||||
>
|
||||
Регистрация
|
||||
</Typography>
|
||||
<InputTextfield
|
||||
TextfieldProps={{
|
||||
value: formik.values.login,
|
||||
placeholder: "username",
|
||||
onBlur: formik.handleBlur,
|
||||
error: formik.touched.login && Boolean(formik.errors.login),
|
||||
helperText: formik.touched.login && formik.errors.login,
|
||||
}}
|
||||
onChange={formik.handleChange}
|
||||
color="#F2F3F7"
|
||||
id="login"
|
||||
label="Логин"
|
||||
gap={upMd ? "10px" : "10px"}
|
||||
/>
|
||||
<PasswordInput
|
||||
TextfieldProps={{
|
||||
value: formik.values.password,
|
||||
placeholder: "Не менее 8 символов",
|
||||
onBlur: formik.handleBlur,
|
||||
error: formik.touched.password && Boolean(formik.errors.password),
|
||||
helperText: formik.touched.password && formik.errors.password,
|
||||
autoComplete: "new-password",
|
||||
}}
|
||||
onChange={formik.handleChange}
|
||||
color="#F2F3F7"
|
||||
id="password"
|
||||
label="Пароль"
|
||||
gap={upMd ? "10px" : "10px"}
|
||||
/>
|
||||
<PasswordInput
|
||||
TextfieldProps={{
|
||||
value: formik.values.repeatPassword,
|
||||
placeholder: "Не менее 8 символов",
|
||||
onBlur: formik.handleBlur,
|
||||
error: formik.touched.repeatPassword && Boolean(formik.errors.repeatPassword),
|
||||
helperText: formik.touched.repeatPassword && formik.errors.repeatPassword,
|
||||
autoComplete: "new-password",
|
||||
}}
|
||||
onChange={formik.handleChange}
|
||||
color="#F2F3F7"
|
||||
id="repeatPassword"
|
||||
label="Повторить пароль"
|
||||
gap={upMd ? "10px" : "10px"}
|
||||
/>
|
||||
<CustomButton
|
||||
fullWidth
|
||||
variant="contained"
|
||||
sx={{
|
||||
backgroundColor: theme.palette.brightPurple.main,
|
||||
textColor: "white",
|
||||
width: "100%",
|
||||
py: "12px",
|
||||
mt: upMd ? undefined : "10px",
|
||||
}}
|
||||
type="submit"
|
||||
disabled={formik.isSubmitting}
|
||||
>
|
||||
Зарегистрироваться
|
||||
</CustomButton>
|
||||
<Link
|
||||
component={RouterLink}
|
||||
to="/signin"
|
||||
state={{ backgroundLocation: location.state.backgroundLocation }}
|
||||
sx={{
|
||||
color: theme.palette.brightPurple.main,
|
||||
mt: "auto",
|
||||
}}
|
||||
>
|
||||
Вход в личный кабинет
|
||||
</Link>
|
||||
</Box>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
@ -655,7 +655,7 @@ const templategenTariff1: Tariff = {
|
||||
price: 0,
|
||||
isCustom: false,
|
||||
privilegies: [
|
||||
{
|
||||
{
|
||||
_id: "p1",
|
||||
name: "n1",
|
||||
privilegeId: "p1",
|
||||
@ -678,7 +678,7 @@ const templategenTariff2: Tariff = {
|
||||
price: 0,
|
||||
isCustom: false,
|
||||
privilegies: [
|
||||
{
|
||||
{
|
||||
_id: "p5",
|
||||
name: "n5",
|
||||
privilegeId: "p5",
|
||||
|
@ -1,8 +1 @@
|
||||
export const cardShadow = `
|
||||
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)
|
||||
`;
|
||||
export const cardShadow = "0px 15px 80px rgb(210 208 225 / 70%)";
|
||||
|
@ -1,27 +1,22 @@
|
||||
{
|
||||
"extends": "./tsconfig.extend.json",
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
"extends": "./tsconfig.extend.json",
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["es5", "dom", "dom.iterable", "esnext"],
|
||||
"types": ["node"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": ["src", "**/*.ts"]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user