test (access && tariffs) create

This commit is contained in:
ArtChaos189 2023-07-25 21:48:52 +03:00
parent 3920ccebfd
commit ec148d261e
12 changed files with 1343 additions and 245 deletions

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,
},
});

110
cypress/e2e/access.cy.ts Normal file

@ -0,0 +1,110 @@
describe("Форма Входа", () => {
beforeEach(() => {
cy.visit("http://localhost:3000");
});
it("должна успешно входить с правильными учетными данными", () => {
const email = "valid_user@example.com";
const password = "valid_password";
cy.get('input[name="email"]').type(email);
cy.get('input[name="password"]').type(password);
cy.get('button[type="submit"]').click();
cy.url().should("include", "http://localhost:3000/users");
});
it("должна отображать сообщение об ошибке при неверном формате электронной почты", () => {
const invalidEmail = "invalid_email";
cy.get('input[name="email"]').type(invalidEmail);
cy.get('input[name="password"]').type("valid_password");
cy.get('button[type="submit"]').click();
cy.contains("Неверный формат эл. почты");
});
it("должна отображать сообщение об ошибке при отсутствии пароля", () => {
cy.get('input[name="email"]').type("valid_email@example.com");
cy.get('button[type="submit"]').click();
cy.contains("Введите пароль");
});
it("должна отображать сообщение об ошибке для недопустимого пароля", () => {
const invalidPassword = "short";
cy.get('input[name="email"]').type("valid_email@example.com");
cy.get('input[name="password"]').type(invalidPassword);
cy.get('button[type="submit"]').click();
cy.contains("Invalid password");
});
});
describe("Форма регистрации", () => {
beforeEach(() => {
cy.visit("http://localhost:3000/signup");
});
it("должна регистрировать нового пользователя с правильными данными", () => {
const email = Cypress._.random(1000) + "@example.com";
const password = "valid_password";
cy.get('input[name="email"]').type(email);
cy.get('input[name="password"]').type(password);
cy.get('input[name="repeatPassword"]').type(password);
cy.get('button[type="submit"]').click();
cy.wait(5000);
cy.url().should("include", "http://localhost:3000/users");
});
it("должна отображать ошибку при неверном формате электронной почты", () => {
const invalidEmail = "invalid_email";
cy.get('input[name="email"]').type(invalidEmail);
cy.get('input[name="password"]').type("valid_password");
cy.get('input[name="repeatPassword"]').type("valid_password");
cy.get('button[type="submit"]').click();
cy.contains("Неверный формат эл. почты");
});
it("должна отображать ошибку при отсутствии пароля", () => {
cy.get('input[name="email"]').type("valid_email@example.com");
cy.get('input[name="repeatPassword"]').type("valid_password");
cy.get('button[type="submit"]').click();
cy.contains("Обязательное поле").should("have.length", 1);
});
it("должна отображать ошибку при несовпадении паролей", () => {
cy.get('input[name="email"]').type("valid_email@example.com");
cy.get('input[name="password"]').type("valid_password");
cy.get('input[name="repeatPassword"]').type("different_password");
cy.get('button[type="submit"]').click();
cy.contains("Пароли не совпадают");
});
it("попытка отправки запроса при уже зарегистрированном пользователе", () => {
const email = "users@gmail.com";
const password = "12344321";
cy.get('input[name="email"]').type(email);
cy.get('input[name="password"]').type(password);
cy.get('input[name="repeatPassword"]').type(password);
cy.intercept("POST", "https://admin.pena.digital/auth/register").as("registerRequest");
cy.get('button[type="submit"]').click();
cy.wait("@registerRequest");
cy.wait(5000);
cy.contains("user with this login is exist");
});
});

195
cypress/e2e/tariffs.cy.ts Normal file

@ -0,0 +1,195 @@
describe("Форма Создания Тарифа", () => {
beforeEach(() => {
cy.visit("http://localhost:3000");
cy.get('input[name="email"]').type("valid_user@example.com");
cy.get('input[name="password"]').type("valid_password");
cy.get('button[type="submit"]').click();
cy.wait(3000);
cy.url().should("include", "http://localhost:3000/users");
cy.visit("http://localhost:3000/tariffs");
});
it("должна отображать сообщение об ошибке при пустом названии тарифа", () => {
cy.get('input[id="tariff-amount"]').type("10");
// Выбрать первую привилегию с нужным текстом из выпадающего списка
cy.get("#privilege-select").click();
cy.get(`[data-cy = "select-option-Количество шаблонов, которые может сделать пользователь сервиса"]`).click();
cy.get(".btn_createTariffBackend").click({ force: true });
cy.contains("Пустое название тарифа");
});
it("должна отображать сообщение об ошибке при отсутствии выбора привилегии", () => {
cy.get('input[id="tariff-name"]').type("Тестовый Тариф");
cy.get('input[id="tariff-amount"]').type("10");
cy.get(".btn_createTariffBackend").click({ force: true });
cy.contains("Не выбрана привилегия");
});
it("Создание трех тарифов", () => {
cy.get('input[id="tariff-name"]').type("Тестовый Тариф 1");
cy.get('input[id="tariff-amount"]').type("10");
cy.get("#privilege-select").click();
cy.get(`[data-cy="select-option-Количество шаблонов, которые может сделать пользователь сервиса"]`).click();
cy.get(".btn_createTariffBackend").click({ force: true });
cy.get('input[id="tariff-name"]').scrollIntoView().clear({ force: true }).type("Тестовый Тариф 2");
cy.get('input[id="tariff-amount"]').scrollIntoView().clear({ force: true }).type("15");
cy.get("#privilege-select").click();
cy.wait(800);
cy.get(`[data-cy="select-option-Количество дней, в течении которых пользование сервисом безлимитно"]`).click();
cy.get(".btn_createTariffBackend").click({ force: true });
cy.get('input[id="tariff-name"]').scrollIntoView().clear({ force: true }).type("Тестовый Тариф 3");
cy.get('input[id="tariff-amount"]').scrollIntoView().clear({ force: true }).type("20");
cy.get("#privilege-select").click();
cy.wait(800);
cy.get(`[data-cy="select-option-Обьём ПенаДиска для хранения шаблонов и результатов шаблонизации"]`).click();
cy.get(".btn_createTariffBackend").click({ force: true });
});
});
describe("Форма Создания Тарифа", () => {
beforeEach(() => {
cy.visit("http://localhost:3000");
cy.get('input[name="email"]').type("valid_user@example.com");
cy.get('input[name="password"]').type("valid_password");
cy.get('button[type="submit"]').click();
cy.wait(3000);
cy.url().should("include", "http://localhost:3000/users");
cy.visit("http://localhost:3000/tariffs");
});
it("Удаление тарифа единично, одного за другим ", () => {
const tariffNamesToFind = ["Тестовый Тариф 1", "Тестовый Тариф 2", "Тестовый Тариф 3"];
// Поиск каждого тарифа в DataGrid
cy.wait(3000);
tariffNamesToFind.forEach((tariffName) => {
cy.get(".tariffs-data-grid").scrollIntoView().contains(tariffName).should("be.visible");
});
const deleteTariffs = () => {
let tariffsFound = true;
cy.get(".tariffs-data-grid .MuiDataGrid-row").then(($rows) => {
const rowCount = $rows.length;
if (rowCount === 1) {
cy.log("Тарифы не найдены. Тест завершен.");
tariffsFound = false;
}
cy.wrap($rows).each(($row) => {
// Шаг 2: В каждом элементе найдите все дивы вложенные внутрь и выберите последний див
cy.wrap($row).find("div").last().scrollIntoView().get(".delete-tariff-button").last().click({ force: true });
});
cy.wait(2000);
cy.contains("Да")
.click()
.then(() => {
if (!tariffsFound) {
return;
}
cy.wait(5000);
deleteTariffs();
});
});
};
deleteTariffs();
// Проверяем что Дата грид тарифов пустой
cy.wait(2000);
cy.get(".tariffs-data-grid .MuiDataGrid-row").should("not.exist");
});
describe("Форма Создания Тарифа", () => {});
it("Удаление тарифов массово через DataGrid", () => {
// Добавляем 3 тариффа
cy.get('input[id="tariff-name"]').type("Тестовый Тариф 1");
cy.get('input[id="tariff-amount"]').type("10");
cy.get("#privilege-select").click();
cy.get(`[data-cy="select-option-Количество шаблонов, которые может сделать пользователь сервиса"]`).click();
cy.get(".btn_createTariffBackend").click({ force: true });
cy.get('input[id="tariff-name"]').scrollIntoView().clear({ force: true }).type("Тестовый Тариф 2");
cy.get('input[id="tariff-amount"]').scrollIntoView().clear({ force: true }).type("15");
cy.get("#privilege-select").click();
cy.wait(800);
cy.get(`[data-cy="select-option-Количество дней, в течении которых пользование сервисом безлимитно"]`).click();
cy.get(".btn_createTariffBackend").click({ force: true });
cy.get('input[id="tariff-name"]').scrollIntoView().clear({ force: true }).type("Тестовый Тариф 3");
cy.get('input[id="tariff-amount"]').scrollIntoView().clear({ force: true }).type("20");
cy.get("#privilege-select").click();
cy.wait(800);
cy.get(`[data-cy="select-option-Обьём ПенаДиска для хранения шаблонов и результатов шаблонизации"]`).click();
cy.get(".btn_createTariffBackend").click({ force: true });
// Добавляем 3 тариффа
cy.wait(3000);
cy.get(".tariffs-data-grid .PrivateSwitchBase-input").first().click({ multiple: true });
// Проверить, что кнопка "Удалить" появилась
cy.wait(2000);
cy.contains("Удаление").should("be.visible");
// Нажать на кнопку "Удалить"
cy.contains("Удаление").click();
// Подтверждение удаления (если нужно)
cy.contains("Да").click();
// Проверяем что Дата грид тарифов пустой
cy.wait(2000);
cy.get(".tariffs-data-grid .MuiDataGrid-row").should("not.exist");
});
it("Добавление тарифом в корзину", () => {
// Добавляем 3 тариффа
cy.get('input[id="tariff-name"]').type("Тестовый Тариф 1");
cy.get('input[id="tariff-amount"]').type("10");
cy.get("#privilege-select").click();
cy.get(`[data-cy="select-option-Количество шаблонов, которые может сделать пользователь сервиса"]`).click();
cy.get(".btn_createTariffBackend").click({ force: true });
cy.get('input[id="tariff-name"]').scrollIntoView().clear({ force: true }).type("Тестовый Тариф 2");
cy.get('input[id="tariff-amount"]').scrollIntoView().clear({ force: true }).type("15");
cy.get("#privilege-select").click();
cy.wait(800);
cy.get(`[data-cy="select-option-Количество дней, в течении которых пользование сервисом безлимитно"]`).click();
cy.get(".btn_createTariffBackend").click({ force: true });
cy.get('input[id="tariff-name"]').scrollIntoView().clear({ force: true }).type("Тестовый Тариф 3");
cy.get('input[id="tariff-amount"]').scrollIntoView().clear({ force: true }).type("20");
cy.get("#privilege-select").click();
cy.wait(800);
cy.get(`[data-cy="select-option-Обьём ПенаДиска для хранения шаблонов и результатов шаблонизации"]`).click();
cy.get(".btn_createTariffBackend").click({ force: true });
// Добавляем 3 тариффа
cy.wait(3000);
cy.get(".tariffs-data-grid .PrivateSwitchBase-input").first().click({ multiple: true });
// Проверить, что кнопка "Удалить" появилась
cy.wait(2000);
cy.contains("рассчитать").should("be.visible");
// Нажать на кнопку "Удалить"
cy.contains("рассчитать").click();
// смотрим что в корзине ровно столько тарифом, сколько мы добовляли
cy.wait(5000);
cy.get(".MuiTable-root tbody tr").its("length").should("eq", 3);
});
});

@ -24,6 +24,7 @@
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",
"axios": "^1.3.4", "axios": "^1.3.4",
"craco": "^0.0.3", "craco": "^0.0.3",
"cypress": "^12.17.2",
"dayjs": "^1.11.5", "dayjs": "^1.11.5",
"formik": "^2.2.9", "formik": "^2.2.9",
"immer": "^10.0.2", "immer": "^10.0.2",
@ -37,6 +38,7 @@
"react-router-dom": "^6.3.0", "react-router-dom": "^6.3.0",
"react-scripts": "^5.0.1", "react-scripts": "^5.0.1",
"reconnecting-eventsource": "^1.6.2", "reconnecting-eventsource": "^1.6.2",
"start-server-and-test": "^2.0.0",
"styled-components": "^5.3.5", "styled-components": "^5.3.5",
"typescript": "^4.8.2", "typescript": "^4.8.2",
"web-vitals": "^2.1.4", "web-vitals": "^2.1.4",
@ -47,6 +49,8 @@
"build": "craco build", "build": "craco build",
"test": "craco test", "test": "craco test",
"test:cart": "craco test src/kitUI/Cart --watchAll=false", "test:cart": "craco test src/kitUI/Cart --watchAll=false",
"test:cypress": "start-server-and-test start http://localhost:3000 cypress",
"cypress": "cypress open",
"eject": "craco eject" "eject": "craco eject"
}, },
"eslintConfig": { "eslintConfig": {

@ -1,15 +1,18 @@
import { styled } from "@mui/material/styles"; import { styled } from "@mui/material/styles";
import { TextField} from "@mui/material"; import { TextField } from "@mui/material";
export default styled(TextField)(({ theme }) => ({ export default styled(TextField)(({ theme }) => ({
color: theme.palette.grayLight.main,
"& .MuiInputLabel-root": {
color: theme.palette.grayLight.main, color: theme.palette.grayLight.main,
"& .MuiInputLabel-root": { },
color: theme.palette.grayLight.main, "& .MuiFilledInput-root": {
}, border: theme.palette.grayLight.main + " 1px solid",
"& .MuiFilledInput-root": { borderRadius: "0",
border: theme.palette.grayLight.main+" 1px solid", backgroundColor: theme.palette.hover.main,
borderRadius: "0", color: theme.palette.grayLight.main,
backgroundColor: theme.palette.hover.main, },
color: theme.palette.grayLight.main,
}
})); "& .MuiFilledInput-root.Mui-error": {
fontSize: "8px",
},
}));

@ -19,11 +19,13 @@ function validate(values: Values) {
const errors = {} as any; const errors = {} as any;
if (!values.email) { if (!values.email) {
errors.email = "Required"; errors.email = "Обязательное поле";
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(values.email)) {
errors.email = "Неверный формат эл. почты";
} }
if (!values.password) { if (!values.password) {
errors.password = "Required"; errors.password = "Введите пароль";
} }
if (values.password && !/^[\S]{8,25}$/i.test(values.password)) { if (values.password && !/^[\S]{8,25}$/i.test(values.password)) {
@ -107,11 +109,34 @@ const SigninForm = () => {
</Box> </Box>
<Box sx={{ display: "flex", alignItems: "center", marginTop: "15px", "> *": { marginRight: "10px" } }}> <Box sx={{ display: "flex", alignItems: "center", marginTop: "15px", "> *": { marginRight: "10px" } }}>
<EmailOutlinedIcon htmlColor={theme.palette.golden.main} /> <EmailOutlinedIcon htmlColor={theme.palette.golden.main} />
<Field as={OutlinedInput} name="email" variant="filled" label="Эл. почта" /> <Field
as={OutlinedInput}
name="email"
variant="filled"
label="Эл. почта"
error={props.touched.email && !!props.errors.email}
helperText={
<Typography sx={{ fontSize: "12px", width: "200px" }}>
{props.touched.email && props.errors.email}
</Typography>
}
/>
</Box> </Box>
<Box sx={{ display: "flex", alignItems: "center", marginTop: "15px", "> *": { marginRight: "10px" } }}> <Box sx={{ display: "flex", alignItems: "center", marginTop: "15px", "> *": { marginRight: "10px" } }}>
<LockOutlinedIcon htmlColor={theme.palette.golden.main} /> <LockOutlinedIcon htmlColor={theme.palette.golden.main} />
<Field as={OutlinedInput} type="password" name="password" variant="filled" label="Пароль" /> <Field
as={OutlinedInput}
type="password"
name="password"
variant="filled"
label="Пароль"
error={props.touched.password && !!props.errors.password}
helperText={
<Typography sx={{ fontSize: "12px", width: "200px" }}>
{props.touched.password && props.errors.password}
</Typography>
}
/>
</Box> </Box>
<Box <Box
component="article" component="article"

@ -1,4 +1,4 @@
import { enqueueSnackbar } from "notistack"; import { enqueueSnackbar, useSnackbar } from "notistack";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { useTheme } from "@mui/material/styles"; import { useTheme } from "@mui/material/styles";
import { Formik, Field, Form } from "formik"; import { Formik, Field, Form } from "formik";
@ -10,142 +10,188 @@ import Logo from "@pages/Logo/index";
import EmailOutlinedIcon from "@mui/icons-material/EmailOutlined"; import EmailOutlinedIcon from "@mui/icons-material/EmailOutlined";
import LockOutlinedIcon from "@mui/icons-material/LockOutlined"; import LockOutlinedIcon from "@mui/icons-material/LockOutlined";
import { authStore } from "@root/stores/auth"; import { authStore } from "@root/stores/auth";
interface Values { interface Values {
email: string; email: string;
password: string; password: string;
repeatPassword: string; repeatPassword: string;
} }
function validate(values: Values) { function validate(values: Values) {
const errors = {} as any; const errors: Partial<Values> = {};
if (!values.email) {
errors.login = "Required"; if (!values.email) {
} errors.email = "Обязательное поле";
if (!values.password) { } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(values.email)) {
errors.password = "Required"; errors.email = "Неверный формат эл. почты";
} else if (!/^[\S]{8,25}$/i.test(values.password)) { }
errors.password = "Invalid password";
} if (!values.password) {
if (values.password !== values.repeatPassword) { errors.password = "Обязательное поле";
errors.repeatPassword = "Passwords do not match"; } else if (!/^[\S]{8,25}$/i.test(values.password)) {
} errors.password = "Неверный пароль";
return errors; }
if (values.password !== values.repeatPassword) {
errors.repeatPassword = "Пароли не совпадают";
}
return errors;
} }
const SignUp = () => { const SignUp = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const theme = useTheme(); const theme = useTheme();
const { makeRequest } = authStore(); const { makeRequest } = authStore();
const { enqueueSnackbar } = useSnackbar();
return ( return (
<Formik <Formik
initialValues={{ initialValues={{
email: "", email: "",
password: "", password: "",
repeatPassword: "", repeatPassword: "",
}}
validate={validate}
onSubmit={(values, formikHelpers) => {
formikHelpers.setSubmitting(true);
makeRequest({
url: "https://admin.pena.digital/auth/register",
body: {
login: values.email,
email: values.email,
password: values.repeatPassword,
phoneNumber: "--",
},
useToken: false,
})
.then((e) => {
navigate("/users");
})
.catch((e) => {
console.log(e);
enqueueSnackbar(
e.response && e.response.data && e.response.data.message ? e.response.data.message : `Unknown error`,
{ variant: "error" } // Устанавливаем вариант уведомления на "error"
);
})
.finally(() => {
formikHelpers.setSubmitting(false);
});
}}
>
{(props) => (
<Form>
<Box
component="section"
sx={{
minHeight: "100vh",
height: "100%",
width: "100%",
backgroundColor: theme.palette.content.main,
display: "flex",
justifyContent: "center",
alignItems: "center",
padding: "15px 0",
}} }}
validate={validate} >
onSubmit={(values, formikHelpers) => { <Box
formikHelpers.setSubmitting(true); component="article"
makeRequest({ sx={{
url: "https://admin.pena.digital/auth/register", width: "350px",
body: { backgroundColor: theme.palette.content.main,
login: values.email, display: "flex",
email: values.email, flexDirection: "column",
password: values.repeatPassword, justifyContent: "center",
phoneNumber: "--", "> *": {
}, marginTop: "15px",
useToken: false, },
}) }}
.then((e) => { >
navigate("/users"); <Typography variant="h6" color={theme.palette.secondary.main}>
}) Новый аккаунт
.catch((e) => { </Typography>
console.log(e); <Logo />
enqueueSnackbar(
e.response && e.response.data && e.response.data.message ? e.response.data.message : `Unknown error`
);
}).finally(() => {
formikHelpers.setSubmitting(false);
});
}}
>
{props =>
<Form>
<Box
component="section"
sx={{
minHeight: "100vh",
height: "100%",
width: "100%",
backgroundColor: theme.palette.content.main,
display: "flex",
justifyContent: "center",
alignItems: "center",
padding: "15px 0",
}}
>
<Box
component="article"
sx={{
width: "350px",
backgroundColor: theme.palette.content.main,
display: "flex",
flexDirection: "column",
justifyContent: "center",
"> *": {
marginTop: "15px",
},
}}
>
<Typography variant="h6" color={theme.palette.secondary.main}>
Новый аккаунт
</Typography>
<Logo />
<Box> <Box>
<Typography variant="h5" color={theme.palette.secondary.main}> <Typography variant="h5" color={theme.palette.secondary.main}>
Добро пожаловать Добро пожаловать
</Typography> </Typography>
<Typography variant="h6" color={theme.palette.secondary.main}> <Typography variant="h6" color={theme.palette.secondary.main}>
Мы рады что вы выбрали нас! Мы рады что вы выбрали нас!
</Typography> </Typography>
</Box> </Box>
<Box sx={{ display: "flex", alignItems: "center", marginTop: "15px", "> *": { marginRight: "10px" } }}> <Box sx={{ display: "flex", alignItems: "center", marginTop: "15px", "> *": { marginRight: "10px" } }}>
<EmailOutlinedIcon htmlColor={theme.palette.golden.main} /> <EmailOutlinedIcon htmlColor={theme.palette.golden.main} />
<Field as={OutlinedInput} name="email" variant="filled" label="Эл. почта" /> <Field
</Box> as={OutlinedInput}
<Box sx={{ display: "flex", alignItems: "center", marginTop: "15px", "> *": { marginRight: "10px" } }}> name="email"
<LockOutlinedIcon htmlColor={theme.palette.golden.main} /> variant="filled"
<Field as={OutlinedInput} type="password" name="password" variant="filled" label="Пароль" /> label="Эл. почта"
</Box> id="email"
<Box sx={{ display: "flex", alignItems: "center", marginTop: "15px", "> *": { marginRight: "10px" } }}> error={props.touched.email && !!props.errors.email}
<LockOutlinedIcon htmlColor={theme.palette.golden.main} /> helperText={
<Field <Typography sx={{ fontSize: "12px", width: "200px" }}>
as={OutlinedInput} {props.touched.email && props.errors.email}
type="password" </Typography>
name="repeatPassword" }
variant="filled" />
label="Повторите пароль" </Box>
/> <Box sx={{ display: "flex", alignItems: "center", marginTop: "15px", "> *": { marginRight: "10px" } }}>
</Box> <LockOutlinedIcon htmlColor={theme.palette.golden.main} />
<Button <Field
type="submit" sx={{}}
disabled={props.isSubmitting} as={OutlinedInput}
sx={{ type="password"
width: "250px", name="password"
margin: "15px auto", variant="filled"
padding: "20px 30px", label="Пароль"
fontSize: 18, id="password"
}} error={props.touched.password && !!props.errors.password}
>Войти</Button> helperText={
<Link to="/signin" style={{ textDecoration: "none" }}> <Typography sx={{ fontSize: "12px", width: "200px" }}>
<Typography color={theme.palette.golden.main}>У меня уже есть аккаунт</Typography> {props.touched.password && props.errors.password}
</Link> </Typography>
</Box> }
</Box> />
</Form> </Box>
} <Box sx={{ display: "flex", alignItems: "center", marginTop: "15px", "> *": { marginRight: "10px" } }}>
</Formik> <LockOutlinedIcon htmlColor={theme.palette.golden.main} />
); <Field
as={OutlinedInput}
type="password"
name="repeatPassword"
variant="filled"
label="Повторите пароль"
id="repeatPassword"
error={props.touched.repeatPassword && !!props.errors.repeatPassword}
helperText={
<Typography sx={{ fontSize: "12px", width: "200px" }}>
{props.touched.repeatPassword && props.errors.repeatPassword}
</Typography>
}
/>
</Box>
<Button
type="submit"
disabled={props.isSubmitting}
sx={{
width: "250px",
margin: "15px auto",
padding: "20px 30px",
fontSize: 18,
}}
>
Войти
</Button>
<Link to="/signin" style={{ textDecoration: "none" }}>
<Typography color={theme.palette.golden.main}>У меня уже есть аккаунт</Typography>
</Link>
</Box>
</Box>
</Form>
)}
</Formik>
);
}; };
export default SignUp; export default SignUp;

@ -1,25 +1,12 @@
import { useState } from "react"; import { useState } from "react";
import { import { Typography, Container, Button, Select, MenuItem, FormControl, InputLabel, useTheme, Box } from "@mui/material";
Typography,
Container,
Button,
Select,
MenuItem,
FormControl,
InputLabel,
useTheme,
Box,
} from "@mui/material";
import { enqueueSnackbar } from "notistack"; import { enqueueSnackbar } from "notistack";
import { CustomTextField } from "@root/kitUI/CustomTextField"; import { CustomTextField } from "@root/kitUI/CustomTextField";
import { requestTariffs } from "@root/services/tariffs.service"; import { requestTariffs } from "@root/services/tariffs.service";
import { authStore } from "@root/stores/auth"; import { authStore } from "@root/stores/auth";
import { import { findPrivilegeById, usePrivilegeStore } from "@root/stores/privilegesStore";
findPrivilegeById,
usePrivilegeStore,
} from "@root/stores/privilegesStore";
import type { Privilege_BACKEND } from "@root/model/tariff"; import type { Privilege_BACKEND } from "@root/model/tariff";
@ -30,10 +17,7 @@ type CreateTariffBackendRequest = {
privilegies: Omit<Privilege_BACKEND, "_id" | "updatedAt">[]; privilegies: Omit<Privilege_BACKEND, "_id" | "updatedAt">[];
}; };
const baseUrl = const baseUrl = process.env.NODE_ENV === "production" ? "/strator" : "https://admin.pena.digital/strator";
process.env.NODE_ENV === "production"
? "/strator"
: "https://admin.pena.digital/strator";
export default function CreateTariff() { export default function CreateTariff() {
const { makeRequest } = authStore(); const { makeRequest } = authStore();
@ -170,7 +154,11 @@ export default function CreateTariff() {
inputProps={{ sx: { pt: "12px" } }} inputProps={{ sx: { pt: "12px" } }}
> >
{privileges.map((privilege) => ( {privileges.map((privilege) => (
<MenuItem key={privilege.description} value={privilege.id}> <MenuItem
data-cy={`select-option-${privilege.description}`}
key={privilege.description}
value={privilege.id}
>
{privilege.description} {privilege.description}
</MenuItem> </MenuItem>
))} ))}
@ -218,6 +206,8 @@ export default function CreateTariff() {
type="number" type="number"
/> />
<Button <Button
className="btn_createTariffBackend"
type="button"
onClick={() => { onClick={() => {
createTariffBackend(); createTariffBackend();
}} }}

@ -26,17 +26,9 @@ type EditTariffBackendRequest = {
privilegies: Omit<Privilege_BACKEND, "_id" | "updatedAt">[]; privilegies: Omit<Privilege_BACKEND, "_id" | "updatedAt">[];
}; };
const baseUrl = const baseUrl = process.env.NODE_ENV === "production" ? "/strator" : "https://admin.pena.digital/strator";
process.env.NODE_ENV === "production"
? "/strator"
: "https://admin.pena.digital/strator";
const editTariff = ({ const editTariff = ({ tarifIid, tariffName, tariffPrice, privilege }: EditProps): Promise<unknown> => {
tarifIid,
tariffName,
tariffPrice,
privilege,
}: EditProps): Promise<unknown> => {
const { makeRequest } = authStore.getState(); const { makeRequest } = authStore.getState();
return makeRequest<EditTariffBackendRequest>({ return makeRequest<EditTariffBackendRequest>({
@ -87,12 +79,7 @@ export default function EditModal({ tariff = undefined }: Props) {
p: 4, p: 4,
}} }}
> >
<Typography <Typography id="modal-modal-title" variant="h6" component="h2" sx={{ whiteSpace: "nowrap" }}>
id="modal-modal-title"
variant="h6"
component="h2"
sx={{ whiteSpace: "nowrap" }}
>
Редактирование тариффа Редактирование тариффа
</Typography> </Typography>
@ -106,9 +93,7 @@ export default function EditModal({ tariff = undefined }: Props) {
value={name} value={name}
sx={{ marginBottom: "10px" }} sx={{ marginBottom: "10px" }}
/> />
<Typography> <Typography>Цена за единицу: {currentTariff.pricePerUnit}</Typography>
Цена за единицу: {currentTariff.pricePerUnit}
</Typography>
<TextField <TextField
onChange={(event) => setPrice(event.target.value)} onChange={(event) => setPrice(event.target.value)}
label="Цена за единицу" label="Цена за единицу"
@ -119,9 +104,7 @@ export default function EditModal({ tariff = undefined }: Props) {
<Button <Button
onClick={() => { onClick={() => {
if (!currentTariff.isFront) { if (!currentTariff.isFront) {
const privilege = findPrivilegeById( const privilege = findPrivilegeById(currentTariff.privilegeId);
currentTariff.privilegeId
);
privilege.privilegeId = privilege.id; privilege.privilegeId = privilege.id;
console.log(privilege); console.log(privilege);
@ -136,11 +119,7 @@ export default function EditModal({ tariff = undefined }: Props) {
editTariff({ editTariff({
tarifIid: currentTariff.id, tarifIid: currentTariff.id,
tariffName: name ? name : currentTariff.name, tariffName: name ? name : currentTariff.name,
tariffPrice: price tariffPrice: price ? Number(price) : currentTariff.price ? currentTariff.price : privilege.price,
? Number(price)
: currentTariff.price
? currentTariff.price
: privilege.price,
isDeleted: currentTariff.isDeleted, isDeleted: currentTariff.isDeleted,
customPricePerUnit: currentTariff.price, customPricePerUnit: currentTariff.price,
privilege: privilege, privilege: privilege,

@ -22,10 +22,7 @@ interface Props {
handleSelectionChange: (selectionModel: GridSelectionModel) => void; handleSelectionChange: (selectionModel: GridSelectionModel) => void;
} }
export default function TariffsDG({ export default function TariffsDG({ selectedTariffs, handleSelectionChange }: Props) {
selectedTariffs,
handleSelectionChange,
}: Props) {
const tariffs = useTariffStore((state) => state.tariffs); const tariffs = useTariffStore((state) => state.tariffs);
const [openDeleteModal, setOpenDeleteModal] = useState(false); const [openDeleteModal, setOpenDeleteModal] = useState(false);
const [changingTariff, setChangingTariff] = useState<Tariff | undefined>(); const [changingTariff, setChangingTariff] = useState<Tariff | undefined>();
@ -39,15 +36,10 @@ export default function TariffsDG({
return { return {
id: tariff.id, id: tariff.id,
name: tariff.name, name: tariff.name,
serviceName: serviceName: privilege?.serviceKey == "templategen" ? "Шаблонизатор" : privilege?.serviceKey,
privilege?.serviceKey == "templategen"
? "Шаблонизатор"
: privilege?.serviceKey,
privilegeName: privilege?.name, privilegeName: privilege?.name,
amount: tariff.amount, amount: tariff.amount,
pricePerUnit: tariff.isCustom pricePerUnit: tariff.isCustom ? (tariff.customPricePerUnit || 0) / 100 : (tariff?.price || 0) / 100,
? (tariff.customPricePerUnit || 0) / 100
: (tariff?.price || 0) / 100,
type: type:
findPrivilegeById(tariff.privilegeId)?.value === "шаблон" findPrivilegeById(tariff.privilegeId)?.value === "шаблон"
? "штука" ? "штука"
@ -67,8 +59,6 @@ export default function TariffsDG({
setGridData(data); setGridData(data);
}, [tariffs]); }, [tariffs]);
console.log(selectedTariffs);
const closeDeleteModal = () => { const closeDeleteModal = () => {
setOpenDeleteModal(false); setOpenDeleteModal(false);
}; };
@ -102,6 +92,7 @@ export default function TariffsDG({
renderCell: ({ row }) => { renderCell: ({ row }) => {
return ( return (
<IconButton <IconButton
className="delete-tariff-button"
onClick={() => { onClick={() => {
setOpenDeleteModal(row.id); setOpenDeleteModal(row.id);
}} }}
@ -113,8 +104,6 @@ export default function TariffsDG({
}, },
]; ];
// console.log(gridData)
return ( return (
<> <>
<Tooltip title="обновить список тарифов"> <Tooltip title="обновить список тарифов">
@ -123,6 +112,7 @@ export default function TariffsDG({
</IconButton> </IconButton>
</Tooltip> </Tooltip>
<DataGrid <DataGrid
className="tariffs-data-grid"
disableSelectionOnClick={true} disableSelectionOnClick={true}
checkboxSelection={true} checkboxSelection={true}
rows={gridData} rows={gridData}
@ -141,10 +131,7 @@ export default function TariffsDG({
mb: "30px", mb: "30px",
}} }}
> >
<Button <Button onClick={() => setOpenDeleteModal(true)} sx={{ mr: "20px", zIndex: "10000" }}>
onClick={() => setOpenDeleteModal(true)}
sx={{ mr: "20px", zIndex: "10000" }}
>
Удаление Удаление
</Button> </Button>
</Box> </Box>

@ -2,11 +2,7 @@
"extends": "./tsconfig.extend.json", "extends": "./tsconfig.extend.json",
"compilerOptions": { "compilerOptions": {
"target": "es5", "target": "es5",
"lib": [ "lib": ["dom", "dom.iterable", "esnext"],
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"esModuleInterop": true, "esModuleInterop": true,
@ -19,9 +15,10 @@
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"noEmit": true, "noEmit": true,
"jsx": "react-jsx" "jsx": "react-jsx",
"types": ["node"],
}, },
"include": [ "include": ["src", "**/*.ts"]
"src"
]
} }

801
yarn.lock

File diff suppressed because it is too large Load Diff