diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 98bb044..8eb6934 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -24,7 +24,6 @@ deploy-to-staging: - if: "$CI_COMMIT_BRANCH == $STAGING_BRANCH" extends: .deploy_template - deploy-to-prod: tags: - front diff --git a/README.md b/README.md index a4afb11..d85c266 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -# pena_hub_admin_front \ No newline at end of file +# pena_hub_admin_front diff --git a/babel.config.js b/babel.config.js index dd242dc..267b543 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,6 +1,3 @@ module.exports = { - presets: [ - ["@babel/preset-env", { targets: { node: "current" } }], - "@babel/preset-typescript", - ], + presets: [["@babel/preset-env", { targets: { node: "current" } }], "@babel/preset-typescript"], }; diff --git a/craco.config.js b/craco.config.js index fa1bc01..c5cd998 100644 --- a/craco.config.js +++ b/craco.config.js @@ -1,17 +1,17 @@ const CracoAlias = require("craco-alias"); module.exports = { - plugins: [ - { - plugin: CracoAlias, - options: { - source: "tsconfig", - // baseUrl SHOULD be specified - // plugin does not take it from tsconfig - baseUrl: "./src", - // tsConfigPath should point to the file where "baseUrl" and "paths" are specified - tsConfigPath: "./tsconfig.extend.json" - } - } - ] -}; \ No newline at end of file + plugins: [ + { + plugin: CracoAlias, + options: { + source: "tsconfig", + // baseUrl SHOULD be specified + // plugin does not take it from tsconfig + baseUrl: "./src", + // tsConfigPath should point to the file where "baseUrl" and "paths" are specified + tsConfigPath: "./tsconfig.extend.json", + }, + }, + ], +}; diff --git a/cypress.config.ts b/cypress.config.ts index a43fe1b..8585a4d 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -1,11 +1,11 @@ import { defineConfig } from "cypress"; export default defineConfig({ - e2e: { - viewportWidth: 1200, - viewportHeight: 800, - fixturesFolder: "tests/e2e/fixtures", - supportFile: false, - defaultCommandTimeout: 100, - }, + e2e: { + viewportWidth: 1200, + viewportHeight: 800, + fixturesFolder: "tests/e2e/fixtures", + supportFile: false, + defaultCommandTimeout: 100, + }, }); diff --git a/cypress/e2e/access.cy.ts b/cypress/e2e/access.cy.ts index 9ad87fa..3e2ba88 100644 --- a/cypress/e2e/access.cy.ts +++ b/cypress/e2e/access.cy.ts @@ -1,110 +1,110 @@ describe("Форма Входа", () => { - beforeEach(() => { - cy.visit("http://localhost:3000"); - }); + beforeEach(() => { + cy.visit("http://localhost:3000"); + }); - it("должна успешно входить с правильными учетными данными", () => { - const email = "valid_user@example.com"; - const password = "valid_password"; + 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.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"); - }); + cy.url().should("include", "http://localhost:3000/users"); + }); - it("должна отображать сообщение об ошибке при неверном формате электронной почты", () => { - const invalidEmail = "invalid_email"; + 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.get('input[name="email"]').type(invalidEmail); + cy.get('input[name="password"]').type("valid_password"); + cy.get('button[type="submit"]').click(); - cy.contains("Неверный формат эл. почты"); - }); + cy.contains("Неверный формат эл. почты"); + }); - it("должна отображать сообщение об ошибке при отсутствии пароля", () => { - cy.get('input[name="email"]').type("valid_email@example.com"); - cy.get('button[type="submit"]').click(); + it("должна отображать сообщение об ошибке при отсутствии пароля", () => { + cy.get('input[name="email"]').type("valid_email@example.com"); + cy.get('button[type="submit"]').click(); - cy.contains("Введите пароль"); - }); + cy.contains("Введите пароль"); + }); - it("должна отображать сообщение об ошибке для недопустимого пароля", () => { - const invalidPassword = "short"; + 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.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"); - }); + cy.contains("Invalid password"); + }); }); describe("Форма регистрации", () => { - beforeEach(() => { - cy.visit("http://localhost:3000/signup"); - }); + beforeEach(() => { + cy.visit("http://localhost:3000/signup"); + }); - it("должна регистрировать нового пользователя с правильными данными", () => { - const email = Cypress._.random(1000) + "@example.com"; - const password = "valid_password"; + 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('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.get('button[type="submit"]').click(); + cy.wait(5000); - cy.url().should("include", "http://localhost:3000/users"); - }); + cy.url().should("include", "http://localhost:3000/users"); + }); - it("должна отображать ошибку при неверном формате электронной почты", () => { - const invalidEmail = "invalid_email"; + 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('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.get('button[type="submit"]').click(); - cy.contains("Неверный формат эл. почты"); - }); + cy.contains("Неверный формат эл. почты"); + }); - it("должна отображать ошибку при отсутствии пароля", () => { - cy.get('input[name="email"]').type("valid_email@example.com"); - cy.get('input[name="repeatPassword"]').type("valid_password"); + 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.get('button[type="submit"]').click(); - cy.contains("Обязательное поле").should("have.length", 1); - }); + 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"); + 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.get('button[type="submit"]').click(); - cy.contains("Пароли не совпадают"); - }); + cy.contains("Пароли не совпадают"); + }); - it("попытка отправки запроса при уже зарегистрированном пользователе", () => { - const email = "users@gmail.com"; - const password = "12344321"; + 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.get('input[name="email"]').type(email); + cy.get('input[name="password"]').type(password); + cy.get('input[name="repeatPassword"]').type(password); - cy.intercept("POST", process.env.REACT_APP_DOMAIN + "/auth/register").as("registerRequest"); - cy.get('button[type="submit"]').click(); - cy.wait("@registerRequest"); + cy.intercept("POST", process.env.REACT_APP_DOMAIN + "/auth/register").as("registerRequest"); + cy.get('button[type="submit"]').click(); + cy.wait("@registerRequest"); - cy.wait(5000); - cy.contains("user with this login is exist"); - }); + cy.wait(5000); + cy.contains("user with this login is exist"); + }); }); diff --git a/cypress/e2e/tariffs.cy.ts b/cypress/e2e/tariffs.cy.ts index a9c7c5a..570728c 100644 --- a/cypress/e2e/tariffs.cy.ts +++ b/cypress/e2e/tariffs.cy.ts @@ -1,246 +1,246 @@ 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"); - }); + 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"); + it("должна отображать сообщение об ошибке при пустом названии тарифа", () => { + cy.get('input[id="tariff-amount"]').type("10"); - // Выбрать первую привилегию с нужным текстом из выпадающего списка - cy.get("#privilege-select").click(); + // Выбрать первую привилегию с нужным текстом из выпадающего списка + cy.get("#privilege-select").click(); - cy.get(`[data-cy = "select-option-Количество шаблонов, которые может сделать пользователь сервиса"]`).click(); + cy.get(`[data-cy = "select-option-Количество шаблонов, которые может сделать пользователь сервиса"]`).click(); - cy.get(".btn_createTariffBackend").click({ force: true }); + cy.get(".btn_createTariffBackend").click({ force: true }); - cy.contains("Пустое название тарифа"); - }); + cy.contains("Пустое название тарифа"); + }); - it("должна отображать сообщение об ошибке при отсутствии выбора привилегии", () => { - cy.get('input[id="tariff-name"]').type("Тестовый Тариф"); - cy.get('input[id="tariff-amount"]').type("10"); + it("должна отображать сообщение об ошибке при отсутствии выбора привилегии", () => { + cy.get('input[id="tariff-name"]').type("Тестовый Тариф"); + cy.get('input[id="tariff-amount"]').type("10"); - cy.get(".btn_createTariffBackend").click({ force: true }); + cy.get(".btn_createTariffBackend").click({ force: true }); - cy.contains("Не выбрана привилегия"); - }); + 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 }); + 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("Тестовый Тариф 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 }); + 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 }); - cy.wait(5000); - }); + cy.wait(5000); + }); }); 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"); - }); + 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"]; + it("Удаление тарифа единично, одного за другим ", () => { + const tariffNamesToFind = ["Тестовый Тариф 1", "Тестовый Тариф 2", "Тестовый Тариф 3"]; - // Поиск каждого тарифа в DataGrid - cy.wait(3000); - tariffNamesToFind.forEach((tariffName) => { - cy.get(".tariffs-data-grid").scrollIntoView().contains(tariffName).should("be.visible"); - }); + // Поиск каждого тарифа в DataGrid + cy.wait(3000); + tariffNamesToFind.forEach((tariffName) => { + cy.get(".tariffs-data-grid").scrollIntoView().contains(tariffName).should("be.visible"); + }); - const deleteTariffs = () => { - let tariffsFound = true; + const deleteTariffs = () => { + let tariffsFound = true; - cy.get(".tariffs-data-grid .MuiDataGrid-row").then(($rows) => { - const rowCount = $rows.length; + cy.get(".tariffs-data-grid .MuiDataGrid-row").then(($rows) => { + const rowCount = $rows.length; - if (rowCount === 1) { - cy.log("Тарифы не найдены. Тест завершен."); - tariffsFound = false; - } + 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.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(2000); + cy.contains("Да") + .click() + .then(() => { + if (!tariffsFound) { + return; + } - cy.wait(5000); - deleteTariffs(); - }); - }); - }; + cy.wait(5000); + deleteTariffs(); + }); + }); + }; - deleteTariffs(); + deleteTariffs(); - // Проверяем что Дата грид тарифов пустой - cy.wait(2000); - cy.get(".tariffs-data-grid .MuiDataGrid-row").should("not.exist"); - }); + // Проверяем что Дата грид тарифов пустой + cy.wait(2000); + cy.get(".tariffs-data-grid .MuiDataGrid-row").should("not.exist"); + }); - 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 }); + 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("Тестовый Тариф 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.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(3000); + cy.get(".tariffs-data-grid .PrivateSwitchBase-input").first().click({ multiple: true }); - // Проверить, что кнопка "Удалить" появилась - cy.wait(2000); - cy.contains("Удаление").should("be.visible"); + // Проверить, что кнопка "Удалить" появилась + cy.wait(2000); + cy.contains("Удаление").should("be.visible"); - // Нажать на кнопку "Удалить" - cy.contains("Удаление").click(); + // Нажать на кнопку "Удалить" + cy.contains("Удаление").click(); - // Подтверждение удаления (если нужно) - cy.contains("Да").click(); + // Подтверждение удаления (если нужно) + cy.contains("Да").click(); - // Проверяем что Дата грид тарифов пустой - cy.wait(2000); - cy.get(".tariffs-data-grid .MuiDataGrid-row").should("not.exist"); - }); + // Проверяем что Дата грид тарифов пустой + cy.wait(2000); + cy.get(".tariffs-data-grid .MuiDataGrid-row").should("not.exist"); + }); - it("Добавление тарифом в корзину", () => { - // Добавляем 3 тариффа + 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"]').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("Тестовый Тариф 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.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(3000); + cy.get(".tariffs-data-grid .PrivateSwitchBase-input").first().click({ multiple: true }); - // Проверить, что кнопка "Удалить" появилась - cy.wait(2000); - cy.contains("рассчитать").should("be.visible"); + // Проверить, что кнопка "Удалить" появилась + cy.wait(2000); + cy.contains("рассчитать").should("be.visible"); - // Нажать на кнопку "Удалить" - cy.contains("рассчитать").click(); + // Нажать на кнопку "Удалить" + cy.contains("рассчитать").click(); - // смотрим что в корзине ровно столько тарифом, сколько мы добовляли - cy.wait(5000); - cy.get(".MuiTable-root tbody tr").its("length").should("eq", 3); - }); + // смотрим что в корзине ровно столько тарифом, сколько мы добовляли + cy.wait(5000); + cy.get(".MuiTable-root tbody tr").its("length").should("eq", 3); + }); }); 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"); - }); + 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-name"]').type("Тестовый Тариф 1"); + it("Смотрим чтобы при указание кастомной цены тарифа поля суммы и Цены за ед отображались верно", () => { + cy.get('input[id="tariff-name"]').type("Тестовый Тариф 1"); - cy.get('input[id="tariff-amount"]').type("60"); - cy.get('input[id="tariff-custom-price"]').type("5"); + cy.get('input[id="tariff-amount"]').type("60"); + cy.get('input[id="tariff-custom-price"]').type("5"); - cy.get("#privilege-select").click(); + cy.get("#privilege-select").click(); - cy.get(`[data-cy="select-option-Количество шаблонов, которые может сделать пользователь сервиса"]`).click(); - cy.get(".btn_createTariffBackend").click({ force: true }); + cy.get(`[data-cy="select-option-Количество шаблонов, которые может сделать пользователь сервиса"]`).click(); + cy.get(".btn_createTariffBackend").click({ force: true }); - cy.wait(3000); + cy.wait(3000); - // Сумму - cy.get(".tariffs-data-grid").get(`[data-field="total"]`).contains("300"); + // Сумму + cy.get(".tariffs-data-grid").get(`[data-field="total"]`).contains("300"); - // Проверяем цену за ед - cy.get(".tariffs-data-grid").get(`[data-field="pricePerUnit"]`).contains("5"); - }); + // Проверяем цену за ед + cy.get(".tariffs-data-grid").get(`[data-field="pricePerUnit"]`).contains("5"); + }); - it("Проверка установки цены тарифа по умолчанию при отсутствии кастомной цены", () => { - cy.get('input[id="tariff-name"]').type("Тестовый Тариф 1"); + it("Проверка установки цены тарифа по умолчанию при отсутствии кастомной цены", () => { + cy.get('input[id="tariff-name"]').type("Тестовый Тариф 1"); - cy.get('input[id="tariff-amount"]').type("80"); + cy.get('input[id="tariff-amount"]').type("80"); - cy.get("#privilege-select").click(); + cy.get("#privilege-select").click(); - cy.get(`[data-cy="select-option-Количество шаблонов, которые может сделать пользователь сервиса"]`).click(); - cy.get(".btn_createTariffBackend").click({ force: true }); + cy.get(`[data-cy="select-option-Количество шаблонов, которые может сделать пользователь сервиса"]`).click(); + cy.get(".btn_createTariffBackend").click({ force: true }); - cy.wait(3000); + cy.wait(3000); - // Сумму - cy.get(".tariffs-data-grid").get(`[data-field="total"]`).contains("0.8"); + // Сумму + cy.get(".tariffs-data-grid").get(`[data-field="total"]`).contains("0.8"); - // Проверяем цену за ед - cy.get(".tariffs-data-grid").get(`[data-field="pricePerUnit"]`).contains("0.01"); - }); + // Проверяем цену за ед + cy.get(".tariffs-data-grid").get(`[data-field="pricePerUnit"]`).contains("0.01"); + }); }); diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..643d473 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,9 @@ +import eslint from "@eslint/js"; +import tseslint from "typescript-eslint"; + +export default tseslint.config(eslint.configs.recommended, ...tseslint.configs.recommended, { + rules: { + semi: "error", + "prefer-const": "error", + }, +}); diff --git a/jest.config.js b/jest.config.js index 3abcbd9..34a9dd8 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,5 +1,5 @@ /** @type {import('ts-jest').JestConfigWithTsJest} */ module.exports = { - preset: "ts-jest", - testEnvironment: "node", + preset: "ts-jest", + testEnvironment: "node", }; diff --git a/package.json b/package.json index 9ebd058..effac26 100644 --- a/package.json +++ b/package.json @@ -1,84 +1,83 @@ { - "name": "adminka", - "version": "0.1.0", - "private": true, - "dependencies": { - "@date-io/dayjs": "^2.15.0", - "@emotion/react": "^11.10.4", - "@emotion/styled": "^11.10.4", - "@frontend/kitui": "^1.0.82", - "@material-ui/pickers": "^3.3.10", - "@mui/icons-material": "^5.10.3", - "@mui/material": "^5.10.5", - "@mui/styled-engine-sc": "^5.10.3", - "@mui/x-data-grid": "^5.17.4", - "@mui/x-data-grid-generator": "^5.17.5", - "@mui/x-data-grid-premium": "^5.17.5", - "@mui/x-date-pickers": "^5.0.3", - "@testing-library/jest-dom": "^5.16.5", - "@testing-library/react": "^13.3.0", - "@testing-library/user-event": "^13.5.0", - "@types/jest": "^27.5.2", - "@types/node": "^16.11.56", - "@types/react": "^18.0.18", - "@types/react-dom": "^18.0.6", - "@types/react-router-dom": "^5.3.3", - "axios": "^1.4.0", - "craco": "^0.0.3", - "cypress": "^12.17.2", - "date-fns": "^3.3.1", - "dayjs": "^1.11.5", - "formik": "^2.2.9", - "immer": "^10.0.2", - "moment": "^2.29.4", - "nanoid": "^4.0.1", - "notistack": "^3.0.1", - "numeral": "^2.0.6", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-error-boundary": "^4.0.13", - "react-numeral": "^1.1.1", - "react-router-dom": "^6.3.0", - "react-scripts": "^5.0.1", - "reconnecting-eventsource": "^1.6.2", - "start-server-and-test": "^2.0.0", - "styled-components": "^5.3.5", - "swr": "^2.2.5", - "typescript": "^4.8.2", - "use-debounce": "^9.0.4", - "web-vitals": "^2.1.4", - "zustand": "^4.3.8" - }, - "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)/\"", - "test:cypress": "start-server-and-test start http://localhost:3000 cypress", - "cypress": "cypress open", - "eject": "craco eject", - "format": "prettier . --write" - }, - "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" - ] - }, - "devDependencies": { - "craco-alias": "^3.0.1", - "prettier": "^3.2.5" - } + "name": "adminka", + "version": "0.1.0", + "private": true, + "type": "module", + "dependencies": { + "@date-io/dayjs": "^2.15.0", + "@emotion/react": "^11.10.4", + "@emotion/styled": "^11.10.4", + "@frontend/kitui": "^1.0.82", + "@material-ui/pickers": "^3.3.10", + "@mui/icons-material": "^5.10.3", + "@mui/material": "^5.10.5", + "@mui/styled-engine-sc": "^5.10.3", + "@mui/x-data-grid": "^5.17.4", + "@mui/x-data-grid-generator": "^5.17.5", + "@mui/x-data-grid-premium": "^5.17.5", + "@mui/x-date-pickers": "^5.0.3", + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.3.0", + "@testing-library/user-event": "^13.5.0", + "@types/jest": "^27.5.2", + "@types/node": "^16.11.56", + "@types/react": "^18.0.18", + "@types/react-dom": "^18.0.6", + "@types/react-router-dom": "^5.3.3", + "axios": "^1.4.0", + "craco": "^0.0.3", + "cypress": "^12.17.2", + "date-fns": "^3.3.1", + "dayjs": "^1.11.5", + "formik": "^2.2.9", + "immer": "^10.0.2", + "moment": "^2.29.4", + "nanoid": "^4.0.1", + "notistack": "^3.0.1", + "numeral": "^2.0.6", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-error-boundary": "^4.0.13", + "react-numeral": "^1.1.1", + "react-router-dom": "^6.3.0", + "react-scripts": "^5.0.1", + "reconnecting-eventsource": "^1.6.2", + "start-server-and-test": "^2.0.0", + "styled-components": "^5.3.5", + "swr": "^2.2.5", + "typescript": "^4.8.2", + "use-debounce": "^9.0.4", + "web-vitals": "^2.1.4", + "zustand": "^4.3.8" + }, + "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)/\"", + "test:cypress": "start-server-and-test start http://localhost:3000 cypress", + "cypress": "cypress open", + "eject": "craco eject", + "format": "prettier . --write", + "lint": "eslint ./src --fix" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "devDependencies": { + "@eslint/js": "^9.3.0", + "craco-alias": "^3.0.1", + "eslint": "^9.3.0", + "prettier": "^3.2.5", + "typescript-eslint": "^7.10.0" + } } diff --git a/public/fonts.css b/public/fonts.css index e8a043f..83b54d0 100644 --- a/public/fonts.css +++ b/public/fonts.css @@ -1,4 +1,6 @@ @font-face { - font-family: "GilroyRegular"; - src: local("GilroyRegular"), url(fonts/GilroyRegular.woff) format("woff"); -} \ No newline at end of file + font-family: "GilroyRegular"; + src: + local("GilroyRegular"), + url(fonts/GilroyRegular.woff) format("woff"); +} diff --git a/public/index.html b/public/index.html index 888d060..a08896c 100644 --- a/public/index.html +++ b/public/index.html @@ -1,22 +1,19 @@ - + - - - - - - - - - - - - React App - - - -
- - + diff --git a/public/manifest.json b/public/manifest.json index 1f2f141..952449d 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,15 +1,15 @@ { - "short_name": "React App", - "name": "Create React App Sample", - "icons": [ - { - "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/x-icon" - } - ], - "start_url": ".", - "display": "standalone", - "theme_color": "#000000", - "background_color": "#ffffff" + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" } diff --git a/src/__tests__/menu-link.test.js b/src/__tests__/menu-link.test.js index ce20363..659678f 100644 --- a/src/__tests__/menu-link.test.js +++ b/src/__tests__/menu-link.test.js @@ -1,44 +1,35 @@ - const puppeteer = require('puppeteer'); - const url = "http://localhost:3000/users"; - const urlMass = ['/users','/tariffs','/discounts','/promocode','/support', '/entities']; +const puppeteer = require("puppeteer"); +const url = "http://localhost:3000/users"; +const urlMass = ["/users", "/tariffs", "/discounts", "/promocode", "/support", "/entities"]; - jest.setTimeout(1000 * 60 * 5); - - let browser; - let page; - - describe('Тест', (() => { - beforeAll(async()=>{ - browser = puppeteer.launch({headless:true}); - page = browser.newPage(); +jest.setTimeout(1000 * 60 * 5); - page.goto(url); - // Set screen size - page.setViewport({width: 1080, height: 1024}); +let browser; +let page; - }) - afterAll(() => browser.quit()); - test('Тест меню',async () => { - -// Ждем загрузки менюшек - page.waitForSelector('.menu') - - // Берем все ссылки с кнопок, у которых есть класс menu и вставляем в массив - let menuLink = page.evaluate(()=>{ - let menuArray = document.querySelectorAll('.menu') - let Urls = Object.values(menuArray).map( - menuItem => ( - menuItem.href.slice(menuItem.href.lastIndexOf('/')) - ) - ) - return Urls - }) -// Проверяем, какие ссылки есть в нашем массиве, а каких нет - for (let i = 0; i < menuLink.length; i++) { - expect(urlMass.find((elem)=>elem===menuLink[i])).toBe(true) - } - - }) -})) +describe("Тест", () => { + beforeAll(async () => { + browser = puppeteer.launch({ headless: true }); + page = browser.newPage(); + page.goto(url); + // Set screen size + page.setViewport({ width: 1080, height: 1024 }); + }); + afterAll(() => browser.quit()); + test("Тест меню", async () => { + // Ждем загрузки менюшек + page.waitForSelector(".menu"); + // Берем все ссылки с кнопок, у которых есть класс menu и вставляем в массив + const menuLink = page.evaluate(() => { + const menuArray = document.querySelectorAll(".menu"); + const Urls = Object.values(menuArray).map((menuItem) => menuItem.href.slice(menuItem.href.lastIndexOf("/"))); + return Urls; + }); + // Проверяем, какие ссылки есть в нашем массиве, а каких нет + for (let i = 0; i < menuLink.length; i++) { + expect(urlMass.find((elem) => elem === menuLink[i])).toBe(true); + } + }); +}); diff --git a/src/__tests__/test.test.js b/src/__tests__/test.test.js index b1a906b..4b71a25 100644 --- a/src/__tests__/test.test.js +++ b/src/__tests__/test.test.js @@ -1,12 +1,12 @@ -const puppeteer = require('puppeteer'); +const puppeteer = require("puppeteer"); (async () => { - const browser = await puppeteer.launch(); - const page = await browser.newPage(); - await page.goto('https://news.ycombinator.com', { - waitUntil: 'networkidle2', - }); - await page.pdf({ path: 'hn.pdf', format: 'a4' }); + const browser = await puppeteer.launch(); + const page = await browser.newPage(); + await page.goto("https://news.ycombinator.com", { + waitUntil: "networkidle2", + }); + await page.pdf({ path: "hn.pdf", format: "a4" }); - await browser.close(); -})(); \ No newline at end of file + await browser.close(); +})(); diff --git a/src/__tests__/tickets.test.ts b/src/__tests__/tickets.test.ts index 78847cb..d9cfa08 100644 --- a/src/__tests__/tickets.test.ts +++ b/src/__tests__/tickets.test.ts @@ -2,56 +2,56 @@ import axios from "axios"; const message = "Artem"; describe("tests", () => { - let statusGetTickets: number; - let dataGetTickets: {}; - let statusGetMessages: number; - let dataGetMessages: []; + let statusGetTickets: number; + let dataGetTickets: {}; + let statusGetMessages: number; + let dataGetMessages: []; - beforeEach(async () => { - await axios({ - method: "post", - url: process.env.REACT_APP_DOMAIN + "/heruvym/getTickets", - data: { - amt: 20, - page: 0, - status: "open", - }, - }).then((result) => { - dataGetTickets = result.data; - statusGetTickets = result.status; - }); + beforeEach(async () => { + await axios({ + method: "post", + url: process.env.REACT_APP_DOMAIN + "/heruvym/getTickets", + data: { + amt: 20, + page: 0, + status: "open", + }, + }).then((result) => { + dataGetTickets = result.data; + statusGetTickets = result.status; + }); - await axios({ - method: "post", - url: process.env.REACT_APP_DOMAIN + "/heruvym/getMessages", - data: { - amt: 100, - page: 0, - srch: "", - ticket: "cgg25qsvc9gd0bq9ne7g", - }, - }).then((result) => { - dataGetMessages = result.data; - statusGetMessages = result.status; - }); - }); + await axios({ + method: "post", + url: process.env.REACT_APP_DOMAIN + "/heruvym/getMessages", + data: { + amt: 100, + page: 0, + srch: "", + ticket: "cgg25qsvc9gd0bq9ne7g", + }, + }).then((result) => { + dataGetMessages = result.data; + statusGetMessages = result.status; + }); + }); - // добавляем сообщения тикету с id cgg25qsvc9gd0bq9ne7g , вписываем текст в переменную message и проверяем тест - test("test sending messages to tickets", () => { - expect(statusGetTickets).toEqual(200); - // проверяем кличество тикетов отсалось неизменным - expect(dataGetTickets).toMatchObject({ count: 12 }); + // добавляем сообщения тикету с id cgg25qsvc9gd0bq9ne7g , вписываем текст в переменную message и проверяем тест + test("test sending messages to tickets", () => { + expect(statusGetTickets).toEqual(200); + // проверяем кличество тикетов отсалось неизменным + expect(dataGetTickets).toMatchObject({ count: 12 }); - expect(statusGetMessages).toBe(200); + expect(statusGetMessages).toBe(200); - expect(dataGetMessages[dataGetMessages.length - 1]).toMatchObject({ - files: [], - message: message, - request_screenshot: "", - session_id: "6421ccdad01874dcffa8b128", - shown: {}, - ticket_id: "cgg25qsvc9gd0bq9ne7g", - user_id: "6421ccdad01874dcffa8b128", - }); - }); + expect(dataGetMessages[dataGetMessages.length - 1]).toMatchObject({ + files: [], + message: message, + request_screenshot: "", + session_id: "6421ccdad01874dcffa8b128", + shown: {}, + ticket_id: "cgg25qsvc9gd0bq9ne7g", + user_id: "6421ccdad01874dcffa8b128", + }); + }); }); diff --git a/src/api/account.ts b/src/api/account.ts index e52526d..e9f4ba3 100644 --- a/src/api/account.ts +++ b/src/api/account.ts @@ -3,49 +3,47 @@ import makeRequest from "@root/api/makeRequest"; import { parseAxiosError } from "@root/utils/parse-error"; type Name = { - firstname: string; - secondname: string; - middlename: string; - orgname: string; + firstname: string; + secondname: string; + middlename: string; + orgname: string; }; type Wallet = { - currency: string; - cash: number; - purchasesAmount: number; - spent: number; - money: number; + currency: string; + cash: number; + purchasesAmount: number; + spent: number; + money: number; }; export type Account = { - _id: string; - userId: string; - cart: string[]; - status: string; - isDeleted: boolean; - createdAt: string; - updatedAt: string; - deletedAt: string; - name: Name; - wallet: Wallet; + _id: string; + userId: string; + cart: string[]; + status: string; + isDeleted: boolean; + createdAt: string; + updatedAt: string; + deletedAt: string; + name: Name; + wallet: Wallet; }; -const baseUrl = process.env.REACT_APP_DOMAIN + "/customer" +const baseUrl = process.env.REACT_APP_DOMAIN + "/customer"; -export const getAccountInfo = async ( - id: string -): Promise<[Account | null, string?]> => { - try { - const accountInfoResponse = await makeRequest({ - url: `${baseUrl}/account/${id}`, - method: "GET", - useToken: true, - }); +export const getAccountInfo = async (id: string): Promise<[Account | null, string?]> => { + try { + const accountInfoResponse = await makeRequest({ + url: `${baseUrl}/account/${id}`, + method: "GET", + useToken: true, + }); - return [accountInfoResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [accountInfoResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Не удалось получить информацию об аккаунте. ${error}`]; - } + return [null, `Не удалось получить информацию об аккаунте. ${error}`]; + } }; diff --git a/src/api/auth.ts b/src/api/auth.ts index 77d5248..7612c99 100644 --- a/src/api/auth.ts +++ b/src/api/auth.ts @@ -2,68 +2,58 @@ import makeRequest from "@root/api/makeRequest"; import { parseAxiosError } from "@root/utils/parse-error"; -import type { - LoginRequest, - RegisterRequest, - RegisterResponse, -} from "@frontend/kitui"; +import type { LoginRequest, RegisterRequest, RegisterResponse } from "@frontend/kitui"; -const baseUrl = process.env.REACT_APP_DOMAIN + "/auth" +const baseUrl = process.env.REACT_APP_DOMAIN + "/auth"; -export const signin = async ( - login: string, - password: string -): Promise<[RegisterResponse | null, string?]> => { - try { - const signinResponse = await makeRequest({ - url: baseUrl + "/login", - body: { login, password }, - useToken: false, - }); +export const signin = async (login: string, password: string): Promise<[RegisterResponse | null, string?]> => { + try { + const signinResponse = await makeRequest({ + url: baseUrl + "/login", + body: { login, password }, + useToken: false, + }); - return [signinResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); - console.error(error) - return [null, `Ошибка авторизации. ${error}`]; - } + return [signinResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); + console.error(error); + return [null, `Ошибка авторизации. ${error}`]; + } }; export const register = async ( - login: string, - password: string, - phoneNumber: string = "--" + login: string, + password: string, + phoneNumber: string = "--" ): Promise<[RegisterResponse | null, string?]> => { - try { - const registerResponse = await makeRequest< - RegisterRequest, - RegisterResponse - >({ - url: baseUrl + "/register", - body: { login, password, phoneNumber }, - useToken: false, - }); + try { + const registerResponse = await makeRequest({ + url: baseUrl + "/register", + body: { login, password, phoneNumber }, + useToken: false, + }); - return [registerResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [registerResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка регистрации. ${error}`]; - } + return [null, `Ошибка регистрации. ${error}`]; + } }; export const logout = async (): Promise<[unknown, string?]> => { - try { - const logoutResponse = await makeRequest({ - url: baseUrl + "/logout", - method: "post", - contentType: true, - }); + try { + const logoutResponse = await makeRequest({ + url: baseUrl + "/logout", + method: "post", + contentType: true, + }); - return [logoutResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [logoutResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка выхода из аккаунта. ${error}`]; - } + return [null, `Ошибка выхода из аккаунта. ${error}`]; + } }; diff --git a/src/api/discounts.ts b/src/api/discounts.ts index 7c4077f..2f4e00d 100644 --- a/src/api/discounts.ts +++ b/src/api/discounts.ts @@ -3,243 +3,224 @@ import makeRequest from "@root/api/makeRequest"; import { parseAxiosError } from "@root/utils/parse-error"; import type { Discount } from "@frontend/kitui"; -import type { - CreateDiscountBody, - DiscountType, - GetDiscountResponse, -} from "@root/model/discount"; +import type { CreateDiscountBody, DiscountType, GetDiscountResponse } from "@root/model/discount"; import useSWR from "swr"; import { enqueueSnackbar } from "notistack"; -const baseUrl = process.env.REACT_APP_DOMAIN + "/price" +const baseUrl = process.env.REACT_APP_DOMAIN + "/price"; interface CreateDiscountParams { - purchasesAmount: number; - cartPurchasesAmount: number; - discountMinValue: number; - discountFactor: number; - discountDescription: string; - discountName: string; - /** ISO string */ - startDate: string; - /** ISO string */ - endDate: string; - serviceType: string; - discountType: DiscountType; - privilegeId: string; + purchasesAmount: number; + cartPurchasesAmount: number; + discountMinValue: number; + discountFactor: number; + discountDescription: string; + discountName: string; + /** ISO string */ + startDate: string; + /** ISO string */ + endDate: string; + serviceType: string; + discountType: DiscountType; + privilegeId: string; } export function createDiscountObject({ - endDate, - startDate, - discountName, - cartPurchasesAmount, - discountDescription, - discountFactor, - discountMinValue, - purchasesAmount, - serviceType, - discountType, - privilegeId, + endDate, + startDate, + discountName, + cartPurchasesAmount, + discountDescription, + discountFactor, + discountMinValue, + purchasesAmount, + serviceType, + discountType, + privilegeId, }: CreateDiscountParams) { - const discount: CreateDiscountBody = { - Name: discountName, - Layer: 1, - Description: discountDescription, - Condition: { - Period: { - From: startDate, - To: endDate, - }, - User: "", - UserType: "", - Coupon: "", - Usage: 0, - PurchasesAmount: 0, - CartPurchasesAmount: 0, - Product: "", - Term: 0, - PriceFrom: 0, - Group: "", - }, - Target: { - Factor: discountFactor, - TargetScope: "Sum", - Overhelm: false, - TargetGroup: "", - Products: [ - { - ID: "", - Factor: 0, - Overhelm: false, - }, - ], - }, - }; + const discount: CreateDiscountBody = { + Name: discountName, + Layer: 1, + Description: discountDescription, + Condition: { + Period: { + From: startDate, + To: endDate, + }, + User: "", + UserType: "", + Coupon: "", + Usage: 0, + PurchasesAmount: 0, + CartPurchasesAmount: 0, + Product: "", + Term: 0, + PriceFrom: 0, + Group: "", + }, + Target: { + Factor: discountFactor, + TargetScope: "Sum", + Overhelm: false, + TargetGroup: "", + Products: [ + { + ID: "", + Factor: 0, + Overhelm: false, + }, + ], + }, + }; - switch (discountType) { - case "privilege": - discount.Layer = 1; - discount.Condition.Product = privilegeId; - discount.Condition.Term = discountMinValue / 100; - discount.Target.Products = [ - { - Factor: discountFactor, - ID: privilegeId, - Overhelm: false, - }, - ]; - break; - case "service": - discount.Layer = 2; - discount.Condition.PriceFrom = discountMinValue; - discount.Condition.Group = serviceType; - discount.Target.TargetGroup = serviceType; - break; - case "cartPurchasesAmount": - discount.Layer = 3; - discount.Condition.CartPurchasesAmount = cartPurchasesAmount; - break; - case "purchasesAmount": - discount.Layer = 4; - discount.Condition.PurchasesAmount = purchasesAmount; - break; - } + switch (discountType) { + case "privilege": + discount.Layer = 1; + discount.Condition.Product = privilegeId; + discount.Condition.Term = discountMinValue / 100; + discount.Target.Products = [ + { + Factor: discountFactor, + ID: privilegeId, + Overhelm: false, + }, + ]; + break; + case "service": + discount.Layer = 2; + discount.Condition.PriceFrom = discountMinValue; + discount.Condition.Group = serviceType; + discount.Target.TargetGroup = serviceType; + break; + case "cartPurchasesAmount": + discount.Layer = 3; + discount.Condition.CartPurchasesAmount = cartPurchasesAmount; + break; + case "purchasesAmount": + discount.Layer = 4; + discount.Condition.PurchasesAmount = purchasesAmount; + break; + } - return discount; + return discount; } -export const changeDiscount = async ( - discountId: string, - discount: Discount -): Promise<[unknown, string?]> => { - try { - const changeDiscountResponse = await makeRequest({ - url: baseUrl + "/discount/" + discountId, - method: "patch", - useToken: true, - body: discount, - }); +export const changeDiscount = async (discountId: string, discount: Discount): Promise<[unknown, string?]> => { + try { + const changeDiscountResponse = await makeRequest({ + url: baseUrl + "/discount/" + discountId, + method: "patch", + useToken: true, + body: discount, + }); - return [changeDiscountResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [changeDiscountResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка изменения скидки. ${error}`]; - } + return [null, `Ошибка изменения скидки. ${error}`]; + } }; -export const createDiscount = async ( - discountParams: CreateDiscountParams -): Promise<[Discount | null, string?]> => { - const discount = createDiscountObject(discountParams); +export const createDiscount = async (discountParams: CreateDiscountParams): Promise<[Discount | null, string?]> => { + const discount = createDiscountObject(discountParams); - try { - const createdDiscountResponse = await makeRequest< - CreateDiscountBody, - Discount - >({ - url: baseUrl + "/discount", - method: "post", - useToken: true, - body: discount, - }); + try { + const createdDiscountResponse = await makeRequest({ + url: baseUrl + "/discount", + method: "post", + useToken: true, + body: discount, + }); - return [createdDiscountResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [createdDiscountResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка создания скидки. ${error}`]; - } + return [null, `Ошибка создания скидки. ${error}`]; + } }; -export const deleteDiscount = async ( - discountId: string -): Promise<[Discount | null, string?]> => { - try { - const deleteDiscountResponse = await makeRequest({ - url: baseUrl + "/discount/" + discountId, - method: "delete", - useToken: true, - }); +export const deleteDiscount = async (discountId: string): Promise<[Discount | null, string?]> => { + try { + const deleteDiscountResponse = await makeRequest({ + url: baseUrl + "/discount/" + discountId, + method: "delete", + useToken: true, + }); - return [deleteDiscountResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [deleteDiscountResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка удаления скидки. ${error}`]; - } + return [null, `Ошибка удаления скидки. ${error}`]; + } }; export const patchDiscount = async ( - discountId: string, - discountParams: CreateDiscountParams + discountId: string, + discountParams: CreateDiscountParams ): Promise<[Discount | null, string?]> => { - const discount = createDiscountObject(discountParams); + const discount = createDiscountObject(discountParams); - try { - const patchDiscountResponse = await makeRequest< - CreateDiscountBody, - Discount - >({ - url: baseUrl + "/discount/" + discountId, - method: "patch", - useToken: true, - body: discount, - }); + try { + const patchDiscountResponse = await makeRequest({ + url: baseUrl + "/discount/" + discountId, + method: "patch", + useToken: true, + body: discount, + }); - return [patchDiscountResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [patchDiscountResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка изменения скидки. ${error}`]; - } + return [null, `Ошибка изменения скидки. ${error}`]; + } }; -export const requestDiscounts = async (): Promise< - [GetDiscountResponse | null, string?] -> => { - try { - const discountsResponse = await makeRequest({ - url: baseUrl + "/discounts", - method: "get", - useToken: true, - }); +export const requestDiscounts = async (): Promise<[GetDiscountResponse | null, string?]> => { + try { + const discountsResponse = await makeRequest({ + url: baseUrl + "/discounts", + method: "get", + useToken: true, + }); - return [discountsResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [discountsResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка получения скидок. ${error}`]; - } + return [null, `Ошибка получения скидок. ${error}`]; + } }; async function getDiscounts() { - try { - const discountsResponse = await makeRequest({ - url: baseUrl + "/discounts", - method: "get", - useToken: true, - }); + try { + const discountsResponse = await makeRequest({ + url: baseUrl + "/discounts", + method: "get", + useToken: true, + }); - return discountsResponse.Discounts.filter((discount) => !discount.Deprecated); - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return discountsResponse.Discounts.filter((discount) => !discount.Deprecated); + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - throw new Error(`Ошибка получения списка скидок. ${error}`); - } + throw new Error(`Ошибка получения списка скидок. ${error}`); + } } export function useDiscounts() { - const { data } = useSWR("discounts", getDiscounts, { - keepPreviousData: true, - suspense: true, - onError: (error) => { - if (!(error instanceof Error)) return; + const { data } = useSWR("discounts", getDiscounts, { + keepPreviousData: true, + suspense: true, + onError: (error) => { + if (!(error instanceof Error)) return; - enqueueSnackbar(error.message, { variant: "error" }); - } - }); + enqueueSnackbar(error.message, { variant: "error" }); + }, + }); - return data; + return data; } diff --git a/src/api/history/requests.ts b/src/api/history/requests.ts index 920ee64..c3ed448 100644 --- a/src/api/history/requests.ts +++ b/src/api/history/requests.ts @@ -3,48 +3,43 @@ import makeRequest from "@root/api/makeRequest"; import { parseAxiosError } from "@root/utils/parse-error"; type RawDetail = { - Key: string; - Value: number | string | RawDetail[]; + Key: string; + Value: number | string | RawDetail[]; }; type History = { - id: string; - userId: string; - comment: string; - key: string; - rawDetails: RawDetail[]; - isDeleted: boolean; - createdAt: string; - updatedAt: string; + id: string; + userId: string; + comment: string; + key: string; + rawDetails: RawDetail[]; + isDeleted: boolean; + createdAt: string; + updatedAt: string; }; type HistoryResponse = { - records: History[]; - totalPages: number; + records: History[]; + totalPages: number; }; const baseUrl = process.env.REACT_APP_DOMAIN + "/customer"; -const getUserHistory = async ( - accountId: string, - page: number -): Promise<[HistoryResponse | null, string?]> => { - try { - const historyResponse = await makeRequest({ - method: "GET", - url: - baseUrl + - `/history?page=${page}&limit=${100}&accountID=${accountId}&type=payCart`, - }); +const getUserHistory = async (accountId: string, page: number): Promise<[HistoryResponse | null, string?]> => { + try { + const historyResponse = await makeRequest({ + method: "GET", + url: baseUrl + `/history?page=${page}&limit=${100}&accountID=${accountId}&type=payCart`, + }); - return [historyResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [historyResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка при получении пользователей. ${error}`]; - } + return [null, `Ошибка при получении пользователей. ${error}`]; + } }; export const historyApi = { - getUserHistory, + getUserHistory, }; diff --git a/src/api/history/swr.ts b/src/api/history/swr.ts index 80be351..1e5d0a3 100644 --- a/src/api/history/swr.ts +++ b/src/api/history/swr.ts @@ -5,39 +5,36 @@ import { enqueueSnackbar } from "notistack"; import { historyApi } from "./requests"; export function useHistory(accountId: string) { - const [currentPage, setCurrentPage] = useState(1); + const [currentPage, setCurrentPage] = useState(1); - const swrResponse = useSWRInfinite( - () => `history-${currentPage}`, - async () => { - const [historyResponse, error] = await historyApi.getUserHistory( - accountId, - currentPage - ); + const swrResponse = useSWRInfinite( + () => `history-${currentPage}`, + async () => { + const [historyResponse, error] = await historyApi.getUserHistory(accountId, currentPage); - if (error) { - throw new Error(error); - } + if (error) { + throw new Error(error); + } - if (!historyResponse) { - throw new Error("Empty history data"); - } + if (!historyResponse) { + throw new Error("Empty history data"); + } - if (currentPage < historyResponse.totalPages) { - setCurrentPage((page) => page + 1); - } + if (currentPage < historyResponse.totalPages) { + setCurrentPage((page) => page + 1); + } - return historyResponse; - }, - { - onError(err) { - console.error("Error fetching users", err); - enqueueSnackbar(err.message, { variant: "error" }); - }, - focusThrottleInterval: 60e3, - keepPreviousData: true, - } - ); + return historyResponse; + }, + { + onError(err) { + console.error("Error fetching users", err); + enqueueSnackbar(err.message, { variant: "error" }); + }, + focusThrottleInterval: 60e3, + keepPreviousData: true, + } + ); - return swrResponse; + return swrResponse; } diff --git a/src/api/makeRequest.ts b/src/api/makeRequest.ts index 0cd48a5..b4601ab 100644 --- a/src/api/makeRequest.ts +++ b/src/api/makeRequest.ts @@ -3,21 +3,30 @@ import { Method, ResponseType, AxiosError } from "axios"; import { clearAuthToken } from "@frontend/kitui"; import { redirect } from "react-router-dom"; -interface MakeRequest { method?: Method | undefined; url: string; body?: unknown; useToken?: boolean | undefined; contentType?: boolean | undefined; responseType?: ResponseType | undefined; signal?: AbortSignal | undefined; withCredentials?: boolean | undefined; } +interface MakeRequest { + method?: Method | undefined; + url: string; + body?: unknown; + useToken?: boolean | undefined; + contentType?: boolean | undefined; + responseType?: ResponseType | undefined; + signal?: AbortSignal | undefined; + withCredentials?: boolean | undefined; +} -async function makeRequest (data:MakeRequest): Promise { - try { - const response = await KIT.makeRequest(data) +async function makeRequest(data: MakeRequest): Promise { + try { + const response = await KIT.makeRequest(data); - return response as TResponse - } catch (e) { - const error = e as AxiosError; - //@ts-ignore - if (error.response?.status === 400 && error.response?.data?.message === "refreshToken is empty") { - clearAuthToken() - redirect("/"); - } - throw e - }; -}; -export default makeRequest; \ No newline at end of file + return response as TResponse; + } catch (e) { + const error = e as AxiosError; + //@ts-ignore + if (error.response?.status === 400 && error.response?.data?.message === "refreshToken is empty") { + clearAuthToken(); + redirect("/"); + } + throw e; + } +} +export default makeRequest; diff --git a/src/api/privilegies.ts b/src/api/privilegies.ts index 3db14a9..c67df33 100644 --- a/src/api/privilegies.ts +++ b/src/api/privilegies.ts @@ -7,85 +7,71 @@ import { Privilege } from "@frontend/kitui"; import type { TMockData } from "./roles"; type SeverPrivilegesResponse = { - templategen: CustomPrivilege[]; - squiz: CustomPrivilege[]; + templategen: CustomPrivilege[]; + squiz: CustomPrivilege[]; }; -const baseUrl = process.env.REACT_APP_DOMAIN + "/strator" +const baseUrl = process.env.REACT_APP_DOMAIN + "/strator"; export const getRoles = async (): Promise<[TMockData | null, string?]> => { - try { - const rolesResponse = await makeRequest({ - method: "get", - url: baseUrl + "/role", - }); + try { + const rolesResponse = await makeRequest({ + method: "get", + url: baseUrl + "/role", + }); - return [rolesResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [rolesResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка запроса ролей. ${error}`]; - } + return [null, `Ошибка запроса ролей. ${error}`]; + } }; -export const putPrivilege = async ( - body: Omit -): Promise<[unknown, string?]> => { - try { - const putedPrivilege = await makeRequest< - Omit, - unknown - >({ - url: baseUrl + "/privilege", - method: "put", - body, - }); +export const putPrivilege = async (body: Omit): Promise<[unknown, string?]> => { + try { + const putedPrivilege = await makeRequest, unknown>({ + url: baseUrl + "/privilege", + method: "put", + body, + }); - return [putedPrivilege]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [putedPrivilege]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка изменения привилегии. ${error}`]; - } + return [null, `Ошибка изменения привилегии. ${error}`]; + } }; -export const requestServicePrivileges = async (): Promise< - [SeverPrivilegesResponse | null, string?] -> => { - try { - const privilegesResponse = await makeRequest< - never, - SeverPrivilegesResponse - >({ - url: baseUrl + "/privilege/service", - method: "get", - }); +export const requestServicePrivileges = async (): Promise<[SeverPrivilegesResponse | null, string?]> => { + try { + const privilegesResponse = await makeRequest({ + url: baseUrl + "/privilege/service", + method: "get", + }); - return [privilegesResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [privilegesResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка запроса привилегий. ${error}`]; - } + return [null, `Ошибка запроса привилегий. ${error}`]; + } }; -export const requestPrivileges = async ( - signal: AbortSignal | undefined -): Promise<[CustomPrivilege[], string?]> => { - try { - const privilegesResponse = await makeRequest( - { - url: baseUrl + "/privilege", - method: "get", - useToken: true, - signal, - } - ); +export const requestPrivileges = async (signal: AbortSignal | undefined): Promise<[CustomPrivilege[], string?]> => { + try { + const privilegesResponse = await makeRequest({ + url: baseUrl + "/privilege", + method: "get", + useToken: true, + signal, + }); - return [privilegesResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [privilegesResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [[], `Ошибка запроса привилегий. ${error}`]; - } + return [[], `Ошибка запроса привилегий. ${error}`]; + } }; diff --git a/src/api/promocode/requests.ts b/src/api/promocode/requests.ts index ed10142..87c18cd 100644 --- a/src/api/promocode/requests.ts +++ b/src/api/promocode/requests.ts @@ -1,11 +1,11 @@ import makeRequest from "@root/api/makeRequest"; import type { - CreatePromocodeBody, - GetPromocodeListBody, - Promocode, - PromocodeList, - PromocodeStatistics, + CreatePromocodeBody, + GetPromocodeListBody, + Promocode, + PromocodeList, + PromocodeStatistics, } from "@root/model/promocodes"; import { parseAxiosError } from "@root/utils/parse-error"; @@ -14,129 +14,117 @@ import { isAxiosError } from "axios"; const baseUrl = process.env.REACT_APP_DOMAIN + "/codeword/promocode"; const getPromocodeList = async (body: GetPromocodeListBody) => { - try { - const promocodeListResponse = await makeRequest< - GetPromocodeListBody, - PromocodeList - >({ - url: baseUrl + "/getList", - method: "POST", - body, - useToken: false, - }); + try { + const promocodeListResponse = await makeRequest({ + url: baseUrl + "/getList", + method: "POST", + body, + useToken: false, + }); - return promocodeListResponse; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); - throw new Error(`Ошибка при получении списка промокодов. ${error}`); - } + return promocodeListResponse; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); + throw new Error(`Ошибка при получении списка промокодов. ${error}`); + } }; const createFastlink = async (id: string) => { - try { - return await makeRequest<{ id: string }, { fastlink: string }>({ - url: baseUrl + "/fastlink", - method: "POST", - body: { id }, - }); - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); - throw new Error(`Ошибка при создании фастлинка. ${error}`); - } + try { + return await makeRequest<{ id: string }, { fastlink: string }>({ + url: baseUrl + "/fastlink", + method: "POST", + body: { id }, + }); + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); + throw new Error(`Ошибка при создании фастлинка. ${error}`); + } }; export const getAllPromocodes = async () => { - try { - const promocodes: Promocode[] = []; + try { + const promocodes: Promocode[] = []; - let page = 0; - while (true) { - const promocodeList = await getPromocodeList({ - limit: 100, - filter: { - active: true, - }, - page, - }); + let page = 0; + while (true) { + const promocodeList = await getPromocodeList({ + limit: 100, + filter: { + active: true, + }, + page, + }); - if (promocodeList.items.length === 0) break; + if (promocodeList.items.length === 0) break; - promocodes.push(...promocodeList.items); - page++; - } + promocodes.push(...promocodeList.items); + page++; + } - return promocodes; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); - throw new Error(`Ошибка при получении списка промокодов. ${error}`); - } + return promocodes; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); + throw new Error(`Ошибка при получении списка промокодов. ${error}`); + } }; const createPromocode = async (body: CreatePromocodeBody) => { - try { - const createPromocodeResponse = await makeRequest< - CreatePromocodeBody, - Promocode - >({ - url: baseUrl + "/create", - method: "POST", - body, - useToken: false, - }); + try { + const createPromocodeResponse = await makeRequest({ + url: baseUrl + "/create", + method: "POST", + body, + useToken: false, + }); - return createPromocodeResponse; - } catch (nativeError) { - if ( - isAxiosError(nativeError) && - nativeError.response?.data.error === "Duplicate Codeword" - ) { - throw new Error(`Промокод уже существует`); - } + return createPromocodeResponse; + } catch (nativeError) { + if (isAxiosError(nativeError) && nativeError.response?.data.error === "Duplicate Codeword") { + throw new Error(`Промокод уже существует`); + } - const [error] = parseAxiosError(nativeError); - throw new Error(`Ошибка создания промокода. ${error}`); - } + const [error] = parseAxiosError(nativeError); + throw new Error(`Ошибка создания промокода. ${error}`); + } }; const deletePromocode = async (id: string): Promise => { - try { - await makeRequest({ - url: `${baseUrl}/${id}`, - method: "DELETE", - useToken: false, - }); - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); - throw new Error(`Ошибка удаления промокода. ${error}`); - } + try { + await makeRequest({ + url: `${baseUrl}/${id}`, + method: "DELETE", + useToken: false, + }); + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); + throw new Error(`Ошибка удаления промокода. ${error}`); + } }; const getPromocodeStatistics = async (id: string, from: number, to: number) => { - try { - const promocodeStatisticsResponse = await makeRequest< - unknown, - PromocodeStatistics - >({ - url: baseUrl + `/stats`, - body: { - id: id, - from: from, - to: to, - }, - method: "POST", - useToken: false, - }); - return promocodeStatisticsResponse; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); - throw new Error(`Ошибка при получении статистики промокода. ${error}`); - } + try { + const promocodeStatisticsResponse = await makeRequest({ + url: baseUrl + `/stats`, + body: { + id: id, + from: from, + to: to, + }, + method: "POST", + useToken: false, + }); + return promocodeStatisticsResponse; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); + throw new Error(`Ошибка при получении статистики промокода. ${error}`); + } }; export const promocodeApi = { - getPromocodeList, - createPromocode, - deletePromocode, - getAllPromocodes, - getPromocodeStatistics, - createFastlink, + getPromocodeList, + createPromocode, + deletePromocode, + getAllPromocodes, + getPromocodeStatistics, + createFastlink, }; diff --git a/src/api/promocode/swr.ts b/src/api/promocode/swr.ts index 2e58d67..8126796 100644 --- a/src/api/promocode/swr.ts +++ b/src/api/promocode/swr.ts @@ -3,147 +3,134 @@ import useSwr, { mutate } from "swr"; import { enqueueSnackbar } from "notistack"; import { promocodeApi } from "./requests"; -import type { - CreatePromocodeBody, - PromocodeList, -} from "@root/model/promocodes"; +import type { CreatePromocodeBody, PromocodeList } from "@root/model/promocodes"; -export function usePromocodes( - page: number, - pageSize: number, - promocodeId: string, - to: number, - from: number -) { - const promocodesCountRef = useRef(0); - const swrResponse = useSwr( - ["promocodes", page, pageSize], - async (key) => { - const result = await promocodeApi.getPromocodeList({ - limit: key[2], - filter: { - active: true, - }, - page: key[1], - }); +export function usePromocodes(page: number, pageSize: number, promocodeId: string, to: number, from: number) { + const promocodesCountRef = useRef(0); + const swrResponse = useSwr( + ["promocodes", page, pageSize], + async (key) => { + const result = await promocodeApi.getPromocodeList({ + limit: key[2], + filter: { + active: true, + }, + page: key[1], + }); - promocodesCountRef.current = result.count; - return result; - }, - { - onError(err) { - console.error("Error fetching promocodes", err); - enqueueSnackbar(err.message, { variant: "error" }); - }, - focusThrottleInterval: 60e3, - keepPreviousData: true, - } - ); + promocodesCountRef.current = result.count; + return result; + }, + { + onError(err) { + console.error("Error fetching promocodes", err); + enqueueSnackbar(err.message, { variant: "error" }); + }, + focusThrottleInterval: 60e3, + keepPreviousData: true, + } + ); - const createPromocode = useCallback( - async function (body: CreatePromocodeBody) { - try { - await promocodeApi.createPromocode(body); - mutate(["promocodes", page, pageSize]); - } catch (error) { - console.error("Error creating promocode", error); - if (error instanceof Error) - enqueueSnackbar(error.message, { variant: "error" }); - } - }, - [page, pageSize] - ); + const createPromocode = useCallback( + async function (body: CreatePromocodeBody) { + try { + await promocodeApi.createPromocode(body); + mutate(["promocodes", page, pageSize]); + } catch (error) { + console.error("Error creating promocode", error); + if (error instanceof Error) enqueueSnackbar(error.message, { variant: "error" }); + } + }, + [page, pageSize] + ); - const deletePromocode = useCallback( - async function (id: string) { - try { - await mutate( - ["promocodes", page, pageSize], - promocodeApi.deletePromocode(id), - { - optimisticData(currentData, displayedData) { - if (!displayedData) return; + const deletePromocode = useCallback( + async function (id: string) { + try { + await mutate( + ["promocodes", page, pageSize], + promocodeApi.deletePromocode(id), + { + optimisticData(currentData, displayedData) { + if (!displayedData) return; - return { - count: displayedData.count - 1, - items: displayedData.items.filter((item) => item.id !== id), - }; - }, - rollbackOnError: true, - populateCache(result, currentData) { - if (!currentData) return; + return { + count: displayedData.count - 1, + items: displayedData.items.filter((item) => item.id !== id), + }; + }, + rollbackOnError: true, + populateCache(result, currentData) { + if (!currentData) return; - return { - count: currentData.count - 1, - items: currentData.items.filter((item) => item.id !== id), - }; - }, - } - ); - } catch (error) { - console.error("Error deleting promocode", error); - if (error instanceof Error) - enqueueSnackbar(error.message, { variant: "error" }); - } - }, - [page, pageSize] - ); + return { + count: currentData.count - 1, + items: currentData.items.filter((item) => item.id !== id), + }; + }, + } + ); + } catch (error) { + console.error("Error deleting promocode", error); + if (error instanceof Error) enqueueSnackbar(error.message, { variant: "error" }); + } + }, + [page, pageSize] + ); - const promocodeStatistics = useSwr( - ["promocodeStatistics", promocodeId, from, to], - async ([_, id, from, to]) => { - if (!id) { - return null; - } + const promocodeStatistics = useSwr( + ["promocodeStatistics", promocodeId, from, to], + async ([_, id, from, to]) => { + if (!id) { + return null; + } - const promocodeStatisticsResponse = - await promocodeApi.getPromocodeStatistics(id, from, to); + const promocodeStatisticsResponse = await promocodeApi.getPromocodeStatistics(id, from, to); - return promocodeStatisticsResponse; - }, - { - onError(err) { - console.error("Error fetching promocode statistics", err); - enqueueSnackbar(err.message, { variant: "error" }); - }, - focusThrottleInterval: 60e3, - keepPreviousData: true, - } - ); + return promocodeStatisticsResponse; + }, + { + onError(err) { + console.error("Error fetching promocode statistics", err); + enqueueSnackbar(err.message, { variant: "error" }); + }, + focusThrottleInterval: 60e3, + keepPreviousData: true, + } + ); - const createFastLink = useCallback( - async function (id: string) { - try { - await promocodeApi.createFastlink(id); - mutate(["promocodes", page, pageSize]); - } catch (error) { - console.error("Error creating fast link", error); - if (error instanceof Error) - enqueueSnackbar(error.message, { variant: "error" }); - } - }, - [page, pageSize] - ); + const createFastLink = useCallback( + async function (id: string) { + try { + await promocodeApi.createFastlink(id); + mutate(["promocodes", page, pageSize]); + } catch (error) { + console.error("Error creating fast link", error); + if (error instanceof Error) enqueueSnackbar(error.message, { variant: "error" }); + } + }, + [page, pageSize] + ); - return { - ...swrResponse, - createPromocode, - deletePromocode, - createFastLink, - promocodeStatistics: promocodeStatistics.data, - promocodesCount: promocodesCountRef.current, - }; + return { + ...swrResponse, + createPromocode, + deletePromocode, + createFastLink, + promocodeStatistics: promocodeStatistics.data, + promocodesCount: promocodesCountRef.current, + }; } export function useAllPromocodes() { - const { data } = useSwr("allPromocodes", promocodeApi.getAllPromocodes, { - keepPreviousData: true, - suspense: true, - onError(err) { - console.error("Error fetching all promocodes", err); - enqueueSnackbar(err.message, { variant: "error" }); - }, - }); + const { data } = useSwr("allPromocodes", promocodeApi.getAllPromocodes, { + keepPreviousData: true, + suspense: true, + onError(err) { + console.error("Error fetching all promocodes", err); + enqueueSnackbar(err.message, { variant: "error" }); + }, + }); - return data; + return data; } diff --git a/src/api/quizStatistics/index.ts b/src/api/quizStatistics/index.ts index 5cd9ff6..13cec85 100644 --- a/src/api/quizStatistics/index.ts +++ b/src/api/quizStatistics/index.ts @@ -1,89 +1,77 @@ import makeRequest from "@root/api/makeRequest"; import type { - GetStatisticSchildBody, - QuizStatisticsItem, - GetPromocodeStatisticsBody, - AllPromocodeStatistics, + GetStatisticSchildBody, + QuizStatisticsItem, + GetPromocodeStatisticsBody, + AllPromocodeStatistics, } from "./types"; export type QuizStatisticResponse = { - Registrations: number; - Quizes: number; - Results: number; + Registrations: number; + Quizes: number; + Results: number; }; type TRequest = { - to: number; - from: number; + to: number; + from: number; }; -export const getStatistic = async ( - to: number, - from: number -): Promise => { - try { - const generalResponse = await makeRequest({ - url: `${process.env.REACT_APP_DOMAIN}/squiz/statistic`, - body: { to, from }, - }); - return generalResponse; - } catch (nativeError) { - return { Registrations: 0, Quizes: 0, Results: 0 }; - } +export const getStatistic = async (to: number, from: number): Promise => { + try { + const generalResponse = await makeRequest({ + url: `${process.env.REACT_APP_DOMAIN}/squiz/statistic`, + body: { to, from }, + }); + return generalResponse; + } catch (nativeError) { + return { Registrations: 0, Quizes: 0, Results: 0 }; + } }; -export const getStatisticSchild = async ( - from: number, - to: number -): Promise => { - try { - const StatisticResponse = await makeRequest< - GetStatisticSchildBody, - QuizStatisticsItem[] - >({ - url: process.env.REACT_APP_DOMAIN + "/customer/quizlogo/stat", - method: "post", - useToken: true, - body: { to, from, page: 0, limit: 100 }, - }); +export const getStatisticSchild = async (from: number, to: number): Promise => { + try { + const StatisticResponse = await makeRequest({ + url: process.env.REACT_APP_DOMAIN + "/customer/quizlogo/stat", + method: "post", + useToken: true, + body: { to, from, page: 0, limit: 100 }, + }); - if (!StatisticResponse) { - throw new Error("Статистика не найдена"); - } + if (!StatisticResponse) { + throw new Error("Статистика не найдена"); + } - return StatisticResponse; - } catch (nativeError) { - return [ - { - ID: "0", - Regs: 0, - Money: 0, - Quizes: [{ QuizID: "0", Regs: 0, Money: 0 }], - }, - ]; - } + return StatisticResponse; + } catch (nativeError) { + return [ + { + ID: "0", + Regs: 0, + Money: 0, + Quizes: [{ QuizID: "0", Regs: 0, Money: 0 }], + }, + ]; + } }; export const getStatisticPromocode = async ( - from: number, - to: number + from: number, + to: number ): Promise> => { - try { - const StatisticPromo = await makeRequest< - GetPromocodeStatisticsBody, - Record - >({ - url: process.env.REACT_APP_DOMAIN + "/customer/promocode/ltv", - method: "post", - useToken: true, - body: { to, from }, - }); + try { + const StatisticPromo = await makeRequest>({ + url: process.env.REACT_APP_DOMAIN + "/customer/promocode/ltv", + method: "post", + useToken: true, + body: { to, from }, + }); - return StatisticPromo; - } catch (nativeError) { - console.log(nativeError); + return StatisticPromo; + } catch (nativeError) { + console.log(nativeError); - return {}; - } + return {}; + } }; diff --git a/src/api/quizStatistics/types.ts b/src/api/quizStatistics/types.ts index 284b7c8..a5ce608 100644 --- a/src/api/quizStatistics/types.ts +++ b/src/api/quizStatistics/types.ts @@ -1,31 +1,31 @@ import { Moment } from "moment"; export type GetStatisticSchildBody = { - to: Moment | null; - from: Moment | null; - page: number; - limit: number; + to: Moment | null; + from: Moment | null; + page: number; + limit: number; }; type StatisticsQuizes = { - QuizID: string; - Money: number; - Regs: number; + QuizID: string; + Money: number; + Regs: number; }; export type QuizStatisticsItem = { - ID: string; - Money: number; - Quizes: StatisticsQuizes[]; - Regs: number; + ID: string; + Money: number; + Quizes: StatisticsQuizes[]; + Regs: number; }; export type GetPromocodeStatisticsBody = { - from: number; - to: number; + from: number; + to: number; }; export type AllPromocodeStatistics = { - Money: number; - Regs: number; + Money: number; + Regs: number; }; diff --git a/src/api/roles.ts b/src/api/roles.ts index 6f302ab..d0470f5 100644 --- a/src/api/roles.ts +++ b/src/api/roles.ts @@ -3,59 +3,59 @@ import makeRequest from "@root/api/makeRequest"; import { parseAxiosError } from "@root/utils/parse-error"; export const MOCK_DATA_USERS = [ - { - key: 0, - id: "someid1", - name: "admin", - desc: "Администратор сервиса", - }, - { - key: 1, - id: "someid2", - name: "manager", - desc: "Менеджер сервиса", - }, - { - key: 2, - id: "someid3", - name: "user", - desc: "Пользователь сервиса", - }, + { + key: 0, + id: "someid1", + name: "admin", + desc: "Администратор сервиса", + }, + { + key: 1, + id: "someid2", + name: "manager", + desc: "Менеджер сервиса", + }, + { + key: 2, + id: "someid3", + name: "user", + desc: "Пользователь сервиса", + }, ]; export type TMockData = typeof MOCK_DATA_USERS; export type UserType = { - _id: string; - login: string; - email: string; - phoneNumber: string; - isDeleted: boolean; - createdAt: string; - updatedAt: string; + _id: string; + login: string; + email: string; + phoneNumber: string; + isDeleted: boolean; + createdAt: string; + updatedAt: string; }; -const baseUrl =process.env.REACT_APP_DOMAIN + "/role" +const baseUrl = process.env.REACT_APP_DOMAIN + "/role"; export const getRoles_mock = (): Promise => { - return new Promise((resolve) => { - setTimeout(() => { - resolve(MOCK_DATA_USERS); - }, 1000); - }); + return new Promise((resolve) => { + setTimeout(() => { + resolve(MOCK_DATA_USERS); + }, 1000); + }); }; export const deleteRole = async (id: string): Promise<[unknown, string?]> => { - try { - const deleteRoleResponse = await makeRequest({ - url: `${baseUrl}/${id}`, - method: "delete", - }); + try { + const deleteRoleResponse = await makeRequest({ + url: `${baseUrl}/${id}`, + method: "delete", + }); - return [deleteRoleResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [deleteRoleResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка удаления роли. ${error}`]; - } + return [null, `Ошибка удаления роли. ${error}`]; + } }; diff --git a/src/api/tariffs.ts b/src/api/tariffs.ts index 23d09e1..a8a0926 100644 --- a/src/api/tariffs.ts +++ b/src/api/tariffs.ts @@ -7,95 +7,87 @@ import type { Tariff } from "@frontend/kitui"; import type { EditTariffRequestBody } from "@root/model/tariff"; type CreateTariffBackendRequest = { - name: string; - description: string; - order: number; - price: number; - isCustom: boolean; - privileges: Omit[]; + name: string; + description: string; + order: number; + price: number; + isCustom: boolean; + privileges: Omit[]; }; type GetTariffsResponse = { - totalPages: number; - tariffs: Tariff[]; + totalPages: number; + tariffs: Tariff[]; }; -const baseUrl =process.env.REACT_APP_DOMAIN + "/strator" +const baseUrl = process.env.REACT_APP_DOMAIN + "/strator"; -export const createTariff = async ( - body: CreateTariffBackendRequest -): Promise<[unknown, string?]> => { - try { - const createdTariffResponse = await makeRequest( - { - url: baseUrl + "/tariff/", - method: "post", - body, - } - ); +export const createTariff = async (body: CreateTariffBackendRequest): Promise<[unknown, string?]> => { + try { + const createdTariffResponse = await makeRequest({ + url: baseUrl + "/tariff/", + method: "post", + body, + }); - return [createdTariffResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [createdTariffResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка создания тарифа. ${error}`]; - } + return [null, `Ошибка создания тарифа. ${error}`]; + } }; export const putTariff = async (tariff: Tariff): Promise<[null, string?]> => { - try { - const putedTariffResponse = await makeRequest({ - method: "put", - url: baseUrl + `/tariff/${tariff._id}`, - body: { - name: tariff.name, - price: tariff.price ?? 0, - isCustom: false, - order: tariff.order || 1, - description: tariff.description ?? "", - privileges: tariff.privileges, - }, - }); + try { + const putedTariffResponse = await makeRequest({ + method: "put", + url: baseUrl + `/tariff/${tariff._id}`, + body: { + name: tariff.name, + price: tariff.price ?? 0, + isCustom: false, + order: tariff.order || 1, + description: tariff.description ?? "", + privileges: tariff.privileges, + }, + }); - return [putedTariffResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [putedTariffResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка редактирования тарифа. ${error}`]; - } + return [null, `Ошибка редактирования тарифа. ${error}`]; + } }; -export const deleteTariff = async ( - tariffId: string -): Promise<[Tariff | null, string?]> => { - try { - const deletedTariffResponse = await makeRequest<{ id: string }, Tariff>({ - method: "delete", - url: baseUrl + "/tariff", - body: { id: tariffId }, - }); +export const deleteTariff = async (tariffId: string): Promise<[Tariff | null, string?]> => { + try { + const deletedTariffResponse = await makeRequest<{ id: string }, Tariff>({ + method: "delete", + url: baseUrl + "/tariff", + body: { id: tariffId }, + }); - return [deletedTariffResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [deletedTariffResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка удаления тарифа. ${error}`]; - } + return [null, `Ошибка удаления тарифа. ${error}`]; + } }; -export const requestTariffs = async ( - page: number -): Promise<[GetTariffsResponse | null, string?]> => { - try { - const tariffsResponse = await makeRequest({ - url: baseUrl + `/tariff/?page=${page}&limit=${100}`, - method: "get", - }); +export const requestTariffs = async (page: number): Promise<[GetTariffsResponse | null, string?]> => { + try { + const tariffsResponse = await makeRequest({ + url: baseUrl + `/tariff/?page=${page}&limit=${100}`, + method: "get", + }); - return [tariffsResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [tariffsResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка запроса тарифов. ${error}`]; - } + return [null, `Ошибка запроса тарифов. ${error}`]; + } }; diff --git a/src/api/tickets.ts b/src/api/tickets.ts index 8f445ea..d0befdc 100644 --- a/src/api/tickets.ts +++ b/src/api/tickets.ts @@ -4,26 +4,21 @@ import { parseAxiosError } from "@root/utils/parse-error"; import type { SendTicketMessageRequest } from "@root/model/ticket"; -const baseUrl = process.env.REACT_APP_DOMAIN + "/heruvym" +const baseUrl = process.env.REACT_APP_DOMAIN + "/heruvym"; -export const sendTicketMessage = async ( - body: SendTicketMessageRequest -): Promise<[unknown, string?]> => { - try { - const sendTicketMessageResponse = await makeRequest< - SendTicketMessageRequest, - unknown - >({ - url: `${baseUrl}/send`, - method: "POST", - useToken: true, - body, - }); +export const sendTicketMessage = async (body: SendTicketMessageRequest): Promise<[unknown, string?]> => { + try { + const sendTicketMessageResponse = await makeRequest({ + url: `${baseUrl}/send`, + method: "POST", + useToken: true, + body, + }); - return [sendTicketMessageResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [sendTicketMessageResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка отправки сообщения. ${error}`]; - } + return [null, `Ошибка отправки сообщения. ${error}`]; + } }; diff --git a/src/api/user/requests.ts b/src/api/user/requests.ts index 5eaedf3..42a2a33 100644 --- a/src/api/user/requests.ts +++ b/src/api/user/requests.ts @@ -5,85 +5,76 @@ import { parseAxiosError } from "@root/utils/parse-error"; import type { UserType } from "@root/api/roles"; export type UsersListResponse = { - totalPages: number; - users: UserType[]; + totalPages: number; + users: UserType[]; }; const baseUrl = process.env.REACT_APP_DOMAIN + "/user"; const getUserInfo = async (id: string): Promise<[UserType | null, string?]> => { - try { - const userInfoResponse = await makeRequest({ - url: `${baseUrl}/${id}`, - method: "GET", - useToken: true, - }); + try { + const userInfoResponse = await makeRequest({ + url: `${baseUrl}/${id}`, + method: "GET", + useToken: true, + }); - return [userInfoResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [userInfoResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка получения информации о пользователе. ${error}`]; - } + return [null, `Ошибка получения информации о пользователе. ${error}`]; + } }; -const getUserList = async ( - page = 1, - limit = 10 -): Promise<[UsersListResponse | null, string?]> => { - try { - const userResponse = await makeRequest({ - method: "get", - url: baseUrl + `/?page=${page}&limit=${limit}`, - }); +const getUserList = async (page = 1, limit = 10): Promise<[UsersListResponse | null, string?]> => { + try { + const userResponse = await makeRequest({ + method: "get", + url: baseUrl + `/?page=${page}&limit=${limit}`, + }); - return [userResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [userResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка при получении пользователей. ${error}`]; - } + return [null, `Ошибка при получении пользователей. ${error}`]; + } }; -const getManagerList = async ( - page = 1, - limit = 10 -): Promise<[UsersListResponse | null, string?]> => { - try { - const managerResponse = await makeRequest({ - method: "get", - url: baseUrl + `/?page=${page}&limit=${limit}`, - }); +const getManagerList = async (page = 1, limit = 10): Promise<[UsersListResponse | null, string?]> => { + try { + const managerResponse = await makeRequest({ + method: "get", + url: baseUrl + `/?page=${page}&limit=${limit}`, + }); - return [managerResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [managerResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка при получении менеджеров. ${error}`]; - } + return [null, `Ошибка при получении менеджеров. ${error}`]; + } }; -const getAdminList = async ( - page = 1, - limit = 10 -): Promise<[UsersListResponse | null, string?]> => { - try { - const adminResponse = await makeRequest({ - method: "get", - url: baseUrl + `/?page=${page}&limit=${limit}`, - }); +const getAdminList = async (page = 1, limit = 10): Promise<[UsersListResponse | null, string?]> => { + try { + const adminResponse = await makeRequest({ + method: "get", + url: baseUrl + `/?page=${page}&limit=${limit}`, + }); - return [adminResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [adminResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка при получении админов. ${error}`]; - } + return [null, `Ошибка при получении админов. ${error}`]; + } }; export const userApi = { - getUserInfo, - getUserList, - getManagerList, - getAdminList, + getUserInfo, + getUserList, + getManagerList, + getAdminList, }; diff --git a/src/api/user/swr.ts b/src/api/user/swr.ts index 1c5a462..4f138b8 100644 --- a/src/api/user/swr.ts +++ b/src/api/user/swr.ts @@ -5,100 +5,94 @@ import { enqueueSnackbar } from "notistack"; import { userApi } from "./requests"; export function useAdmins(page: number, pageSize: number) { - const adminPagesRef = useRef(0); + const adminPagesRef = useRef(0); - const swrResponse = useSwr( - ["admin", page, pageSize], - async ([_, page, pageSize]) => { - const [adminResponse, error] = await userApi.getManagerList( - page, - pageSize - ); + const swrResponse = useSwr( + ["admin", page, pageSize], + async ([_, page, pageSize]) => { + const [adminResponse, error] = await userApi.getManagerList(page, pageSize); - if (error) { - throw new Error(error); - } + if (error) { + throw new Error(error); + } - adminPagesRef.current = adminResponse?.totalPages || 1; - return adminResponse; - }, - { - onError(err) { - console.error("Error fetching users", err); - enqueueSnackbar(err.message, { variant: "error" }); - }, - focusThrottleInterval: 60e3, - keepPreviousData: true, - } - ); + adminPagesRef.current = adminResponse?.totalPages || 1; + return adminResponse; + }, + { + onError(err) { + console.error("Error fetching users", err); + enqueueSnackbar(err.message, { variant: "error" }); + }, + focusThrottleInterval: 60e3, + keepPreviousData: true, + } + ); - return { - ...swrResponse, - adminPages: adminPagesRef.current, - }; + return { + ...swrResponse, + adminPages: adminPagesRef.current, + }; } export function useManagers(page: number, pageSize: number) { - const managerPagesRef = useRef(0); + const managerPagesRef = useRef(0); - const swrResponse = useSwr( - ["manager", page, pageSize], - async ([_, page, pageSize]) => { - const [managerResponse, error] = await userApi.getManagerList( - page, - pageSize - ); + const swrResponse = useSwr( + ["manager", page, pageSize], + async ([_, page, pageSize]) => { + const [managerResponse, error] = await userApi.getManagerList(page, pageSize); - if (error) { - throw new Error(error); - } + if (error) { + throw new Error(error); + } - managerPagesRef.current = managerResponse?.totalPages || 1; - return managerResponse; - }, - { - onError(err) { - console.error("Error fetching users", err); - enqueueSnackbar(err.message, { variant: "error" }); - }, - focusThrottleInterval: 60e3, - keepPreviousData: true, - } - ); + managerPagesRef.current = managerResponse?.totalPages || 1; + return managerResponse; + }, + { + onError(err) { + console.error("Error fetching users", err); + enqueueSnackbar(err.message, { variant: "error" }); + }, + focusThrottleInterval: 60e3, + keepPreviousData: true, + } + ); - return { - ...swrResponse, - managerPages: managerPagesRef.current, - }; + return { + ...swrResponse, + managerPages: managerPagesRef.current, + }; } export function useUsers(page: number, pageSize: number) { - const userPagesRef = useRef(0); + const userPagesRef = useRef(0); - const swrResponse = useSwr( - ["users", page, pageSize], - async ([_, page, pageSize]) => { - const [userResponse, error] = await userApi.getUserList(page, pageSize); + const swrResponse = useSwr( + ["users", page, pageSize], + async ([_, page, pageSize]) => { + const [userResponse, error] = await userApi.getUserList(page, pageSize); - if (error) { - throw new Error(error); - } + if (error) { + throw new Error(error); + } - userPagesRef.current = userResponse?.totalPages || 1; - return userResponse; - }, - { - onError(err) { - console.error("Error fetching users", err); - enqueueSnackbar(err.message, { variant: "error" }); - }, - focusThrottleInterval: 60e3, - keepPreviousData: true, - } - ); + userPagesRef.current = userResponse?.totalPages || 1; + return userResponse; + }, + { + onError(err) { + console.error("Error fetching users", err); + enqueueSnackbar(err.message, { variant: "error" }); + }, + focusThrottleInterval: 60e3, + keepPreviousData: true, + } + ); - return { - ...swrResponse, - userPagesCount: userPagesRef.current, - }; + return { + ...swrResponse, + userPagesCount: userPagesRef.current, + }; } diff --git a/src/api/verification.ts b/src/api/verification.ts index d19e6d4..b1c39d8 100644 --- a/src/api/verification.ts +++ b/src/api/verification.ts @@ -3,65 +3,58 @@ import makeRequest from "@root/api/makeRequest"; import { parseAxiosError } from "@root/utils/parse-error"; type File = { - name: "inn" | "rule" | "egrule" | "certificate"; - url: string; + name: "inn" | "rule" | "egrule" | "certificate"; + url: string; }; export type Verification = { - _id: string; - accepted: boolean; - status: "org" | "nko"; - updated_at: string; - comment: string; - taxnumber: string; - files: File[]; + _id: string; + accepted: boolean; + status: "org" | "nko"; + updated_at: string; + comment: string; + taxnumber: string; + files: File[]; }; type PatchVerificationBody = { - id?: string; - status?: "org" | "nko"; - comment?: string; - accepted?: boolean; - taxnumber?: string; + id?: string; + status?: "org" | "nko"; + comment?: string; + accepted?: boolean; + taxnumber?: string; }; -const baseUrl = process.env.REACT_APP_DOMAIN + "/verification" +const baseUrl = process.env.REACT_APP_DOMAIN + "/verification"; -export const verification = async ( - userId: string -): Promise<[Verification | null, string?]> => { - try { - const verificationResponse = await makeRequest({ - method: "get", - url: baseUrl + `/verification/${userId}`, - }); +export const verification = async (userId: string): Promise<[Verification | null, string?]> => { + try { + const verificationResponse = await makeRequest({ + method: "get", + url: baseUrl + `/verification/${userId}`, + }); - return [verificationResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [verificationResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка верификации. ${error}`]; - } + return [null, `Ошибка верификации. ${error}`]; + } }; -export const patchVerification = async ( - body: PatchVerificationBody -): Promise<[unknown, string?]> => { - try { - const patchedVerificationResponse = await makeRequest< - PatchVerificationBody, - unknown - >({ - method: "patch", - useToken: true, - url: baseUrl + `/verification`, - body, - }); +export const patchVerification = async (body: PatchVerificationBody): Promise<[unknown, string?]> => { + try { + const patchedVerificationResponse = await makeRequest({ + method: "patch", + useToken: true, + url: baseUrl + `/verification`, + body, + }); - return [patchedVerificationResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [patchedVerificationResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [null, `Ошибка изменения верификации. ${error}`]; - } + return [null, `Ошибка изменения верификации. ${error}`]; + } }; diff --git a/src/index.css b/src/index.css index b958aa5..9c31422 100644 --- a/src/index.css +++ b/src/index.css @@ -1,4 +1,6 @@ @font-face { - font-family: "GilroyRegular"; - src: local("GilroyRegular"), url(./fonts/GilroyRegular.woff) format("woff"); -} \ No newline at end of file + font-family: "GilroyRegular"; + src: + local("GilroyRegular"), + url(./fonts/GilroyRegular.woff) format("woff"); +} diff --git a/src/kitUI/Article.tsx b/src/kitUI/Article.tsx index 83e671b..27425fb 100644 --- a/src/kitUI/Article.tsx +++ b/src/kitUI/Article.tsx @@ -2,18 +2,18 @@ import React from "react"; import { Box, Typography } from "@mui/material"; type ArticleProps = { - header: JSX.Element; - body: JSX.Element; - isBoby?: boolean; + header: JSX.Element; + body: JSX.Element; + isBoby?: boolean; }; export const Article = ({ header, body, isBoby = false }: ArticleProps) => { - return ( - - - {header} - - {isBoby ? {body} : } - - ); + return ( + + + {header} + + {isBoby ? {body} : } + + ); }; diff --git a/src/kitUI/Cart/Cart.tsx b/src/kitUI/Cart/Cart.tsx index f5d77c9..faa23c9 100644 --- a/src/kitUI/Cart/Cart.tsx +++ b/src/kitUI/Cart/Cart.tsx @@ -1,20 +1,20 @@ import { calcCart } from "@frontend/kitui"; import Input from "@kitUI/input"; import { - Alert, - Box, - Button, - Checkbox, - FormControlLabel, - Paper, - Table, - TableBody, - TableCell, - TableHead, - TableRow, - Typography, - useMediaQuery, - useTheme + Alert, + Box, + Button, + Checkbox, + FormControlLabel, + Paper, + Table, + TableBody, + TableCell, + TableHead, + TableRow, + Typography, + useMediaQuery, + useTheme, } from "@mui/material"; import { useDiscounts } from "@root/api/discounts"; import { useAllPromocodes } from "@root/api/promocode/swr"; @@ -27,249 +27,237 @@ import { currencyFormatter } from "@root/utils/currencyFormatter"; import { useState } from "react"; import CartItemRow from "./CartItemRow"; - export default function Cart() { - const theme = useTheme(); - const mobile = useMediaQuery(theme.breakpoints.down(400)); - const discounts = useDiscounts(); - const promocodes = useAllPromocodes(); - const cartData = useCartStore((store) => store.cartData); - const tariffs = useTariffStore(state => state.tariffs); - const [couponField, setCouponField] = useState(""); - const [loyaltyField, setLoyaltyField] = useState(""); - const [errorMessage, setErrorMessage] = useState(null); - const [isNonCommercial, setIsNonCommercial] = useState(false); - const selectedTariffIds = useTariffStore(state => state.selectedTariffIds); + const theme = useTheme(); + const mobile = useMediaQuery(theme.breakpoints.down(400)); + const discounts = useDiscounts(); + const promocodes = useAllPromocodes(); + const cartData = useCartStore((store) => store.cartData); + const tariffs = useTariffStore((state) => state.tariffs); + const [couponField, setCouponField] = useState(""); + const [loyaltyField, setLoyaltyField] = useState(""); + const [errorMessage, setErrorMessage] = useState(null); + const [isNonCommercial, setIsNonCommercial] = useState(false); + const selectedTariffIds = useTariffStore((state) => state.selectedTariffIds); - async function handleCalcCartClick() { - await requestPrivileges(); - await requestDiscounts(); + async function handleCalcCartClick() { + await requestPrivileges(); + await requestDiscounts(); - const cartTariffs = tariffs.filter(tariff => selectedTariffIds.includes(tariff._id)); + const cartTariffs = tariffs.filter((tariff) => selectedTariffIds.includes(tariff._id)); - let loyaltyValue = parseInt(loyaltyField); + let loyaltyValue = parseInt(loyaltyField); - if (!isFinite(loyaltyValue)) loyaltyValue = 0; + if (!isFinite(loyaltyValue)) loyaltyValue = 0; - const promocode = promocodes.find(promocode => { - if (promocode.dueTo < (Date.now() / 1000)) return false; + const promocode = promocodes.find((promocode) => { + if (promocode.dueTo < Date.now() / 1000) return false; - return promocode.codeword === couponField.trim(); - }); + return promocode.codeword === couponField.trim(); + }); - const userId = crypto.randomUUID(); + const userId = crypto.randomUUID(); - const discountsWithPromocodeDiscount = promocode ? [ - ...discounts, - createDiscountFromPromocode(promocode, userId), - ] : discounts; + const discountsWithPromocodeDiscount = promocode + ? [...discounts, createDiscountFromPromocode(promocode, userId)] + : discounts; - try { - const cartData = calcCart(cartTariffs, discountsWithPromocodeDiscount, loyaltyValue, userId); + try { + const cartData = calcCart(cartTariffs, discountsWithPromocodeDiscount, loyaltyValue, userId); - setErrorMessage(null); - setCartData(cartData); - } catch (error: any) { - setErrorMessage(error.message); - setCartData(null); - } - } + setErrorMessage(null); + setCartData(cartData); + } catch (error: any) { + setErrorMessage(error.message); + setCartData(null); + } + } - return ( - - корзина - - setIsNonCommercial(checked)} - control={ - - } - label="НКО" - sx={{ - color: theme.palette.secondary.main, - }} - /> - - setCouponField(e.target.value)} - InputProps={{ - style: { - backgroundColor: theme.palette.content.main, - color: theme.palette.secondary.main, - }, - }} - InputLabelProps={{ - style: { - color: theme.palette.secondary.main, - }, - }} - /> - - {/* {cartTotal?.couponState && + return ( + + корзина + + setIsNonCommercial(checked)} + control={ + + } + label="НКО" + sx={{ + color: theme.palette.secondary.main, + }} + /> + + setCouponField(e.target.value)} + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + }, + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main, + }, + }} + /> + + {/* {cartTotal?.couponState && (cartTotal.couponState === "applied" ? ( Купон применен! ) : ( Подходящий купон не найден! )) } */} - - setLoyaltyField(e.target.value)} - InputProps={{ - style: { - backgroundColor: theme.palette.content.main, - color: theme.palette.secondary.main, - }, - }} - InputLabelProps={{ - style: { - color: theme.palette.secondary.main, - }, - }} - /> - - - + + setLoyaltyField(e.target.value)} + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + }, + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main, + }, + }} + /> + + + - {cartData?.services.length && ( - <> - - - - - - Имя - - - - - Описание - - - - - Скидки - - - - - стоимость - - - - - - {cartData.services.flatMap(service => service.tariffs.map(tariffCartData => { - const appliedDiscounts = tariffCartData.privileges.flatMap( - privilege => Array.from(privilege.appliedDiscounts) - ).sort((a, b) => a.Layer - b.Layer); + {cartData?.services.length && ( + <> +
+ + + + + Имя + + + + + Описание + + + + + Скидки + + + + + стоимость + + + + + + {cartData.services.flatMap((service) => + service.tariffs.map((tariffCartData) => { + const appliedDiscounts = tariffCartData.privileges + .flatMap((privilege) => Array.from(privilege.appliedDiscounts)) + .sort((a, b) => a.Layer - b.Layer); - return ( - - ); - }))} - -
+ return ( + + ); + }) + )} + + - - ИТОГО: {currencyFormatter.format(cartData.priceAfterDiscounts / 100)} - - - )} + + ИТОГО: {currencyFormatter.format(cartData.priceAfterDiscounts / 100)} + + + )} - {errorMessage !== null && ( - - {errorMessage} - - )} -
- ); + {errorMessage !== null && ( + + {errorMessage} + + )} + + ); } diff --git a/src/kitUI/Cart/CartItemRow.tsx b/src/kitUI/Cart/CartItemRow.tsx index e0ac53d..e8d3a7a 100644 --- a/src/kitUI/Cart/CartItemRow.tsx +++ b/src/kitUI/Cart/CartItemRow.tsx @@ -4,51 +4,42 @@ import { Discount, TariffCartData } from "@frontend/kitui"; import { currencyFormatter } from "@root/utils/currencyFormatter"; import { useEffect } from "react"; - interface Props { - tariffCartData: TariffCartData; - appliedDiscounts: Discount[]; + tariffCartData: TariffCartData; + appliedDiscounts: Discount[]; } export default function CartItemRow({ tariffCartData, appliedDiscounts }: Props) { - const theme = useTheme(); + const theme = useTheme(); - useEffect(() => { - if (tariffCartData.privileges.length > 1) { - console.warn(`Количество привилегий в тарифе ${tariffCartData.name}(${tariffCartData.id}) больше одного`); - } - }, [tariffCartData.id, tariffCartData.name, tariffCartData.privileges.length]); + useEffect(() => { + if (tariffCartData.privileges.length > 1) { + console.warn(`Количество привилегий в тарифе ${tariffCartData.name}(${tariffCartData.id}) больше одного`); + } + }, [tariffCartData.id, tariffCartData.name, tariffCartData.privileges.length]); - return ( - - - {tariffCartData.name} - - - {tariffCartData.privileges[0].description} - - - {appliedDiscounts.map((discount, index, arr) => ( - - - {index < arr.length - 1 && ( - - )} - - ))} - - - {currencyFormatter.format(tariffCartData.price / 100)} - - - ); + return ( + + {tariffCartData.name} + {tariffCartData.privileges[0].description} + + {appliedDiscounts.map((discount, index, arr) => ( + + + {index < arr.length - 1 && } + + ))} + + {currencyFormatter.format(tariffCartData.price / 100)} + + ); } diff --git a/src/kitUI/Cart/DiscountTooltip.tsx b/src/kitUI/Cart/DiscountTooltip.tsx index 2badb49..2fbcdd1 100644 --- a/src/kitUI/Cart/DiscountTooltip.tsx +++ b/src/kitUI/Cart/DiscountTooltip.tsx @@ -2,27 +2,26 @@ import { Tooltip, Typography } from "@mui/material"; import { Discount, findDiscountFactor } from "@frontend/kitui"; import { formatDiscountFactor } from "@root/utils/formatDiscountFactor"; - interface Props { - discount: Discount; + discount: Discount; } export function DiscountTooltip({ discount }: Props) { - const discountText = formatDiscountFactor(findDiscountFactor(discount)); + const discountText = formatDiscountFactor(findDiscountFactor(discount)); - return discountText ? ( - - Слой: {discount.Layer} - Название: {discount.Name} - Описание: {discount.Description} - - } - > - {discountText} - - ) : ( - Ошибка поиска значения скидки - ); + return discountText ? ( + + Слой: {discount.Layer} + Название: {discount.Name} + Описание: {discount.Description} + + } + > + {discountText} + + ) : ( + Ошибка поиска значения скидки + ); } diff --git a/src/kitUI/CustomTextField.tsx b/src/kitUI/CustomTextField.tsx index e373820..d003880 100644 --- a/src/kitUI/CustomTextField.tsx +++ b/src/kitUI/CustomTextField.tsx @@ -1,46 +1,55 @@ import { SxProps, TextField, Theme, useTheme } from "@mui/material"; import { HTMLInputTypeAttribute, ChangeEvent } from "react"; -import {InputBaseProps} from "@mui/material/InputBase"; +import { InputBaseProps } from "@mui/material/InputBase"; - -export function CustomTextField({ id, label, value, name, onBlur,error, type, sx, onChange: setValue }: { - id: string; - label: string; - value: number | string | null; - name?: string; - onBlur?: InputBaseProps['onBlur']; - error?: boolean; - type?: HTMLInputTypeAttribute; - sx?: SxProps; - onChange: (e: ChangeEvent) => void; +export function CustomTextField({ + id, + label, + value, + name, + onBlur, + error, + type, + sx, + onChange: setValue, +}: { + id: string; + label: string; + value: number | string | null; + name?: string; + onBlur?: InputBaseProps["onBlur"]; + error?: boolean; + type?: HTMLInputTypeAttribute; + sx?: SxProps; + onChange: (e: ChangeEvent) => void; }) { - const theme = useTheme(); + const theme = useTheme(); - return ( - - ); -} \ No newline at end of file + return ( + + ); +} diff --git a/src/kitUI/CustomWrapper.tsx b/src/kitUI/CustomWrapper.tsx index 64b8bc6..9cdf8ce 100644 --- a/src/kitUI/CustomWrapper.tsx +++ b/src/kitUI/CustomWrapper.tsx @@ -3,26 +3,26 @@ import { useState } from "react"; import { Box, SxProps, Theme, Typography, useMediaQuery, useTheme } from "@mui/material"; interface CustomWrapperProps { - text: string; - sx?: SxProps; - result?: boolean; - children: JSX.Element; + text: string; + sx?: SxProps; + result?: boolean; + children: JSX.Element; } export const CustomWrapper = ({ text, sx, result, children }: CustomWrapperProps) => { - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - const upSm = useMediaQuery(theme.breakpoints.up("sm")); + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const upSm = useMediaQuery(theme.breakpoints.up("sm")); - const [isExpanded, setIsExpanded] = useState(false); + const [isExpanded, setIsExpanded] = useState(false); - return ( - - - setIsExpanded((prev) => !prev)} - sx={{ - height: "88px", - px: "20px", - display: "flex", - alignItems: "center", - justifyContent: "space-between", - cursor: "pointer", - userSelect: "none", - }} - > - - {text} - + ...sx, + }} + > + + setIsExpanded((prev) => !prev)} + sx={{ + height: "88px", + px: "20px", + display: "flex", + alignItems: "center", + justifyContent: "space-between", + cursor: "pointer", + userSelect: "none", + }} + > + + {text} + - - {result ? ( - <> - - - - + + {result ? ( + <> + + + + - - - ) : ( - - - - - )} - - - {isExpanded && <>{children}} - - - ); + + + ) : ( + + + + + )} + + + {isExpanded && <>{children}} + + + ); }; diff --git a/src/kitUI/cleverButton.tsx b/src/kitUI/cleverButton.tsx index 082856a..7f8ff2e 100644 --- a/src/kitUI/cleverButton.tsx +++ b/src/kitUI/cleverButton.tsx @@ -1,26 +1,21 @@ import { styled } from "@mui/material/styles"; -import { Button, Skeleton} from "@mui/material"; +import { Button, Skeleton } from "@mui/material"; const BeautifulButton = styled(Button)(({ theme }) => ({ - width: "250px", - margin: "15px auto", - padding: "20px 30px", - fontSize: 18 + width: "250px", + margin: "15px auto", + padding: "20px 30px", + fontSize: 18, })); interface Props { - isReady: boolean - text:string - type?: "button" | "reset" | "submit" + isReady: boolean; + text: string; + type?: "button" | "reset" | "submit"; } -export default ({ - isReady = true, - text, - type = "button" -}:Props) => { - - if (isReady) { - return {text} - } - return {text} -} +export default ({ isReady = true, text, type = "button" }: Props) => { + if (isReady) { + return {text}; + } + return {text}; +}; diff --git a/src/kitUI/datagrid.tsx b/src/kitUI/datagrid.tsx index b9d7790..d245c1b 100644 --- a/src/kitUI/datagrid.tsx +++ b/src/kitUI/datagrid.tsx @@ -1,28 +1,27 @@ import { DataGrid } from "@mui/x-data-grid"; import { styled } from "@mui/material/styles"; - export default styled(DataGrid)(({ theme }) => ({ - width: "100%", - minHeight: "400px", - margin: "10px 0", - color: theme.palette.secondary.main, - "& .MuiDataGrid-iconSeparator": { - display: "none" - }, - "& .css-levciy-MuiTablePagination-displayedRows": { - color: theme.palette.secondary.main - }, - "& .MuiSvgIcon-root": { - color: theme.palette.secondary.main - }, - "& .MuiTablePagination-selectLabel": { - color: theme.palette.secondary.main - }, - "& .MuiInputBase-root": { - color: theme.palette.secondary.main - }, - "& .MuiButton-text": { - color: theme.palette.secondary.main - }, + width: "100%", + minHeight: "400px", + margin: "10px 0", + color: theme.palette.secondary.main, + "& .MuiDataGrid-iconSeparator": { + display: "none", + }, + "& .css-levciy-MuiTablePagination-displayedRows": { + color: theme.palette.secondary.main, + }, + "& .MuiSvgIcon-root": { + color: theme.palette.secondary.main, + }, + "& .MuiTablePagination-selectLabel": { + color: theme.palette.secondary.main, + }, + "& .MuiInputBase-root": { + color: theme.palette.secondary.main, + }, + "& .MuiButton-text": { + color: theme.palette.secondary.main, + }, })) as typeof DataGrid; diff --git a/src/kitUI/input.tsx b/src/kitUI/input.tsx index 757351f..ff290d7 100644 --- a/src/kitUI/input.tsx +++ b/src/kitUI/input.tsx @@ -1,16 +1,16 @@ -import {TextField} from "@mui/material"; +import { TextField } from "@mui/material"; import { styled } from "@mui/material/styles"; export default styled(TextField)(({ theme }) => ({ - variant: "outlined", - height: "40px", - size: "small", - color: theme.palette.secondary.main, - width: "140px", - backgroundColor: theme.palette.content.main, - "& .MuiFormLabel-root": { - color: theme.palette.secondary.main, - }, - "& .Mui-focused": { - color: theme.palette.secondary.main, - } -})); \ No newline at end of file + variant: "outlined", + height: "40px", + size: "small", + color: theme.palette.secondary.main, + width: "140px", + backgroundColor: theme.palette.content.main, + "& .MuiFormLabel-root": { + color: theme.palette.secondary.main, + }, + "& .Mui-focused": { + color: theme.palette.secondary.main, + }, +})); diff --git a/src/kitUI/outlinedInput.tsx b/src/kitUI/outlinedInput.tsx index bcbd380..f39eaaa 100644 --- a/src/kitUI/outlinedInput.tsx +++ b/src/kitUI/outlinedInput.tsx @@ -1,18 +1,18 @@ import { styled } from "@mui/material/styles"; import { TextField } from "@mui/material"; export default styled(TextField)(({ theme }) => ({ - color: theme.palette.grayLight.main, - "& .MuiInputLabel-root": { - color: theme.palette.grayLight.main, - }, - "& .MuiFilledInput-root": { - border: theme.palette.grayLight.main + " 1px solid", - borderRadius: "0", - backgroundColor: theme.palette.hover.main, - 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", + borderRadius: "0", + backgroundColor: theme.palette.hover.main, + color: theme.palette.grayLight.main, + }, - "& .MuiFilledInput-root.Mui-error": { - fontSize: "8px", - }, + "& .MuiFilledInput-root.Mui-error": { + fontSize: "8px", + }, })); diff --git a/src/kitUI/privateRoute.tsx b/src/kitUI/privateRoute.tsx index e59e659..872cc2a 100644 --- a/src/kitUI/privateRoute.tsx +++ b/src/kitUI/privateRoute.tsx @@ -3,15 +3,15 @@ import * as React from "react"; import { useLocation, Navigate } from "react-router-dom"; interface Props { - children: JSX.Element; + children: JSX.Element; } export default function PrivateRoute({ children }: Props) { - const token = useToken(); - const location = useLocation(); - //Если пользователь авторизован, перенаправляем его на нужный путь. Иначе выкидываем в регистрацию - if (token) { - return children; - } - return ; -}; + const token = useToken(); + const location = useLocation(); + //Если пользователь авторизован, перенаправляем его на нужный путь. Иначе выкидываем в регистрацию + if (token) { + return children; + } + return ; +} diff --git a/src/kitUI/publicRoute.tsx b/src/kitUI/publicRoute.tsx index e6c1878..0840ff8 100644 --- a/src/kitUI/publicRoute.tsx +++ b/src/kitUI/publicRoute.tsx @@ -2,20 +2,19 @@ import { useLocation, Navigate } from "react-router-dom"; import { useToken } from "@frontend/kitui"; - interface Props { - children: JSX.Element; + children: JSX.Element; } const PublicRoute = ({ children }: Props) => { - const location = useLocation(); - const token = useToken(); + const location = useLocation(); + const token = useToken(); - if (token) { - return ; - } + if (token) { + return ; + } - return children; + return children; }; export default PublicRoute; diff --git a/src/model/discount.ts b/src/model/discount.ts index 463d4c6..b009aad 100644 --- a/src/model/discount.ts +++ b/src/model/discount.ts @@ -1,50 +1,51 @@ import { Discount } from "@frontend/kitui"; - export type GetDiscountResponse = { - Discounts: Discount[]; + Discounts: Discount[]; }; export const discountTypes = { - "purchasesAmount": "Лояльность", - "cartPurchasesAmount": "Корзина", - "service": "Сервис", - "privilege": "Товар", + purchasesAmount: "Лояльность", + cartPurchasesAmount: "Корзина", + service: "Сервис", + privilege: "Товар", } as const; export type DiscountType = keyof typeof discountTypes; export type CreateDiscountBody = { - Name: string; - Layer: 1 | 2 | 3 | 4; - Description: string; - Condition: { - Period: { - /** ISO string */ - From: string; - /** ISO string */ - To: string; - }; - User: string; - UserType: string; - Coupon: string; - PurchasesAmount: number; - CartPurchasesAmount: number; - Product: string; - Term: number; - Usage: number; - PriceFrom: number; - Group: string; - }; - Target: { - Factor: number; - TargetScope: "Sum" | "Group" | "Each"; - Overhelm: boolean; - TargetGroup: string; - Products: [{ - ID: string; - Factor: number; - Overhelm: false; - }]; - }; + Name: string; + Layer: 1 | 2 | 3 | 4; + Description: string; + Condition: { + Period: { + /** ISO string */ + From: string; + /** ISO string */ + To: string; + }; + User: string; + UserType: string; + Coupon: string; + PurchasesAmount: number; + CartPurchasesAmount: number; + Product: string; + Term: number; + Usage: number; + PriceFrom: number; + Group: string; + }; + Target: { + Factor: number; + TargetScope: "Sum" | "Group" | "Each"; + Overhelm: boolean; + TargetGroup: string; + Products: [ + { + ID: string; + Factor: number; + Overhelm: false; + }, + ]; + }; }; diff --git a/src/model/privilege.ts b/src/model/privilege.ts index b1a40ce..25d9bb6 100644 --- a/src/model/privilege.ts +++ b/src/model/privilege.ts @@ -1,16 +1,16 @@ export const SERVICE_LIST = [ - { - serviceKey: "templategen", - displayName: "Шаблонизатор документов", - }, - { - serviceKey: "squiz", - displayName: "Опросник", - }, - { - serviceKey: "dwarfener", - displayName: "Аналитика сокращателя", - }, - ] as const; - + { + serviceKey: "templategen", + displayName: "Шаблонизатор документов", + }, + { + serviceKey: "squiz", + displayName: "Опросник", + }, + { + serviceKey: "dwarfener", + displayName: "Аналитика сокращателя", + }, +] as const; + export type ServiceType = (typeof SERVICE_LIST)[number]["serviceKey"]; diff --git a/src/model/promocodes.ts b/src/model/promocodes.ts index d3805f6..34ca326 100644 --- a/src/model/promocodes.ts +++ b/src/model/promocodes.ts @@ -1,51 +1,51 @@ export type CreatePromocodeBody = { - codeword: string; - description: string; - greetings: string; - dueTo: number; - activationCount: number; - bonus: { - privilege: { - privilegeID: string; - amount: number; - serviceKey: string; - }; - discount: { - layer: number; - factor: number; - target: string; - threshold: number; - }; - }; + codeword: string; + description: string; + greetings: string; + dueTo: number; + activationCount: number; + bonus: { + privilege: { + privilegeID: string; + amount: number; + serviceKey: string; + }; + discount: { + layer: number; + factor: number; + target: string; + threshold: number; + }; + }; }; export type GetPromocodeListBody = { - page: number; - limit: number; - filter: { - active: boolean; - text?: string; - }; + page: number; + limit: number; + filter: { + active: boolean; + text?: string; + }; }; export type Promocode = CreatePromocodeBody & { - id: string; - dueTo: number; - activationLimit: number; - outdated: boolean; - offLimit: boolean; - delete: boolean; - createdAt: string; - fastLinks: string[]; + id: string; + dueTo: number; + activationLimit: number; + outdated: boolean; + offLimit: boolean; + delete: boolean; + createdAt: string; + fastLinks: string[]; }; export type PromocodeList = { - count: number; - items: Promocode[]; + count: number; + items: Promocode[]; }; export type PromocodeStatistics = { - id: string; - usageCount: number; - usageMap: Record; + id: string; + usageCount: number; + usageMap: Record; }; diff --git a/src/model/tariff.ts b/src/model/tariff.ts index 68535dc..1af595d 100644 --- a/src/model/tariff.ts +++ b/src/model/tariff.ts @@ -1,18 +1,18 @@ import { Privilege } from "@frontend/kitui"; export const SERVICE_LIST = [ - { - serviceKey: "templategen", - displayName: "Шаблонизатор документов", - }, - { - serviceKey: "squiz", - displayName: "Опросник", - }, - { - serviceKey: "dwarfener", - displayName: "Аналитика сокращателя", - }, + { + serviceKey: "templategen", + displayName: "Шаблонизатор документов", + }, + { + serviceKey: "squiz", + displayName: "Опросник", + }, + { + serviceKey: "dwarfener", + displayName: "Аналитика сокращателя", + }, ] as const; export type ServiceType = (typeof SERVICE_LIST)[number]["serviceKey"]; @@ -20,10 +20,10 @@ export type ServiceType = (typeof SERVICE_LIST)[number]["serviceKey"]; export type PrivilegeType = "unlim" | "gencount" | "activequiz" | "abcount" | "extended"; export type EditTariffRequestBody = { - description: string; - name: string; - order: number; - price: number; - isCustom: boolean; - privileges: Omit[]; + description: string; + name: string; + order: number; + price: number; + isCustom: boolean; + privileges: Omit[]; }; diff --git a/src/model/ticket.ts b/src/model/ticket.ts index be540e2..4be39e3 100644 --- a/src/model/ticket.ts +++ b/src/model/ticket.ts @@ -1,44 +1,42 @@ - - export interface CreateTicketRequest { - Title: string; - Message: string; -}; + Title: string; + Message: string; +} export interface CreateTicketResponse { - Ticket: string; -}; + Ticket: string; +} export interface SendTicketMessageRequest { - message: string; - ticket: string; - lang: string; - files: string[]; -}; + message: string; + ticket: string; + lang: string; + files: string[]; +} export type TicketStatus = "open"; export interface Ticket { - id: string; - user: string; - sess: string; - ans: string; - state: string; - top_message: TicketMessage; - title: string; - created_at: string; - updated_at: string; - rate: number; -}; + id: string; + user: string; + sess: string; + ans: string; + state: string; + top_message: TicketMessage; + title: string; + created_at: string; + updated_at: string; + rate: number; +} export interface TicketMessage { - id: string; - ticket_id: string; - user_id: string, - session_id: string; - message: string; - files: string[], - shown: { [key: string]: number; }, - request_screenshot: string, - created_at: string; -}; + id: string; + ticket_id: string; + user_id: string; + session_id: string; + message: string; + files: string[]; + shown: { [key: string]: number }; + request_screenshot: string; + created_at: string; +} diff --git a/src/model/user.ts b/src/model/user.ts index 2f38bfd..1bb8a52 100644 --- a/src/model/user.ts +++ b/src/model/user.ts @@ -1,5 +1,5 @@ export interface User { - ID: string; - Type: "" | "nko"; - PurchasesAmount: number; -} \ No newline at end of file + ID: string; + Type: "" | "nko"; + PurchasesAmount: number; +} diff --git a/src/pages/Authorization/restore.tsx b/src/pages/Authorization/restore.tsx index 2b54362..2997f8d 100644 --- a/src/pages/Authorization/restore.tsx +++ b/src/pages/Authorization/restore.tsx @@ -11,118 +11,118 @@ import LockOutlinedIcon from "@mui/icons-material/LockOutlined"; import OutlinedInput from "@kitUI/outlinedInput"; export default () => { - const theme = useTheme(); - const navigate = useNavigate(); - const [restore, setRestore] = React.useState(true); - const [isReady, setIsReady] = React.useState(true); - if (restore) { - return ( - { - setRestore(false); - }} - > -
- - *": { - marginTop: "15px", - }, - }} - > - - Восстановление пароля - - + const theme = useTheme(); + const navigate = useNavigate(); + const [restore, setRestore] = React.useState(true); + const [isReady, setIsReady] = React.useState(true); + if (restore) { + return ( + { + setRestore(false); + }} + > + + + *": { + marginTop: "15px", + }, + }} + > + + Восстановление пароля + + - *": { marginRight: "10px" } }}> - - - - - - Я помню пароль - - - - - - ); - } else { - return ( - {}} - > -
- - *": { - marginTop: "15px", - }, - }} - > - - Восстановление пароля - - + *": { marginRight: "10px" } }}> + + + + + + Я помню пароль + + + +
+
+ ); + } else { + return ( + {}} + > +
+ + *": { + marginTop: "15px", + }, + }} + > + + Восстановление пароля + + - *": { marginRight: "10px" } }}> - - - + *": { marginRight: "10px" } }}> + + + - - - Я помню пароль - - - -
-
- ); - } + + + Я помню пароль + +
+
+ +
+ ); + } }; diff --git a/src/pages/Authorization/signin.tsx b/src/pages/Authorization/signin.tsx index c7defab..d154e10 100644 --- a/src/pages/Authorization/signin.tsx +++ b/src/pages/Authorization/signin.tsx @@ -3,13 +3,7 @@ import { enqueueSnackbar } from "notistack"; import { useTheme } from "@mui/material/styles"; import { Formik, Field, Form, FormikHelpers } from "formik"; import { Link } from "react-router-dom"; -import { - Box, - Checkbox, - Typography, - FormControlLabel, - Button, useMediaQuery, -} from "@mui/material"; +import { Box, Checkbox, Typography, FormControlLabel, Button, useMediaQuery } from "@mui/material"; import Logo from "@pages/Logo"; import OutlinedInput from "@kitUI/outlinedInput"; import EmailOutlinedIcon from "@mui/icons-material/EmailOutlined"; @@ -17,210 +11,197 @@ import LockOutlinedIcon from "@mui/icons-material/LockOutlined"; import { signin } from "@root/api/auth"; interface Values { - email: string; - password: string; + email: string; + password: string; } function validate(values: Values) { - const errors = {} as any; + const errors = {} as any; - if (!values.email) { - errors.email = "Обязательное поле"; - } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(values.email)) { - errors.email = "Неверный формат эл. почты"; - } + if (!values.email) { + errors.email = "Обязательное поле"; + } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(values.email)) { + errors.email = "Неверный формат эл. почты"; + } - if (!values.password) { - errors.password = "Введите пароль"; - } + if (!values.password) { + errors.password = "Введите пароль"; + } - if (values.password && !/^[\S]{8,25}$/i.test(values.password)) { - errors.password = "Invalid password"; - } + if (values.password && !/^[\S]{8,25}$/i.test(values.password)) { + errors.password = "Invalid password"; + } - return errors; + return errors; } const SigninForm = () => { - const theme = useTheme(); - const navigate = useNavigate(); - const isMobile = useMediaQuery(theme.breakpoints.down(600)); - const initialValues: Values = { - email: "", - password: "", - }; + const theme = useTheme(); + const navigate = useNavigate(); + const isMobile = useMediaQuery(theme.breakpoints.down(600)); + const initialValues: Values = { + email: "", + password: "", + }; - const onSignFormSubmit = async ( - values: Values, - formikHelpers: FormikHelpers - ) => { - formikHelpers.setSubmitting(true); + const onSignFormSubmit = async (values: Values, formikHelpers: FormikHelpers) => { + formikHelpers.setSubmitting(true); - const [_, signinError] = await signin(values.email, values.password); + const [_, signinError] = await signin(values.email, values.password); - formikHelpers.setSubmitting(false); + formikHelpers.setSubmitting(false); - if (signinError) { - return enqueueSnackbar(signinError); - } + if (signinError) { + return enqueueSnackbar(signinError); + } - navigate("/users"); - }; + navigate("/users"); + }; - return ( - - {(props) => ( -
- - *": { - marginTop: "15px", - }, - padding: isMobile ? "0 16px" : undefined - }} - > - - - - Добро пожаловать - - - Мы рады что вы выбрали нас! - - - *": { marginRight: "10px" }, - }} - > - - - {props.touched.email && props.errors.email} - - } - /> - - *": { marginRight: "10px" }, - }} - > - - - {props.touched.password && props.errors.password} - - } - /> - - - - } - label="Запомнить этот компьютер" - /> - - - - Забыли пароль? - - - - - - У вас нет аккаунта?  - - - - Зарегестрируйтесь - - - - - -
- )} -
- ); + return ( + + {(props) => ( +
+ + *": { + marginTop: "15px", + }, + padding: isMobile ? "0 16px" : undefined, + }} + > + + + + Добро пожаловать + + + Мы рады что вы выбрали нас! + + + *": { marginRight: "10px" }, + }} + > + + + {props.touched.email && props.errors.email} + + } + /> + + *": { marginRight: "10px" }, + }} + > + + + {props.touched.password && props.errors.password} + + } + /> + + + + } + label="Запомнить этот компьютер" + /> + + + Забыли пароль? + + + + У вас нет аккаунта?  + + Зарегестрируйтесь + + + + +
+ )} +
+ ); }; -export default SigninForm; \ No newline at end of file +export default SigninForm; diff --git a/src/pages/Authorization/signup.tsx b/src/pages/Authorization/signup.tsx index 8177618..85da833 100644 --- a/src/pages/Authorization/signup.tsx +++ b/src/pages/Authorization/signup.tsx @@ -12,201 +12,192 @@ import LockOutlinedIcon from "@mui/icons-material/LockOutlined"; import { register } from "@root/api/auth"; interface Values { - email: string; - password: string; - repeatPassword: string; + email: string; + password: string; + repeatPassword: string; } function validate(values: Values) { - const errors: Partial = {}; + const errors: Partial = {}; - if (!values.email) { - errors.email = "Обязательное поле"; - } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(values.email)) { - errors.email = "Неверный формат эл. почты"; - } + if (!values.email) { + errors.email = "Обязательное поле"; + } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(values.email)) { + errors.email = "Неверный формат эл. почты"; + } - if (!values.password) { - errors.password = "Обязательное поле"; - } else if (!/^[\S]{8,25}$/i.test(values.password)) { - errors.password = "Неверный пароль"; - } - if (values.password !== values.repeatPassword) { - errors.repeatPassword = "Пароли не совпадают"; - } + if (!values.password) { + errors.password = "Обязательное поле"; + } else if (!/^[\S]{8,25}$/i.test(values.password)) { + errors.password = "Неверный пароль"; + } + if (values.password !== values.repeatPassword) { + errors.repeatPassword = "Пароли не совпадают"; + } - return errors; + return errors; } const SignUp = () => { - const navigate = useNavigate(); - const theme = useTheme(); + const navigate = useNavigate(); + const theme = useTheme(); - return ( - { - formikHelpers.setSubmitting(true); + return ( + { + formikHelpers.setSubmitting(true); - const [_, registerError] = await register( - values.email, - values.repeatPassword - ); + const [_, registerError] = await register(values.email, values.repeatPassword); - formikHelpers.setSubmitting(false); + formikHelpers.setSubmitting(false); - if (registerError) { - return enqueueSnackbar(registerError); - } + if (registerError) { + return enqueueSnackbar(registerError); + } - navigate("/users"); - }} - > - {(props) => ( -
- - *": { - marginTop: "15px", - }, - }} - > - - Новый аккаунт - - + navigate("/users"); + }} + > + {(props) => ( + + + *": { + marginTop: "15px", + }, + }} + > + + Новый аккаунт + + - - - Добро пожаловать - - - Мы рады что вы выбрали нас! - - - *": { marginRight: "10px" }, - }} - > - - - {props.touched.email && props.errors.email} - - } - /> - - *": { marginRight: "10px" }, - }} - > - - - {props.touched.password && props.errors.password} - - } - /> - - *": { marginRight: "10px" }, - }} - > - - - {props.touched.repeatPassword && - props.errors.repeatPassword} - - } - /> - - - - - У меня уже есть аккаунт - - - - - - )} -
- ); + + + Добро пожаловать + + + Мы рады что вы выбрали нас! + + + *": { marginRight: "10px" }, + }} + > + + + {props.touched.email && props.errors.email} + + } + /> + + *": { marginRight: "10px" }, + }} + > + + + {props.touched.password && props.errors.password} + + } + /> + + *": { marginRight: "10px" }, + }} + > + + + {props.touched.repeatPassword && props.errors.repeatPassword} + + } + /> + + + + У меня уже есть аккаунт + +
+ + + )} + + ); }; export default SignUp; diff --git a/src/pages/Error404/index.tsx b/src/pages/Error404/index.tsx index 3709b15..d92fb1e 100644 --- a/src/pages/Error404/index.tsx +++ b/src/pages/Error404/index.tsx @@ -1,65 +1,73 @@ import * as React from "react"; -import {Box, Button, Typography} from "@mui/material"; +import { Box, Button, Typography } from "@mui/material"; import { ThemeProvider } from "@mui/material"; import theme from "../../theme"; -import CssBaseline from '@mui/material/CssBaseline'; -import {Link} from "react-router-dom"; +import CssBaseline from "@mui/material/CssBaseline"; +import { Link } from "react-router-dom"; +const Error404: React.FC = () => { + return ( + + + + + + + + 4 + + + 0 + + + 4 + + + + + + + + + + + + ); +}; -const Error404: React.FC = () => { - return ( - - - - - - - - 4 - - - 0 - - - 4 - - - - - - - - - - - - - ); -} - - -export default Error404; \ No newline at end of file +export default Error404; diff --git a/src/pages/Logo/index.tsx b/src/pages/Logo/index.tsx index 86fcaf6..a4d6d1f 100644 --- a/src/pages/Logo/index.tsx +++ b/src/pages/Logo/index.tsx @@ -2,41 +2,42 @@ import * as React from "react"; import { Box, Typography } from "@mui/material"; import theme from "../../theme"; +const Authorization: React.FC = () => { + return ( + + + + PENA + + + HUB + + + + ); +}; -const Authorization: React.FC = () => { - return ( - - - - PENA - - - HUB - - - - ); -} - - -export default Authorization; \ No newline at end of file +export default Authorization; diff --git a/src/pages/Sections/index.tsx b/src/pages/Sections/index.tsx index 1036a98..dbd5c9b 100644 --- a/src/pages/Sections/index.tsx +++ b/src/pages/Sections/index.tsx @@ -2,171 +2,173 @@ import * as React from "react"; import { Box, Typography, Button } from "@mui/material"; import { ThemeProvider } from "@mui/material"; import theme from "../../theme"; -import CssBaseline from '@mui/material/CssBaseline'; +import CssBaseline from "@mui/material/CssBaseline"; import Logo from "../Logo"; +const Authorization: React.FC = () => { + return ( + + + + + + + -const Authorization: React.FC = () => { - return ( - - - - - - - - + + + Все пользователи + - - - - Все пользователи - - + + - - + + + Общая статистика + - - - - Общая статистика - - + + - - + + + Шаблонизатор документов + - - - - Шаблонизатор документов - - + + - - + + + Конструктор опросов + - - - - Конструктор опросов - - + + - - + + + Сокращатель ссылок + - - - - Сокращатель ссылок - - + + + + + + + + ); +}; - - - - - - - - - ); -} - - -export default Authorization; \ No newline at end of file +export default Authorization; diff --git a/src/pages/Setting/CardPrivilegie.tsx b/src/pages/Setting/CardPrivilegie.tsx index f4de843..19358b0 100644 --- a/src/pages/Setting/CardPrivilegie.tsx +++ b/src/pages/Setting/CardPrivilegie.tsx @@ -1,169 +1,177 @@ import { KeyboardEvent, useRef, useState } from "react"; import { enqueueSnackbar } from "notistack"; -import {Box, IconButton, TextField, Tooltip, Typography, useMediaQuery, useTheme} from "@mui/material"; +import { Box, IconButton, TextField, Tooltip, Typography, useMediaQuery, useTheme } from "@mui/material"; import ModeEditOutlineOutlinedIcon from "@mui/icons-material/ModeEditOutlineOutlined"; import { CustomPrivilege } from "@frontend/kitui"; import { putPrivilege } from "@root/api/privilegies"; -import SaveIcon from '@mui/icons-material/Save'; +import SaveIcon from "@mui/icons-material/Save"; import { currencyFormatter } from "@root/utils/currencyFormatter"; - interface CardPrivilege { - privilege: CustomPrivilege; + privilege: CustomPrivilege; } export const СardPrivilege = ({ privilege }: CardPrivilege) => { - const [inputOpen, setInputOpen] = useState(false); - const [inputValue, setInputValue] = useState(""); - const priceRef = useRef(null); - const theme = useTheme(); - const mobile = useMediaQuery(theme.breakpoints.down(600)); + const [inputOpen, setInputOpen] = useState(false); + const [inputValue, setInputValue] = useState(""); + const priceRef = useRef(null); + const theme = useTheme(); + const mobile = useMediaQuery(theme.breakpoints.down(600)); - const translationType = { - count: "за единицу", - day: "за день", - mb: "за МБ", - }; + const translationType = { + count: "за единицу", + day: "за день", + mb: "за МБ", + }; - const putPrivileges = async () => { + const putPrivileges = async () => { + const [, putedPrivilegeError] = await putPrivilege({ + name: privilege.name, + privilegeId: privilege.privilegeId, + serviceKey: privilege.serviceKey, + description: privilege.description, + amount: 1, + type: privilege.type, + value: privilege.value, + price: 100 * Number(inputValue), + }); - const [, putedPrivilegeError] = await putPrivilege({ - name: privilege.name, - privilegeId: privilege.privilegeId, - serviceKey: privilege.serviceKey, - description: privilege.description, - amount: 1, - type: privilege.type, - value: privilege.value, - price: 100 * Number(inputValue), - }); + if (putedPrivilegeError) { + return enqueueSnackbar(putedPrivilegeError); + } - if (putedPrivilegeError) { - return enqueueSnackbar(putedPrivilegeError); - } + if (!priceRef.current) { + return; + } - if (!priceRef.current) { - return; - } + enqueueSnackbar("Изменения сохранены"); - enqueueSnackbar("Изменения сохранены"); + priceRef.current.innerText = "price: " + inputValue; + setInputValue(""); + setInputOpen(false); + }; - priceRef.current.innerText = "price: " + inputValue; - setInputValue(""); - setInputOpen(false); - }; + const onTextfieldKeyDown = (event: KeyboardEvent) => { + if (event.key === "Escape") { + return setInputOpen(false); + } + if (event.key === "Enter" && inputValue !== "") { + putPrivileges(); + setInputOpen(false); + } + }; - const onTextfieldKeyDown = (event: KeyboardEvent) => { - if (event.key === "Escape") { - return setInputOpen(false); - } - if (event.key === "Enter" && inputValue !== "") { - putPrivileges(); - setInputOpen(false); - } - }; + function handleSavePrice() { + setInputOpen(false); + if (!inputValue) return; - function handleSavePrice() { - setInputOpen(false); - if (!inputValue) return; + putPrivileges(); + } - putPrivileges(); -} + return ( + - - - - {privilege.name} - - {privilege.description}} - > - - - - - - - - setInputOpen(!inputOpen)}> - - - - - - {inputOpen ? ( - setInputValue(event.target.value)} - sx={{ - alignItems: "center", - maxWidth: "400px", - width: "100%", - marginLeft: "5px", - "& .MuiInputBase-root": { - backgroundColor: "#F2F3F7", - height: "48px", - }, - }} - inputProps={{ - sx: { - borderRadius: "10px", - fontSize: "18px", - lineHeight: "21px", - py: 0, - }, - }} - InputProps={{ - endAdornment: ( - - - - ) - }} - /> - ) : ( -
- price: {currencyFormatter.format(privilege.price / 100)} -
- )} - {translationType[privilege.type]} -
-
- ); + display: "flex", + alignItems: "center", + border: "1px solid gray", + }} + > + + + + {privilege.name} + + {privilege.description}} + > + + + + + + + + setInputOpen(!inputOpen)}> + + + + + + {inputOpen ? ( + setInputValue(event.target.value)} + sx={{ + alignItems: "center", + maxWidth: "400px", + width: "100%", + marginLeft: "5px", + "& .MuiInputBase-root": { + backgroundColor: "#F2F3F7", + height: "48px", + }, + }} + inputProps={{ + sx: { + borderRadius: "10px", + fontSize: "18px", + lineHeight: "21px", + py: 0, + }, + }} + InputProps={{ + endAdornment: ( + + + + ), + }} + /> + ) : ( +
+ price: {currencyFormatter.format(privilege.price / 100)} +
+ )} + {translationType[privilege.type]} +
+ + ); }; diff --git a/src/pages/Setting/ConditionalRender.tsx b/src/pages/Setting/ConditionalRender.tsx index c9f1919..b829654 100644 --- a/src/pages/Setting/ConditionalRender.tsx +++ b/src/pages/Setting/ConditionalRender.tsx @@ -1,47 +1,47 @@ import React, { useEffect, useState } from "react"; type ConditionalRenderProps = { - isLoading: boolean; - role: string; - childrenUser?: JSX.Element; - childrenAdmin?: JSX.Element; - childrenManager?: JSX.Element; + isLoading: boolean; + role: string; + childrenUser?: JSX.Element; + childrenAdmin?: JSX.Element; + childrenManager?: JSX.Element; }; const ConditionalRender = ({ - isLoading, - role, - childrenUser, - childrenAdmin, - childrenManager, + isLoading, + role, + childrenUser, + childrenAdmin, + childrenManager, }: ConditionalRenderProps): JSX.Element => { - // const [role, setRole] = useState(""); + // const [role, setRole] = useState(""); - // useEffect(() => { - // const axiosAccount = async () => { - // try { - // const { data } = await axios.get(process.env.REACT_APP_DOMAIN + "/user/643e23f3dba63ba17272664d"); - // setRole(data.role); - // } catch (error) { - // console.error("Ошибка при получение роли пользавателя"); - // } - // }; - // axiosAccount(); - // }, [role]); + // useEffect(() => { + // const axiosAccount = async () => { + // try { + // const { data } = await axios.get(process.env.REACT_APP_DOMAIN + "/user/643e23f3dba63ba17272664d"); + // setRole(data.role); + // } catch (error) { + // console.error("Ошибка при получение роли пользавателя"); + // } + // }; + // axiosAccount(); + // }, [role]); - if (!isLoading) { - if (role === "admin") { - return childrenAdmin ? childrenAdmin :
Администратор
; - } - if (role === "user") { - return childrenUser ? childrenUser :
Пользователь
; - } - if (role === "manager") { - return childrenManager ? childrenManager :
Менеджер
; - } - } + if (!isLoading) { + if (role === "admin") { + return childrenAdmin ? childrenAdmin :
Администратор
; + } + if (role === "user") { + return childrenUser ? childrenUser :
Пользователь
; + } + if (role === "manager") { + return childrenManager ? childrenManager :
Менеджер
; + } + } - return ; + return ; }; export default ConditionalRender; diff --git a/src/pages/Setting/FormCreateRoles.tsx b/src/pages/Setting/FormCreateRoles.tsx index 900a556..3e3a037 100644 --- a/src/pages/Setting/FormCreateRoles.tsx +++ b/src/pages/Setting/FormCreateRoles.tsx @@ -1,81 +1,83 @@ import { useState } from "react"; import { - Button, - Checkbox, - FormControl, - ListItemText, - MenuItem, - Select, - SelectChangeEvent, - TextField, useMediaQuery, useTheme, + Button, + Checkbox, + FormControl, + ListItemText, + MenuItem, + Select, + SelectChangeEvent, + TextField, + useMediaQuery, + useTheme, } from "@mui/material"; import { MOCK_DATA_USERS } from "@root/api/roles"; const ITEM_HEIGHT = 48; const ITEM_PADDING_TOP = 8; const MenuProps = { - PaperProps: { - style: { - maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP, - }, - }, + PaperProps: { + style: { + maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP, + }, + }, }; export default function CreateForm() { - const [personName, setPersonName] = useState([]); - const theme = useTheme(); - const mobile = useMediaQuery(theme.breakpoints.down(600)); - const handleChange = (event: SelectChangeEvent) => { - const { - target: { value }, - } = event; - setPersonName(typeof value === "string" ? value.split(",") : value); - }; + const [personName, setPersonName] = useState([]); + const theme = useTheme(); + const mobile = useMediaQuery(theme.breakpoints.down(600)); + const handleChange = (event: SelectChangeEvent) => { + const { + target: { value }, + } = event; + setPersonName(typeof value === "string" ? value.split(",") : value); + }; - return ( - <> - - - - - - - ); + return ( + <> + + + + + + + ); } diff --git a/src/pages/Setting/FormDeleteRoles.tsx b/src/pages/Setting/FormDeleteRoles.tsx index 11f1bae..08f6fd6 100644 --- a/src/pages/Setting/FormDeleteRoles.tsx +++ b/src/pages/Setting/FormDeleteRoles.tsx @@ -1,13 +1,13 @@ import { useState } from "react"; import { - Button, - Checkbox, - FormControl, - ListItemText, - MenuItem, - Select, - SelectChangeEvent, - TextField, + Button, + Checkbox, + FormControl, + ListItemText, + MenuItem, + Select, + SelectChangeEvent, + TextField, } from "@mui/material"; import { MOCK_DATA_USERS } from "@root/api/roles"; import { deleteRole } from "@root/api/roles"; @@ -16,82 +16,76 @@ import { enqueueSnackbar } from "notistack"; const ITEM_HEIGHT = 48; const ITEM_PADDING_TOP = 8; const MenuProps = { - PaperProps: { - style: { - maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP, - }, - }, + PaperProps: { + style: { + maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP, + }, + }, }; export default function DeleteForm() { - const [personName, setPersonName] = useState([]); - const [roleId, setRoleId] = useState(); + const [personName, setPersonName] = useState([]); + const [roleId, setRoleId] = useState(); - const handleChange = (event: SelectChangeEvent) => { - const { - target: { value }, - } = event; - setPersonName(typeof value === "string" ? value.split(",") : value); - }; + const handleChange = (event: SelectChangeEvent) => { + const { + target: { value }, + } = event; + setPersonName(typeof value === "string" ? value.split(",") : value); + }; - const rolesDelete = async (id = "") => { - const [_, deletedRoleError] = await deleteRole(id); + const rolesDelete = async (id = "") => { + const [_, deletedRoleError] = await deleteRole(id); - if (deletedRoleError) { - return enqueueSnackbar(deletedRoleError); - } - }; + if (deletedRoleError) { + return enqueueSnackbar(deletedRoleError); + } + }; - return ( - <> - - - - - - - ); + return ( + <> + + + + + + + ); } diff --git a/src/pages/Setting/ListPrivilegie.tsx b/src/pages/Setting/ListPrivilegie.tsx index 62618a6..cb42cbe 100644 --- a/src/pages/Setting/ListPrivilegie.tsx +++ b/src/pages/Setting/ListPrivilegie.tsx @@ -6,21 +6,17 @@ import { requestPrivileges } from "@root/services/privilegies.service"; import { СardPrivilege } from "./CardPrivilegie"; export default function ListPrivilege() { - const privileges = usePrivilegeStore((state) => state.privileges); + const privileges = usePrivilegeStore((state) => state.privileges); - useEffect(() => { - requestPrivileges(); - }, []); + useEffect(() => { + requestPrivileges(); + }, []); - return ( - <> - {privileges.map(privilege => ( - <СardPrivilege - key={privilege._id} - privilege={privilege} - /> - ) - )} - - ); + return ( + <> + {privileges.map((privilege) => ( + <СardPrivilege key={privilege._id} privilege={privilege} /> + ))} + + ); } diff --git a/src/pages/Setting/PrivilegiesWrapper.tsx b/src/pages/Setting/PrivilegiesWrapper.tsx index 6926678..d1fe0b4 100644 --- a/src/pages/Setting/PrivilegiesWrapper.tsx +++ b/src/pages/Setting/PrivilegiesWrapper.tsx @@ -5,24 +5,24 @@ import { Box, SxProps, Theme, Typography, useMediaQuery, useTheme } from "@mui/m import ListPrivilege from "./ListPrivilegie"; interface CustomWrapperProps { - text: string; - sx?: SxProps; - result?: boolean; + text: string; + sx?: SxProps; + result?: boolean; } export const PrivilegesWrapper = ({ text, sx, result }: CustomWrapperProps) => { - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - const upSm = useMediaQuery(theme.breakpoints.up("sm")); - const [isExpanded, setIsExpanded] = useState(false); + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const upSm = useMediaQuery(theme.breakpoints.up("sm")); + const [isExpanded, setIsExpanded] = useState(false); - return ( - { 0px 2.76726px 8.55082px rgba(210, 208, 225, 0.067 4749)`, - ...sx, - }} - > - - setIsExpanded((prev) => !prev)} - sx={{ - height: "88px", - px: "20px", - display: "flex", - alignItems: "center", - justifyContent: "space-between", - cursor: "pointer", - userSelect: "none", - }} - > - - {text} - + ...sx, + }} + > + + setIsExpanded((prev) => !prev)} + sx={{ + height: "88px", + px: "20px", + display: "flex", + alignItems: "center", + justifyContent: "space-between", + cursor: "pointer", + userSelect: "none", + }} + > + + {text} + - - {result ? ( - <> - - - - + + {result ? ( + <> + + + + - - - ) : ( - - - - - )} - - - {isExpanded && } - - - ); + + + ) : ( + + + + + )} + + + {isExpanded && } + + + ); }; diff --git a/src/pages/Setting/SettingRoles.tsx b/src/pages/Setting/SettingRoles.tsx index 3f81964..a262637 100644 --- a/src/pages/Setting/SettingRoles.tsx +++ b/src/pages/Setting/SettingRoles.tsx @@ -1,13 +1,13 @@ import { - AccordionDetails, - Table, - TableBody, - TableCell, - TableHead, - TableRow, - Typography, - useMediaQuery, - useTheme + AccordionDetails, + Table, + TableBody, + TableCell, + TableHead, + TableRow, + Typography, + useMediaQuery, + useTheme, } from "@mui/material"; import { CustomWrapper } from "@root/kitUI/CustomWrapper"; @@ -19,118 +19,116 @@ import { PrivilegesWrapper } from "./PrivilegiesWrapper"; import theme from "../../theme"; export const SettingRoles = (): JSX.Element => { - const theme = useTheme(); - const mobile = useMediaQuery(theme.breakpoints.down(600)); - return ( - - - - - - - - Настройки ролей - - - - + const theme = useTheme(); + const mobile = useMediaQuery(theme.breakpoints.down(600)); + return ( + + +
+ + + + + Настройки ролей + + + + - - - - - -
- - - - - - Удаление ролей - - - - + + + + + +
+ + + + + + Удаление ролей + + + + - - - - - -
- - } - /> + + + + + + + + } + /> - -
- ); + + + ); }; diff --git a/src/pages/dashboard/Content/DiscountManagement/ControlPanel.tsx b/src/pages/dashboard/Content/DiscountManagement/ControlPanel.tsx index 65c7c63..7d35e7b 100644 --- a/src/pages/dashboard/Content/DiscountManagement/ControlPanel.tsx +++ b/src/pages/dashboard/Content/DiscountManagement/ControlPanel.tsx @@ -1,6 +1,6 @@ import { enqueueSnackbar } from "notistack"; import { GridSelectionModel } from "@mui/x-data-grid"; -import {Box, Button, useMediaQuery, useTheme} from "@mui/material"; +import { Box, Button, useMediaQuery, useTheme } from "@mui/material"; import { changeDiscount } from "@root/api/discounts"; import { findDiscountsById } from "@root/stores/discounts"; @@ -9,56 +9,58 @@ import { requestDiscounts } from "@root/services/discounts.service"; import { mutate } from "swr"; interface Props { - selectedRows: GridSelectionModel; + selectedRows: GridSelectionModel; } export default function DiscountDataGrid({ selectedRows }: Props) { - const theme = useTheme(); - const mobile = useMediaQuery(theme.breakpoints.down(400)); - const changeData = async (isActive: boolean) => { - let done = 0; - let fatal = 0; + const theme = useTheme(); + const mobile = useMediaQuery(theme.breakpoints.down(400)); + const changeData = async (isActive: boolean) => { + let done = 0; + let fatal = 0; - for (const id of selectedRows) { - const discount = findDiscountsById(String(id)); + for (const id of selectedRows) { + const discount = findDiscountsById(String(id)); - if (!discount) { - return enqueueSnackbar("Скидка не найдена"); - } + if (!discount) { + return enqueueSnackbar("Скидка не найдена"); + } - const [, changedDiscountError] = await changeDiscount(String(id), { - ...discount, - Deprecated: isActive, - }); + const [, changedDiscountError] = await changeDiscount(String(id), { + ...discount, + Deprecated: isActive, + }); - if (changedDiscountError) { - done += 1; - } else { - fatal += 1; - } - mutate("discounts"); - } + if (changedDiscountError) { + done += 1; + } else { + fatal += 1; + } + mutate("discounts"); + } - await requestDiscounts(); + await requestDiscounts(); - if (done) { - enqueueSnackbar("Успешно изменён статус " + done + " скидок"); - } + if (done) { + enqueueSnackbar("Успешно изменён статус " + done + " скидок"); + } - if (fatal) { - enqueueSnackbar(fatal + " скидок не изменили статус"); - } - }; + if (fatal) { + enqueueSnackbar(fatal + " скидок не изменили статус"); + } + }; - return ( - - - - - ); + return ( + + + + + ); } diff --git a/src/pages/dashboard/Content/DiscountManagement/CreateDiscount.tsx b/src/pages/dashboard/Content/DiscountManagement/CreateDiscount.tsx index 84dbb36..3a26fd5 100644 --- a/src/pages/dashboard/Content/DiscountManagement/CreateDiscount.tsx +++ b/src/pages/dashboard/Content/DiscountManagement/CreateDiscount.tsx @@ -1,22 +1,20 @@ import { - Box, - Typography, - Button, - useTheme, - FormControl, - FormLabel, - RadioGroup, - FormControlLabel, - Radio, - InputLabel, TextField, + Box, + Typography, + Button, + useTheme, + FormControl, + FormLabel, + RadioGroup, + FormControlLabel, + Radio, + InputLabel, + TextField, } from "@mui/material"; import MenuItem from "@mui/material/MenuItem"; import Select, { SelectChangeEvent } from "@mui/material/Select"; import { SERVICE_LIST, ServiceType } from "@root/model/tariff"; -import { - resetPrivilegeArray, - usePrivilegeStore, -} from "@root/stores/privilegesStore"; +import { resetPrivilegeArray, usePrivilegeStore } from "@root/stores/privilegesStore"; import { addDiscount } from "@root/stores/discounts"; import { enqueueSnackbar } from "notistack"; import { DiscountType, discountTypes } from "@root/model/discount"; @@ -26,494 +24,480 @@ import { Formik, Field, Form, FormikHelpers } from "formik"; import { mutate } from "swr"; interface Values { - discountNameField: string, - discountDescriptionField: string, - discountFactorField: string, - serviceType: string, - discountType: DiscountType, - purchasesAmountField: string, - cartPurchasesAmountField: string, - discountMinValueField: string, - privilegeIdField: string, + discountNameField: string; + discountDescriptionField: string; + discountFactorField: string; + serviceType: string; + discountType: DiscountType; + purchasesAmountField: string; + cartPurchasesAmountField: string; + discountMinValueField: string; + privilegeIdField: string; } export default function CreateDiscount() { - const theme = useTheme(); - const privileges = usePrivilegeStore((state) => state.privileges); + const theme = useTheme(); + const privileges = usePrivilegeStore((state) => state.privileges); - usePrivileges({ onNewPrivileges: resetPrivilegeArray }); + usePrivileges({ onNewPrivileges: resetPrivilegeArray }); - const initialValues: Values = { - discountNameField: "", - discountDescriptionField: "", - discountFactorField: "", - serviceType: "", - discountType: "purchasesAmount", - purchasesAmountField: "", - cartPurchasesAmountField: "", - discountMinValueField: "", - privilegeIdField: "", - } + const initialValues: Values = { + discountNameField: "", + discountDescriptionField: "", + discountFactorField: "", + serviceType: "", + discountType: "purchasesAmount", + purchasesAmountField: "", + cartPurchasesAmountField: "", + discountMinValueField: "", + privilegeIdField: "", + }; - const handleCreateDiscount = async( - values: Values, - formikHelpers: FormikHelpers - ) => { - const purchasesAmount = Number(parseFloat(values.purchasesAmountField.replace(",", "."))) * 100; - - const discountFactor = - (100 - parseFloat(values.discountFactorField.replace(",", "."))) / 100; - const cartPurchasesAmount = Number(parseFloat( - values.cartPurchasesAmountField.replace(",", ".")) * 100 - ); - const discountMinValue = Number(parseFloat( - values.discountMinValueField.replace(",", ".")) * 100 - ); + const handleCreateDiscount = async (values: Values, formikHelpers: FormikHelpers) => { + const purchasesAmount = Number(parseFloat(values.purchasesAmountField.replace(",", "."))) * 100; - const [createdDiscountResponse, createdDiscountError] = - await createDiscount({ - cartPurchasesAmount, - discountFactor, - discountMinValue, - purchasesAmount, - discountDescription: values.discountDescriptionField, - discountName: values.discountNameField, - startDate: new Date().toISOString(), - endDate: new Date(Date.now() + 1000 * 3600 * 24 * 30).toISOString(), - serviceType: values.serviceType, - discountType: values.discountType, - privilegeId: values.privilegeIdField, - }); + const discountFactor = (100 - parseFloat(values.discountFactorField.replace(",", "."))) / 100; + const cartPurchasesAmount = Number(parseFloat(values.cartPurchasesAmountField.replace(",", ".")) * 100); + const discountMinValue = Number(parseFloat(values.discountMinValueField.replace(",", ".")) * 100); - if (createdDiscountError) { - console.error("Error creating discount", createdDiscountError); + const [createdDiscountResponse, createdDiscountError] = await createDiscount({ + cartPurchasesAmount, + discountFactor, + discountMinValue, + purchasesAmount, + discountDescription: values.discountDescriptionField, + discountName: values.discountNameField, + startDate: new Date().toISOString(), + endDate: new Date(Date.now() + 1000 * 3600 * 24 * 30).toISOString(), + serviceType: values.serviceType, + discountType: values.discountType, + privilegeId: values.privilegeIdField, + }); - return enqueueSnackbar(createdDiscountError); - } + if (createdDiscountError) { + console.error("Error creating discount", createdDiscountError); - if (createdDiscountResponse) { - mutate("discounts"); - addDiscount(createdDiscountResponse); - } - } + return enqueueSnackbar(createdDiscountError); + } - const validateFulledFields = (values: Values) => { - const errors = {} as any; - if (values.discountNameField.length === 0) { - errors.discountNameField = 'Поле "Имя" пустое' - } - if (values.discountDescriptionField.length === 0) { - errors.discountDescriptionField = 'Поле "Описание" пустое' - } - if (((100 - parseFloat(values.discountFactorField.replace(",", "."))) / 100) < 0) { - errors.discountFactorField = "Процент скидки не может быть больше 100" - } - if (!isFinite(((100 - parseFloat(values.discountFactorField.replace(",", "."))) / 100))) { - errors.discountFactorField = 'Поле "Процент скидки" не число' - } - if (values.discountType === "privilege" && !values.privilegeIdField) { - errors.privilegeIdField = "Привилегия не выбрана" - } - if (values.discountType === "service" && !values.serviceType) { - errors.serviceType = "Сервис не выбран" - } - if (values.discountType === "purchasesAmount" && !isFinite(parseFloat(values.purchasesAmountField.replace(",", ".")))) { - errors.purchasesAmountField = 'Поле "Внесено больше" не число' - } - if (values.discountType === "cartPurchasesAmount" && !isFinite(parseFloat(values.cartPurchasesAmountField.replace(",", ".")))) { - errors.cartPurchasesAmountField = 'Поле "Объём в корзине" не число' - } - if (values.discountType === ("service" || "privilege") && !isFinite(parseFloat(values.discountMinValueField.replace(",", ".")))) { - errors.discountMinValueField = 'Поле "Минимальное значение" не число' - } - console.error(errors) - return errors; - } + if (createdDiscountResponse) { + mutate("discounts"); + addDiscount(createdDiscountResponse); + } + }; + const validateFulledFields = (values: Values) => { + const errors = {} as any; + if (values.discountNameField.length === 0) { + errors.discountNameField = 'Поле "Имя" пустое'; + } + if (values.discountDescriptionField.length === 0) { + errors.discountDescriptionField = 'Поле "Описание" пустое'; + } + if ((100 - parseFloat(values.discountFactorField.replace(",", "."))) / 100 < 0) { + errors.discountFactorField = "Процент скидки не может быть больше 100"; + } + if (!isFinite((100 - parseFloat(values.discountFactorField.replace(",", "."))) / 100)) { + errors.discountFactorField = 'Поле "Процент скидки" не число'; + } + if (values.discountType === "privilege" && !values.privilegeIdField) { + errors.privilegeIdField = "Привилегия не выбрана"; + } + if (values.discountType === "service" && !values.serviceType) { + errors.serviceType = "Сервис не выбран"; + } + if ( + values.discountType === "purchasesAmount" && + !isFinite(parseFloat(values.purchasesAmountField.replace(",", "."))) + ) { + errors.purchasesAmountField = 'Поле "Внесено больше" не число'; + } + if ( + values.discountType === "cartPurchasesAmount" && + !isFinite(parseFloat(values.cartPurchasesAmountField.replace(",", "."))) + ) { + errors.cartPurchasesAmountField = 'Поле "Объём в корзине" не число'; + } + if ( + values.discountType === ("service" || "privilege") && + !isFinite(parseFloat(values.discountMinValueField.replace(",", "."))) + ) { + errors.discountMinValueField = 'Поле "Минимальное значение" не число'; + } + console.error(errors); + return errors; + }; - return ( - - {(props) => ( -
- - - {props.errors.discountNameField} - - } - InputProps={{ - style: { - backgroundColor: theme.palette.content.main, - color: theme.palette.secondary.main, - } - }} - InputLabelProps={{ - style: { - color: theme.palette.secondary.main - } - }} - /> - - {props.errors.discountDescriptionField} - - } - InputProps={{ - style: { - backgroundColor: theme.palette.content.main, - color: theme.palette.secondary.main, - } - }} - InputLabelProps={{ - style: { - color: theme.palette.secondary.main - } - }} - /> - - Условия: - - - {props.errors.discountFactorField} - - } - InputProps={{ - style: { - backgroundColor: theme.palette.content.main, - color: theme.palette.secondary.main, - } - }} - InputLabelProps={{ - style: { - color: theme.palette.secondary.main - } - }} - /> - - - Тип скидки - - - ) => { - props.setFieldValue("discountType", event.target.value as DiscountType); - }} - onBlur={props.handleBlur} - > - {Object.keys(discountTypes).map((type) => ( - } - label={discountTypes[type as DiscountType]} - /> - ))} - - - {props.values.discountType === "purchasesAmount" && ( - { - props.setFieldValue("purchasesAmountField", e.target.value.replace(/[^\d]/g, '')) - }} - value={props.values.purchasesAmountField} - onBlur={props.handleBlur} - sx={{ - marginTop: "15px", - }} - helperText={ - - {props.errors.purchasesAmountField} - - } - InputProps={{ - style: { - backgroundColor: theme.palette.content.main, - color: theme.palette.secondary.main, - } - }} - InputLabelProps={{ - style: { - color: theme.palette.secondary.main - } - }} - /> - )} - {props.values.discountType === "cartPurchasesAmount" && ( - { - props.setFieldValue("cartPurchasesAmountField", e.target.value.replace(/[^\d]/g, '')) - }} - value={props.values.cartPurchasesAmountField} - onBlur={props.handleBlur} - sx={{ - marginTop: "15px", - }} - helperText={ - - {props.errors.cartPurchasesAmountField} - - } - InputProps={{ - style: { - backgroundColor: theme.palette.content.main, - color: theme.palette.secondary.main, - } - }} - InputLabelProps={{ - style: { - color: theme.palette.secondary.main - } - }} - /> - )} - {props.values.discountType === "service" && ( - <> - - { - props.setFieldValue("discountMinValueField", e.target.value.replace(/[^\d]/g, '')) - }} - value={props.values.discountMinValueField} - error={props.touched.discountMinValueField && !!props.errors.discountMinValueField} - sx={{ - marginTop: "15px", - }} - helperText={ - - {props.errors.discountMinValueField} - - } - InputProps={{ - style: { - backgroundColor: theme.palette.content.main, - color: theme.palette.secondary.main, - } - }} - InputLabelProps={{ - style: { - color: theme.palette.secondary.main - } - }} - /> - - )} - {props.values.discountType === "privilege" && ( - <> - - - Привилегия - - - - { - props.setFieldValue("discountMinValueField", e.target.value.replace(/[^\d]/g, '')) - }} - value={props.values.discountMinValueField} - error={props.touched.discountMinValueField && !!props.errors.discountMinValueField} - sx={{ - marginTop: "15px", - }} - helperText={ - - {props.errors.discountMinValueField} - - } - InputProps={{ - style: { - backgroundColor: theme.palette.content.main, - color: theme.palette.secondary.main, - } - }} - InputLabelProps={{ - style: { - color: theme.palette.secondary.main - } - }} - /> - - )} - - - - -
- )} -
- - ); + return ( + + {(props) => ( +
+ + {props.errors.discountNameField} + } + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + }, + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main, + }, + }} + /> + + {props.errors.discountDescriptionField} + + } + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + }, + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main, + }, + }} + /> + + Условия: + + {props.errors.discountFactorField} + } + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + }, + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main, + }, + }} + /> + + + Тип скидки + + ) => { + props.setFieldValue("discountType", event.target.value as DiscountType); + }} + onBlur={props.handleBlur} + > + {Object.keys(discountTypes).map((type) => ( + } + label={discountTypes[type as DiscountType]} + /> + ))} + + + {props.values.discountType === "purchasesAmount" && ( + { + props.setFieldValue("purchasesAmountField", e.target.value.replace(/[^\d]/g, "")); + }} + value={props.values.purchasesAmountField} + onBlur={props.handleBlur} + sx={{ + marginTop: "15px", + }} + helperText={ + {props.errors.purchasesAmountField} + } + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + }, + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main, + }, + }} + /> + )} + {props.values.discountType === "cartPurchasesAmount" && ( + { + props.setFieldValue("cartPurchasesAmountField", e.target.value.replace(/[^\d]/g, "")); + }} + value={props.values.cartPurchasesAmountField} + onBlur={props.handleBlur} + sx={{ + marginTop: "15px", + }} + helperText={ + + {props.errors.cartPurchasesAmountField} + + } + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + }, + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main, + }, + }} + /> + )} + {props.values.discountType === "service" && ( + <> + + { + props.setFieldValue("discountMinValueField", e.target.value.replace(/[^\d]/g, "")); + }} + value={props.values.discountMinValueField} + error={props.touched.discountMinValueField && !!props.errors.discountMinValueField} + sx={{ + marginTop: "15px", + }} + helperText={ + + {props.errors.discountMinValueField} + + } + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + }, + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main, + }, + }} + /> + + )} + {props.values.discountType === "privilege" && ( + <> + + + Привилегия + + + + { + props.setFieldValue("discountMinValueField", e.target.value.replace(/[^\d]/g, "")); + }} + value={props.values.discountMinValueField} + error={props.touched.discountMinValueField && !!props.errors.discountMinValueField} + sx={{ + marginTop: "15px", + }} + helperText={ + + {props.errors.discountMinValueField} + + } + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + }, + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main, + }, + }} + /> + + )} + + + + +
+ )} +
+ ); } diff --git a/src/pages/dashboard/Content/DiscountManagement/DatePickers.tsx b/src/pages/dashboard/Content/DiscountManagement/DatePickers.tsx index c769219..59f203d 100644 --- a/src/pages/dashboard/Content/DiscountManagement/DatePickers.tsx +++ b/src/pages/dashboard/Content/DiscountManagement/DatePickers.tsx @@ -3,133 +3,133 @@ import { DesktopDatePicker } from "@mui/x-date-pickers/DesktopDatePicker"; import { useState } from "react"; export default function DatePickers() { - const theme = useTheme(); - const [isInfinite, setIsInfinite] = useState(false); - const [startDate, setStartDate] = useState(new Date()); - const [endDate, setEndDate] = useState(new Date()); + const theme = useTheme(); + const [isInfinite, setIsInfinite] = useState(false); + const [startDate, setStartDate] = useState(new Date()); + const [endDate, setEndDate] = useState(new Date()); - return ( - <> - - Дата действия: - - - - С - - { - if (e) { - setStartDate(e); - } - }} - renderInput={(params) => } - InputProps={{ - sx: { - height: "40px", - color: theme.palette.secondary.main, - border: "1px solid", - borderColor: theme.palette.secondary.main, - "& .MuiSvgIcon-root": { color: theme.palette.secondary.main }, - }, - }} - /> - - по - - { - if (e) { - setEndDate(e); - } - }} - renderInput={(params) => } - InputProps={{ - sx: { - height: "40px", - color: theme.palette.secondary.main, - border: "1px solid", - borderColor: theme.palette.secondary.main, - "& .MuiSvgIcon-root": { color: theme.palette.secondary.main }, - }, - }} - /> - - - - setIsInfinite((p) => !p)} - /> - - - Бессрочно - - - - ); + return ( + <> + + Дата действия: + + + + С + + { + if (e) { + setStartDate(e); + } + }} + renderInput={(params) => } + InputProps={{ + sx: { + height: "40px", + color: theme.palette.secondary.main, + border: "1px solid", + borderColor: theme.palette.secondary.main, + "& .MuiSvgIcon-root": { color: theme.palette.secondary.main }, + }, + }} + /> + + по + + { + if (e) { + setEndDate(e); + } + }} + renderInput={(params) => } + InputProps={{ + sx: { + height: "40px", + color: theme.palette.secondary.main, + border: "1px solid", + borderColor: theme.palette.secondary.main, + "& .MuiSvgIcon-root": { color: theme.palette.secondary.main }, + }, + }} + /> + + + + setIsInfinite((p) => !p)} + /> + + + Бессрочно + + + + ); } diff --git a/src/pages/dashboard/Content/DiscountManagement/DiscountDataGrid.tsx b/src/pages/dashboard/Content/DiscountManagement/DiscountDataGrid.tsx index b746f11..971283c 100644 --- a/src/pages/dashboard/Content/DiscountManagement/DiscountDataGrid.tsx +++ b/src/pages/dashboard/Content/DiscountManagement/DiscountDataGrid.tsx @@ -1,16 +1,11 @@ import { useEffect } from "react"; import { Box, IconButton, useTheme, Tooltip } from "@mui/material"; +import { DataGrid, GridColDef, GridRowsProp, GridToolbar } from "@mui/x-data-grid"; import { - DataGrid, - GridColDef, - GridRowsProp, - GridToolbar, -} from "@mui/x-data-grid"; -import { - openEditDiscountDialog, - setSelectedDiscountIds, - updateDiscount, - useDiscountStore, + openEditDiscountDialog, + setSelectedDiscountIds, + updateDiscount, + useDiscountStore, } from "@root/stores/discounts"; import DeleteIcon from "@mui/icons-material/Delete"; import EditIcon from "@mui/icons-material/Edit"; @@ -22,180 +17,163 @@ import { formatDiscountFactor } from "@root/utils/formatDiscountFactor"; import { mutate } from "swr"; const columns: GridColDef[] = [ - // { - // field: "id", - // headerName: "ID", - // width: 70, - // sortable: false, - // }, - { - field: "name", - headerName: "Название скидки", - width: 150, - sortable: false, - renderCell: ({ row, value }) => ( - {value} - ), - }, - { - field: "description", - headerName: "Описание", - width: 120, - sortable: false, - }, - { - field: "conditionType", - headerName: "Тип условия", - width: 120, - sortable: false, - }, - { - field: "factor", - headerName: "Процент скидки", - width: 120, - sortable: false, - }, - { - field: "value", - headerName: "Значение", - width: 120, - sortable: false, - }, - { - field: "active", - headerName: "Активна", - width: 80, - sortable: false, - }, - { - field: "edit", - headerName: "Изменить", - width: 80, - sortable: false, - renderCell: ({ row }) => { - return ( - { - openEditDiscountDialog(row.id); - }} - > - - - ); - }, - }, - { - field: "delete", - headerName: "Удалить", - width: 80, - sortable: false, - renderCell: ({ row }) => ( - { - deleteDiscount(row.id).then(([discount]) => { - mutate("discounts"); - if (discount) { - updateDiscount(discount); - } - }); - }} - > - - - ), - }, + // { + // field: "id", + // headerName: "ID", + // width: 70, + // sortable: false, + // }, + { + field: "name", + headerName: "Название скидки", + width: 150, + sortable: false, + renderCell: ({ row, value }) => {value}, + }, + { + field: "description", + headerName: "Описание", + width: 120, + sortable: false, + }, + { + field: "conditionType", + headerName: "Тип условия", + width: 120, + sortable: false, + }, + { + field: "factor", + headerName: "Процент скидки", + width: 120, + sortable: false, + }, + { + field: "value", + headerName: "Значение", + width: 120, + sortable: false, + }, + { + field: "active", + headerName: "Активна", + width: 80, + sortable: false, + }, + { + field: "edit", + headerName: "Изменить", + width: 80, + sortable: false, + renderCell: ({ row }) => { + return ( + { + openEditDiscountDialog(row.id); + }} + > + + + ); + }, + }, + { + field: "delete", + headerName: "Удалить", + width: 80, + sortable: false, + renderCell: ({ row }) => ( + { + deleteDiscount(row.id).then(([discount]) => { + mutate("discounts"); + if (discount) { + updateDiscount(discount); + } + }); + }} + > + + + ), + }, ]; const layerTranslate = ["", "Товар", "Сервис", "корзина", "лояльность"]; -const layerValue = [ - "", - "Term", - "PriceFrom", - "CartPurchasesAmount", - "PurchasesAmount", -]; +const layerValue = ["", "Term", "PriceFrom", "CartPurchasesAmount", "PurchasesAmount"]; interface Props { - selectedRowsHC: (array: GridSelectionModel) => void; + selectedRowsHC: (array: GridSelectionModel) => void; } export default function DiscountDataGrid({ selectedRowsHC }: Props) { - const theme = useTheme(); - const selectedDiscountIds = useDiscountStore( - (state) => state.selectedDiscountIds - ); - const realDiscounts = useDiscountStore((state) => state.discounts); + const theme = useTheme(); + const selectedDiscountIds = useDiscountStore((state) => state.selectedDiscountIds); + const realDiscounts = useDiscountStore((state) => state.discounts); - useEffect(() => { - requestDiscounts(); - }, []); + useEffect(() => { + requestDiscounts(); + }, []); - const rowBackDicounts: GridRowsProp = realDiscounts - .filter(({ Layer }) => Layer > 0) - .map((discount) => { - return { - id: discount.ID, - name: discount.Name, - description: discount.Description, - conditionType: layerTranslate[discount.Layer], - factor: formatDiscountFactor(discount.Target.Factor), - value: (discount.Layer === 1) ? - discount.Condition[ - layerValue[discount.Layer] as keyof typeof discount.Condition - ]: Number(discount.Condition[ - layerValue[discount.Layer] as keyof typeof discount.Condition - ])/100, - active: discount.Deprecated ? "🚫" : "✅", - deleted: discount.Audit.Deleted, - }; - }); + const rowBackDicounts: GridRowsProp = realDiscounts + .filter(({ Layer }) => Layer > 0) + .map((discount) => { + return { + id: discount.ID, + name: discount.Name, + description: discount.Description, + conditionType: layerTranslate[discount.Layer], + factor: formatDiscountFactor(discount.Target.Factor), + value: + discount.Layer === 1 + ? discount.Condition[layerValue[discount.Layer] as keyof typeof discount.Condition] + : Number(discount.Condition[layerValue[discount.Layer] as keyof typeof discount.Condition]) / 100, + active: discount.Deprecated ? "🚫" : "✅", + deleted: discount.Audit.Deleted, + }; + }); - return ( - - - - - - - - { - selectedRowsHC(array); - setSelectedDiscountIds(array); - }} - disableSelectionOnClick - sx={{ - color: theme.palette.secondary.main, - "& .MuiDataGrid-iconSeparator": { - display: "none", - }, - "& .css-levciy-MuiTablePagination-displayedRows": { - color: theme.palette.secondary.main, - }, - "& .MuiSvgIcon-root": { - color: theme.palette.secondary.main, - }, - "& .MuiTablePagination-selectLabel": { - color: theme.palette.secondary.main, - }, - "& .MuiInputBase-root": { - color: theme.palette.secondary.main, - }, - "& .MuiButton-text": { - color: theme.palette.secondary.main, - }, - }} - components={{ Toolbar: GridToolbar }} - /> - - - ); + return ( + + + + + + + + { + selectedRowsHC(array); + setSelectedDiscountIds(array); + }} + disableSelectionOnClick + sx={{ + color: theme.palette.secondary.main, + "& .MuiDataGrid-iconSeparator": { + display: "none", + }, + "& .css-levciy-MuiTablePagination-displayedRows": { + color: theme.palette.secondary.main, + }, + "& .MuiSvgIcon-root": { + color: theme.palette.secondary.main, + }, + "& .MuiTablePagination-selectLabel": { + color: theme.palette.secondary.main, + }, + "& .MuiInputBase-root": { + color: theme.palette.secondary.main, + }, + "& .MuiButton-text": { + color: theme.palette.secondary.main, + }, + }} + components={{ Toolbar: GridToolbar }} + /> + + + ); } diff --git a/src/pages/dashboard/Content/DiscountManagement/DiscountManagement.tsx b/src/pages/dashboard/Content/DiscountManagement/DiscountManagement.tsx index fbbc215..c42d0b4 100644 --- a/src/pages/dashboard/Content/DiscountManagement/DiscountManagement.tsx +++ b/src/pages/dashboard/Content/DiscountManagement/DiscountManagement.tsx @@ -8,38 +8,35 @@ import ControlPanel from "./ControlPanel"; import { useState } from "react"; import { GridSelectionModel } from "@mui/x-data-grid"; - const DiscountManagement: React.FC = () => { - const theme = useTheme(); - const [selectedRows, setSelectedRows] = useState([]) - const selectedRowsHC = (array:GridSelectionModel) => { - setSelectedRows(array) - } + const theme = useTheme(); + const [selectedRows, setSelectedRows] = useState([]); + const selectedRowsHC = (array: GridSelectionModel) => { + setSelectedRows(array); + }; - return ( - - - СКИДКИ - - - - - - - ); + return ( + + + СКИДКИ + + + + + + + ); }; - export default DiscountManagement; diff --git a/src/pages/dashboard/Content/DiscountManagement/EditDiscountDialog.tsx b/src/pages/dashboard/Content/DiscountManagement/EditDiscountDialog.tsx index 62c8b6d..f491ddc 100644 --- a/src/pages/dashboard/Content/DiscountManagement/EditDiscountDialog.tsx +++ b/src/pages/dashboard/Content/DiscountManagement/EditDiscountDialog.tsx @@ -1,32 +1,25 @@ import { - Box, - Button, - Dialog, - FormControl, - FormControlLabel, - FormLabel, - InputLabel, - MenuItem, - Radio, - RadioGroup, - Select, - SelectChangeEvent, - Typography, - useTheme, + Box, + Button, + Dialog, + FormControl, + FormControlLabel, + FormLabel, + InputLabel, + MenuItem, + Radio, + RadioGroup, + Select, + SelectChangeEvent, + Typography, + useTheme, } from "@mui/material"; import { patchDiscount } from "@root/api/discounts"; import { CustomTextField } from "@root/kitUI/CustomTextField"; import { DiscountType, discountTypes } from "@root/model/discount"; import { ServiceType, SERVICE_LIST } from "@root/model/tariff"; -import { - closeEditDiscountDialog, - updateDiscount, - useDiscountStore, -} from "@root/stores/discounts"; -import { - resetPrivilegeArray, - usePrivilegeStore, -} from "@root/stores/privilegesStore"; +import { closeEditDiscountDialog, updateDiscount, useDiscountStore } from "@root/stores/discounts"; +import { resetPrivilegeArray, usePrivilegeStore } from "@root/stores/privilegesStore"; import { getDiscountTypeFromLayer } from "@root/utils/discount"; import usePrivileges from "@root/utils/hooks/usePrivileges"; import { enqueueSnackbar } from "notistack"; @@ -34,359 +27,336 @@ import { useEffect, useState } from "react"; import { mutate } from "swr"; export default function EditDiscountDialog() { - const theme = useTheme(); - const editDiscountId = useDiscountStore((state) => state.editDiscountId); - const discounts = useDiscountStore((state) => state.discounts); - const privileges = usePrivilegeStore((state) => state.privileges); - const [serviceType, setServiceType] = useState("templategen"); - const [discountType, setDiscountType] = - useState("purchasesAmount"); - const [discountNameField, setDiscountNameField] = useState(""); - const [discountDescriptionField, setDiscountDescriptionField] = - useState(""); - const [privilegeIdField, setPrivilegeIdField] = useState(""); - const [discountFactorField, setDiscountFactorField] = useState("0"); - const [purchasesAmountField, setPurchasesAmountField] = useState("0"); - const [cartPurchasesAmountField, setCartPurchasesAmountField] = - useState("0"); - const [discountMinValueField, setDiscountMinValueField] = - useState("0"); + const theme = useTheme(); + const editDiscountId = useDiscountStore((state) => state.editDiscountId); + const discounts = useDiscountStore((state) => state.discounts); + const privileges = usePrivilegeStore((state) => state.privileges); + const [serviceType, setServiceType] = useState("templategen"); + const [discountType, setDiscountType] = useState("purchasesAmount"); + const [discountNameField, setDiscountNameField] = useState(""); + const [discountDescriptionField, setDiscountDescriptionField] = useState(""); + const [privilegeIdField, setPrivilegeIdField] = useState(""); + const [discountFactorField, setDiscountFactorField] = useState("0"); + const [purchasesAmountField, setPurchasesAmountField] = useState("0"); + const [cartPurchasesAmountField, setCartPurchasesAmountField] = useState("0"); + const [discountMinValueField, setDiscountMinValueField] = useState("0"); - const discount = discounts.find((discount) => discount.ID === editDiscountId); + const discount = discounts.find((discount) => discount.ID === editDiscountId); - usePrivileges({ onNewPrivileges: resetPrivilegeArray }); + usePrivileges({ onNewPrivileges: resetPrivilegeArray }); - useEffect( - function setDiscountFields() { - if (!discount) return; + useEffect( + function setDiscountFields() { + if (!discount) return; - setServiceType(discount.Condition.Group ?? ""); - setDiscountType(getDiscountTypeFromLayer(discount.Layer)); - setDiscountNameField(discount.Name); - setDiscountDescriptionField(discount.Description); - setPrivilegeIdField(discount.Condition.Product ?? ""); - setDiscountFactorField(((1 - discount.Target.Factor) * 100).toFixed(2)); - setPurchasesAmountField(discount.Condition.PurchasesAmount ?? ""); - setCartPurchasesAmountField( - discount.Condition.CartPurchasesAmount ?? "" - ); - setDiscountMinValueField(discount.Condition.PriceFrom ?? ""); - }, - [discount] - ); + setServiceType(discount.Condition.Group ?? ""); + setDiscountType(getDiscountTypeFromLayer(discount.Layer)); + setDiscountNameField(discount.Name); + setDiscountDescriptionField(discount.Description); + setPrivilegeIdField(discount.Condition.Product ?? ""); + setDiscountFactorField(((1 - discount.Target.Factor) * 100).toFixed(2)); + setPurchasesAmountField(discount.Condition.PurchasesAmount ?? ""); + setCartPurchasesAmountField(discount.Condition.CartPurchasesAmount ?? ""); + setDiscountMinValueField(discount.Condition.PriceFrom ?? ""); + }, + [discount] + ); - const handleDiscountTypeChange = ( - event: React.ChangeEvent - ) => { - setDiscountType(event.target.value as DiscountType); - }; + const handleDiscountTypeChange = (event: React.ChangeEvent) => { + setDiscountType(event.target.value as DiscountType); + }; - const handleServiceTypeChange = (event: SelectChangeEvent) => { - setServiceType(event.target.value as ServiceType); - }; + const handleServiceTypeChange = (event: SelectChangeEvent) => { + setServiceType(event.target.value as ServiceType); + }; - async function handleSaveDiscount() { - if (!discount) return; + async function handleSaveDiscount() { + if (!discount) return; - const purchasesAmount = parseFloat(purchasesAmountField.replace(",", ".")); - const discountFactor = - (100 - parseFloat(discountFactorField.replace(",", "."))) / 100; - const cartPurchasesAmount = parseFloat( - cartPurchasesAmountField.replace(",", ".") - ); - const discountMinValue = parseFloat( - discountMinValueField.replace(",", ".") - ); + const purchasesAmount = parseFloat(purchasesAmountField.replace(",", ".")); + const discountFactor = (100 - parseFloat(discountFactorField.replace(",", "."))) / 100; + const cartPurchasesAmount = parseFloat(cartPurchasesAmountField.replace(",", ".")); + const discountMinValue = parseFloat(discountMinValueField.replace(",", ".")); - if (!isFinite(purchasesAmount)) - return enqueueSnackbar("Поле purchasesAmount не число"); - if (!isFinite(discountFactor)) - return enqueueSnackbar("Поле discountFactor не число"); - if (!isFinite(cartPurchasesAmount)) - return enqueueSnackbar("Поле cartPurchasesAmount не число"); - if (!isFinite(discountMinValue)) - return enqueueSnackbar("Поле discountMinValue не число"); - if (discountType === "privilege" && !privilegeIdField) - return enqueueSnackbar("Привилегия не выбрана"); - if (!discountNameField) return enqueueSnackbar('Поле "Имя" пустое'); - if (!discountDescriptionField) - return enqueueSnackbar('Поле "Описание" пустое'); - if (discountFactor < 0) - return enqueueSnackbar("Процент скидки не может быть больше 100"); + if (!isFinite(purchasesAmount)) return enqueueSnackbar("Поле purchasesAmount не число"); + if (!isFinite(discountFactor)) return enqueueSnackbar("Поле discountFactor не число"); + if (!isFinite(cartPurchasesAmount)) return enqueueSnackbar("Поле cartPurchasesAmount не число"); + if (!isFinite(discountMinValue)) return enqueueSnackbar("Поле discountMinValue не число"); + if (discountType === "privilege" && !privilegeIdField) return enqueueSnackbar("Привилегия не выбрана"); + if (!discountNameField) return enqueueSnackbar('Поле "Имя" пустое'); + if (!discountDescriptionField) return enqueueSnackbar('Поле "Описание" пустое'); + if (discountFactor < 0) return enqueueSnackbar("Процент скидки не может быть больше 100"); - const [patchedDiscountResponse, patchedDiscountError] = await patchDiscount( - discount.ID, - { - cartPurchasesAmount, - discountFactor, - discountMinValue, - purchasesAmount, - discountDescription: discountDescriptionField, - discountName: discountNameField, - startDate: new Date().toISOString(), - endDate: new Date(Date.now() + 1000 * 3600 * 24 * 30).toISOString(), - serviceType, - discountType, - privilegeId: privilegeIdField, - } - ); + const [patchedDiscountResponse, patchedDiscountError] = await patchDiscount(discount.ID, { + cartPurchasesAmount, + discountFactor, + discountMinValue, + purchasesAmount, + discountDescription: discountDescriptionField, + discountName: discountNameField, + startDate: new Date().toISOString(), + endDate: new Date(Date.now() + 1000 * 3600 * 24 * 30).toISOString(), + serviceType, + discountType, + privilegeId: privilegeIdField, + }); - if (patchedDiscountError) { - console.error("Error patching discount", patchedDiscountError); + if (patchedDiscountError) { + console.error("Error patching discount", patchedDiscountError); - return enqueueSnackbar(patchedDiscountError); - } + return enqueueSnackbar(patchedDiscountError); + } - if (patchedDiscountResponse) { - mutate("discounts"); - updateDiscount(patchedDiscountResponse); - closeEditDiscountDialog(); - } - } + if (patchedDiscountResponse) { + mutate("discounts"); + updateDiscount(patchedDiscountResponse); + closeEditDiscountDialog(); + } + } - return ( - - - setDiscountNameField(e.target.value)} - /> - setDiscountDescriptionField(e.target.value)} - /> - - Условия: - - setDiscountFactorField(e.target.value)} - /> - - - Тип скидки - - - {Object.keys(discountTypes).map((type) => ( - } - label={discountTypes[type as DiscountType]} - sx={{ color: "white" }} - /> - ))} - - - {discountType === "purchasesAmount" && ( - setPurchasesAmountField(e.target.value)} - /> - )} - {discountType === "cartPurchasesAmount" && ( - setCartPurchasesAmountField(e.target.value)} - /> - )} - {discountType === "service" && ( - <> - - setDiscountMinValueField(e.target.value)} - /> - - )} - {discountType === "privilege" && ( - <> - - - Привилегия - - - - setDiscountMinValueField(e.target.value)} - /> - - )} - - - - - - ); + return ( + + + setDiscountNameField(e.target.value)} + /> + setDiscountDescriptionField(e.target.value)} + /> + + Условия: + + setDiscountFactorField(e.target.value)} + /> + + + Тип скидки + + + {Object.keys(discountTypes).map((type) => ( + } + label={discountTypes[type as DiscountType]} + sx={{ color: "white" }} + /> + ))} + + + {discountType === "purchasesAmount" && ( + setPurchasesAmountField(e.target.value)} + /> + )} + {discountType === "cartPurchasesAmount" && ( + setCartPurchasesAmountField(e.target.value)} + /> + )} + {discountType === "service" && ( + <> + + setDiscountMinValueField(e.target.value)} + /> + + )} + {discountType === "privilege" && ( + <> + + + Привилегия + + + + setDiscountMinValueField(e.target.value)} + /> + + )} + + + + + + ); } diff --git a/src/pages/dashboard/Content/Entities.tsx b/src/pages/dashboard/Content/Entities.tsx index 9d32a3b..a39003d 100644 --- a/src/pages/dashboard/Content/Entities.tsx +++ b/src/pages/dashboard/Content/Entities.tsx @@ -1,142 +1,169 @@ import * as React from "react"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; import { Typography } from "@mui/material"; -import Table from '@mui/material/Table'; -import TableHead from '@mui/material/TableHead'; -import TableBody from '@mui/material/TableBody'; -import TableCell from '@mui/material/TableCell'; -import TableRow from '@mui/material/TableRow'; +import Table from "@mui/material/Table"; +import TableHead from "@mui/material/TableHead"; +import TableBody from "@mui/material/TableBody"; +import TableCell from "@mui/material/TableCell"; +import TableRow from "@mui/material/TableRow"; import theme from "../../../theme"; +const Users: React.FC = () => { + const [selectedValue, setSelectedValue] = React.useState("a"); + const handleChange = (event: React.ChangeEvent) => { + setSelectedValue(event.target.value); + }; -const Users: React.FC = () => { - const [selectedValue, setSelectedValue] = React.useState('a'); - const handleChange = (event: React.ChangeEvent) => { - setSelectedValue(event.target.value); - }; + const navigate = useNavigate(); - const navigate = useNavigate(); + return ( + + + Юридические лица + - return ( - - - Юридические лица - + + + + + + ID + + + + + Дата / время регистрации + + + + -
- - - - - ID - - - - - Дата / время регистрации - - - - + + navigate("/modalEntities")} + > + + + 1 + + + + + 2022 + + + - - navigate("/modalEntities") }> - - - 1 - - - - - 2022 - - - + navigate("/modalEntities")} + > + + + 2 + + + + + 2021 + + + - navigate("/modalEntities") }> - - - 2 - - - - - 2021 - - - + navigate("/modalEntities")} + > + + + 3 + + + + + 2020 + + + + +
+
+ ); +}; - navigate("/modalEntities") }> - - - 3 - - - - - 2020 - - - - - -
- ); -} - - -export default Users; \ No newline at end of file +export default Users; diff --git a/src/pages/dashboard/Content/PromocodeManagement/CreatePromocodeForm.tsx b/src/pages/dashboard/Content/PromocodeManagement/CreatePromocodeForm.tsx index 8bc598a..534301f 100644 --- a/src/pages/dashboard/Content/PromocodeManagement/CreatePromocodeForm.tsx +++ b/src/pages/dashboard/Content/PromocodeManagement/CreatePromocodeForm.tsx @@ -1,13 +1,4 @@ -import { - Button, - FormControlLabel, - MenuItem, - Radio, - RadioGroup, - Select, - TextField, - Typography, -} from "@mui/material"; +import { Button, FormControlLabel, MenuItem, Radio, RadioGroup, Select, TextField, Typography } from "@mui/material"; import { DesktopDatePicker } from "@mui/x-date-pickers/DesktopDatePicker"; import { Field, Form, Formik } from "formik"; import { useEffect, useState } from "react"; @@ -26,406 +17,347 @@ import { enqueueSnackbar } from "notistack"; type BonusType = "discount" | "privilege"; type FormValues = { - codeword: string; - description: string; - greetings: string; - dueTo: number; - activationCount: number; - privilegeId: string; - amount: number; - layer: 1 | 2; - factor: number; - target: string; - threshold: number; - serviceKey: string; + codeword: string; + description: string; + greetings: string; + dueTo: number; + activationCount: number; + privilegeId: string; + amount: number; + layer: 1 | 2; + factor: number; + target: string; + threshold: number; + serviceKey: string; }; type SelectChangeProps = { - target: { - name: string; - value: string; - }; + target: { + name: string; + value: string; + }; }; const initialValues: FormValues = { - codeword: "", - description: "", - greetings: "", - dueTo: 0, - activationCount: 0, - privilegeId: "", - amount: 0, - layer: 1, - factor: 0, - target: "", - threshold: 0, - serviceKey: "", + codeword: "", + description: "", + greetings: "", + dueTo: 0, + activationCount: 0, + privilegeId: "", + amount: 0, + layer: 1, + factor: 0, + target: "", + threshold: 0, + serviceKey: "", }; type Props = { - createPromocode: (body: CreatePromocodeBody) => Promise; + createPromocode: (body: CreatePromocodeBody) => Promise; }; export const CreatePromocodeForm = ({ createPromocode }: Props) => { - const [bonusType, setBonusType] = useState("discount"); - const { privileges } = usePrivilegeStore(); + const [bonusType, setBonusType] = useState("discount"); + const { privileges } = usePrivilegeStore(); - useEffect(() => { - requestPrivileges(); - }, []); + useEffect(() => { + requestPrivileges(); + }, []); - const submitForm = (values: FormValues) => { - const currentPrivilege = privileges.find( - (item) => item.privilegeId === values.privilegeId - ); + const submitForm = (values: FormValues) => { + const currentPrivilege = privileges.find((item) => item.privilegeId === values.privilegeId); - const body = { ...values }; + const body = { ...values }; - if ( - (body.layer === 1 && bonusType === "discount") || - bonusType === "privilege" - ) { - if (currentPrivilege === undefined) { - enqueueSnackbar("Привилегия не выбрана"); + if ((body.layer === 1 && bonusType === "discount") || bonusType === "privilege") { + if (currentPrivilege === undefined) { + enqueueSnackbar("Привилегия не выбрана"); - return; - } + return; + } - body.serviceKey = currentPrivilege?.serviceKey; - body.target = body.privilegeId; - } - if (body.layer === 2 && bonusType === "discount") { - if (!body.serviceKey) { - enqueueSnackbar("Сервис не выбран"); + body.serviceKey = currentPrivilege?.serviceKey; + body.target = body.privilegeId; + } + if (body.layer === 2 && bonusType === "discount") { + if (!body.serviceKey) { + enqueueSnackbar("Сервис не выбран"); - return; - } + return; + } - body.target = body.serviceKey; - } + body.target = body.serviceKey; + } - const factorFromDiscountValue = 1 - body.factor / 100; + const factorFromDiscountValue = 1 - body.factor / 100; - return createPromocode({ - codeword: body.codeword, - description: body.description, - greetings: body.greetings, - dueTo: body.dueTo, - activationCount: body.activationCount, - bonus: { - privilege: { - privilegeID: body.privilegeId, - amount: body.amount, - serviceKey: body.serviceKey, - }, - discount: { - layer: body.layer, - factor: factorFromDiscountValue, - target: body.target, - threshold: body.threshold, - }, - }, - }); - }; + return createPromocode({ + codeword: body.codeword, + description: body.description, + greetings: body.greetings, + dueTo: body.dueTo, + activationCount: body.activationCount, + bonus: { + privilege: { + privilegeID: body.privilegeId, + amount: body.amount, + serviceKey: body.serviceKey, + }, + discount: { + layer: body.layer, + factor: factorFromDiscountValue, + target: body.target, + threshold: body.threshold, + }, + }, + }); + }; - return ( - - {({ values, handleChange, handleBlur, setFieldValue }) => ( -
- - - - - Время существования промокода - - { - setFieldValue("dueTo", event.$d.getTime() / 1000 || null); - }} - renderInput={(params: TextFieldProps) => } - InputProps={{ - sx: { - height: "40px", - color: theme.palette.secondary.main, - border: "1px solid", - borderColor: theme.palette.secondary.main, - "& .MuiSvgIcon-root": { color: theme.palette.secondary.main }, - }, - }} - /> - - setFieldValue( - "activationCount", - Number(target.value.replace(/\D/g, "")) - ) - } - /> - ) => { - setBonusType(target.value as BonusType); - }} - onBlur={handleBlur} - > - } - label="Скидка" - /> - } - label="Привилегия" - /> - - {bonusType === "discount" && ( - <> - ) => { - setFieldValue("target", ""); - setFieldValue("layer", Number(target.value)); - }} - onBlur={handleBlur} - > - } - label="Привилегия" - /> - } - label="Сервис" - /> - - { - setFieldValue( - "factor", - Number(target.value.replace(/\D/g, "")) - ); - }} - /> - - {values.layer === 1 ? "Выбор привилегии" : "Выбор сервиса"} - - {values.layer === 1 ? ( - { - setFieldValue("target", target.value); - setFieldValue("privilegeId", target.value); - }} - children={privileges.map(({ name, privilegeId }) => ( - - {name} - - ))} - /> - ) : ( - { - setFieldValue("target", target.value); - setFieldValue("serviceKey", target.value); - }} - children={SERVICE_LIST.map(({ displayName, serviceKey }) => ( - - {displayName} - - ))} - /> - )} - - setFieldValue( - "threshold", - Number(target.value.replace(/\D/g, "")) - ) - } - /> - - )} - {bonusType === "privilege" && ( - <> - - Выбор привилегии - - ( - - {name} - - ))} - /> - - setFieldValue( - "amount", - Number(target.value.replace(/\D/g, "")) - ) - } - /> - - )} - - - )} -
- ); + return ( + + {({ values, handleChange, handleBlur, setFieldValue }) => ( +
+ + + + + Время существования промокода + + { + setFieldValue("dueTo", event.$d.getTime() / 1000 || null); + }} + renderInput={(params: TextFieldProps) => } + InputProps={{ + sx: { + height: "40px", + color: theme.palette.secondary.main, + border: "1px solid", + borderColor: theme.palette.secondary.main, + "& .MuiSvgIcon-root": { color: theme.palette.secondary.main }, + }, + }} + /> + setFieldValue("activationCount", Number(target.value.replace(/\D/g, "")))} + /> + ) => { + setBonusType(target.value as BonusType); + }} + onBlur={handleBlur} + > + } label="Скидка" /> + } label="Привилегия" /> + + {bonusType === "discount" && ( + <> + ) => { + setFieldValue("target", ""); + setFieldValue("layer", Number(target.value)); + }} + onBlur={handleBlur} + > + } label="Привилегия" /> + } label="Сервис" /> + + { + setFieldValue("factor", Number(target.value.replace(/\D/g, ""))); + }} + /> + + {values.layer === 1 ? "Выбор привилегии" : "Выбор сервиса"} + + {values.layer === 1 ? ( + { + setFieldValue("target", target.value); + setFieldValue("privilegeId", target.value); + }} + children={privileges.map(({ name, privilegeId }) => ( + + {name} + + ))} + /> + ) : ( + { + setFieldValue("target", target.value); + setFieldValue("serviceKey", target.value); + }} + children={SERVICE_LIST.map(({ displayName, serviceKey }) => ( + + {displayName} + + ))} + /> + )} + setFieldValue("threshold", Number(target.value.replace(/\D/g, "")))} + /> + + )} + {bonusType === "privilege" && ( + <> + + Выбор привилегии + + ( + + {name} + + ))} + /> + setFieldValue("amount", Number(target.value.replace(/\D/g, "")))} + /> + + )} + + + )} +
+ ); }; type CustomTextFieldProps = { - name: string; - label: string; - required?: boolean; - onChange: (event: ChangeEvent) => void; + name: string; + label: string; + required?: boolean; + onChange: (event: ChangeEvent) => void; }; -const CustomTextField = ({ - name, - label, - required = false, - onChange, -}: CustomTextFieldProps) => ( - +const CustomTextField = ({ name, label, required = false, onChange }: CustomTextFieldProps) => ( + ); diff --git a/src/pages/dashboard/Content/PromocodeManagement/DeleteModal.tsx b/src/pages/dashboard/Content/PromocodeManagement/DeleteModal.tsx index 5302a00..e03fd20 100644 --- a/src/pages/dashboard/Content/PromocodeManagement/DeleteModal.tsx +++ b/src/pages/dashboard/Content/PromocodeManagement/DeleteModal.tsx @@ -1,59 +1,54 @@ -import * as React from 'react'; -import Box from '@mui/material/Box'; -import Modal from '@mui/material/Modal'; -import Button from '@mui/material/Button'; -import { Typography } from '@mui/material'; +import * as React from "react"; +import Box from "@mui/material/Box"; +import Modal from "@mui/material/Modal"; +import Button from "@mui/material/Button"; +import { Typography } from "@mui/material"; const style = { - position: 'absolute' as 'absolute', - top: '50%', - left: '50%', - transform: 'translate(-50%, -50%)', - width: 400, - bgcolor: '#c1c1c1', - border: '2px solid #000', - boxShadow: 24, - pt: 2, - px: 4, - pb: 3, + position: "absolute" as const, + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + width: 400, + bgcolor: "#c1c1c1", + border: "2px solid #000", + boxShadow: 24, + pt: 2, + px: 4, + pb: 3, }; interface Props { - id: string; - setModal: (id: string) => void; - deletePromocode: (id: string) => Promise; + id: string; + setModal: (id: string) => void; + deletePromocode: (id: string) => Promise; } -export default function ({ - id, - setModal, - deletePromocode -}: Props) { - return ( - setModal("")} - > - - Точно удалить промокод? - - - - - - - ); +export default function ({ id, setModal, deletePromocode }: Props) { + return ( + setModal("")}> + + + Точно удалить промокод? + + + + + + + + ); } diff --git a/src/pages/dashboard/Content/PromocodeManagement/StatisticsModal.tsx b/src/pages/dashboard/Content/PromocodeManagement/StatisticsModal.tsx index fa73018..3adfbcb 100644 --- a/src/pages/dashboard/Content/PromocodeManagement/StatisticsModal.tsx +++ b/src/pages/dashboard/Content/PromocodeManagement/StatisticsModal.tsx @@ -1,14 +1,5 @@ import { useEffect, useState } from "react"; -import { - Box, - Button, - Typography, - Modal, - TextField, - useTheme, - useMediaQuery, - IconButton, -} from "@mui/material"; +import { Box, Button, Typography, Modal, TextField, useTheme, useMediaQuery, IconButton } from "@mui/material"; import { DataGrid, GridLoadingOverlay, GridToolbar } from "@mui/x-data-grid"; import { DatePicker } from "@mui/x-date-pickers/DatePicker"; @@ -21,318 +12,293 @@ import type { GridColDef } from "@mui/x-data-grid"; import type { Promocode, PromocodeStatistics } from "@root/model/promocodes"; const host = window.location.hostname; -let isTest = host.includes("s"); +const isTest = host.includes("s"); type StatisticsModalProps = { - id: string; - to: number; - from: number; - setId: (id: string) => void; - setTo: (date: number) => void; - setFrom: (date: number) => void; - promocodes: Promocode[]; - promocodeStatistics: PromocodeStatistics | null | undefined; - createFastLink: (id: string) => Promise; + id: string; + to: number; + from: number; + setId: (id: string) => void; + setTo: (date: number) => void; + setFrom: (date: number) => void; + promocodes: Promocode[]; + promocodeStatistics: PromocodeStatistics | null | undefined; + createFastLink: (id: string) => Promise; }; type Row = { - id: number; - link: string; - useCount: number; + id: number; + link: string; + useCount: number; }; const COLUMNS: GridColDef[] = [ - { - field: "copy", - headerName: "копировать", - width: 50, - sortable: false, - valueGetter: ({ row }) => String(row.useCount), - renderCell: (params) => { - return ( - navigator.clipboard.writeText(`https://${isTest ? "s" : ""}quiz.pena.digital/?fl=${params.row.link}`)} - > - - - ); - }, - }, - { - field: "link", - headerName: "Ссылка", - width: 320, - sortable: false, - valueGetter: ({ row }) => row.link, - renderCell: ({ value }) => - value?.split("|").map((link) => {link}), - }, - { - field: "useCount", - headerName: "Использований", - width: 120, - sortable: false, - valueGetter: ({ row }) => String(row.useCount), - }, - { - field: "purchasesCount", - headerName: "Покупок", - width: 70, - sortable: false, - valueGetter: ({ row }) => String(0), - }, + { + field: "copy", + headerName: "копировать", + width: 50, + sortable: false, + valueGetter: ({ row }) => String(row.useCount), + renderCell: (params) => { + return ( + + navigator.clipboard.writeText(`https://${isTest ? "s" : ""}quiz.pena.digital/?fl=${params.row.link}`) + } + > + + + ); + }, + }, + { + field: "link", + headerName: "Ссылка", + width: 320, + sortable: false, + valueGetter: ({ row }) => row.link, + renderCell: ({ value }) => value?.split("|").map((link) => {link}), + }, + { + field: "useCount", + headerName: "Использований", + width: 120, + sortable: false, + valueGetter: ({ row }) => String(row.useCount), + }, + { + field: "purchasesCount", + headerName: "Покупок", + width: 70, + sortable: false, + valueGetter: ({ row }) => String(0), + }, ]; export const StatisticsModal = ({ - id, - setId, - setFrom, - from, - to, - setTo, - promocodeStatistics, - promocodes, - createFastLink, + id, + setId, + setFrom, + from, + to, + setTo, + promocodeStatistics, + promocodes, + createFastLink, }: StatisticsModalProps) => { - const [startDate, setStartDate] = useState(new Date()); - const [endDate, setEndDate] = useState(new Date()); - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down(550)); - const [rows, setRows] = useState([]); - const { privileges } = usePrivilegeStore(); - const currentPrivilegeId = promocodes.find((promocode) => promocode.id === id) - ?.bonus.privilege.privilegeID; - const privilege = privileges.find( - (item) => item.privilegeId === currentPrivilegeId - ); - const promocode = promocodes.find((item) => item.id === id); - const createFastlink = async () => { - await createFastLink(id); - }; + const [startDate, setStartDate] = useState(new Date()); + const [endDate, setEndDate] = useState(new Date()); + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down(550)); + const [rows, setRows] = useState([]); + const { privileges } = usePrivilegeStore(); + const currentPrivilegeId = promocodes.find((promocode) => promocode.id === id)?.bonus.privilege.privilegeID; + const privilege = privileges.find((item) => item.privilegeId === currentPrivilegeId); + const promocode = promocodes.find((item) => item.id === id); + const createFastlink = async () => { + await createFastLink(id); + }; - const getParseData = () => { - const rows = promocodes - .find((promocode) => promocode.id === id) - ?.fastLinks?.map((link, index) => ({ - link, - id: index, - useCount: promocodeStatistics?.usageMap[link] ?? 0, - })) as Row[]; + const getParseData = () => { + const rows = promocodes + .find((promocode) => promocode.id === id) + ?.fastLinks?.map((link, index) => ({ + link, + id: index, + useCount: promocodeStatistics?.usageMap[link] ?? 0, + })) as Row[]; - setRows(rows); - }; + setRows(rows); + }; - useEffect(() => { - if (id.length > 0) { - getParseData(); - } + useEffect(() => { + if (id.length > 0) { + getParseData(); + } - if (!id) { - setRows([]); - } - }, [id, promocodes]); + if (!id) { + setRows([]); + } + }, [id, promocodes]); - // const formatTo = to === null ? 0 : moment(to).unix() - // const formatFrom = from === null ? 0 : moment(from).unix() - // useEffect(() => { - // (async () => { - // const gottenGeneral = await promocodeStatistics(id, startDate, endDate) - // setGeneral(gottenGeneral[0]) - // })() - // }, [to, from]); - return ( - { - setId(""); - setStartDate(new Date()); - setEndDate(new Date()); - }} - sx={{ - "& > .MuiBox-root": { outline: "none", padding: "32px 32px 16px" }, - }} - > - - - - - - - - - - от - - date && setStartDate(date)} - renderInput={(params) => ( - - )} - InputProps={{ - sx: { - height: "40px", - color: theme.palette.secondary.main, - border: "1px solid", - borderColor: theme.palette.secondary.main, - "& .MuiSvgIcon-root": { - color: theme.palette.secondary.main, - }, - }, - }} - /> - - - - до - - date && setEndDate(date)} - renderInput={(params) => ( - - )} - InputProps={{ - sx: { - height: "40px", - color: theme.palette.secondary.main, - border: "1px solid", - borderColor: theme.palette.secondary.main, - "& .MuiSvgIcon-root": { - color: theme.palette.secondary.main, - }, - }, - }} - /> - - - - - {privilege === undefined ? ( - - Нет привилегии - - ) : ( - - название привилегии: {privilege.name} - - {promocode?.activationCount} активаций из{" "} - {promocode?.activationLimit} - - приветствие: "{promocode?.greetings}" - {promocode?.bonus?.discount?.factor !== undefined && ( - - скидка: {100 - promocode?.bonus?.discount?.factor * 100}% - - )} - { - - количество привилегии: {promocode?.bonus?.privilege?.amount} - - } - {promocode?.dueTo !== undefined && promocode.dueTo > 0 && ( - - действует до: {new Date(promocode.dueTo).toLocaleString()} - - )} - - )} - - - ); + // const formatTo = to === null ? 0 : moment(to).unix() + // const formatFrom = from === null ? 0 : moment(from).unix() + // useEffect(() => { + // (async () => { + // const gottenGeneral = await promocodeStatistics(id, startDate, endDate) + // setGeneral(gottenGeneral[0]) + // })() + // }, [to, from]); + return ( + { + setId(""); + setStartDate(new Date()); + setEndDate(new Date()); + }} + sx={{ + "& > .MuiBox-root": { outline: "none", padding: "32px 32px 16px" }, + }} + > + + + + + + + + + от + date && setStartDate(date)} + renderInput={(params) => } + InputProps={{ + sx: { + height: "40px", + color: theme.palette.secondary.main, + border: "1px solid", + borderColor: theme.palette.secondary.main, + "& .MuiSvgIcon-root": { + color: theme.palette.secondary.main, + }, + }, + }} + /> + + + до + date && setEndDate(date)} + renderInput={(params) => } + InputProps={{ + sx: { + height: "40px", + color: theme.palette.secondary.main, + border: "1px solid", + borderColor: theme.palette.secondary.main, + "& .MuiSvgIcon-root": { + color: theme.palette.secondary.main, + }, + }, + }} + /> + + + + + {privilege === undefined ? ( + + Нет привилегии + + ) : ( + + название привилегии: {privilege.name} + + {promocode?.activationCount} активаций из {promocode?.activationLimit} + + приветствие: "{promocode?.greetings}" + {promocode?.bonus?.discount?.factor !== undefined && ( + скидка: {100 - promocode?.bonus?.discount?.factor * 100}% + )} + {количество привилегии: {promocode?.bonus?.privilege?.amount}} + {promocode?.dueTo !== undefined && promocode.dueTo > 0 && ( + действует до: {new Date(promocode.dueTo).toLocaleString()} + )} + + )} + + + ); }; diff --git a/src/pages/dashboard/Content/PromocodeManagement/index.tsx b/src/pages/dashboard/Content/PromocodeManagement/index.tsx index ba726f4..e5be7c5 100644 --- a/src/pages/dashboard/Content/PromocodeManagement/index.tsx +++ b/src/pages/dashboard/Content/PromocodeManagement/index.tsx @@ -11,104 +11,96 @@ import { StatisticsModal } from "./StatisticsModal"; import DeleteModal from "./DeleteModal"; export const PromocodeManagement = () => { - const theme = useTheme(); + const theme = useTheme(); - const [deleteModal, setDeleteModal] = useState(""); - const deleteModalHC = (id: string) => setDeleteModal(id); + const [deleteModal, setDeleteModal] = useState(""); + const deleteModalHC = (id: string) => setDeleteModal(id); - const [showStatisticsModalId, setShowStatisticsModalId] = - useState(""); - const [page, setPage] = useState(0); - const [to, setTo] = useState(0); - const [from, setFrom] = useState(0); - const [pageSize, setPageSize] = useState(10); - const { - data, - error, - isValidating, - promocodesCount, - promocodeStatistics, - deletePromocode, - createPromocode, - createFastLink, - } = usePromocodes(page, pageSize, showStatisticsModalId, to, from); - const columns = usePromocodeGridColDef( - setShowStatisticsModalId, - deleteModalHC - ); - if (error) return Ошибка загрузки промокодов; + const [showStatisticsModalId, setShowStatisticsModalId] = useState(""); + const [page, setPage] = useState(0); + const [to, setTo] = useState(0); + const [from, setFrom] = useState(0); + const [pageSize, setPageSize] = useState(10); + const { + data, + error, + isValidating, + promocodesCount, + promocodeStatistics, + deletePromocode, + createPromocode, + createFastLink, + } = usePromocodes(page, pageSize, showStatisticsModalId, to, from); + const columns = usePromocodeGridColDef(setShowStatisticsModalId, deleteModalHC); + if (error) return Ошибка загрузки промокодов; - return ( - - - Создание промокода - - - - - - - - - ); + return ( + + + Создание промокода + + + + + + + + + ); }; diff --git a/src/pages/dashboard/Content/PromocodeManagement/usePromocodeGridColDef.tsx b/src/pages/dashboard/Content/PromocodeManagement/usePromocodeGridColDef.tsx index 707e7f2..dba8423 100644 --- a/src/pages/dashboard/Content/PromocodeManagement/usePromocodeGridColDef.tsx +++ b/src/pages/dashboard/Content/PromocodeManagement/usePromocodeGridColDef.tsx @@ -6,96 +6,92 @@ import { useMemo, useState } from "react"; import { BarChart, Delete } from "@mui/icons-material"; import { promocodeApi } from "@root/api/promocode/requests"; -export function usePromocodeGridColDef( - setStatistics: (id: string) => void, - deletePromocode: (id: string) => void -) { - const validity = (value: string | number) => { - if (value === 0) { - return "неоганичен"; - } else { - return new Date(value).toLocaleString(); - } - }; - return useMemo[]>( - () => [ - { - field: "id", - headerName: "ID", - width: 30, - sortable: false, - valueGetter: ({ row }) => row.id, - }, - { - field: "codeword", - headerName: "Кодовое слово", - width: 160, - sortable: false, - valueGetter: ({ row }) => row.codeword, - }, - { - field: "factor", - headerName: "Коэф. скидки", - width: 120, - sortable: false, - valueGetter: ({ row }) => - Math.round(row.bonus.discount.factor * 1000) / 1000, - }, - { - field: "activationCount", - headerName: "Кол-во активаций", - width: 140, - sortable: false, - valueGetter: ({ row }) => row.activationCount, - }, - { - field: "dueTo", - headerName: "Истекает", - width: 160, - sortable: false, - valueGetter: ({ row }) => row.dueTo * 1000, - valueFormatter: ({ value }) => `${validity(value)}`, - }, - { - field: "description", - headerName: "Описание", - minWidth: 200, - flex: 1, - sortable: false, - valueGetter: ({ row }) => row.description, - }, - { - field: "settings", - headerName: "", - width: 60, - sortable: false, - renderCell: (params) => { - return ( - { - setStatistics(params.row.id); - promocodeApi.getPromocodeStatistics(params.row.id, 0, 0); - }} - > - - - ); - }, - }, - { - field: "delete", - headerName: "", - width: 60, - sortable: false, - renderCell: (params) => { - return ( - deletePromocode(params.row.id)}> - - - ); - }, - }, - ], - [deletePromocode, setStatistics] - ); +export function usePromocodeGridColDef(setStatistics: (id: string) => void, deletePromocode: (id: string) => void) { + const validity = (value: string | number) => { + if (value === 0) { + return "неоганичен"; + } else { + return new Date(value).toLocaleString(); + } + }; + return useMemo[]>( + () => [ + { + field: "id", + headerName: "ID", + width: 30, + sortable: false, + valueGetter: ({ row }) => row.id, + }, + { + field: "codeword", + headerName: "Кодовое слово", + width: 160, + sortable: false, + valueGetter: ({ row }) => row.codeword, + }, + { + field: "factor", + headerName: "Коэф. скидки", + width: 120, + sortable: false, + valueGetter: ({ row }) => Math.round(row.bonus.discount.factor * 1000) / 1000, + }, + { + field: "activationCount", + headerName: "Кол-во активаций", + width: 140, + sortable: false, + valueGetter: ({ row }) => row.activationCount, + }, + { + field: "dueTo", + headerName: "Истекает", + width: 160, + sortable: false, + valueGetter: ({ row }) => row.dueTo * 1000, + valueFormatter: ({ value }) => `${validity(value)}`, + }, + { + field: "description", + headerName: "Описание", + minWidth: 200, + flex: 1, + sortable: false, + valueGetter: ({ row }) => row.description, + }, + { + field: "settings", + headerName: "", + width: 60, + sortable: false, + renderCell: (params) => { + return ( + { + setStatistics(params.row.id); + promocodeApi.getPromocodeStatistics(params.row.id, 0, 0); + }} + > + + + ); + }, + }, + { + field: "delete", + headerName: "", + width: 60, + sortable: false, + renderCell: (params) => { + return ( + deletePromocode(params.row.id)}> + + + ); + }, + }, + ], + [deletePromocode, setStatistics] + ); } diff --git a/src/pages/dashboard/Content/QuizStatistics/DateFilter.tsx b/src/pages/dashboard/Content/QuizStatistics/DateFilter.tsx index 93d052c..636e392 100644 --- a/src/pages/dashboard/Content/QuizStatistics/DateFilter.tsx +++ b/src/pages/dashboard/Content/QuizStatistics/DateFilter.tsx @@ -4,83 +4,73 @@ import { DatePicker } from "@mui/x-date-pickers"; import type { Moment } from "moment"; type DateFilterProps = { - from: Moment | null; - to: Moment | null; - setFrom: (date: Moment | null) => void; - setTo: (date: Moment | null) => void; + from: Moment | null; + to: Moment | null; + setFrom: (date: Moment | null) => void; + setTo: (date: Moment | null) => void; }; export const DateFilter = ({ to, setTo, from, setFrom }: DateFilterProps) => { - const theme = useTheme(); + const theme = useTheme(); - return ( - <> - - - Дата начала - - date && setFrom(date.startOf('day'))} - renderInput={(params) => ( - - )} - InputProps={{ - sx: { - height: "40px", - color: theme.palette.secondary.main, - border: "1px solid", - borderColor: theme.palette.secondary.main, - "& .MuiSvgIcon-root": { color: theme.palette.secondary.main }, - }, - }} - /> - - - - Дата окончания - - date && setTo(date.endOf('day'))} - renderInput={(params) => ( - - )} - InputProps={{ - sx: { - height: "40px", - color: theme.palette.secondary.main, - border: "1px solid", - borderColor: theme.palette.secondary.main, - "& .MuiSvgIcon-root": { - color: theme.palette.secondary.main, - }, - }, - }} - /> - - - ); + return ( + <> + + + Дата начала + + date && setFrom(date.startOf("day"))} + renderInput={(params) => } + InputProps={{ + sx: { + height: "40px", + color: theme.palette.secondary.main, + border: "1px solid", + borderColor: theme.palette.secondary.main, + "& .MuiSvgIcon-root": { color: theme.palette.secondary.main }, + }, + }} + /> + + + + Дата окончания + + date && setTo(date.endOf("day"))} + renderInput={(params) => } + InputProps={{ + sx: { + height: "40px", + color: theme.palette.secondary.main, + border: "1px solid", + borderColor: theme.palette.secondary.main, + "& .MuiSvgIcon-root": { + color: theme.palette.secondary.main, + }, + }, + }} + /> + + + ); }; diff --git a/src/pages/dashboard/Content/QuizStatistics/QuizInfo.tsx b/src/pages/dashboard/Content/QuizStatistics/QuizInfo.tsx index 3c0c492..0342094 100644 --- a/src/pages/dashboard/Content/QuizStatistics/QuizInfo.tsx +++ b/src/pages/dashboard/Content/QuizStatistics/QuizInfo.tsx @@ -1,14 +1,6 @@ import { useState } from "react"; import moment from "moment"; -import { - Table, - TableBody, - TableCell, - TableHead, - TableRow, - Button, - useTheme, -} from "@mui/material"; +import { Table, TableBody, TableCell, TableHead, TableRow, Button, useTheme } from "@mui/material"; import { DateFilter } from "./DateFilter"; @@ -17,67 +9,67 @@ import { useQuizStatistic } from "@root/utils/hooks/useQuizStatistic"; import type { Moment } from "moment"; export const QuizInfo = () => { - const [from, setFrom] = useState(null); - const [to, setTo] = useState(moment()); - const theme = useTheme(); - const { Registrations, Quizes, Results } = useQuizStatistic({ - from, - to, - }); + const [from, setFrom] = useState(null); + const [to, setTo] = useState(moment()); + const theme = useTheme(); + const { Registrations, Quizes, Results } = useQuizStatistic({ + from, + to, + }); - const resetTime = () => { - setFrom(moment()); - setTo(moment()); - }; + const resetTime = () => { + setFrom(moment()); + setTo(moment()); + }; - return ( - <> - - - - - - - Регистраций - - - Quiz - - - Результаты - - - - - - - {Registrations} - - - {Quizes} - - - {Results} - - - -
- - ); + return ( + <> + + + + + + + Регистраций + + + Quiz + + + Результаты + + + + + + + {Registrations} + + + {Quizes} + + + {Results} + + + +
+ + ); }; diff --git a/src/pages/dashboard/Content/QuizStatistics/StastisticsPromocode.tsx b/src/pages/dashboard/Content/QuizStatistics/StastisticsPromocode.tsx index 5223fd7..c5c109c 100644 --- a/src/pages/dashboard/Content/QuizStatistics/StastisticsPromocode.tsx +++ b/src/pages/dashboard/Content/QuizStatistics/StastisticsPromocode.tsx @@ -1,14 +1,6 @@ import { useState } from "react"; import moment from "moment"; -import { - Table, - TableBody, - TableCell, - TableHead, - TableRow, - Typography, - useTheme, -} from "@mui/material"; +import { Table, TableBody, TableCell, TableHead, TableRow, Typography, useTheme } from "@mui/material"; import { DateFilter } from "./DateFilter"; @@ -18,63 +10,61 @@ import { usePromocodeStatistics } from "@root/utils/hooks/usePromocodeStatistics import type { Moment } from "moment"; export const StatisticsPromocode = () => { - const [from, setFrom] = useState( - moment(moment().subtract(4, "weeks")) - ); - const [to, setTo] = useState(moment()); - const promocodes = useAllPromocodes(); - const promocodeStatistics = usePromocodeStatistics({ to, from }); - const theme = useTheme(); + const [from, setFrom] = useState(moment(moment().subtract(4, "weeks"))); + const [to, setTo] = useState(moment()); + const promocodes = useAllPromocodes(); + const promocodeStatistics = usePromocodeStatistics({ to, from }); + const theme = useTheme(); - return ( - <> - Статистика промокодов - - - - - - Промокод - - - Регистации - - - Внесено - - - - {Object.entries(promocodeStatistics).map(([key, { Regs, Money }]) => ( - - - - {promocodes.find(({ id }) => id === key)?.codeword ?? ""} - - - {Regs} - - - {(Money / 100).toFixed(2)} - - - - ))} -
- - ); + return ( + <> + Статистика промокодов + + + + + + Промокод + + + Регистации + + + Внесено + + + + {Object.entries(promocodeStatistics).map(([key, { Regs, Money }]) => ( + + + + {promocodes.find(({ id }) => id === key)?.codeword ?? ""} + + + {Regs} + + + {(Money / 100).toFixed(2)} + + + + ))} +
+ + ); }; diff --git a/src/pages/dashboard/Content/QuizStatistics/StatisticsSchild.tsx b/src/pages/dashboard/Content/QuizStatistics/StatisticsSchild.tsx index 4567084..030fa9c 100644 --- a/src/pages/dashboard/Content/QuizStatistics/StatisticsSchild.tsx +++ b/src/pages/dashboard/Content/QuizStatistics/StatisticsSchild.tsx @@ -1,17 +1,17 @@ import moment from "moment"; import { useEffect, useState } from "react"; import { - Accordion, - AccordionDetails, - AccordionSummary, - Table, - TableBody, - TableCell, - TableHead, - TableRow, - Typography, - Button, - useTheme, + Accordion, + AccordionDetails, + AccordionSummary, + Table, + TableBody, + TableCell, + TableHead, + TableRow, + Typography, + Button, + useTheme, } from "@mui/material"; import { GridToolbar } from "@mui/x-data-grid"; import { enqueueSnackbar } from "notistack"; @@ -29,168 +29,154 @@ import type { QuizStatisticsItem } from "@root/api/quizStatistics/types"; import type { GridColDef } from "@mui/x-data-grid"; const COLUMNS: GridColDef[] = [ - { - field: "user", - headerName: "Пользователь", - width: 250, - valueGetter: ({ row }) => row.ID, - }, - { - field: "regs", - headerName: "Регистраций", - width: 200, - valueGetter: ({ row }) => String(row.Regs), - }, - { - field: "money", - headerName: "Деньги", - width: 200, - valueGetter: ({ row }) => String(row.Money), - }, + { + field: "user", + headerName: "Пользователь", + width: 250, + valueGetter: ({ row }) => row.ID, + }, + { + field: "regs", + headerName: "Регистраций", + width: 200, + valueGetter: ({ row }) => String(row.Regs), + }, + { + field: "money", + headerName: "Деньги", + width: 200, + valueGetter: ({ row }) => String(row.Money), + }, ]; export const StatisticsSchild = () => { - const [openUserModal, setOpenUserModal] = useState(false); - const [activeUserId, setActiveUserId] = useState(""); - const [page, setPage] = useState(1); - const [pageSize, setPageSize] = useState(10); + const [openUserModal, setOpenUserModal] = useState(false); + const [activeUserId, setActiveUserId] = useState(""); + const [page, setPage] = useState(1); + const [pageSize, setPageSize] = useState(10); - const [from, setFrom] = useState( - moment(moment().subtract(4, "weeks")) - ); - const [to, setTo] = useState(moment()); + const [from, setFrom] = useState(moment(moment().subtract(4, "weeks"))); + const [to, setTo] = useState(moment()); - const theme = useTheme(); - const statistics = useSchildStatistics(from, to) - .map((obj) => ({...obj, Money: Number((obj.Money / 100).toFixed(2))})); + const theme = useTheme(); + const statistics = useSchildStatistics(from, to).map((obj) => ({ + ...obj, + Money: Number((obj.Money / 100).toFixed(2)), + })); - useEffect(() => { - if (!openUserModal) { - setActiveUserId(""); - } - }, [openUserModal]); + useEffect(() => { + if (!openUserModal) { + setActiveUserId(""); + } + }, [openUserModal]); - useEffect(() => { - if (activeUserId) { - setOpenUserModal(true); + useEffect(() => { + if (activeUserId) { + setOpenUserModal(true); - return; - } + return; + } - setOpenUserModal(false); - }, [activeUserId]); + setOpenUserModal(false); + }, [activeUserId]); - const copyQuizLink = (quizId: string) => { - navigator.clipboard.writeText( - `https://${ - window.location.href.includes("/admin.") ? "" : "s." - }hbpn.link/${quizId}` - ); + const copyQuizLink = (quizId: string) => { + navigator.clipboard.writeText(`https://${window.location.href.includes("/admin.") ? "" : "s."}hbpn.link/${quizId}`); - enqueueSnackbar("Ссылка успешно скопирована"); - }; + enqueueSnackbar("Ссылка успешно скопирована"); + }; - return ( - <> - - Статистика переходов с шильдика - - - ID} - checkboxSelection={true} - rows={statistics} - components={{ Toolbar: GridToolbar }} - rowCount={statistics.length} - rowsPerPageOptions={[1, 10, 25, 50, 100]} - paginationMode="client" - disableSelectionOnClick - page={page} - pageSize={pageSize} - onPageChange={setPage} - onPageSizeChange={setPageSize} - onCellClick={({ id, field }) => - field === "user" && setActiveUserId(String(id)) - } - getRowHeight={() => "auto"} - columns={[ - ...COLUMNS, - { - field: "quizes", - headerName: "Квизы", - flex: 1, - minWidth: 220, - valueGetter: ({ row }) => String(row.Quizes.length), - renderCell: ({ row }) => ( - - } - aria-controls="panel1-content" - id="panel1-header" - > - Статистика по квизам - - - - - - - QuizID - - - Регистрации - - - Деньги - - - + return ( + <> + Статистика переходов с шильдика + + ID} + checkboxSelection={true} + rows={statistics} + components={{ Toolbar: GridToolbar }} + rowCount={statistics.length} + rowsPerPageOptions={[1, 10, 25, 50, 100]} + paginationMode="client" + disableSelectionOnClick + page={page} + pageSize={pageSize} + onPageChange={setPage} + onPageSizeChange={setPageSize} + onCellClick={({ id, field }) => field === "user" && setActiveUserId(String(id))} + getRowHeight={() => "auto"} + columns={[ + ...COLUMNS, + { + field: "quizes", + headerName: "Квизы", + flex: 1, + minWidth: 220, + valueGetter: ({ row }) => String(row.Quizes.length), + renderCell: ({ row }) => ( + + } + aria-controls="panel1-content" + id="panel1-header" + > + Статистика по квизам + + +
+ + + + QuizID + + + Регистрации + + + Деньги + + + - - {row.Quizes.map(({ QuizID, Regs, Money }) => ( - - - - - - {Regs} - - - {(Money / 100).toFixed(2)} - - - ))} - -
-
-
- ), - }, - ]} - /> - setOpenUserModal(false)} - userId={activeUserId} - /> - - ); + + {row.Quizes.map(({ QuizID, Regs, Money }) => ( + + + + + + {Regs} + + + {(Money / 100).toFixed(2)} + + + ))} + + + + + ), + }, + ]} + /> + setOpenUserModal(false)} userId={activeUserId} /> + + ); }; diff --git a/src/pages/dashboard/Content/QuizStatistics/index.tsx b/src/pages/dashboard/Content/QuizStatistics/index.tsx index 5c256a4..9688054 100644 --- a/src/pages/dashboard/Content/QuizStatistics/index.tsx +++ b/src/pages/dashboard/Content/QuizStatistics/index.tsx @@ -8,11 +8,11 @@ import { StatisticsSchild } from "./StatisticsSchild"; import { StatisticsPromocode } from "./StastisticsPromocode"; export const QuizStatistics = () => ( - - - - Loading...
}> - - - + + + + Loading...}> + + + ); diff --git a/src/pages/dashboard/Content/ServiceUsersDG.tsx b/src/pages/dashboard/Content/ServiceUsersDG.tsx index 2aca18d..3c603ac 100644 --- a/src/pages/dashboard/Content/ServiceUsersDG.tsx +++ b/src/pages/dashboard/Content/ServiceUsersDG.tsx @@ -7,86 +7,86 @@ import DataGrid from "@kitUI/datagrid"; import type { UserType } from "@root/api/roles"; const columns: GridColDef[] = [ - { - field: "login", - headerName: "Логин", - width: 200, - valueGetter: ({ row }) => row.login, - }, - { - field: "email", - headerName: "E-mail", - width: 200, - valueGetter: ({ row }) => row.email, - }, - { - field: "phoneNumber", - headerName: "Номер телефона", - width: 200, - valueGetter: ({ row }) => row.phoneNumber, - }, - { - field: "isDeleted", - headerName: "Удалено", - width: 100, - valueGetter: ({ row }) => `${row.isDeleted ? "true" : "false"}`, - }, - { - field: "createdAt", - headerName: "Дата создания", - width: 200, - valueGetter: ({ row }) => row.createdAt, - }, + { + field: "login", + headerName: "Логин", + width: 200, + valueGetter: ({ row }) => row.login, + }, + { + field: "email", + headerName: "E-mail", + width: 200, + valueGetter: ({ row }) => row.email, + }, + { + field: "phoneNumber", + headerName: "Номер телефона", + width: 200, + valueGetter: ({ row }) => row.phoneNumber, + }, + { + field: "isDeleted", + headerName: "Удалено", + width: 100, + valueGetter: ({ row }) => `${row.isDeleted ? "true" : "false"}`, + }, + { + field: "createdAt", + headerName: "Дата создания", + width: 200, + valueGetter: ({ row }) => row.createdAt, + }, ]; interface Props { - handleSelectionChange: (selectionModel: GridSelectionModel) => void; - users: UserType[]; - page: number; - setPage: (page: number) => void; - pageSize: number; - pagesCount: number; - onPageSizeChange?: (count: number) => void; + handleSelectionChange: (selectionModel: GridSelectionModel) => void; + users: UserType[]; + page: number; + setPage: (page: number) => void; + pageSize: number; + pagesCount: number; + onPageSizeChange?: (count: number) => void; } export default function ServiceUsersDG({ - handleSelectionChange, - users = [], - page, - setPage, - pageSize = 10, - pagesCount = 1, - onPageSizeChange, + handleSelectionChange, + users = [], + page, + setPage, + pageSize = 10, + pagesCount = 1, + onPageSizeChange, }: Props) { - const navigate = useNavigate(); + const navigate = useNavigate(); - return ( - <> - {users.length ? ( - users._id} - checkboxSelection={true} - rows={users} - columns={columns} - components={{ Toolbar: GridToolbar }} - rowCount={pageSize * pagesCount} - rowsPerPageOptions={[10, 25, 50, 100]} - paginationMode="server" - page={page} - pageSize={pageSize} - onPageChange={setPage} - onPageSizeChange={onPageSizeChange} - onSelectionModelChange={handleSelectionChange} - onCellClick={({ row }, event) => { - event.stopPropagation(); + return ( + <> + {users.length ? ( + users._id} + checkboxSelection={true} + rows={users} + columns={columns} + components={{ Toolbar: GridToolbar }} + rowCount={pageSize * pagesCount} + rowsPerPageOptions={[10, 25, 50, 100]} + paginationMode="server" + page={page} + pageSize={pageSize} + onPageChange={setPage} + onPageSizeChange={onPageSizeChange} + onSelectionModelChange={handleSelectionChange} + onCellClick={({ row }, event) => { + event.stopPropagation(); - navigate(row._id); - }} - /> - ) : ( - Loading... - )} - - ); + navigate(row._id); + }} + /> + ) : ( + Loading... + )} + + ); } diff --git a/src/pages/dashboard/Content/Support/Chat/Chat.tsx b/src/pages/dashboard/Content/Support/Chat/Chat.tsx index d03f39b..0585ca9 100644 --- a/src/pages/dashboard/Content/Support/Chat/Chat.tsx +++ b/src/pages/dashboard/Content/Support/Chat/Chat.tsx @@ -1,5 +1,12 @@ import { Box, IconButton, InputAdornment, TextField, Typography, useMediaQuery, useTheme } from "@mui/material"; -import { addOrUpdateMessages, clearMessageState, incrementMessageApiPage, setIsPreventAutoscroll, setTicketMessagesFetchState, useMessageStore } from "@root/stores/messages"; +import { + addOrUpdateMessages, + clearMessageState, + incrementMessageApiPage, + setIsPreventAutoscroll, + setTicketMessagesFetchState, + useMessageStore, +} from "@root/stores/messages"; import Message from "./Message"; import SendIcon from "@mui/icons-material/Send"; import AttachFileIcon from "@mui/icons-material/AttachFile"; @@ -9,7 +16,14 @@ import { TicketMessage } from "@root/model/ticket"; import { sendTicketMessage } from "@root/api/tickets"; import { enqueueSnackbar } from "notistack"; import { useTicketStore } from "@root/stores/tickets"; -import { getMessageFromFetchError, throttle, useEventListener, useSSESubscription, useTicketMessages, useToken } from "@frontend/kitui"; +import { + getMessageFromFetchError, + throttle, + useEventListener, + useSSESubscription, + useTicketMessages, + useToken, +} from "@frontend/kitui"; import makeRequest from "@root/api/makeRequest"; import ChatImage from "./ChatImage"; import ChatDocument from "./ChatDocument"; @@ -17,319 +31,334 @@ import ChatVideo from "./ChatVideo"; import ChatMessage from "./ChatMessage"; import { ACCEPT_SEND_MEDIA_TYPES_MAP, MAX_FILE_SIZE, MAX_PHOTO_SIZE, MAX_VIDEO_SIZE } from "./fileUpload"; -const tooLarge = "Файл слишком большой" +const tooLarge = "Файл слишком большой"; const checkAcceptableMediaType = (file: File) => { - if (file === null) return "" - - const segments = file?.name.split('.'); - const extension = segments[segments.length - 1]; - const type = extension.toLowerCase(); - - switch (type) { - case ACCEPT_SEND_MEDIA_TYPES_MAP.document.find(name => name === type): - if (file.size > MAX_FILE_SIZE) return tooLarge - return "" - - case ACCEPT_SEND_MEDIA_TYPES_MAP.picture.find(name => name === type): - if (file.size > MAX_PHOTO_SIZE) return tooLarge - return "" - - case ACCEPT_SEND_MEDIA_TYPES_MAP.video.find(name => name === type): - if (file.size > MAX_VIDEO_SIZE) return tooLarge - return "" - - default: - return "Не удалось отправить файл. Недопустимый тип" - } - } + if (file === null) return ""; + + const segments = file?.name.split("."); + const extension = segments[segments.length - 1]; + const type = extension.toLowerCase(); + + switch (type) { + case ACCEPT_SEND_MEDIA_TYPES_MAP.document.find((name) => name === type): + if (file.size > MAX_FILE_SIZE) return tooLarge; + return ""; + + case ACCEPT_SEND_MEDIA_TYPES_MAP.picture.find((name) => name === type): + if (file.size > MAX_PHOTO_SIZE) return tooLarge; + return ""; + + case ACCEPT_SEND_MEDIA_TYPES_MAP.video.find((name) => name === type): + if (file.size > MAX_VIDEO_SIZE) return tooLarge; + return ""; + + default: + return "Не удалось отправить файл. Недопустимый тип"; + } +}; export default function Chat() { - const token = useToken(); - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - const tickets = useTicketStore(state => state.tickets); - const messages = useMessageStore(state => state.messages); - const [messageField, setMessageField] = useState(""); - const ticketId = useParams().ticketId; - const chatBoxRef = useRef(null); - const messageApiPage = useMessageStore(state => state.apiPage); - const messagesPerPage = useMessageStore(state => state.messagesPerPage); - const isPreventAutoscroll = useMessageStore(state => state.isPreventAutoscroll); - const fetchState = useMessageStore(state => state.ticketMessagesFetchState); - const lastMessageId = useMessageStore(state => state.lastMessageId); - const fileInputRef = useRef(null); - const [disableFileButton, setDisableFileButton] = useState(false); + const token = useToken(); + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const tickets = useTicketStore((state) => state.tickets); + const messages = useMessageStore((state) => state.messages); + const [messageField, setMessageField] = useState(""); + const ticketId = useParams().ticketId; + const chatBoxRef = useRef(null); + const messageApiPage = useMessageStore((state) => state.apiPage); + const messagesPerPage = useMessageStore((state) => state.messagesPerPage); + const isPreventAutoscroll = useMessageStore((state) => state.isPreventAutoscroll); + const fetchState = useMessageStore((state) => state.ticketMessagesFetchState); + const lastMessageId = useMessageStore((state) => state.lastMessageId); + const fileInputRef = useRef(null); + const [disableFileButton, setDisableFileButton] = useState(false); - const ticket = tickets.find(ticket => ticket.id === ticketId); + const ticket = tickets.find((ticket) => ticket.id === ticketId); - useTicketMessages({ - url: process.env.REACT_APP_DOMAIN + "/heruvym/getMessages", - ticketId, - messagesPerPage, - messageApiPage, - onSuccess: messages => { - if (chatBoxRef.current && chatBoxRef.current.scrollTop < 1) chatBoxRef.current.scrollTop = 1; - addOrUpdateMessages(messages); - }, - onError: (error: Error) => { - const message = getMessageFromFetchError(error); - if (message) enqueueSnackbar(message); - }, - onFetchStateChange: setTicketMessagesFetchState, - }); + useTicketMessages({ + url: process.env.REACT_APP_DOMAIN + "/heruvym/getMessages", + ticketId, + messagesPerPage, + messageApiPage, + onSuccess: (messages) => { + if (chatBoxRef.current && chatBoxRef.current.scrollTop < 1) chatBoxRef.current.scrollTop = 1; + addOrUpdateMessages(messages); + }, + onError: (error: Error) => { + const message = getMessageFromFetchError(error); + if (message) enqueueSnackbar(message); + }, + onFetchStateChange: setTicketMessagesFetchState, + }); - useSSESubscription({ - enabled: Boolean(token) && Boolean(ticketId), - url: process.env.REACT_APP_DOMAIN + `/heruvym/ticket?ticket=${ticketId}&Authorization=${token}`, - onNewData: addOrUpdateMessages, - onDisconnect: () => { - clearMessageState(); - setIsPreventAutoscroll(false); - }, - marker: "ticket message" - }); + useSSESubscription({ + enabled: Boolean(token) && Boolean(ticketId), + url: process.env.REACT_APP_DOMAIN + `/heruvym/ticket?ticket=${ticketId}&Authorization=${token}`, + onNewData: addOrUpdateMessages, + onDisconnect: () => { + clearMessageState(); + setIsPreventAutoscroll(false); + }, + marker: "ticket message", + }); - const throttledScrollHandler = useMemo(() => throttle(() => { - const chatBox = chatBoxRef.current; - if (!chatBox) return; + const throttledScrollHandler = useMemo( + () => + throttle(() => { + const chatBox = chatBoxRef.current; + if (!chatBox) return; - const scrollBottom = chatBox.scrollHeight - chatBox.scrollTop - chatBox.clientHeight; - const isPreventAutoscroll = scrollBottom > chatBox.clientHeight * 20; - setIsPreventAutoscroll(isPreventAutoscroll); + const scrollBottom = chatBox.scrollHeight - chatBox.scrollTop - chatBox.clientHeight; + const isPreventAutoscroll = scrollBottom > chatBox.clientHeight * 20; + setIsPreventAutoscroll(isPreventAutoscroll); - if (fetchState !== "idle") return; + if (fetchState !== "idle") return; - if (chatBox.scrollTop < chatBox.clientHeight) { - incrementMessageApiPage(); - } - }, 200), [fetchState]); + if (chatBox.scrollTop < chatBox.clientHeight) { + incrementMessageApiPage(); + } + }, 200), + [fetchState] + ); - useEventListener("scroll", throttledScrollHandler, chatBoxRef); + useEventListener("scroll", throttledScrollHandler, chatBoxRef); - useEffect(function scrollOnNewMessage() { - if (!chatBoxRef.current) return; + useEffect( + function scrollOnNewMessage() { + if (!chatBoxRef.current) return; - if (!isPreventAutoscroll) { - setTimeout(() => { - scrollToBottom(); - }, 50); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [lastMessageId]); + if (!isPreventAutoscroll) { + setTimeout(() => { + scrollToBottom(); + }, 50); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, + [lastMessageId] + ); - function scrollToBottom(behavior?: ScrollBehavior) { - if (!chatBoxRef.current) return; + function scrollToBottom(behavior?: ScrollBehavior) { + if (!chatBoxRef.current) return; - const chatBox = chatBoxRef.current; - chatBox.scroll({ - left: 0, - top: chatBox.scrollHeight, - behavior, - }); - } + const chatBox = chatBoxRef.current; + chatBox.scroll({ + left: 0, + top: chatBox.scrollHeight, + behavior, + }); + } - function handleSendMessage() { - if (!ticket || !messageField) return; + function handleSendMessage() { + if (!ticket || !messageField) return; - sendTicketMessage({ - files: [], - lang: "ru", - message: messageField, - ticket: ticket.id, - }); - setMessageField(""); - } + sendTicketMessage({ + files: [], + lang: "ru", + message: messageField, + ticket: ticket.id, + }); + setMessageField(""); + } - const sendFile = async (file: File) => { - if (file === undefined) return true; - - let data; - - const ticketId = ticket?.id - if (ticketId !== undefined) { - try { - const body = new FormData(); - - body.append(file.name, file); - body.append("ticket", ticketId); - await makeRequest({ - url: process.env.REACT_APP_DOMAIN + "/heruvym/sendFiles", - body: body, - method: "POST", - }); - } catch (error: any) { - const errorMessage = getMessageFromFetchError(error); - if (errorMessage) enqueueSnackbar(errorMessage); - } - return true; - } - }; - const sendFileHC = async (file: File) => { - const check = checkAcceptableMediaType(file) - if (check.length > 0) { - enqueueSnackbar(check) - return - } - setDisableFileButton(true) - await sendFile(file) - setDisableFileButton(false) - }; + const sendFile = async (file: File) => { + if (file === undefined) return true; - function handleTextfieldKeyPress(e: KeyboardEvent) { - if (e.key === "Enter" && !e.shiftKey) { - e.preventDefault(); - handleSendMessage(); - } - } + let data; - return ( - - {ticket ? ticket.title : "Выберите тикет"} - - {ticket && - messages.map((message) => { - const isFileVideo = () => { - if (message.files) { - return (ACCEPT_SEND_MEDIA_TYPES_MAP.video.some((fileType) => - message.files[0].toLowerCase().endsWith(fileType), - )) - } - }; - const isFileImage = () => { - if (message.files) { - return (ACCEPT_SEND_MEDIA_TYPES_MAP.picture.some((fileType) => - message.files[0].toLowerCase().endsWith(fileType), - )) - } - }; - const isFileDocument = () => { - if (message.files) { - return (ACCEPT_SEND_MEDIA_TYPES_MAP.document.some((fileType) => - message.files[0].toLowerCase().endsWith(fileType), - )) - } - }; - if (message.files !== null && message.files.length > 0 && isFileImage()) { - return - } - if (message.files !== null && message.files.length > 0 && isFileVideo()) { - return - } - if (message.files !== null && message.files.length > 0 && isFileDocument()) { - return - } - return + const ticketId = ticket?.id; + if (ticketId !== undefined) { + try { + const body = new FormData(); - }) - } - - {ticket && - setMessageField(e.target.value)} - onKeyPress={handleTextfieldKeyPress} - id="message-input" - placeholder="Написать сообщение" - fullWidth - multiline - maxRows={8} - InputProps={{ - style: { - backgroundColor: theme.palette.content.main, - color: theme.palette.secondary.main, - }, - endAdornment: ( - - - - - { - if (!disableFileButton) fileInputRef.current?.click() - }} - sx={{ - height: "45px", - width: "45px", - p: 0, - }} - > - { - if (e.target.files?.[0]) sendFileHC(e.target.files?.[0]); - }} - style={{ display: "none" }} - type="file" - /> - - - - ) - }} - InputLabelProps={{ - style: { - color: theme.palette.secondary.main, - } - }} - /> - } - - ); + body.append(file.name, file); + body.append("ticket", ticketId); + await makeRequest({ + url: process.env.REACT_APP_DOMAIN + "/heruvym/sendFiles", + body: body, + method: "POST", + }); + } catch (error: any) { + const errorMessage = getMessageFromFetchError(error); + if (errorMessage) enqueueSnackbar(errorMessage); + } + return true; + } + }; + const sendFileHC = async (file: File) => { + const check = checkAcceptableMediaType(file); + if (check.length > 0) { + enqueueSnackbar(check); + return; + } + setDisableFileButton(true); + await sendFile(file); + setDisableFileButton(false); + }; + + function handleTextfieldKeyPress(e: KeyboardEvent) { + if (e.key === "Enter" && !e.shiftKey) { + e.preventDefault(); + handleSendMessage(); + } + } + + return ( + + {ticket ? ticket.title : "Выберите тикет"} + + {ticket && + messages.map((message) => { + const isFileVideo = () => { + if (message.files) { + return ACCEPT_SEND_MEDIA_TYPES_MAP.video.some((fileType) => + message.files[0].toLowerCase().endsWith(fileType) + ); + } + }; + const isFileImage = () => { + if (message.files) { + return ACCEPT_SEND_MEDIA_TYPES_MAP.picture.some((fileType) => + message.files[0].toLowerCase().endsWith(fileType) + ); + } + }; + const isFileDocument = () => { + if (message.files) { + return ACCEPT_SEND_MEDIA_TYPES_MAP.document.some((fileType) => + message.files[0].toLowerCase().endsWith(fileType) + ); + } + }; + if (message.files !== null && message.files.length > 0 && isFileImage()) { + return ( + + ); + } + if (message.files !== null && message.files.length > 0 && isFileVideo()) { + return ( + + ); + } + if (message.files !== null && message.files.length > 0 && isFileDocument()) { + return ( + + ); + } + return ( + + ); + })} + + {ticket && ( + setMessageField(e.target.value)} + onKeyPress={handleTextfieldKeyPress} + id="message-input" + placeholder="Написать сообщение" + fullWidth + multiline + maxRows={8} + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + }, + endAdornment: ( + + + + + { + if (!disableFileButton) fileInputRef.current?.click(); + }} + sx={{ + height: "45px", + width: "45px", + p: 0, + }} + > + { + if (e.target.files?.[0]) sendFileHC(e.target.files?.[0]); + }} + style={{ display: "none" }} + type="file" + /> + + + + ), + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main, + }, + }} + /> + )} + + ); } diff --git a/src/pages/dashboard/Content/Support/Chat/ChatDocument.tsx b/src/pages/dashboard/Content/Support/Chat/ChatDocument.tsx index 572202c..187fdb3 100644 --- a/src/pages/dashboard/Content/Support/Chat/ChatDocument.tsx +++ b/src/pages/dashboard/Content/Support/Chat/ChatDocument.tsx @@ -1,62 +1,58 @@ import { Box, Link, Typography, useMediaQuery, useTheme } from "@mui/material"; -import DownloadIcon from '@mui/icons-material/Download'; +import DownloadIcon from "@mui/icons-material/Download"; interface Props { - unAuthenticated?: boolean; - isSelf: boolean; - file: string; - createdAt: string; + unAuthenticated?: boolean; + isSelf: boolean; + file: string; + createdAt: string; } -export default function ChatDocument({ - unAuthenticated = false, - isSelf, - file, - createdAt, -}: Props) { - const theme = useTheme(); +export default function ChatDocument({ unAuthenticated = false, isSelf, file, createdAt }: Props) { + const theme = useTheme(); - const date = new Date(createdAt); + const date = new Date(createdAt); - return ( - - - {new Date(createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} - - - - - - - - ); + return ( + + + {new Date(createdAt).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })} + + + + + + + + ); } diff --git a/src/pages/dashboard/Content/Support/Chat/ChatImage.tsx b/src/pages/dashboard/Content/Support/Chat/ChatImage.tsx index bb82339..1ea138b 100644 --- a/src/pages/dashboard/Content/Support/Chat/ChatImage.tsx +++ b/src/pages/dashboard/Content/Support/Chat/ChatImage.tsx @@ -1,68 +1,57 @@ -import { - Box, - ButtonBase, - Link, - Typography, - useMediaQuery, - useTheme, -} from "@mui/material"; +import { Box, ButtonBase, Link, Typography, useMediaQuery, useTheme } from "@mui/material"; import { useNavigate } from "react-router-dom"; interface Props { - unAuthenticated?: boolean; - isSelf: boolean; - file: string; - createdAt: string; + unAuthenticated?: boolean; + isSelf: boolean; + file: string; + createdAt: string; } -export default function ChatImage({ - unAuthenticated = false, - isSelf, - file, - createdAt, -}: Props) { - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - const navigate = useNavigate(); +export default function ChatImage({ unAuthenticated = false, isSelf, file, createdAt }: Props) { + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const navigate = useNavigate(); - - return ( - - - {new Date(createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} - - - - - - - - ); + return ( + + + {new Date(createdAt).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })} + + + + + + + + ); } diff --git a/src/pages/dashboard/Content/Support/Chat/ChatMessage.tsx b/src/pages/dashboard/Content/Support/Chat/ChatMessage.tsx index cf71127..9232687 100644 --- a/src/pages/dashboard/Content/Support/Chat/ChatMessage.tsx +++ b/src/pages/dashboard/Content/Support/Chat/ChatMessage.tsx @@ -1,53 +1,48 @@ import { Box, Typography, useMediaQuery, useTheme } from "@mui/material"; interface Props { - unAuthenticated?: boolean; - isSelf: boolean; - text: string; - createdAt: string; + unAuthenticated?: boolean; + isSelf: boolean; + text: string; + createdAt: string; } -export default function ChatMessage({ - unAuthenticated = false, - isSelf, - text, - createdAt, -}: Props) { - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); +export default function ChatMessage({ unAuthenticated = false, isSelf, text, createdAt }: Props) { + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); - - - return ( - - - {new Date(createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} - - - - {text} - - - - ); + return ( + + + {new Date(createdAt).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })} + + + + {text} + + + + ); } diff --git a/src/pages/dashboard/Content/Support/Chat/ChatVideo.tsx b/src/pages/dashboard/Content/Support/Chat/ChatVideo.tsx index b1c4c88..f3b98b7 100644 --- a/src/pages/dashboard/Content/Support/Chat/ChatVideo.tsx +++ b/src/pages/dashboard/Content/Support/Chat/ChatVideo.tsx @@ -2,66 +2,61 @@ import { Box, Typography, useMediaQuery, useTheme } from "@mui/material"; import { useNavigate } from "react-router-dom"; interface Props { - unAuthenticated?: boolean; - isSelf: boolean; - file: string; - createdAt: string; + unAuthenticated?: boolean; + isSelf: boolean; + file: string; + createdAt: string; } -export default function ChatImage({ - unAuthenticated = false, - isSelf, - file, - createdAt, -}: Props) { - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - const navigate = useNavigate(); +export default function ChatImage({ unAuthenticated = false, isSelf, file, createdAt }: Props) { + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const navigate = useNavigate(); - return ( - - - {new Date(createdAt).toLocaleTimeString([], { - hour: "2-digit", - minute: "2-digit", - })} - - - - - - - - ); + return ( + + + {new Date(createdAt).toLocaleTimeString([], { + hour: "2-digit", + minute: "2-digit", + })} + + + + + + + + ); } diff --git a/src/pages/dashboard/Content/Support/Chat/Message.tsx b/src/pages/dashboard/Content/Support/Chat/Message.tsx index ad9c249..66fc58a 100644 --- a/src/pages/dashboard/Content/Support/Chat/Message.tsx +++ b/src/pages/dashboard/Content/Support/Chat/Message.tsx @@ -1,44 +1,49 @@ import { Box, Typography, useTheme } from "@mui/material"; import { TicketMessage } from "@root/model/ticket"; - interface Props { - message: TicketMessage; - isSelf?: boolean; + message: TicketMessage; + isSelf?: boolean; } export default function Message({ message, isSelf }: Props) { - const theme = useTheme(); + const theme = useTheme(); - const time = ( - - {new Date(message.created_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} - - ); - return ( - - {isSelf && time} - - - {message.message} - - - {!isSelf && time} - - ); -} \ No newline at end of file + const time = ( + + {new Date(message.created_at).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })} + + ); + return ( + + {isSelf && time} + + + {message.message} + + + {!isSelf && time} + + ); +} diff --git a/src/pages/dashboard/Content/Support/Chat/fileUpload.ts b/src/pages/dashboard/Content/Support/Chat/fileUpload.ts index 19410e1..2b356ab 100644 --- a/src/pages/dashboard/Content/Support/Chat/fileUpload.ts +++ b/src/pages/dashboard/Content/Support/Chat/fileUpload.ts @@ -3,7 +3,7 @@ export const MAX_PHOTO_SIZE = 5242880; export const MAX_VIDEO_SIZE = 52428800; export const ACCEPT_SEND_MEDIA_TYPES_MAP = { - picture: ["jpg", "png"], - video: ["mp4"], - document: ["doc", "docx", "pdf", "txt", "xlsx", "csv"], - } as const; \ No newline at end of file + picture: ["jpg", "png"], + video: ["mp4"], + document: ["doc", "docx", "pdf", "txt", "xlsx", "csv"], +} as const; diff --git a/src/pages/dashboard/Content/Support/ChatImageNewWindow.tsx b/src/pages/dashboard/Content/Support/ChatImageNewWindow.tsx index 4c7040b..382a5f3 100644 --- a/src/pages/dashboard/Content/Support/ChatImageNewWindow.tsx +++ b/src/pages/dashboard/Content/Support/ChatImageNewWindow.tsx @@ -2,18 +2,18 @@ import { Box } from "@mui/material"; import { useLocation } from "react-router-dom"; export default function ChatImageNewWindow() { - const location = useLocation(); - const srcImage = location.pathname.split("image/")[1]; - return ( - <> - - - ); + const location = useLocation(); + const srcImage = location.pathname.split("image/")[1]; + return ( + <> + + + ); } diff --git a/src/pages/dashboard/Content/Support/Collapse.tsx b/src/pages/dashboard/Content/Support/Collapse.tsx index e6e6962..9c65dcb 100644 --- a/src/pages/dashboard/Content/Support/Collapse.tsx +++ b/src/pages/dashboard/Content/Support/Collapse.tsx @@ -3,51 +3,51 @@ import { Box, Typography, useTheme } from "@mui/material"; import ExpandIcon from "./ExpandIcon"; interface Props { - headerText: string; - children: (callback: () => void) => ReactNode; + headerText: string; + children: (callback: () => void) => ReactNode; } export default function Collapse({ headerText, children }: Props) { - const theme = useTheme(); - const [isExpanded, setIsExpanded] = useState(false); + const theme = useTheme(); + const [isExpanded, setIsExpanded] = useState(false); - return ( - - setIsExpanded((prev) => !prev)} - sx={{ - height: "72px", - p: "16px", - backgroundColor: theme.palette.menu.main, - borderRadius: "12px", + return ( + + setIsExpanded((prev) => !prev)} + sx={{ + height: "72px", + p: "16px", + backgroundColor: theme.palette.menu.main, + borderRadius: "12px", - display: "flex", - justifyContent: "space-between", - alignItems: "center", - cursor: "pointer", - userSelect: "none", - }} - > - {headerText} - - - {isExpanded && ( - - {children(() => setIsExpanded(false))} - - )} - - ); + display: "flex", + justifyContent: "space-between", + alignItems: "center", + cursor: "pointer", + userSelect: "none", + }} + > + {headerText} + + + {isExpanded && ( + + {children(() => setIsExpanded(false))} + + )} + + ); } diff --git a/src/pages/dashboard/Content/Support/ExpandIcon.tsx b/src/pages/dashboard/Content/Support/ExpandIcon.tsx index eb6502f..ae3ab18 100644 --- a/src/pages/dashboard/Content/Support/ExpandIcon.tsx +++ b/src/pages/dashboard/Content/Support/ExpandIcon.tsx @@ -1,17 +1,34 @@ import { useTheme } from "@mui/material"; - interface Props { - isExpanded: boolean; + isExpanded: boolean; } export default function ExpandIcon({ isExpanded }: Props) { - const theme = useTheme(); + const theme = useTheme(); - return ( - - - - - ); + return ( + + + + + ); } diff --git a/src/pages/dashboard/Content/Support/Support.tsx b/src/pages/dashboard/Content/Support/Support.tsx index 55735aa..625a404 100644 --- a/src/pages/dashboard/Content/Support/Support.tsx +++ b/src/pages/dashboard/Content/Support/Support.tsx @@ -4,100 +4,79 @@ import Chat from "./Chat/Chat"; import Collapse from "./Collapse"; import TicketList from "./TicketList/TicketList"; import { Ticket } from "@root/model/ticket"; -import { - clearTickets, - setTicketsFetchState, - updateTickets, - useTicketStore, -} from "@root/stores/tickets"; +import { clearTickets, setTicketsFetchState, updateTickets, useTicketStore } from "@root/stores/tickets"; import { enqueueSnackbar } from "notistack"; import { clearMessageState } from "@root/stores/messages"; -import { - getMessageFromFetchError, - useSSESubscription, - useTicketsFetcher, - useToken, -} from "@frontend/kitui"; +import { getMessageFromFetchError, useSSESubscription, useTicketsFetcher, useToken } from "@frontend/kitui"; import ModalUser from "@root/pages/dashboard/ModalUser"; export default function Support() { - const [openUserModal, setOpenUserModal] = useState(false); - const [activeUserId, setActiveUserId] = useState(""); - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - const ticketsPerPage = useTicketStore((state) => state.ticketsPerPage); - const ticketApiPage = useTicketStore((state) => state.apiPage); - const token = useToken(); + const [openUserModal, setOpenUserModal] = useState(false); + const [activeUserId, setActiveUserId] = useState(""); + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const ticketsPerPage = useTicketStore((state) => state.ticketsPerPage); + const ticketApiPage = useTicketStore((state) => state.apiPage); + const token = useToken(); - useTicketsFetcher({ - url: process.env.REACT_APP_DOMAIN + "/heruvym/getTickets", - ticketsPerPage, - ticketApiPage, - onSuccess: (result) => { - if (result.data) updateTickets(result.data); - }, - onError: (error: Error) => { - const message = getMessageFromFetchError(error); - if (message) enqueueSnackbar(message); - }, - onFetchStateChange: setTicketsFetchState, - }); + useTicketsFetcher({ + url: process.env.REACT_APP_DOMAIN + "/heruvym/getTickets", + ticketsPerPage, + ticketApiPage, + onSuccess: (result) => { + if (result.data) updateTickets(result.data); + }, + onError: (error: Error) => { + const message = getMessageFromFetchError(error); + if (message) enqueueSnackbar(message); + }, + onFetchStateChange: setTicketsFetchState, + }); - useSSESubscription({ - enabled: Boolean(token), - url: - process.env.REACT_APP_DOMAIN + - `/heruvym/subscribe?Authorization=${token}`, - onNewData: updateTickets, - onDisconnect: () => { - clearMessageState(); - clearTickets(); - }, - marker: "ticket", - }); + useSSESubscription({ + enabled: Boolean(token), + url: process.env.REACT_APP_DOMAIN + `/heruvym/subscribe?Authorization=${token}`, + onNewData: updateTickets, + onDisconnect: () => { + clearMessageState(); + clearTickets(); + }, + marker: "ticket", + }); - useEffect(() => { - if (!openUserModal) { - setActiveUserId(""); - } - }, [openUserModal]); + useEffect(() => { + if (!openUserModal) { + setActiveUserId(""); + } + }, [openUserModal]); - useEffect(() => { - if (activeUserId) { - setOpenUserModal(true); + useEffect(() => { + if (activeUserId) { + setOpenUserModal(true); - return; - } + return; + } - setOpenUserModal(false); - }, [activeUserId]); + setOpenUserModal(false); + }, [activeUserId]); - return ( - - {!upMd && ( - - {(closeCollapse) => ( - - )} - - )} - - {upMd && } - setOpenUserModal(false)} - userId={activeUserId} - /> - - ); + return ( + + {!upMd && ( + + {(closeCollapse) => } + + )} + + {upMd && } + setOpenUserModal(false)} userId={activeUserId} /> + + ); } diff --git a/src/pages/dashboard/Content/Support/TicketList/CloseTicketModal.tsx b/src/pages/dashboard/Content/Support/TicketList/CloseTicketModal.tsx index 1c12a27..daafdcc 100644 --- a/src/pages/dashboard/Content/Support/TicketList/CloseTicketModal.tsx +++ b/src/pages/dashboard/Content/Support/TicketList/CloseTicketModal.tsx @@ -1,87 +1,83 @@ import Modal from "@mui/material/Modal"; -import {closeDeleteTariffDialog} from "@stores/tariffs"; +import { closeDeleteTariffDialog } from "@stores/tariffs"; import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; import Button from "@mui/material/Button"; import makeRequest from "@root/api/makeRequest"; -import {parseAxiosError} from "@root/utils/parse-error"; -interface Props{ - ticketId: string | undefined, - openModal: boolean, - setOpenModal: (a: boolean) => void +import { parseAxiosError } from "@root/utils/parse-error"; +interface Props { + ticketId: string | undefined; + openModal: boolean; + setOpenModal: (a: boolean) => void; } -export default function CloseTicketModal({ticketId, openModal, setOpenModal}: Props) { +export default function CloseTicketModal({ ticketId, openModal, setOpenModal }: Props) { + const CloseTicket = async () => { + try { + const ticketCloseResponse = await makeRequest({ + url: process.env.REACT_APP_DOMAIN + "/heruvym/close", + method: "post", + useToken: true, + body: { + ticket: ticketId, + }, + }); - const CloseTicket = async () => { - try { - const ticketCloseResponse = await makeRequest({ - url: process.env.REACT_APP_DOMAIN + "/heruvym/close" , - method: "post", - useToken: true, - body: { - "ticket": ticketId - }, - }); + return [ticketCloseResponse]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); - return [ticketCloseResponse]; - } catch (nativeError) { - const [error] = parseAxiosError(nativeError); + return [null, `Не удалось закрыть тикет. ${error}`]; + } + }; - return [null, `Не удалось закрыть тикет. ${error}`]; - } - } - - return ( - setOpenModal(false)} - aria-labelledby="modal-modal-title" - aria-describedby="modal-modal-description" - > - - - Вы уверены, что хотите закрыть тикет? - - - - - - - - ) -} \ No newline at end of file + return ( + setOpenModal(false)} + aria-labelledby="modal-modal-title" + aria-describedby="modal-modal-description" + > + + + Вы уверены, что хотите закрыть тикет? + + + + + + + + ); +} diff --git a/src/pages/dashboard/Content/Support/TicketList/TicketItem.tsx b/src/pages/dashboard/Content/Support/TicketList/TicketItem.tsx index 82a8456..03deacd 100644 --- a/src/pages/dashboard/Content/Support/TicketList/TicketItem.tsx +++ b/src/pages/dashboard/Content/Support/TicketList/TicketItem.tsx @@ -1,108 +1,97 @@ import CircleIcon from "@mui/icons-material/Circle"; -import { - Box, - Card, - CardActionArea, - CardContent, - CardHeader, - Divider, - Typography, - useTheme, -} from "@mui/material"; +import { Box, Card, CardActionArea, CardContent, CardHeader, Divider, Typography, useTheme } from "@mui/material"; import { green } from "@mui/material/colors"; import { Ticket } from "@root/model/ticket"; import { useNavigate, useParams } from "react-router-dom"; const flexCenterSx = { - textAlign: "center", - display: "flex", - justifyContent: "center", - alignItems: "center", - padding: "10px", + textAlign: "center", + display: "flex", + justifyContent: "center", + alignItems: "center", + padding: "10px", }; interface Props { - ticket: Ticket; - setActiveUserId: (userId: string) => void; + ticket: Ticket; + setActiveUserId: (userId: string) => void; } export default function TicketItem({ ticket, setActiveUserId }: Props) { - const theme = useTheme(); - const navigate = useNavigate(); - const ticketId = useParams().ticketId; + const theme = useTheme(); + const navigate = useNavigate(); + const ticketId = useParams().ticketId; - const isUnread = ticket.user === ticket.top_message.user_id; - const isSelected = ticket.id === ticketId; + const isUnread = ticket.user === ticket.top_message.user_id; + const isSelected = ticket.id === ticketId; - const unreadSx = { - border: "1px solid", - borderColor: theme.palette.golden.main, - backgroundColor: theme.palette.goldenMedium.main, - }; + const unreadSx = { + border: "1px solid", + borderColor: theme.palette.golden.main, + backgroundColor: theme.palette.goldenMedium.main, + }; - const selectedSx = { - border: `2px solid ${theme.palette.secondary.main}`, - }; + const selectedSx = { + border: `2px solid ${theme.palette.secondary.main}`, + }; - function handleCardClick() { - navigate(`/support/${ticket.id}`); - } + function handleCardClick() { + navigate(`/support/${ticket.id}`); + } - return ( - - - {ticket.title}} - disableTypography - sx={{ - textAlign: "center", - p: "4px", - }} - /> - - - - {new Date(ticket.top_message.created_at).toLocaleDateString()} - - - {ticket.top_message.message} - - - - - setActiveUserId(ticket.user)}> - ИНФО - - - - - ); + return ( + + + {ticket.title}} + disableTypography + sx={{ + textAlign: "center", + p: "4px", + }} + /> + + + {new Date(ticket.top_message.created_at).toLocaleDateString()} + + {ticket.top_message.message} + + + + + setActiveUserId(ticket.user)}> + ИНФО + + + + + ); } diff --git a/src/pages/dashboard/Content/Support/TicketList/TicketList.tsx b/src/pages/dashboard/Content/Support/TicketList/TicketList.tsx index 433a7ac..1f3e4ea 100644 --- a/src/pages/dashboard/Content/Support/TicketList/TicketList.tsx +++ b/src/pages/dashboard/Content/Support/TicketList/TicketList.tsx @@ -3,152 +3,140 @@ import SearchOutlinedIcon from "@mui/icons-material/SearchOutlined"; import { Box, Button, useMediaQuery, useTheme } from "@mui/material"; import { Ticket } from "@root/model/ticket"; import { incrementTicketsApiPage, useTicketStore } from "@root/stores/tickets"; -import {useEffect, useRef, useState} from "react"; +import { useEffect, useRef, useState } from "react"; import TicketItem from "./TicketItem"; import { throttle } from "@frontend/kitui"; import makeRequest from "@root/api/makeRequest"; -import {parseAxiosError} from "@root/utils/parse-error"; -import {useParams} from "react-router-dom"; +import { parseAxiosError } from "@root/utils/parse-error"; +import { useParams } from "react-router-dom"; import CloseTicketModal from "@pages/dashboard/Content/Support/TicketList/CloseTicketModal"; type TicketListProps = { - closeCollapse?: () => void; - setActiveUserId: (id: string) => void; + closeCollapse?: () => void; + setActiveUserId: (id: string) => void; }; -export default function TicketList({ - closeCollapse, - setActiveUserId, -}: TicketListProps) { - const theme = useTheme(); - const upMd = useMediaQuery(theme.breakpoints.up("md")); - const tickets = useTicketStore((state) => state.tickets); - const ticketsFetchState = useTicketStore((state) => state.ticketsFetchState); - const ticketsBoxRef = useRef(null); - const ticketId = useParams().ticketId; - const [openModal, setOpenModal] = useState(false) +export default function TicketList({ closeCollapse, setActiveUserId }: TicketListProps) { + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const tickets = useTicketStore((state) => state.tickets); + const ticketsFetchState = useTicketStore((state) => state.ticketsFetchState); + const ticketsBoxRef = useRef(null); + const ticketId = useParams().ticketId; + const [openModal, setOpenModal] = useState(false); - useEffect( - function updateCurrentPageOnScroll() { - if (!ticketsBoxRef.current) return; + useEffect( + function updateCurrentPageOnScroll() { + if (!ticketsBoxRef.current) return; - const ticketsBox = ticketsBoxRef.current; - const scrollHandler = () => { - const scrollBottom = - ticketsBox.scrollHeight - - ticketsBox.scrollTop - - ticketsBox.clientHeight; - if ( - scrollBottom < ticketsBox.clientHeight && - ticketsFetchState === "idle" - ) - incrementTicketsApiPage(); - }; + const ticketsBox = ticketsBoxRef.current; + const scrollHandler = () => { + const scrollBottom = ticketsBox.scrollHeight - ticketsBox.scrollTop - ticketsBox.clientHeight; + if (scrollBottom < ticketsBox.clientHeight && ticketsFetchState === "idle") incrementTicketsApiPage(); + }; - const throttledScrollHandler = throttle(scrollHandler, 200); - ticketsBox.addEventListener("scroll", throttledScrollHandler); + const throttledScrollHandler = throttle(scrollHandler, 200); + ticketsBox.addEventListener("scroll", throttledScrollHandler); - return () => { - ticketsBox.removeEventListener("scroll", throttledScrollHandler); - }; - }, - [ticketsFetchState] - ); + return () => { + ticketsBox.removeEventListener("scroll", throttledScrollHandler); + }; + }, + [ticketsFetchState] + ); - const sortedTickets = tickets - .sort(sortTicketsByUpdateTime) - .sort(sortTicketsByUnread); + const sortedTickets = tickets.sort(sortTicketsByUpdateTime).sort(sortTicketsByUnread); - return ( - - - - - - - - {sortedTickets.map((ticket) => ( - - - - ))} - - - ); + return ( + + + + + + + + {sortedTickets.map((ticket) => ( + + + + ))} + + + ); } function sortTicketsByUpdateTime(ticket1: Ticket, ticket2: Ticket) { - const date1 = new Date(ticket1.updated_at).getTime(); - const date2 = new Date(ticket2.updated_at).getTime(); - return date2 - date1; + const date1 = new Date(ticket1.updated_at).getTime(); + const date2 = new Date(ticket2.updated_at).getTime(); + return date2 - date1; } function sortTicketsByUnread(ticket1: Ticket, ticket2: Ticket) { - const isUnread1 = ticket1.user === ticket1.top_message.user_id; - const isUnread2 = ticket2.user === ticket2.top_message.user_id; - return Number(isUnread2) - Number(isUnread1); + const isUnread1 = ticket1.user === ticket1.top_message.user_id; + const isUnread2 = ticket2.user === ticket2.top_message.user_id; + return Number(isUnread2) - Number(isUnread1); } diff --git a/src/pages/dashboard/Content/Tariffs/Contractor/index.tsx b/src/pages/dashboard/Content/Tariffs/Contractor/index.tsx index 2a0e466..e2dd4b5 100644 --- a/src/pages/dashboard/Content/Tariffs/Contractor/index.tsx +++ b/src/pages/dashboard/Content/Tariffs/Contractor/index.tsx @@ -2,80 +2,84 @@ import * as React from "react"; import { Box, Typography, Button } from "@mui/material"; import theme from "../../../../../theme"; - export interface MWProps { - openModal: (type:number, num: number) => void + openModal: (type: number, num: number) => void; } -const Contractor: React.FC = ({ openModal }) => { - return ( - - - Сокращатель ссылок - +const Contractor: React.FC = ({ openModal }) => { + return ( + + + Сокращатель ссылок + - - - - - - - ); -} + + + + + + + ); +}; - -export default Contractor; \ No newline at end of file +export default Contractor; diff --git a/src/pages/dashboard/Content/Tariffs/CreateTariff.tsx b/src/pages/dashboard/Content/Tariffs/CreateTariff.tsx index d96d06d..274da22 100644 --- a/src/pages/dashboard/Content/Tariffs/CreateTariff.tsx +++ b/src/pages/dashboard/Content/Tariffs/CreateTariff.tsx @@ -1,354 +1,331 @@ import { - Typography, - Container, - Button, - Select, - MenuItem, - FormControl, - InputLabel, - useTheme, - Box, TextField, + Typography, + Container, + Button, + Select, + MenuItem, + FormControl, + InputLabel, + useTheme, + Box, + TextField, } from "@mui/material"; import { enqueueSnackbar } from "notistack"; import { requestTariffs } from "@root/services/tariffs.service"; import { createTariff } from "@root/api/tariffs"; -import { - findPrivilegeById, - usePrivilegeStore, -} from "@root/stores/privilegesStore"; +import { findPrivilegeById, usePrivilegeStore } from "@root/stores/privilegesStore"; import { Privilege } from "@frontend/kitui"; import { currencyFormatter } from "@root/utils/currencyFormatter"; import { Formik, Field, Form, FormikHelpers } from "formik"; interface Values { - nameField: string, - descriptionField: string, - amountField: string, - customPriceField: string, - privilegeIdField: string, - orderField: number, - privilege: Privilege | null + nameField: string; + descriptionField: string; + amountField: string; + customPriceField: string; + privilegeIdField: string; + orderField: number; + privilege: Privilege | null; } export default function CreateTariff() { - const theme = useTheme(); + const theme = useTheme(); - const privileges = usePrivilegeStore((store) => store.privileges); + const privileges = usePrivilegeStore((store) => store.privileges); - const checkFulledFields = (values: Values) => { - const errors = {} as any; + const checkFulledFields = (values: Values) => { + const errors = {} as any; - if (values.nameField.length === 0) { - errors.nameField = "Пустое название тарифа" - } - if (values.amountField.length === 0) { - errors.amountField = "Пустое кол-во едениц привилегии" - } - if (values.privilegeIdField.length === 0) { - errors.privilegeIdField = "Не выбрана привилегия" - } - return errors; + if (values.nameField.length === 0) { + errors.nameField = "Пустое название тарифа"; + } + if (values.amountField.length === 0) { + errors.amountField = "Пустое кол-во едениц привилегии"; + } + if (values.privilegeIdField.length === 0) { + errors.privilegeIdField = "Не выбрана привилегия"; + } + return errors; + }; - }; + const initialValues: Values = { + nameField: "", + descriptionField: "", + amountField: "", + customPriceField: "", + privilegeIdField: "", + orderField: 0, + privilege: null, + }; - const initialValues: Values = { - nameField: "", - descriptionField: "", - amountField: "", - customPriceField: "", - privilegeIdField: "", - orderField: 0, - privilege: null - }; + const createTariffBackend = async (values: Values, formikHelpers: FormikHelpers) => { + if (values.privilege !== null) { + const [, createdTariffError] = await createTariff({ + name: values.nameField, + price: Number(values.customPriceField) * 100, + order: values.orderField, + isCustom: false, + description: values.descriptionField, + privileges: [ + { + name: values.privilege.name, + privilegeId: values.privilege.privilegeId ?? "", + serviceKey: values.privilege.serviceKey, + description: values.privilege.description, + type: values.privilege.type, + value: values.privilege.value ?? "", + price: values.privilege.price, + amount: Number(values.amountField), + }, + ], + }); - const createTariffBackend = async ( - values: Values, - formikHelpers: FormikHelpers - ) => { - if (values.privilege !== null) { - const [, createdTariffError] = await createTariff({ - name: values.nameField, - price: Number(values.customPriceField) * 100, - order: values.orderField, - isCustom: false, - description: values.descriptionField, - privileges: [ - { - name: values.privilege.name, - privilegeId: values.privilege.privilegeId ?? "", - serviceKey: values.privilege.serviceKey, - description: values.privilege.description, - type: values.privilege.type, - value: values.privilege.value ?? "", - price: values.privilege.price, - amount: Number(values.amountField), - }, - ], - }); + if (createdTariffError) { + return enqueueSnackbar(createdTariffError); + } - if (createdTariffError) { - return enqueueSnackbar(createdTariffError); - } + enqueueSnackbar("Тариф создан"); - enqueueSnackbar("Тариф создан"); + await requestTariffs(); + } + }; - await requestTariffs(); - } - }; + // const createTariffFrontend = () => { + // if (checkFulledFields() && privilege !== null) { + // updateTariffStore({ + // id: nanoid(5), + // name: nameField, + // amount: Number(amountField), + // isFront: true, + // privilegeId: privilege.privilegeId, + // customPricePerUnit: customPriceField.length !== 0 ? Number(customPriceField)*100: undefined, + // }) + // } + // } - // const createTariffFrontend = () => { - // if (checkFulledFields() && privilege !== null) { - // updateTariffStore({ - // id: nanoid(5), - // name: nameField, - // amount: Number(amountField), - // isFront: true, - // privilegeId: privilege.privilegeId, - // customPricePerUnit: customPriceField.length !== 0 ? Number(customPriceField)*100: undefined, - // }) - // } - // } - - return ( - - {(props) => ( -
- - - Создание тарифа - - - - Привилегия - - - - {props.values.privilege && ( - - - Имя: {props.values.privilege.name} - - - Сервис: {props.values.privilege.serviceKey} - - - Единица: {props.values.privilege.type} - - - Стандартная цена за единицу: {currencyFormatter.format(props.values.privilege.price / 100)} - - - )} - - {props.errors.nameField} - - } - InputProps={{ - style: { - backgroundColor: theme.palette.content.main, - color: theme.palette.secondary.main, - } - }} - InputLabelProps={{ - style: { - color: theme.palette.secondary.main - } - }} - /> - { - props.setFieldValue("amountField", e.target.value.replace(/[^\d]/g, '')) - }} - value={props.values.amountField} - onBlur={props.handleBlur} - label="Кол-во единиц привилегии" - error={props.touched.amountField && !!props.errors.amountField} - helperText={ - - {props.errors.amountField} - - } - InputProps={{ - style: { - backgroundColor: theme.palette.content.main, - color: theme.palette.secondary.main, - } - }} - InputLabelProps={{ - style: { - color: theme.palette.secondary.main - } - }} - /> - { - props.setFieldValue("customPriceField", e.target.value.replace(/[^\d]/g, '')) - }} - value={props.values.customPriceField} - onBlur={props.handleBlur} - label="Кастомная цена (не обязательно)" - InputProps={{ - style: { - backgroundColor: theme.palette.content.main, - color: theme.palette.secondary.main, - } - }} - InputLabelProps={{ - style: { - color: theme.palette.secondary.main - } - }} - /> - { - props.setFieldValue("descriptionField", e.target.value) - }} - value={props.values.descriptionField} - onBlur={props.handleBlur} - label="Описание" - multiline={true} - InputProps={{ - style: { - backgroundColor: theme.palette.content.main, - color: theme.palette.secondary.main, - } - }} - InputLabelProps={{ - style: { - color: theme.palette.secondary.main - } - }} - /> - { - props.setFieldValue("orderField", e.target.value) - }} - value={props.values.orderField} - onBlur={props.handleBlur} - label="порядковый номер" - InputProps={{ - style: { - backgroundColor: theme.palette.content.main, - color: theme.palette.secondary.main, - } - }} - type={'number'} - InputLabelProps={{ - style: { - color: theme.palette.secondary.main - } - }} - /> - - -
- )} -
- - ); + return ( + + {(props) => ( +
+ + + Создание тарифа + + + + Привилегия + + + + {props.values.privilege && ( + + + Имя: {props.values.privilege.name} + + + Сервис: {props.values.privilege.serviceKey} + + + Единица: {props.values.privilege.type} + + + Стандартная цена за единицу:{" "} + {currencyFormatter.format(props.values.privilege.price / 100)} + + + )} + {props.errors.nameField}} + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + }, + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main, + }, + }} + /> + { + props.setFieldValue("amountField", e.target.value.replace(/[^\d]/g, "")); + }} + value={props.values.amountField} + onBlur={props.handleBlur} + label="Кол-во единиц привилегии" + error={props.touched.amountField && !!props.errors.amountField} + helperText={{props.errors.amountField}} + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + }, + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main, + }, + }} + /> + { + props.setFieldValue("customPriceField", e.target.value.replace(/[^\d]/g, "")); + }} + value={props.values.customPriceField} + onBlur={props.handleBlur} + label="Кастомная цена (не обязательно)" + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + }, + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main, + }, + }} + /> + { + props.setFieldValue("descriptionField", e.target.value); + }} + value={props.values.descriptionField} + onBlur={props.handleBlur} + label="Описание" + multiline={true} + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + }, + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main, + }, + }} + /> + { + props.setFieldValue("orderField", e.target.value); + }} + value={props.values.orderField} + onBlur={props.handleBlur} + label="порядковый номер" + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + }, + }} + type={"number"} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main, + }, + }} + /> + + +
+ )} +
+ ); } diff --git a/src/pages/dashboard/Content/Tariffs/CustomButton.tsx b/src/pages/dashboard/Content/Tariffs/CustomButton.tsx index 37b5e3e..5e2aed9 100644 --- a/src/pages/dashboard/Content/Tariffs/CustomButton.tsx +++ b/src/pages/dashboard/Content/Tariffs/CustomButton.tsx @@ -1,32 +1,31 @@ import { Button, SxProps, Theme, useTheme } from "@mui/material"; import { MouseEventHandler, ReactNode } from "react"; - interface Props { - onClick?: MouseEventHandler; - children: ReactNode; - sx?: SxProps; + onClick?: MouseEventHandler; + children: ReactNode; + sx?: SxProps; } export default function CustomButton({ onClick, children, sx }: Props) { - const theme = useTheme(); + const theme = useTheme(); - return ( - - ); -} \ No newline at end of file + return ( + + ); +} diff --git a/src/pages/dashboard/Content/Tariffs/CustomHeader.tsx b/src/pages/dashboard/Content/Tariffs/CustomHeader.tsx index 492d011..bc1aa72 100644 --- a/src/pages/dashboard/Content/Tariffs/CustomHeader.tsx +++ b/src/pages/dashboard/Content/Tariffs/CustomHeader.tsx @@ -1,28 +1,27 @@ import { Typography, useTheme } from "@mui/material"; import { ReactNode } from "react"; - interface Props { - children: ReactNode; + children: ReactNode; } export default function CustomHeader({ children }: Props) { - const theme = useTheme(); + const theme = useTheme(); - return ( - - {children} - - ); -} \ No newline at end of file + return ( + + {children} + + ); +} diff --git a/src/pages/dashboard/Content/Tariffs/DeleteModal.tsx b/src/pages/dashboard/Content/Tariffs/DeleteModal.tsx index 54e344f..184ee0b 100644 --- a/src/pages/dashboard/Content/Tariffs/DeleteModal.tsx +++ b/src/pages/dashboard/Content/Tariffs/DeleteModal.tsx @@ -8,87 +8,81 @@ import { enqueueSnackbar } from "notistack"; import { deleteTariff } from "@root/api/tariffs"; import { devlog } from "@frontend/kitui"; - export default function DeleteModal() { - const deleteTariffIds = useTariffStore(state => state.deleteTariffIds); + const deleteTariffIds = useTariffStore((state) => state.deleteTariffIds); - async function deleteManyTariffs(tariffIds: string[]) { - const results = await Promise.allSettled( - tariffIds.map((tariffId) => deleteTariff(tariffId)) - ); - - let deletedCount = 0; - let errorCount = 0; - const errors: unknown[] = []; - - results.forEach((result) => { - if (result.status === "fulfilled") deletedCount++; - else { - errorCount++; - errors.push(result.reason); - } - }); - - return { deletedCount, errorCount, errors }; - } + async function deleteManyTariffs(tariffIds: string[]) { + const results = await Promise.allSettled(tariffIds.map((tariffId) => deleteTariff(tariffId))); - async function handleTariffDeleteClick() { - if (!deleteTariffIds?.length) return; + let deletedCount = 0; + let errorCount = 0; + const errors: unknown[] = []; - const results = await deleteManyTariffs(deleteTariffIds); + results.forEach((result) => { + if (result.status === "fulfilled") deletedCount++; + else { + errorCount++; + errors.push(result.reason); + } + }); - enqueueSnackbar(`Тарифов удалено: ${results.deletedCount}, ошибок: ${results.errorCount}`); - if (results.errors.length) devlog("Errors deleting tariffs", results.errors); - closeDeleteTariffDialog(); + return { deletedCount, errorCount, errors }; + } - requestTariffs(); - }; + async function handleTariffDeleteClick() { + if (!deleteTariffIds?.length) return; - return ( - - - - Вы уверены, что хотите удалить тариф(ы)? - - - - {/* Тариф: + const results = await deleteManyTariffs(deleteTariffIds); + + enqueueSnackbar(`Тарифов удалено: ${results.deletedCount}, ошибок: ${results.errorCount}`); + if (results.errors.length) devlog("Errors deleting tariffs", results.errors); + closeDeleteTariffDialog(); + + requestTariffs(); + } + + return ( + + + + Вы уверены, что хотите удалить тариф(ы)? + + + + {/* Тариф: {tariffName.map((name, index) => ( {name}; ))} */} - - - - ); + + + + ); } diff --git a/src/pages/dashboard/Content/Tariffs/EditModal.tsx b/src/pages/dashboard/Content/Tariffs/EditModal.tsx index fd16621..fee5eaf 100644 --- a/src/pages/dashboard/Content/Tariffs/EditModal.tsx +++ b/src/pages/dashboard/Content/Tariffs/EditModal.tsx @@ -12,128 +12,118 @@ import { requestTariffs } from "@root/services/tariffs.service"; import { currencyFormatter } from "@root/utils/currencyFormatter"; export default function EditModal() { - const [nameField, setNameField] = useState(""); - const [priceField, setPriceField] = useState(""); - const [descriptionField, setDescriptionField] = useState(""); - const [orderField, setOrderField] = useState(""); - const tariffs = useTariffStore((state) => state.tariffs); - const editTariffId = useTariffStore((state) => state.editTariffId); + const [nameField, setNameField] = useState(""); + const [priceField, setPriceField] = useState(""); + const [descriptionField, setDescriptionField] = useState(""); + const [orderField, setOrderField] = useState(""); + const tariffs = useTariffStore((state) => state.tariffs); + const editTariffId = useTariffStore((state) => state.editTariffId); - const tariff = tariffs.find((tariff) => tariff._id === editTariffId); + const tariff = tariffs.find((tariff) => tariff._id === editTariffId); - useEffect( - function setCurrentTariffFields() { - if (!tariff) return; + useEffect( + function setCurrentTariffFields() { + if (!tariff) return; - setNameField(tariff.name); - setPriceField((tariff.price || 0).toString()); - }, - [tariff] - ); + setNameField(tariff.name); + setPriceField((tariff.price || 0).toString()); + }, + [tariff] + ); - async function handleEditClick() { - if (!tariff) return enqueueSnackbar(`Тариф ${editTariffId} не найден`); + async function handleEditClick() { + if (!tariff) return enqueueSnackbar(`Тариф ${editTariffId} не найден`); - const price = parseFloat(priceField); + const price = parseFloat(priceField); - if (!isFinite(price)) - return enqueueSnackbar('Поле "Цена за единицу" не число'); - if (!nameField) return enqueueSnackbar('Поле "Имя" пустое'); + if (!isFinite(price)) return enqueueSnackbar('Поле "Цена за единицу" не число'); + if (!nameField) return enqueueSnackbar('Поле "Имя" пустое'); - const updatedTariff = structuredClone(tariff); - updatedTariff.name = nameField; - updatedTariff.price = price; - updatedTariff.description = descriptionField; - updatedTariff.order = parseInt(orderField); + const updatedTariff = structuredClone(tariff); + updatedTariff.name = nameField; + updatedTariff.price = price; + updatedTariff.description = descriptionField; + updatedTariff.order = parseInt(orderField); - const [_, putedTariffError] = await putTariff(updatedTariff); + const [_, putedTariffError] = await putTariff(updatedTariff); - if (putedTariffError) { - devlog("Error updating tariff", putedTariffError); + if (putedTariffError) { + devlog("Error updating tariff", putedTariffError); - return enqueueSnackbar(putedTariffError); - } - closeEditTariffDialog(); - requestTariffs(); - } + return enqueueSnackbar(putedTariffError); + } + closeEditTariffDialog(); + requestTariffs(); + } - return ( - - - - Редактирование тариффа - + return ( + + + + Редактирование тариффа + - {tariff && ( - - Название Тарифа: {tariff.name} - setNameField(event.target.value)} - label="Имя" - name="name" - value={nameField} - sx={{ marginBottom: "10px" }} - /> - - Цена: {Math.trunc((tariff.price ?? 0) / 100)} - - - setPriceField(String(+target.value * 100)) - } - label="Цена" - name="price" - value={Math.trunc(Number(priceField) / 100)} - sx={{ marginBottom: "10px" }} - /> + {tariff && ( + + Название Тарифа: {tariff.name} + setNameField(event.target.value)} + label="Имя" + name="name" + value={nameField} + sx={{ marginBottom: "10px" }} + /> + Цена: {Math.trunc((tariff.price ?? 0) / 100)} + setPriceField(String(+target.value * 100))} + label="Цена" + name="price" + value={Math.trunc(Number(priceField) / 100)} + sx={{ marginBottom: "10px" }} + /> - Описание: {tariff.description} - setDescriptionField(event.target.value)} - label="описание" - name="description" - value={descriptionField} - sx={{ marginBottom: "10px" }} - /> - Порядок: {tariff.order} - setOrderField(event.target.value)} - label="Порядок" - name="order" - value={orderField} - sx={{ marginBottom: "10px" }} - /> - - - )} - - - ); + Описание: {tariff.description} + setDescriptionField(event.target.value)} + label="описание" + name="description" + value={descriptionField} + sx={{ marginBottom: "10px" }} + /> + Порядок: {tariff.order} + setOrderField(event.target.value)} + label="Порядок" + name="order" + value={orderField} + sx={{ marginBottom: "10px" }} + /> + + + )} +
+ + ); } diff --git a/src/pages/dashboard/Content/Tariffs/Privileges/ChangePriceModal.tsx b/src/pages/dashboard/Content/Tariffs/Privileges/ChangePriceModal.tsx index a5b0d7e..fa4bd16 100644 --- a/src/pages/dashboard/Content/Tariffs/Privileges/ChangePriceModal.tsx +++ b/src/pages/dashboard/Content/Tariffs/Privileges/ChangePriceModal.tsx @@ -9,36 +9,36 @@ import { CustomTextField } from "@root/kitUI/CustomTextField"; import { enqueueSnackbar } from "notistack"; export default function ChangePriceModal() { - const theme = useTheme(); - // const isModalOpen = usePrivilegeStore((state) => state.isModalOpen); - // const modalPriceField = usePrivilegeStore((state) => state.modalPriceField); + const theme = useTheme(); + // const isModalOpen = usePrivilegeStore((state) => state.isModalOpen); + // const modalPriceField = usePrivilegeStore((state) => state.modalPriceField); - function handleSaveChange() { - // const errorMessage = changePrivilegePrice(); - // if (errorMessage) enqueueSnackbar(errorMessage); - } + function handleSaveChange() { + // const errorMessage = changePrivilegePrice(); + // if (errorMessage) enqueueSnackbar(errorMessage); + } - return ( - <> - // - // - // changeModalPriceField(e.target.value)} - // type="number" - // /> - // - // - // - ); + return ( + <> + // + // + // changeModalPriceField(e.target.value)} + // type="number" + // /> + // + // + // + ); } diff --git a/src/pages/dashboard/Content/Tariffs/Privileges/Privileges.tsx b/src/pages/dashboard/Content/Tariffs/Privileges/Privileges.tsx index b851822..97e41c5 100644 --- a/src/pages/dashboard/Content/Tariffs/Privileges/Privileges.tsx +++ b/src/pages/dashboard/Content/Tariffs/Privileges/Privileges.tsx @@ -7,33 +7,33 @@ import { requestPrivileges } from "@root/services/privilegies.service"; import { currencyFormatter } from "@root/utils/currencyFormatter"; const columns: GridColDef[] = [ - { field: "id", headerName: "id", width: 150 }, - { field: "name", headerName: "Привелегия", width: 150 }, - { field: "description", headerName: "Описание", width: 550 }, //инфо из гитлаба. - { field: "type", headerName: "Тип", width: 150 }, - { field: "price", headerName: "Стоимость", width: 100 }, + { field: "id", headerName: "id", width: 150 }, + { field: "name", headerName: "Привелегия", width: 150 }, + { field: "description", headerName: "Описание", width: 550 }, //инфо из гитлаба. + { field: "type", headerName: "Тип", width: 150 }, + { field: "price", headerName: "Стоимость", width: 100 }, ]; export default function Privileges() { - const privileges = usePrivilegeStore((state) => state.privileges); - const privilegesGridData = privileges - .filter((privilege) => !privilege.isDeleted) - .map((privilege) => ({ - id: privilege.privilegeId, - name: privilege.name, - description: privilege.description, - type: privilege.type, - price: currencyFormatter.format(privilege.price / 100), - })); + const privileges = usePrivilegeStore((state) => state.privileges); + const privilegesGridData = privileges + .filter((privilege) => !privilege.isDeleted) + .map((privilege) => ({ + id: privilege.privilegeId, + name: privilege.name, + description: privilege.description, + type: privilege.type, + price: currencyFormatter.format(privilege.price / 100), + })); - return ( - <> - - - - - - - - ); + return ( + <> + + + + + + + + ); } diff --git a/src/pages/dashboard/Content/Tariffs/Quiz/index.tsx b/src/pages/dashboard/Content/Tariffs/Quiz/index.tsx index 30ccc25..673a50f 100644 --- a/src/pages/dashboard/Content/Tariffs/Quiz/index.tsx +++ b/src/pages/dashboard/Content/Tariffs/Quiz/index.tsx @@ -2,80 +2,84 @@ import * as React from "react"; import { Box, Typography, Button } from "@mui/material"; import theme from "../../../../../theme"; - export interface MWProps { - openModal: (type:number, num: number) => void + openModal: (type: number, num: number) => void; } -const Quiz: React.FC = ({ openModal }) => { - return ( - - - Опросник - +const Quiz: React.FC = ({ openModal }) => { + return ( + + + Опросник + - - - - - - - ); -} + + + + + + + ); +}; - -export default Quiz; \ No newline at end of file +export default Quiz; diff --git a/src/pages/dashboard/Content/Tariffs/TariffsInfo.tsx b/src/pages/dashboard/Content/Tariffs/TariffsInfo.tsx index cc4be27..a375a19 100644 --- a/src/pages/dashboard/Content/Tariffs/TariffsInfo.tsx +++ b/src/pages/dashboard/Content/Tariffs/TariffsInfo.tsx @@ -4,20 +4,18 @@ import TariffsDG from "./tariffsDG"; import { Suspense } from "react"; import { ErrorBoundary } from "react-error-boundary"; - export default function TariffsInfo() { - - return ( - <> - - Список тарифов - - - Что-то пошло не так}> - }> - - - - - ); + return ( + <> + + Список тарифов + + + Что-то пошло не так}> + }> + + + + + ); } diff --git a/src/pages/dashboard/Content/Tariffs/Templater/index.tsx b/src/pages/dashboard/Content/Tariffs/Templater/index.tsx index 4a5b9e7..c6334fb 100644 --- a/src/pages/dashboard/Content/Tariffs/Templater/index.tsx +++ b/src/pages/dashboard/Content/Tariffs/Templater/index.tsx @@ -2,94 +2,99 @@ import * as React from "react"; import { Box, Typography, Button } from "@mui/material"; import theme from "../../../../../theme"; - export interface MWProps { - openModal: (type:number, num: number) => void + openModal: (type: number, num: number) => void; } -const Templater: React.FC = ({ openModal }) => { - return ( - - - Шаблонизатор документов - +const Templater: React.FC = ({ openModal }) => { + return ( + + + Шаблонизатор документов + - - - - - - - - ); -} + + + + + + + + ); +}; - -export default Templater; \ No newline at end of file +export default Templater; diff --git a/src/pages/dashboard/Content/Tariffs/index.tsx b/src/pages/dashboard/Content/Tariffs/index.tsx index b5a40e8..2e316e3 100644 --- a/src/pages/dashboard/Content/Tariffs/index.tsx +++ b/src/pages/dashboard/Content/Tariffs/index.tsx @@ -10,26 +10,26 @@ import ChangePriceModal from "./Privileges/ChangePriceModal"; import TariffsInfo from "./TariffsInfo"; export default function Tariffs() { - useEffect(() => { - requestTariffs(); - requestPrivileges(); - }, []); + useEffect(() => { + requestTariffs(); + requestPrivileges(); + }, []); - return ( - - Список привилегий - - - - - - ); + return ( + + Список привилегий + + + + + + ); } diff --git a/src/pages/dashboard/Content/Tariffs/tariffsDG.tsx b/src/pages/dashboard/Content/Tariffs/tariffsDG.tsx index ac5fc23..f8e8416 100644 --- a/src/pages/dashboard/Content/Tariffs/tariffsDG.tsx +++ b/src/pages/dashboard/Content/Tariffs/tariffsDG.tsx @@ -7,92 +7,111 @@ import DeleteModal from "@root/pages/dashboard/Content/Tariffs/DeleteModal"; import EditModal from "./EditModal"; import AutorenewIcon from "@mui/icons-material/Autorenew"; import { requestTariffs } from "@root/services/tariffs.service"; -import { openDeleteTariffDialog, openEditTariffDialog, setSelectedTariffIds, useTariffStore } from "@root/stores/tariffs"; +import { + openDeleteTariffDialog, + openEditTariffDialog, + setSelectedTariffIds, + useTariffStore, +} from "@root/stores/tariffs"; import { Tariff } from "@frontend/kitui"; import { getTariffPrice } from "@root/utils/tariffPrice"; import { currencyFormatter } from "@root/utils/currencyFormatter"; - const columns: GridColDef[] = [ - { field: "_id", headerName: "ID", width: 100, valueGetter: ({ row }) => row.order || 1 }, - { - field: "edit", - headerName: "Изменение", - width: 60, - renderCell: ({ row }) => { - return ( - openEditTariffDialog(row._id)}> - - - ); - }, - }, - { field: "name", headerName: "Название тарифа", width: 150, valueGetter: ({ row }) => row.name }, - { field: "amount", headerName: "Количество", width: 110, valueGetter: ({ row }) => row.privileges[0].amount }, - { field: "serviceName", headerName: "Сервис", width: 150, valueGetter: ({ row }) => row.privileges[0].serviceKey }, - { field: "privilegeName", headerName: "Привилегия", width: 150, valueGetter: ({ row }) => row.privileges[0].name }, - { field: "type", headerName: "Единица", width: 100, valueGetter: ({ row }) => row.privileges[0].type }, - { field: "pricePerUnit", headerName: "Цена за ед.", width: 100, valueGetter: ({ row }) => currencyFormatter.format(row.privileges[0].price / 100) }, - { field: "isCustom", headerName: "Кастомная цена", width: 130, valueGetter: ({ row }) => row.isCustom ? "Да" : "Нет" }, - { field: "total", headerName: "Сумма", width: 100, valueGetter: ({ row }) => currencyFormatter.format(getTariffPrice(row) / 100) }, - { - field: "delete", - headerName: "Удаление", - width: 60, - renderCell: ({ row }) => { - return ( - openDeleteTariffDialog([row._id])}> - - - ); - }, - }, + { field: "_id", headerName: "ID", width: 100, valueGetter: ({ row }) => row.order || 1 }, + { + field: "edit", + headerName: "Изменение", + width: 60, + renderCell: ({ row }) => { + return ( + openEditTariffDialog(row._id)}> + + + ); + }, + }, + { field: "name", headerName: "Название тарифа", width: 150, valueGetter: ({ row }) => row.name }, + { field: "amount", headerName: "Количество", width: 110, valueGetter: ({ row }) => row.privileges[0].amount }, + { field: "serviceName", headerName: "Сервис", width: 150, valueGetter: ({ row }) => row.privileges[0].serviceKey }, + { field: "privilegeName", headerName: "Привилегия", width: 150, valueGetter: ({ row }) => row.privileges[0].name }, + { field: "type", headerName: "Единица", width: 100, valueGetter: ({ row }) => row.privileges[0].type }, + { + field: "pricePerUnit", + headerName: "Цена за ед.", + width: 100, + valueGetter: ({ row }) => currencyFormatter.format(row.privileges[0].price / 100), + }, + { + field: "isCustom", + headerName: "Кастомная цена", + width: 130, + valueGetter: ({ row }) => (row.isCustom ? "Да" : "Нет"), + }, + { + field: "total", + headerName: "Сумма", + width: 100, + valueGetter: ({ row }) => currencyFormatter.format(getTariffPrice(row) / 100), + }, + { + field: "delete", + headerName: "Удаление", + width: 60, + renderCell: ({ row }) => { + return ( + openDeleteTariffDialog([row._id])}> + + + ); + }, + }, ]; export default function TariffsDG() { - const tariffs = useTariffStore((state) => state.tariffs); - const selectedTariffIds = useTariffStore(state => state.selectedTariffIds); + const tariffs = useTariffStore((state) => state.tariffs); + const selectedTariffIds = useTariffStore((state) => state.selectedTariffIds); - return ( - <> - - requestTariffs()}> - - - - row._id} - components={{ Toolbar: GridToolbar }} - onSelectionModelChange={setSelectedTariffIds} - sx={{ - minHeight: "600px", - }} - /> - {selectedTariffIds.length ? ( - - - - ) : null} - - - - ); + return ( + <> + + requestTariffs()}> + + + + row._id} + components={{ Toolbar: GridToolbar }} + onSelectionModelChange={setSelectedTariffIds} + sx={{ + minHeight: "600px", + }} + /> + {selectedTariffIds.length ? ( + + + + ) : null} + + + + ); } diff --git a/src/pages/dashboard/Content/Users.tsx b/src/pages/dashboard/Content/Users.tsx index 3ea33aa..805e148 100644 --- a/src/pages/dashboard/Content/Users.tsx +++ b/src/pages/dashboard/Content/Users.tsx @@ -2,20 +2,20 @@ import * as React from "react"; import { useState, useEffect } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { - AccordionDetails, - AccordionSummary, - Accordion, - Skeleton, - Radio, - Box, - Typography, - TextField, - Button, - Table, - TableHead, - TableBody, - TableCell, - TableRow, + AccordionDetails, + AccordionSummary, + Accordion, + Skeleton, + Radio, + Box, + Typography, + TextField, + Button, + Table, + TableHead, + TableBody, + TableCell, + TableRow, } from "@mui/material"; import { GridSelectionModel } from "@mui/x-data-grid"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; @@ -31,107 +31,96 @@ import { getRoles_mock, TMockData } from "../../../api/roles"; import theme from "../../../theme"; type Pages = { - adminPage: number; - managerPage: number; - userPage: number; + adminPage: number; + managerPage: number; + userPage: number; }; type PagesSize = { - adminPageSize: number; - managerPageSize: number; - userPageSize: number; + adminPageSize: number; + managerPageSize: number; + userPageSize: number; }; const Users: React.FC = () => { - const radioboxes = ["admin", "manager", "user"]; + const radioboxes = ["admin", "manager", "user"]; - const [selectedValue, setSelectedValue] = React.useState("admin"); + const [selectedValue, setSelectedValue] = React.useState("admin"); - const navigate = useNavigate(); + const navigate = useNavigate(); - const [mockData, setMockData] = React.useState([]); + const [mockData, setMockData] = React.useState([]); - const handleChangeData = () => { - getRoles_mock().then((mockdata) => { - setMockData(mockdata); - setAccordionText(mockdata[0].desc || ""); - }); - }; + const handleChangeData = () => { + getRoles_mock().then((mockdata) => { + setMockData(mockdata); + setAccordionText(mockdata[0].desc || ""); + }); + }; - const [accordionState, setAccordionState] = React.useState(true); - const [accordionText, setAccordionText] = React.useState(""); + const [accordionState, setAccordionState] = React.useState(true); + const [accordionText, setAccordionText] = React.useState(""); - const handleChange = (value: string) => { - setSelectedValue(value); - setAccordionText(mockData.find(({ name }) => name === value)?.desc || ""); + const handleChange = (value: string) => { + setSelectedValue(value); + setAccordionText(mockData.find(({ name }) => name === value)?.desc || ""); - if (selectedValue === "manager") { - } - }; + if (selectedValue === "manager") { + } + }; - const handleToggleAccordion = () => { - setAccordionState(!accordionState); - }; + const handleToggleAccordion = () => { + setAccordionState(!accordionState); + }; - const [roles, setRoles] = React.useState([]); + const [roles, setRoles] = React.useState([]); - const [page, setPage] = useState({ - adminPage: 0, - managerPage: 0, - userPage: 0, - }); - const [pageSize, setPageSize] = useState({ - adminPageSize: 10, - managerPageSize: 10, - userPageSize: 10, - }); - const [openUserModal, setOpenUserModal] = useState(false); - const [activeUserId, setActiveUserId] = useState(""); + const [page, setPage] = useState({ + adminPage: 0, + managerPage: 0, + userPage: 0, + }); + const [pageSize, setPageSize] = useState({ + adminPageSize: 10, + managerPageSize: 10, + userPageSize: 10, + }); + const [openUserModal, setOpenUserModal] = useState(false); + const [activeUserId, setActiveUserId] = useState(""); - const { userId } = useParams(); - const { data: adminData, adminPages } = useAdmins( - page.adminPage + 1, - pageSize.adminPageSize - ); - const { data: managerData, managerPages } = useManagers( - page.managerPage + 1, - pageSize.managerPageSize - ); - const { data: userData, userPagesCount } = useUsers( - page.userPage + 1, - pageSize.userPageSize - ); + const { userId } = useParams(); + const { data: adminData, adminPages } = useAdmins(page.adminPage + 1, pageSize.adminPageSize); + const { data: managerData, managerPages } = useManagers(page.managerPage + 1, pageSize.managerPageSize); + const { data: userData, userPagesCount } = useUsers(page.userPage + 1, pageSize.userPageSize); - useEffect(() => { - handleChangeData(); - }, []); + useEffect(() => { + handleChangeData(); + }, []); - useEffect(() => { - if (userId) { - setActiveUserId(userId); - setOpenUserModal(true); + useEffect(() => { + if (userId) { + setActiveUserId(userId); + setOpenUserModal(true); - return; - } + return; + } - setActiveUserId(""); - setOpenUserModal(false); - }, [userId]); + setActiveUserId(""); + setOpenUserModal(false); + }, [userId]); - useEffect(() => { - getRoles().then(([rolesResponse]) => { - if (rolesResponse) { - setRoles(rolesResponse); - } - }); - }, [selectedValue]); + useEffect(() => { + getRoles().then(([rolesResponse]) => { + if (rolesResponse) { + setRoles(rolesResponse); + } + }); + }, [selectedValue]); - const [selectedTariffs, setSelectedTariffs] = useState( - [] - ); - return ( - - {/* */} - - - } - aria-controls="panel1a-content" - id="panel1a-header" - > - - {accordionText} - - - - - - - - - Имя - - - - - Описание - - - - - Отобразить - - - - + + } + aria-controls="panel1a-content" + id="panel1a-header" + > + + {accordionText} + + + +
+ + + + + Имя + + + + + Описание + + + + + Отобразить + + + + - - {mockData.length ? ( - mockData.map(function (item, index) { - return ( - handleChange(radioboxes[index])} - > - - - {item.name} - - - - - {item.desc} - - - - - - - ); - }) - ) : ( - - - - - - )} - -
-
-
+ + {mockData.length ? ( + mockData.map(function (item, index) { + return ( + handleChange(radioboxes[index])} + > + + + {item.name} + + + + + {item.desc} + + + + + + + ); + }) + ) : ( + + + + + + )} + + + + - - - - - - - + + + + + + + - - - - - - - - - - setPage((pages) => ({ ...pages, adminPage })) - } - pagesCount={adminPages} - pageSize={pageSize.adminPageSize} - handleSelectionChange={setSelectedTariffs} - onPageSizeChange={(adminPageSize) => - setPageSize((pageSize) => ({ ...pageSize, adminPageSize })) - } - /> - } - childrenManager={ - - setPage((pages) => ({ ...pages, managerPage })) - } - pagesCount={managerPages} - pageSize={pageSize.managerPageSize} - handleSelectionChange={setSelectedTariffs} - onPageSizeChange={(managerPageSize) => - setPageSize((pageSize) => ({ ...pageSize, managerPageSize })) - } - /> - } - childrenUser={ - - setPage((pages) => ({ ...pages, userPage })) - } - pagesCount={userPagesCount} - pageSize={pageSize.userPageSize} - handleSelectionChange={setSelectedTariffs} - onPageSizeChange={(userPageSize) => - setPageSize((pageSize) => ({ ...pageSize, userPageSize })) - } - /> - } - /> - - { - setOpenUserModal(false); - navigate(-1); - }} - /> -
- ); + + + + + + +
+ + setPage((pages) => ({ ...pages, adminPage }))} + pagesCount={adminPages} + pageSize={pageSize.adminPageSize} + handleSelectionChange={setSelectedTariffs} + onPageSizeChange={(adminPageSize) => setPageSize((pageSize) => ({ ...pageSize, adminPageSize }))} + /> + } + childrenManager={ + setPage((pages) => ({ ...pages, managerPage }))} + pagesCount={managerPages} + pageSize={pageSize.managerPageSize} + handleSelectionChange={setSelectedTariffs} + onPageSizeChange={(managerPageSize) => setPageSize((pageSize) => ({ ...pageSize, managerPageSize }))} + /> + } + childrenUser={ + setPage((pages) => ({ ...pages, userPage }))} + pagesCount={userPagesCount} + pageSize={pageSize.userPageSize} + handleSelectionChange={setSelectedTariffs} + onPageSizeChange={(userPageSize) => setPageSize((pageSize) => ({ ...pageSize, userPageSize }))} + /> + } + /> + + { + setOpenUserModal(false); + navigate(-1); + }} + /> +
+ ); }; export default Users; diff --git a/src/pages/dashboard/Header/index.tsx b/src/pages/dashboard/Header/index.tsx index ef2ee86..d0e5d53 100644 --- a/src/pages/dashboard/Header/index.tsx +++ b/src/pages/dashboard/Header/index.tsx @@ -7,73 +7,73 @@ import { clearAuthToken } from "@frontend/kitui"; import { logout } from "@root/api/auth"; const Header: React.FC = () => { - return ( - - - - - + return ( + + + + + - - - Добро пожаловать, Администратор сервиса - + + + Добро пожаловать, Администратор сервиса + - { - logout().then(clearAuthToken); - }} - sx={{ - display: "flex", - flexDirection: "column", - justifyContent: "center", - alignItems: "center", - cursor: "pointer", - padding: "0 31px", - }} - > - - - - - - ); + { + logout().then(clearAuthToken); + }} + sx={{ + display: "flex", + flexDirection: "column", + justifyContent: "center", + alignItems: "center", + cursor: "pointer", + padding: "0 31px", + }} + > + + + + + + ); }; export default Header; diff --git a/src/pages/dashboard/Menu/index.tsx b/src/pages/dashboard/Menu/index.tsx index a4e355c..3e4e0e9 100644 --- a/src/pages/dashboard/Menu/index.tsx +++ b/src/pages/dashboard/Menu/index.tsx @@ -32,233 +32,233 @@ import Paper from "@mui/material/Paper"; const drawerWidth = 240; const openedMixin = (theme: Theme): CSSObject => ({ - width: drawerWidth, - background: "#2f3339", - transition: theme.transitions.create("width", { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.enteringScreen, - }), - overflowX: "hidden", + width: drawerWidth, + background: "#2f3339", + transition: theme.transitions.create("width", { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.enteringScreen, + }), + overflowX: "hidden", }); const closedMixin = (theme: Theme): CSSObject => ({ - transition: theme.transitions.create("width", { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen, - }), - overflowX: "hidden", - width: `calc(${theme.spacing(7)} + 1px)`, - background: "#2f3339", - // marginTop: '20px', - [theme.breakpoints.up("sm")]: { - width: `calc(${theme.spacing(8)} + 1px)`, - }, + transition: theme.transitions.create("width", { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + overflowX: "hidden", + width: `calc(${theme.spacing(7)} + 1px)`, + background: "#2f3339", + // marginTop: '20px', + [theme.breakpoints.up("sm")]: { + width: `calc(${theme.spacing(8)} + 1px)`, + }, }); const DrawerHeader = styled("div")(({ theme }) => ({ - display: "flex", - alignItems: "center", - justifyContent: "flex-end", - padding: theme.spacing(0, 1), - ...theme.mixins.toolbar, + display: "flex", + alignItems: "center", + justifyContent: "flex-end", + padding: theme.spacing(0, 1), + ...theme.mixins.toolbar, })); interface AppBarProps extends MuiAppBarProps { - open?: boolean; + open?: boolean; } const AppBar = styled(MuiAppBar, { - shouldForwardProp: (prop) => prop !== "open", + shouldForwardProp: (prop) => prop !== "open", })(({ theme, open }) => ({ - zIndex: theme.zIndex.drawer + 1, - transition: theme.transitions.create(["width", "margin"], { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen, - }), - ...(open && { - marginLeft: drawerWidth, - width: `calc(100% - ${drawerWidth}px)`, - transition: theme.transitions.create(["width", "margin"], { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.enteringScreen, - }), - }), + zIndex: theme.zIndex.drawer + 1, + transition: theme.transitions.create(["width", "margin"], { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + ...(open && { + marginLeft: drawerWidth, + width: `calc(100% - ${drawerWidth}px)`, + transition: theme.transitions.create(["width", "margin"], { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.enteringScreen, + }), + }), })); const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== "open" })(({ theme, open }) => ({ - width: drawerWidth, - flexShrink: 0, - boxSizing: "border-box", - ...(open && { - ...openedMixin(theme), - "& .MuiDrawer-paper": openedMixin(theme), - }), - ...(!open && { - ...closedMixin(theme), - "& .MuiDrawer-paper": closedMixin(theme), - }), + width: drawerWidth, + flexShrink: 0, + boxSizing: "border-box", + ...(open && { + ...openedMixin(theme), + "& .MuiDrawer-paper": openedMixin(theme), + }), + ...(!open && { + ...closedMixin(theme), + "& .MuiDrawer-paper": closedMixin(theme), + }), })); const links: { path: string; element: JSX.Element; title: string; className: string }[] = [ - { path: "/quizStatistics", element: <>📝, title: "Статистика Quiz", className: "menu" }, - { path: "/users", element: , title: "Информация о проекте", className: "menu" }, - { path: "/entities", element: , title: "Юридические лица", className: "menu" }, - { path: "/tariffs", element: , title: "Тарифы", className: "menu" }, - { path: "/discounts", element: , title: "Скидки", className: "menu" }, - { path: "/promocode", element: , title: "Промокод", className: "menu" }, - { path: "/settingRoles", element: , title: "Настройки", className: "menu" }, - { path: "/support", element: , title: "Служба поддержки", className: "menu" }, + { path: "/quizStatistics", element: <>📝, title: "Статистика Quiz", className: "menu" }, + { path: "/users", element: , title: "Информация о проекте", className: "menu" }, + { path: "/entities", element: , title: "Юридические лица", className: "menu" }, + { path: "/tariffs", element: , title: "Тарифы", className: "menu" }, + { path: "/discounts", element: , title: "Скидки", className: "menu" }, + { path: "/promocode", element: , title: "Промокод", className: "menu" }, + { path: "/settingRoles", element: , title: "Настройки", className: "menu" }, + { path: "/support", element: , title: "Служба поддержки", className: "menu" }, ]; interface Props { - visible: boolean; - SladeMobileHC?: () => void; + visible: boolean; + SladeMobileHC?: () => void; } const Navigation = (props: Props) => { - return ( - - {links.map((e, index) => ( - - - - - {e.element} - - - - - - ))} - - ); + return ( + + {links.map((e, index) => ( + + + + + {e.element} + + + + + + ))} + + ); }; const Menu: React.FC = () => { - const tablet = useMediaQuery("(max-width:900px)"); + const tablet = useMediaQuery("(max-width:900px)"); - const mobile = useMediaQuery("(max-width:600px)"); + const mobile = useMediaQuery("(max-width:600px)"); - const theme = useTheme(); - const [open, setOpen] = React.useState(tablet ? false : true); + const theme = useTheme(); + const [open, setOpen] = React.useState(tablet ? false : true); - const handleDrawerOpen = () => { - if (!mobile) { - setOpen(true); - } else { - SladeMobileHC(); - } - }; + const handleDrawerOpen = () => { + if (!mobile) { + setOpen(true); + } else { + SladeMobileHC(); + } + }; - const handleDrawerClose = () => { - setOpen(false); - }; + const handleDrawerClose = () => { + setOpen(false); + }; - const [sladeMobile, setSladeMobile] = React.useState(false); - const SladeMobileHC = () => { - if (mobile) { - setSladeMobile((old) => !old); - } - }; + const [sladeMobile, setSladeMobile] = React.useState(false); + const SladeMobileHC = () => { + if (mobile) { + setSladeMobile((old) => !old); + } + }; - return ( - - - - - - - - -
- - - {!mobile ? ( - - - - - - - - - - ) : null} - - {sladeMobile ? ( - - - - - - - - - - ) : null} - - ); + return ( + + + + + + + + +
+ + + {!mobile ? ( + + + + + + + + + + ) : null} + + {sladeMobile ? ( + + + + + + + + + + ) : null} + + ); }; export default Menu; diff --git a/src/pages/dashboard/ModalAdmin/index.tsx b/src/pages/dashboard/ModalAdmin/index.tsx index f17c2c9..6fe5394 100644 --- a/src/pages/dashboard/ModalAdmin/index.tsx +++ b/src/pages/dashboard/ModalAdmin/index.tsx @@ -1,129 +1,147 @@ import * as React from "react"; -import { useLinkClickHandler } from "react-router-dom"; +import { useLinkClickHandler } from "react-router-dom"; import { Box, Modal, Fade, Backdrop, Typography } from "@mui/material"; import theme from "../../../theme"; export interface MWProps { - open: boolean + open: boolean; } -const ModalAdmin = ({open}: MWProps ) => { - return ( - - - - - - Администратор сервиса - - - - - - ТЕКСТ - - - ТЕКСТ - - - ТЕКСТ - - - ТЕКСТ - - - - Long text Long text Long text Long text Long text - Long text Long text Long text Long text Long text - Long text Long text Long text - - - - - - - ); -} +const ModalAdmin = ({ open }: MWProps) => { + return ( + + + + + + Администратор сервиса + + + + + ТЕКСТ + + + ТЕКСТ + + + ТЕКСТ + + + ТЕКСТ + + + + Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text Long + text Long text Long text + + + + + + + ); +}; export default ModalAdmin; diff --git a/src/pages/dashboard/ModalEntities/index.tsx b/src/pages/dashboard/ModalEntities/index.tsx index ef1cba7..dd022a4 100644 --- a/src/pages/dashboard/ModalEntities/index.tsx +++ b/src/pages/dashboard/ModalEntities/index.tsx @@ -1,183 +1,190 @@ import * as React from "react"; -import { useLinkClickHandler } from "react-router-dom"; +import { useLinkClickHandler } from "react-router-dom"; import { Box, Modal, Fade, Backdrop, Typography } from "@mui/material"; import Tabs from "@mui/material/Tabs"; import Tab from "@mui/material/Tab"; -import { DataGrid, GridColDef } from '@mui/x-data-grid'; +import { DataGrid, GridColDef } from "@mui/x-data-grid"; import theme from "../../../theme"; - export interface MWProps { - open: boolean + open: boolean; } const columns: GridColDef[] = [ - { - field: 'id', - headerName: 'ID', - width: 30, - sortable: false, - }, - { - field: 'dateTime', - headerName: 'Дата / время', - width: 150, - sortable: false, - }, - { - field: 'email', - headerName: 'Почта', - width: 110, - sortable: false, - },{ - field: 'summa', - headerName: 'Сумма', - type: 'number', - width: 110, - sortable: false, - }, - { - field: 'idLong', - headerName: 'ID long', - type: 'number', - width: 110, - sortable: false, - }, - { - field: 'paymentStatus', - headerName: 'Статус платежа', - width: 160, - sortable: false, - }, + { + field: "id", + headerName: "ID", + width: 30, + sortable: false, + }, + { + field: "dateTime", + headerName: "Дата / время", + width: 150, + sortable: false, + }, + { + field: "email", + headerName: "Почта", + width: 110, + sortable: false, + }, + { + field: "summa", + headerName: "Сумма", + type: "number", + width: 110, + sortable: false, + }, + { + field: "idLong", + headerName: "ID long", + type: "number", + width: 110, + sortable: false, + }, + { + field: "paymentStatus", + headerName: "Статус платежа", + width: 160, + sortable: false, + }, ]; const rows = [ - { id: 1, dateTime: '22.09.22 12:15', email: 'asd@mail.ru', summa: 100500, idLong: 123, paymentStatus: "В обработке" }, - { id: 1, dateTime: '22.09.22 12:15', email: 'asd@mail.ru', summa: 100500, idLong: 123, paymentStatus: "В обработке" }, - { id: 1, dateTime: '22.09.22 12:15', email: 'asd@mail.ru', summa: 100500, idLong: 123, paymentStatus: "В обработке" }, - { id: 1, dateTime: '22.09.22 12:15', email: 'asd@mail.ru', summa: 100500, idLong: 123, paymentStatus: "В обработке" }, - { id: 1, dateTime: '22.09.22 12:15', email: 'asd@mail.ru', summa: 100500, idLong: 123, paymentStatus: "В обработке" }, + { id: 1, dateTime: "22.09.22 12:15", email: "asd@mail.ru", summa: 100500, idLong: 123, paymentStatus: "В обработке" }, + { id: 1, dateTime: "22.09.22 12:15", email: "asd@mail.ru", summa: 100500, idLong: 123, paymentStatus: "В обработке" }, + { id: 1, dateTime: "22.09.22 12:15", email: "asd@mail.ru", summa: 100500, idLong: 123, paymentStatus: "В обработке" }, + { id: 1, dateTime: "22.09.22 12:15", email: "asd@mail.ru", summa: 100500, idLong: 123, paymentStatus: "В обработке" }, + { id: 1, dateTime: "22.09.22 12:15", email: "asd@mail.ru", summa: 100500, idLong: 123, paymentStatus: "В обработке" }, ]; -const ModalEntities = ({open}: MWProps ) => { - const [value, setValue] = React.useState(0); +const ModalEntities = ({ open }: MWProps) => { + const [value, setValue] = React.useState(0); - const handleChange = (event: React.SyntheticEvent, newValue: number) => { - setValue(newValue); - }; - - return ( - - - - - - Юридическое лицо - + const handleChange = (event: React.SyntheticEvent, newValue: number) => { + setValue(newValue); + }; - + return ( + + + + + + Юридическое лицо + - - - - - - { value == 0 && ( - - Id: 1 - Email: 2 - Номер телефона: 3 - - ) } + + + + + - { value == 1 && ( - - - - ) } - - - - - - - ); -} + {value == 0 && ( + + Id: 1 + Email: 2 + Номер телефона: 3 + + )} + {value == 1 && ( + + + + )} + + + + + + ); +}; export default ModalEntities; diff --git a/src/pages/dashboard/ModalUser/PurchaseTab.tsx b/src/pages/dashboard/ModalUser/PurchaseTab.tsx index 361b3f7..b6b304c 100644 --- a/src/pages/dashboard/ModalUser/PurchaseTab.tsx +++ b/src/pages/dashboard/ModalUser/PurchaseTab.tsx @@ -12,303 +12,292 @@ import type { ChangeEvent } from "react"; import type { GridColDef } from "@mui/x-data-grid"; type PurchaseTabProps = { - userId: string; + userId: string; }; const COLUMNS: GridColDef[] = [ - { - field: "date", - headerName: "Дата", - minWidth: 130, - maxWidth: 200, - flex: 1, - sortable: false, - }, - { - field: "time", - headerName: "Время", - minWidth: 90, - maxWidth: 180, - flex: 1, - sortable: false, - }, - { - field: "product", - headerName: "Товар", - minWidth: 200, - maxWidth: 280, - flex: 1, - sortable: false, - }, - { - field: "amount", - headerName: "Кол-во", - minWidth: 70, - maxWidth: 70, - flex: 1, - sortable: false, - }, + { + field: "date", + headerName: "Дата", + minWidth: 130, + maxWidth: 200, + flex: 1, + sortable: false, + }, + { + field: "time", + headerName: "Время", + minWidth: 90, + maxWidth: 180, + flex: 1, + sortable: false, + }, + { + field: "product", + headerName: "Товар", + minWidth: 200, + maxWidth: 280, + flex: 1, + sortable: false, + }, + { + field: "amount", + headerName: "Кол-во", + minWidth: 70, + maxWidth: 70, + flex: 1, + sortable: false, + }, ]; const ROWS = [ - { - id: "row_1", - date: "19.02.2023", - time: "17:01", - product: "Шаблонизатор", - amount: "1 шт.", - }, - { - id: "row_2", - date: "28.02.2023", - time: "10:43", - product: "Шаблонизатор", - amount: "1 шт.", - }, - { - id: "row_3", - date: "04.03.2023", - time: "21:09", - product: "Сокращатель ссылок", - amount: "2 шт.", - }, + { + id: "row_1", + date: "19.02.2023", + time: "17:01", + product: "Шаблонизатор", + amount: "1 шт.", + }, + { + id: "row_2", + date: "28.02.2023", + time: "10:43", + product: "Шаблонизатор", + amount: "1 шт.", + }, + { + id: "row_3", + date: "04.03.2023", + time: "21:09", + product: "Сокращатель ссылок", + amount: "2 шт.", + }, ]; export const PurchaseTab = ({ userId }: PurchaseTabProps) => { - const [canScrollToRight, setCanScrollToRight] = useState(true); - const [canScrollToLeft, setCanScrollToLeft] = useState(false); - const theme = useTheme(); - const smallScreen = useMediaQuery(theme.breakpoints.down(830)); - const gridContainer = useRef(null); - const { data: historyData } = useHistory(userId); + const [canScrollToRight, setCanScrollToRight] = useState(true); + const [canScrollToLeft, setCanScrollToLeft] = useState(false); + const theme = useTheme(); + const smallScreen = useMediaQuery(theme.breakpoints.down(830)); + const gridContainer = useRef(null); + const { data: historyData } = useHistory(userId); - const rows = - historyData?.[0].records.map((history) => ({ - id: history.id, - date: format(history.updatedAt, "dd.MM.yyyy"), - time: format(history.updatedAt, "HH:mm"), - product: "", - amount: "", - })) ?? []; + const rows = + historyData?.[0].records.map((history) => ({ + id: history.id, + date: format(history.updatedAt, "dd.MM.yyyy"), + time: format(history.updatedAt, "HH:mm"), + product: "", + amount: "", + })) ?? []; - useEffect(() => { - const handleScroll = (nativeEvent: unknown) => { - const { target } = nativeEvent as ChangeEvent; + useEffect(() => { + const handleScroll = (nativeEvent: unknown) => { + const { target } = nativeEvent as ChangeEvent; - if (target.scrollLeft > 0) { - setCanScrollToLeft(true); - } else { - setCanScrollToLeft(false); - } + if (target.scrollLeft > 0) { + setCanScrollToLeft(true); + } else { + setCanScrollToLeft(false); + } - if (target.clientWidth + target.scrollLeft >= target.scrollWidth - 1) { - setCanScrollToRight(false); - } else { - setCanScrollToRight(true); - } - }; + if (target.clientWidth + target.scrollLeft >= target.scrollWidth - 1) { + setCanScrollToRight(false); + } else { + setCanScrollToRight(true); + } + }; - const addScrollEvent = () => { - const grid = gridContainer.current?.querySelector( - ".MuiDataGrid-virtualScroller" - ); + const addScrollEvent = () => { + const grid = gridContainer.current?.querySelector(".MuiDataGrid-virtualScroller"); - if (grid) { - grid.addEventListener("scroll", handleScroll); + if (grid) { + grid.addEventListener("scroll", handleScroll); - return; - } + return; + } - setTimeout(addScrollEvent, 100); - }; + setTimeout(addScrollEvent, 100); + }; - addScrollEvent(); + addScrollEvent(); - return () => { - const grid = gridContainer.current?.querySelector( - ".MuiDataGrid-virtualScroller" - ); + return () => { + const grid = gridContainer.current?.querySelector(".MuiDataGrid-virtualScroller"); - grid?.removeEventListener("scroll", handleScroll); - }; - }, []); + grid?.removeEventListener("scroll", handleScroll); + }; + }, []); - const scrollDataGrid = (toStart = false) => { - const grid = gridContainer.current?.querySelector( - ".MuiDataGrid-virtualScroller" - ); + const scrollDataGrid = (toStart = false) => { + const grid = gridContainer.current?.querySelector(".MuiDataGrid-virtualScroller"); - if (grid) { - scrollBlock(grid, { left: toStart ? 0 : grid.scrollWidth }); - } - }; + if (grid) { + scrollBlock(grid, { left: toStart ? 0 : grid.scrollWidth }); + } + }; - return ( - - - {smallScreen && ( - - {canScrollToLeft && ( - scrollDataGrid(true)} - > - forward - - )} - {canScrollToRight && ( - scrollDataGrid(false)} - > - forward - - )} - - )} - - ); + return ( + + + {smallScreen && ( + + {canScrollToLeft && ( + scrollDataGrid(true)} + > + forward + + )} + {canScrollToRight && ( + scrollDataGrid(false)} + > + forward + + )} + + )} + + ); }; const a = { - id: "65e4f1b157004756bc5bb15c", - userId: "64eb6ce57047f28fdabf69ec", - comment: "Успешная оплата корзины", - key: "payCart", - rawDetails: [ - [ - { Key: "id", Value: "65e4f1881747c1eea8007d3b" }, - { - Key: "name", - Value: - "Количество Заявок, Скрытие шильдика в опроснике, 2024-03-03T21:54:16.434Z", - }, - { Key: "price", Value: 0 }, - { Key: "iscustom", Value: true }, - { - Key: "privileges", - Value: [ - [ - { Key: "id", Value: "" }, - { Key: "name", Value: "Количество Заявок" }, - { Key: "privilegeid", Value: "quizCnt" }, - { Key: "servicekey", Value: "squiz" }, - { - Key: "description", - Value: "Количество полных прохождений опросов", - }, - { Key: "amount", Value: 100 }, - { Key: "type", Value: "count" }, - { Key: "value", Value: "заявка" }, - { Key: "price", Value: 2000 }, - ], - [ - { Key: "id", Value: "" }, - { Key: "name", Value: "Скрытие шильдика в опроснике" }, - { Key: "privilegeid", Value: "squizHideBadge" }, - { Key: "servicekey", Value: "squiz" }, - { - Key: "description", - Value: "Количество дней скрытия шильдика в опроснике", - }, - { Key: "amount", Value: 30 }, - { Key: "type", Value: "day" }, - { Key: "value", Value: "день" }, - { Key: "price", Value: 0 }, - ], - ], - }, - { Key: "deleted", Value: false }, - { Key: "createdat", Value: "2024-03-03T21:54:16.825Z" }, - { Key: "updatedat", Value: "2024-03-03T21:54:16.825Z" }, - { Key: "deletedat", Value: null }, - ], - ], - isDeleted: false, - createdAt: "2024-03-03T21:54:57.433Z", - updatedAt: "2024-03-03T21:54:57.433Z", + id: "65e4f1b157004756bc5bb15c", + userId: "64eb6ce57047f28fdabf69ec", + comment: "Успешная оплата корзины", + key: "payCart", + rawDetails: [ + [ + { Key: "id", Value: "65e4f1881747c1eea8007d3b" }, + { + Key: "name", + Value: "Количество Заявок, Скрытие шильдика в опроснике, 2024-03-03T21:54:16.434Z", + }, + { Key: "price", Value: 0 }, + { Key: "iscustom", Value: true }, + { + Key: "privileges", + Value: [ + [ + { Key: "id", Value: "" }, + { Key: "name", Value: "Количество Заявок" }, + { Key: "privilegeid", Value: "quizCnt" }, + { Key: "servicekey", Value: "squiz" }, + { + Key: "description", + Value: "Количество полных прохождений опросов", + }, + { Key: "amount", Value: 100 }, + { Key: "type", Value: "count" }, + { Key: "value", Value: "заявка" }, + { Key: "price", Value: 2000 }, + ], + [ + { Key: "id", Value: "" }, + { Key: "name", Value: "Скрытие шильдика в опроснике" }, + { Key: "privilegeid", Value: "squizHideBadge" }, + { Key: "servicekey", Value: "squiz" }, + { + Key: "description", + Value: "Количество дней скрытия шильдика в опроснике", + }, + { Key: "amount", Value: 30 }, + { Key: "type", Value: "day" }, + { Key: "value", Value: "день" }, + { Key: "price", Value: 0 }, + ], + ], + }, + { Key: "deleted", Value: false }, + { Key: "createdat", Value: "2024-03-03T21:54:16.825Z" }, + { Key: "updatedat", Value: "2024-03-03T21:54:16.825Z" }, + { Key: "deletedat", Value: null }, + ], + ], + isDeleted: false, + createdAt: "2024-03-03T21:54:57.433Z", + updatedAt: "2024-03-03T21:54:57.433Z", }; diff --git a/src/pages/dashboard/ModalUser/QuizTab.tsx b/src/pages/dashboard/ModalUser/QuizTab.tsx index ce79b86..6796120 100644 --- a/src/pages/dashboard/ModalUser/QuizTab.tsx +++ b/src/pages/dashboard/ModalUser/QuizTab.tsx @@ -1,47 +1,45 @@ -import {Box, Button, TextField, Typography} from "@mui/material"; -import {ChangeEvent, useState} from "react"; +import { Box, Button, TextField, Typography } from "@mui/material"; +import { ChangeEvent, useState } from "react"; import makeRequest from "@root/api/makeRequest"; type QuizTabProps = { - userId: string; + userId: string; }; export default function QuizTab({ userId }: QuizTabProps) { - const [quizId, setQuizId] = useState("") - return( - - - Передача Квиза - - - )=>{ - setQuizId(event.target.value.split("link/")[1]) - - }} - /> - - - - - ) -} \ No newline at end of file + const [quizId, setQuizId] = useState(""); + return ( + + + Передача Квиза + + + ) => { + setQuizId(event.target.value.split("link/")[1]); + }} + /> + + + + ); +} diff --git a/src/pages/dashboard/ModalUser/TransactionsTab.tsx b/src/pages/dashboard/ModalUser/TransactionsTab.tsx index 75283d7..4414d4a 100644 --- a/src/pages/dashboard/ModalUser/TransactionsTab.tsx +++ b/src/pages/dashboard/ModalUser/TransactionsTab.tsx @@ -10,243 +10,233 @@ import type { ChangeEvent } from "react"; import type { GridColDef } from "@mui/x-data-grid"; const COLUMNS: GridColDef[] = [ - { - field: "date", - headerName: "Дата", - width: 130, - sortable: false, - }, - { - field: "time", - headerName: "Время", - width: 90, - sortable: false, - }, - { - field: "amount", - headerName: "Сумма", - width: 140, - sortable: false, - }, - { - field: "email", - headerName: "Email", - width: 280, - sortable: false, - }, - { - field: "status", - headerName: "Статус", - width: 100, - sortable: false, - renderCell: ({ value }) => ( - - {value ? "accepted" : "timeout"} - - ), - }, + { + field: "date", + headerName: "Дата", + width: 130, + sortable: false, + }, + { + field: "time", + headerName: "Время", + width: 90, + sortable: false, + }, + { + field: "amount", + headerName: "Сумма", + width: 140, + sortable: false, + }, + { + field: "email", + headerName: "Email", + width: 280, + sortable: false, + }, + { + field: "status", + headerName: "Статус", + width: 100, + sortable: false, + renderCell: ({ value }) => ( + + {value ? "accepted" : "timeout"} + + ), + }, ]; const ROWS = [ - { - id: "row_1", - date: "19.02.2023", - time: "17:01", - amount: "2 065 руб.", - email: "emailexample@gmail.com", - status: true, - }, - { - id: "row_2", - date: "28.02.2023", - time: "10:43", - amount: "21 250 руб.", - email: "verylongemailexample@gmail.com", - status: true, - }, - { - id: "row_3", - date: "04.03.2023", - time: "21:09", - amount: "108 065 руб.", - email: "emailexample@gmail.com", - status: false, - }, + { + id: "row_1", + date: "19.02.2023", + time: "17:01", + amount: "2 065 руб.", + email: "emailexample@gmail.com", + status: true, + }, + { + id: "row_2", + date: "28.02.2023", + time: "10:43", + amount: "21 250 руб.", + email: "verylongemailexample@gmail.com", + status: true, + }, + { + id: "row_3", + date: "04.03.2023", + time: "21:09", + amount: "108 065 руб.", + email: "emailexample@gmail.com", + status: false, + }, ]; export const TransactionsTab = () => { - const [canScrollToRight, setCanScrollToRight] = useState(true); - const [canScrollToLeft, setCanScrollToLeft] = useState(false); - const theme = useTheme(); - const smallScreen = useMediaQuery(theme.breakpoints.down(1070)); - const gridContainer = useRef(null); + const [canScrollToRight, setCanScrollToRight] = useState(true); + const [canScrollToLeft, setCanScrollToLeft] = useState(false); + const theme = useTheme(); + const smallScreen = useMediaQuery(theme.breakpoints.down(1070)); + const gridContainer = useRef(null); - useEffect(() => { - const handleScroll = (nativeEvent: unknown) => { - const { target } = nativeEvent as ChangeEvent; + useEffect(() => { + const handleScroll = (nativeEvent: unknown) => { + const { target } = nativeEvent as ChangeEvent; - if (target.scrollLeft > 0) { - setCanScrollToLeft(true); - } else { - setCanScrollToLeft(false); - } + if (target.scrollLeft > 0) { + setCanScrollToLeft(true); + } else { + setCanScrollToLeft(false); + } - if (target.clientWidth + target.scrollLeft >= target.scrollWidth - 1) { - setCanScrollToRight(false); - } else { - setCanScrollToRight(true); - } - }; + if (target.clientWidth + target.scrollLeft >= target.scrollWidth - 1) { + setCanScrollToRight(false); + } else { + setCanScrollToRight(true); + } + }; - const addScrollEvent = () => { - const grid = gridContainer.current?.querySelector( - ".MuiDataGrid-virtualScroller" - ); + const addScrollEvent = () => { + const grid = gridContainer.current?.querySelector(".MuiDataGrid-virtualScroller"); - if (grid) { - grid.addEventListener("scroll", handleScroll); + if (grid) { + grid.addEventListener("scroll", handleScroll); - return; - } + return; + } - setTimeout(addScrollEvent, 100); - }; + setTimeout(addScrollEvent, 100); + }; - addScrollEvent(); + addScrollEvent(); - return () => { - const grid = gridContainer.current?.querySelector( - ".MuiDataGrid-virtualScroller" - ); + return () => { + const grid = gridContainer.current?.querySelector(".MuiDataGrid-virtualScroller"); - grid?.removeEventListener("scroll", handleScroll); - }; - }, []); + grid?.removeEventListener("scroll", handleScroll); + }; + }, []); - const scrollDataGrid = (toStart = false) => { - const grid = gridContainer.current?.querySelector( - ".MuiDataGrid-virtualScroller" - ); + const scrollDataGrid = (toStart = false) => { + const grid = gridContainer.current?.querySelector(".MuiDataGrid-virtualScroller"); - if (grid) { - scrollBlock(grid, { left: toStart ? 0 : grid.scrollWidth }); - } - }; + if (grid) { + scrollBlock(grid, { left: toStart ? 0 : grid.scrollWidth }); + } + }; - return ( - - - {smallScreen && ( - - {canScrollToLeft && ( - scrollDataGrid(true)} - > - forward - - )} - {canScrollToRight && ( - scrollDataGrid(false)} - > - forward - - )} - - )} - - ); + return ( + + + {smallScreen && ( + + {canScrollToLeft && ( + scrollDataGrid(true)} + > + forward + + )} + {canScrollToRight && ( + scrollDataGrid(false)} + > + forward + + )} + + )} + + ); }; diff --git a/src/pages/dashboard/ModalUser/UserTab.tsx b/src/pages/dashboard/ModalUser/UserTab.tsx index cc719ca..89b66cb 100644 --- a/src/pages/dashboard/ModalUser/UserTab.tsx +++ b/src/pages/dashboard/ModalUser/UserTab.tsx @@ -8,84 +8,72 @@ import type { UserType } from "@root/api/roles"; import type { Account } from "@root/api/account"; type UserTabProps = { - userId: string; + userId: string; }; export const UserTab = ({ userId }: UserTabProps) => { - const [user, setUser] = useState(null); - const [account, setAccount] = useState(null); - const theme = useTheme(); - const mobile = useMediaQuery(theme.breakpoints.down(700)); + const [user, setUser] = useState(null); + const [account, setAccount] = useState(null); + const theme = useTheme(); + const mobile = useMediaQuery(theme.breakpoints.down(700)); - useEffect(() => { - if (userId) { - userApi.getUserInfo(userId).then(([userInfo]) => setUser(userInfo)); - getAccountInfo(userId).then(([accountsInfo]) => setAccount(accountsInfo)); - } - }, []); + useEffect(() => { + if (userId) { + userApi.getUserInfo(userId).then(([userInfo]) => setUser(userInfo)); + getAccountInfo(userId).then(([accountsInfo]) => setAccount(accountsInfo)); + } + }, []); - return ( - - - - ID - - {user?._id} - - - - Дата регистрации - - {new Date(user?.createdAt || "").toLocaleDateString()} - - - - Email - - {user?.email || user?.login} - - - - Телефон - - {user?.phoneNumber} - - - - Тип: - - {account?.status === "no" && "Физ. лицо"} - {account?.status === "org" && "Юр. лицо"} - {account?.status === "nko" && "НКО"} - - - - - - ФИО: - - {`${account?.name.secondname || ""} ${ - account?.name.firstname || "" - } ${account?.name.middlename || ""}`} - - - - - Внутренний кошелек - - - {`${account ? account.wallet.money / 100 : 0} ${ - account?.wallet.currency || "RUB" - }.`} - - - - - ); + return ( + + + + ID + {user?._id} + + + Дата регистрации + + {new Date(user?.createdAt || "").toLocaleDateString()} + + + + Email + {user?.email || user?.login} + + + Телефон + {user?.phoneNumber} + + + Тип: + + {account?.status === "no" && "Физ. лицо"} + {account?.status === "org" && "Юр. лицо"} + {account?.status === "nko" && "НКО"} + + + + + + ФИО: + + {`${account?.name.secondname || ""} ${account?.name.firstname || ""} ${account?.name.middlename || ""}`} + + + + Внутренний кошелек + + {`${account ? account.wallet.money / 100 : 0} ${account?.wallet.currency || "RUB"}.`} + + + + + ); }; diff --git a/src/pages/dashboard/ModalUser/VerificationTab.tsx b/src/pages/dashboard/ModalUser/VerificationTab.tsx index 3bc5f3e..1779153 100644 --- a/src/pages/dashboard/ModalUser/VerificationTab.tsx +++ b/src/pages/dashboard/ModalUser/VerificationTab.tsx @@ -9,186 +9,164 @@ import type { Verification } from "@root/api/verification"; import makeRequest from "@root/api/makeRequest"; type VerificationTabProps = { - userId: string; + userId: string; }; export const VerificationTab = ({ userId }: VerificationTabProps) => { - const [isLoading, setIsLoading] = useState(true); - const [verificationInfo, setVerificationInfo] = useState(null); - const [comment, setComment] = useState(""); - const [INN, setINN] = useState(""); + const [isLoading, setIsLoading] = useState(true); + const [verificationInfo, setVerificationInfo] = useState(null); + const [comment, setComment] = useState(""); + const [INN, setINN] = useState(""); - const requestVefification = async () => { - setIsLoading(true); + const requestVefification = async () => { + setIsLoading(true); - const [verificationResponse, verificationError] = await verification( - userId - ); + const [verificationResponse, verificationError] = await verification(userId); - setIsLoading(false); + setIsLoading(false); - if (verificationError) { - return console.error("Error verifying:", verificationError); - } + if (verificationError) { + return console.error("Error verifying:", verificationError); + } - if (verificationResponse) { - setVerificationInfo(verificationResponse); - setComment(verificationResponse.comment); - setINN(verificationResponse.taxnumber) - } - }; + if (verificationResponse) { + setVerificationInfo(verificationResponse); + setComment(verificationResponse.comment); + setINN(verificationResponse.taxnumber); + } + }; - const debouncedINNHC = useDebouncedCallback((str) => { - if (!verificationInfo) { - return; - } - patchVerification({ - comment, - taxnumber: str, - id: verificationInfo?._id, - status: verificationInfo?.status, - accepted: verificationInfo?.accepted, - }); - }, 2000); - + const debouncedINNHC = useDebouncedCallback((str) => { + if (!verificationInfo) { + return; + } + patchVerification({ + comment, + taxnumber: str, + id: verificationInfo?._id, + status: verificationInfo?.status, + accepted: verificationInfo?.accepted, + }); + }, 2000); - useEffect(() => { - requestVefification(); - }, []); + useEffect(() => { + requestVefification(); + }, []); - const verify = async (accepted: boolean) => { - if (!verificationInfo) { - return; - } + const verify = async (accepted: boolean) => { + if (!verificationInfo) { + return; + } - const [_, patchVerificationError] = await patchVerification({ - accepted, - comment, - id: verificationInfo._id, - taxnumber: INN, - status: verificationInfo.status, - }); + const [_, patchVerificationError] = await patchVerification({ + accepted, + comment, + id: verificationInfo._id, + taxnumber: INN, + status: verificationInfo.status, + }); - if (accepted && _ === "OK") await makeRequest({ - method: "patch", - useToken: true, - url: process.env.REACT_APP_DOMAIN + `/customer/account/${userId}`, - body: {status: verificationInfo.status} - }); + if (accepted && _ === "OK") + await makeRequest({ + method: "patch", + useToken: true, + url: process.env.REACT_APP_DOMAIN + `/customer/account/${userId}`, + body: { status: verificationInfo.status }, + }); - if (patchVerificationError) { - return console.error("Error verifying:", patchVerificationError); - } + if (patchVerificationError) { + return console.error("Error verifying:", patchVerificationError); + } - await requestVefification(); - }; + await requestVefification(); + }; - return ( - - - {verificationInfo?.accepted ? "Верификация пройдена" : "Не верифицирован"} - - {isLoading ? ( - - Загрузка данных... - - ) : - verificationInfo && verificationInfo.files.length > 0 ? ( - verificationInfo.files.map(({ name, url }, index) => ( - - - {index + 1}.{" "} - {name === "inn" - ? "Скан ИНН организации (выписка из ЕГЮРЛ)" - : name === "rule" - ? "Устав организации" - : name === "certificate" - ? "Свидетельство о регистрации НКО" - : `Скан документа ${index + 1}`} - - - - {url.split("/").pop()?.split(".")?.[0]} - - - - )) - ) : ( - - Пользователь не загружал данные - - )} - ) =>{ - if (!verificationInfo) { - return; - } - setINN(event.target.value.replace(/[^0-9]/g,"")) - debouncedINNHC(event.target.value.replace(/[^0-9]/g,""))} - } - placeholder="ИНН" - /> - {verificationInfo?.comment && ( - - - Комментарий: - - {verificationInfo.comment} - - )} - ) => - setComment(event.target.value) - } - /> - - - - - - ); + return ( + + + {verificationInfo?.accepted ? "Верификация пройдена" : "Не верифицирован"} + + {isLoading ? ( + Загрузка данных... + ) : verificationInfo && verificationInfo.files.length > 0 ? ( + verificationInfo.files.map(({ name, url }, index) => ( + + + {index + 1}.{" "} + {name === "inn" + ? "Скан ИНН организации (выписка из ЕГЮРЛ)" + : name === "rule" + ? "Устав организации" + : name === "certificate" + ? "Свидетельство о регистрации НКО" + : `Скан документа ${index + 1}`} + + + + {url.split("/").pop()?.split(".")?.[0]} + + + + )) + ) : ( + + Пользователь не загружал данные + + )} + ) => { + if (!verificationInfo) { + return; + } + setINN(event.target.value.replace(/[^0-9]/g, "")); + debouncedINNHC(event.target.value.replace(/[^0-9]/g, "")); + }} + placeholder="ИНН" + /> + {verificationInfo?.comment && ( + + + Комментарий: + + {verificationInfo.comment} + + )} + ) => setComment(event.target.value)} + /> + + + + + + ); }; diff --git a/src/pages/dashboard/ModalUser/index.tsx b/src/pages/dashboard/ModalUser/index.tsx index da0686e..3528e6e 100644 --- a/src/pages/dashboard/ModalUser/index.tsx +++ b/src/pages/dashboard/ModalUser/index.tsx @@ -1,15 +1,5 @@ import { useState } from "react"; -import { - Box, - Modal, - Fade, - Backdrop, - Typography, - Tabs, - Tab, - useTheme, - useMediaQuery, -} from "@mui/material"; +import { Box, Modal, Fade, Backdrop, Typography, Tabs, Tab, useTheme, useMediaQuery } from "@mui/material"; import { UserTab } from "./UserTab"; import { PurchaseTab } from "./PurchaseTab"; @@ -21,190 +11,180 @@ import { ReactComponent as PackageIcon } from "@root/assets/icons/package.svg"; import { ReactComponent as TransactionsIcon } from "@root/assets/icons/transactions.svg"; import { ReactComponent as CheckIcon } from "@root/assets/icons/check.svg"; import { ReactComponent as CloseIcon } from "@root/assets/icons/close.svg"; -import QuizIcon from '@mui/icons-material/Quiz'; +import QuizIcon from "@mui/icons-material/Quiz"; import forwardIcon from "@root/assets/icons/forward.svg"; import type { SyntheticEvent } from "react"; import QuizTab from "@pages/dashboard/ModalUser/QuizTab"; const TABS = [ - { name: "Пользователь", icon: UserIcon, activeStyles: { fill: "#7E2AEA" } }, - { - name: "Покупка товаров и услуг", - icon: PackageIcon, - activeStyles: { stroke: "#7E2AEA" }, - }, - { - name: "Транзакции", - icon: TransactionsIcon, - activeStyles: { stroke: "#7E2AEA" }, - }, - { name: "Верификация", icon: CheckIcon, activeStyles: { stroke: "#7E2AEA" } }, - { name: "Квизы", icon: QuizIcon, activeStyles: { stroke: "#7E2AEA" } }, + { name: "Пользователь", icon: UserIcon, activeStyles: { fill: "#7E2AEA" } }, + { + name: "Покупка товаров и услуг", + icon: PackageIcon, + activeStyles: { stroke: "#7E2AEA" }, + }, + { + name: "Транзакции", + icon: TransactionsIcon, + activeStyles: { stroke: "#7E2AEA" }, + }, + { name: "Верификация", icon: CheckIcon, activeStyles: { stroke: "#7E2AEA" } }, + { name: "Квизы", icon: QuizIcon, activeStyles: { stroke: "#7E2AEA" } }, ]; type ModalUserProps = { - open: boolean; - onClose: () => void; - userId: string; + open: boolean; + onClose: () => void; + userId: string; }; const ModalUser = ({ open, onClose, userId }: ModalUserProps) => { - const [value, setValue] = useState(0); - const [openNavigation, setOpenNavigation] = useState(false); - const theme = useTheme(); - const tablet = useMediaQuery(theme.breakpoints.down(1070)); - const mobile = useMediaQuery(theme.breakpoints.down(700)); + const [value, setValue] = useState(0); + const [openNavigation, setOpenNavigation] = useState(false); + const theme = useTheme(); + const tablet = useMediaQuery(theme.breakpoints.down(1070)); + const mobile = useMediaQuery(theme.breakpoints.down(700)); - return ( - <> - {open && ( - - - - {mobile && ( - - - - )} - - Пользователь сервиса - + return ( + <> + {open && ( + + + + {mobile && ( + + + + )} + + Пользователь сервиса + - - - {mobile && ( - forward setOpenNavigation((isOpened) => !isOpened)} - /> - )} - - setValue(newValue) - } - aria-label="Vertical tabs example" - sx={{ - padding: mobile ? "16px" : "10px", - width: "100%", - }} - TabIndicatorProps={{ style: { background: "transparent" } }} - > - {TABS.map(({ name, icon: Icon, activeStyles }) => ( - } - iconPosition="start" - key={name} - label={mobile ? (openNavigation ? name : "") : name} - sx={{ - justifyContent: "flex-start", - textTransform: "inherit", - minHeight: "auto", - minWidth: "auto", - fontSize: "15px", - padding: mobile ? "9px" : "15px", - marginBottom: mobile ? "15px" : "5px", - color: theme.palette.common.black, - whiteSpace: "nowrap", - lineHeight: "18px", - transition: ".2s", - "&.MuiButtonBase-root.Mui-selected": { - borderRadius: "12px", - color: "#7E2AEA", - background: "rgba(126, 42, 234, 0.07)", - "& svg": activeStyles, - }, - }} - /> - ))} - - + + + {mobile && ( + forward setOpenNavigation((isOpened) => !isOpened)} + /> + )} + setValue(newValue)} + aria-label="Vertical tabs example" + sx={{ + padding: mobile ? "16px" : "10px", + width: "100%", + }} + TabIndicatorProps={{ style: { background: "transparent" } }} + > + {TABS.map(({ name, icon: Icon, activeStyles }) => ( + } + iconPosition="start" + key={name} + label={mobile ? (openNavigation ? name : "") : name} + sx={{ + justifyContent: "flex-start", + textTransform: "inherit", + minHeight: "auto", + minWidth: "auto", + fontSize: "15px", + padding: mobile ? "9px" : "15px", + marginBottom: mobile ? "15px" : "5px", + color: theme.palette.common.black, + whiteSpace: "nowrap", + lineHeight: "18px", + transition: ".2s", + "&.MuiButtonBase-root.Mui-selected": { + borderRadius: "12px", + color: "#7E2AEA", + background: "rgba(126, 42, 234, 0.07)", + "& svg": activeStyles, + }, + }} + /> + ))} + + - - {value === 0 && } - {value === 1 && } - {value === 2 && } - {value === 3 && } - {value === 4 && } - - - - - - )} - - ); + + {value === 0 && } + {value === 1 && } + {value === 2 && } + {value === 3 && } + {value === 4 && } + + + + + + )} + + ); }; export default ModalUser; diff --git a/src/pages/dashboard/index.tsx b/src/pages/dashboard/index.tsx index ad783a2..6ca03c7 100644 --- a/src/pages/dashboard/index.tsx +++ b/src/pages/dashboard/index.tsx @@ -12,54 +12,54 @@ import ModalEntities from "./ModalEntities"; import { useMatch } from "react-router-dom"; export default () => { - const theme = useTheme(); - return ( - - - - - - - - - - - + const theme = useTheme(); + return ( + + + + + + + + + + + - - - - ); + + + + ); }; diff --git a/src/services/discounts.service.ts b/src/services/discounts.service.ts index 2090b67..36ce0b3 100644 --- a/src/services/discounts.service.ts +++ b/src/services/discounts.service.ts @@ -4,25 +4,25 @@ import { requestDiscounts as requestDiscountsRequest } from "@root/api/discounts import type { Discount } from "@frontend/kitui"; const filterDiscounts = (discounts: Discount[]) => { - const activeDiscounts = discounts.filter((discount) => !discount.Deprecated); + const activeDiscounts = discounts.filter((discount) => !discount.Deprecated); - setDiscounts(activeDiscounts); + setDiscounts(activeDiscounts); }; export const requestDiscounts = async (): Promise => { - const [discounts, discountsError] = await requestDiscountsRequest(); + const [discounts, discountsError] = await requestDiscountsRequest(); - if (discountsError) { - console.error(discountsError); + if (discountsError) { + console.error(discountsError); - return []; - } + return []; + } - if (discounts) { - filterDiscounts(discounts.Discounts); + if (discounts) { + filterDiscounts(discounts.Discounts); - return discounts.Discounts; - } + return discounts.Discounts; + } - return []; + return []; }; diff --git a/src/services/privilegies.service.ts b/src/services/privilegies.service.ts index 89185fe..5746b74 100644 --- a/src/services/privilegies.service.ts +++ b/src/services/privilegies.service.ts @@ -4,28 +4,28 @@ import { requestServicePrivileges } from "@root/api/privilegies"; import type { CustomPrivilege } from "@frontend/kitui"; const mutatePrivileges = (privileges: CustomPrivilege[]) => { - let extracted: CustomPrivilege[] = []; - for (let serviceKey in privileges) { - //Приходит объект. В его значениях массивы привилегий для разных сервисов. Высыпаем в общую кучу и обновляем стор - extracted = extracted.concat(privileges[serviceKey]); - } + let extracted: CustomPrivilege[] = []; + for (const serviceKey in privileges) { + //Приходит объект. В его значениях массивы привилегий для разных сервисов. Высыпаем в общую кучу и обновляем стор + extracted = extracted.concat(privileges[serviceKey]); + } - resetPrivilegeArray(extracted); + resetPrivilegeArray(extracted); }; export const requestPrivileges = async () => { - const [privilegesResponse, privilegesError] = - await requestServicePrivileges(); + const [privilegesResponse, privilegesError] = await requestServicePrivileges(); - if (privilegesError) { - return console.error(privilegesError); - } + if (privilegesError) { + return console.error(privilegesError); + } - let allPrivileges: CustomPrivilege[] = []; + let allPrivileges: CustomPrivilege[] = []; - if (privilegesResponse) { - if (privilegesResponse.templategen !== undefined) allPrivileges = allPrivileges.concat(privilegesResponse.templategen); - if (privilegesResponse.squiz !== undefined) allPrivileges = allPrivileges.concat(privilegesResponse.squiz); - mutatePrivileges(allPrivileges); - } + if (privilegesResponse) { + if (privilegesResponse.templategen !== undefined) + allPrivileges = allPrivileges.concat(privilegesResponse.templategen); + if (privilegesResponse.squiz !== undefined) allPrivileges = allPrivileges.concat(privilegesResponse.squiz); + mutatePrivileges(allPrivileges); + } }; diff --git a/src/services/tariffs.service.ts b/src/services/tariffs.service.ts index 28fc083..7168ff8 100644 --- a/src/services/tariffs.service.ts +++ b/src/services/tariffs.service.ts @@ -4,33 +4,25 @@ import { requestTariffs as requestTariffsRequest } from "@root/api/tariffs"; import type { Tariff } from "@frontend/kitui"; const mutateTariffs = (tariffs: Tariff[]) => { - const nonDeletedTariffs = tariffs.filter(({ isDeleted }) => !isDeleted); + const nonDeletedTariffs = tariffs.filter(({ isDeleted }) => !isDeleted); - updateTariffs(nonDeletedTariffs); + updateTariffs(nonDeletedTariffs); }; -export const requestTariffs = async ( - page: number = 1, - existingTariffs: Tariff[] = [] -): Promise => { - const [tariffsResponse, tariffsResponseError] = await requestTariffsRequest( - page - ); +export const requestTariffs = async (page: number = 1, existingTariffs: Tariff[] = []): Promise => { + const [tariffsResponse, tariffsResponseError] = await requestTariffsRequest(page); - if (tariffsResponseError) { - console.error(tariffsResponseError); + if (tariffsResponseError) { + console.error(tariffsResponseError); - return; - } + return; + } - if (tariffsResponse) { - if (page < tariffsResponse.totalPages) { - return requestTariffs(page + 1, [ - ...existingTariffs, - ...tariffsResponse.tariffs, - ]); - } + if (tariffsResponse) { + if (page < tariffsResponse.totalPages) { + return requestTariffs(page + 1, [...existingTariffs, ...tariffsResponse.tariffs]); + } - mutateTariffs([...existingTariffs, ...tariffsResponse.tariffs]); - } + mutateTariffs([...existingTariffs, ...tariffsResponse.tariffs]); + } }; diff --git a/src/stores/cart.ts b/src/stores/cart.ts index 418542c..8321918 100644 --- a/src/stores/cart.ts +++ b/src/stores/cart.ts @@ -2,23 +2,19 @@ import { create } from "zustand"; import { devtools } from "zustand/middleware"; import { CartData } from "@frontend/kitui"; - interface CartStore { - cartData: CartData | null; + cartData: CartData | null; } const initialState: CartStore = { - cartData: null, + cartData: null, }; export const useCartStore = create()( - devtools( - (set, get) => initialState, - { - name: "Cart", - enabled: process.env.NODE_ENV === "development", - } - ) + devtools((set, get) => initialState, { + name: "Cart", + enabled: process.env.NODE_ENV === "development", + }) ); export const setCartData = (cartData: CartStore["cartData"]) => useCartStore.setState({ cartData }); diff --git a/src/stores/discounts.ts b/src/stores/discounts.ts index 8533e99..8993d61 100644 --- a/src/stores/discounts.ts +++ b/src/stores/discounts.ts @@ -4,46 +4,48 @@ import { devtools } from "zustand/middleware"; import { produce } from "immer"; import { Discount } from "@frontend/kitui"; - interface DiscountStore { - discounts: Discount[]; - selectedDiscountIds: GridSelectionModel, - editDiscountId: string | null; + discounts: Discount[]; + selectedDiscountIds: GridSelectionModel; + editDiscountId: string | null; } export const useDiscountStore = create()( - devtools( - (set, get) => ({ - discounts: [], - selectedDiscountIds: [], - editDiscountId: null, - }), - { - name: "Discount", - enabled: process.env.NODE_ENV === "development", - } - ) + devtools( + (set, get) => ({ + discounts: [], + selectedDiscountIds: [], + editDiscountId: null, + }), + { + name: "Discount", + enabled: process.env.NODE_ENV === "development", + } + ) ); export const setDiscounts = (discounts: DiscountStore["discounts"]) => useDiscountStore.setState({ discounts }); -export const findDiscountsById = (discountId: string): (Discount | null) => useDiscountStore.getState().discounts.find(discount => discount.ID === discountId) ?? null; +export const findDiscountsById = (discountId: string): Discount | null => + useDiscountStore.getState().discounts.find((discount) => discount.ID === discountId) ?? null; -export const addDiscount = (discount: DiscountStore["discounts"][number]) => useDiscountStore.setState( - state => ({ discounts: [...state.discounts, discount] }) -); +export const addDiscount = (discount: DiscountStore["discounts"][number]) => + useDiscountStore.setState((state) => ({ discounts: [...state.discounts, discount] })); -export const updateDiscount = (updatedDiscount: DiscountStore["discounts"][number]) => useDiscountStore.setState( - produce(state => { - const discountIndex = state.discounts.findIndex(discount => discount.ID === updatedDiscount.ID); - if (discountIndex === -1) throw new Error(`Discount not found when updating: ${updatedDiscount.ID}`); +export const updateDiscount = (updatedDiscount: DiscountStore["discounts"][number]) => + useDiscountStore.setState( + produce((state) => { + const discountIndex = state.discounts.findIndex((discount) => discount.ID === updatedDiscount.ID); + if (discountIndex === -1) throw new Error(`Discount not found when updating: ${updatedDiscount.ID}`); - state.discounts.splice(discountIndex, 1, updatedDiscount); - }) -); + state.discounts.splice(discountIndex, 1, updatedDiscount); + }) + ); -export const setSelectedDiscountIds = (selectedDiscountIds: DiscountStore["selectedDiscountIds"]) => useDiscountStore.setState({ selectedDiscountIds }); +export const setSelectedDiscountIds = (selectedDiscountIds: DiscountStore["selectedDiscountIds"]) => + useDiscountStore.setState({ selectedDiscountIds }); -export const openEditDiscountDialog = (editDiscountId: DiscountStore["editDiscountId"]) => useDiscountStore.setState({ editDiscountId }); +export const openEditDiscountDialog = (editDiscountId: DiscountStore["editDiscountId"]) => + useDiscountStore.setState({ editDiscountId }); export const closeEditDiscountDialog = () => useDiscountStore.setState({ editDiscountId: null }); diff --git a/src/stores/messages.ts b/src/stores/messages.ts index 5a00be7..e827e20 100644 --- a/src/stores/messages.ts +++ b/src/stores/messages.ts @@ -3,70 +3,73 @@ import { TicketMessage } from "@root/model/ticket"; import { create } from "zustand"; import { devtools } from "zustand/middleware"; - interface MessageStore { - messages: TicketMessage[]; - fetchState: "idle" | "fetching" | "all fetched"; - apiPage: number; - messagesPerPage: number; - lastMessageId: string | undefined; - isPreventAutoscroll: boolean; - ticketMessagesFetchState: FetchState; + messages: TicketMessage[]; + fetchState: "idle" | "fetching" | "all fetched"; + apiPage: number; + messagesPerPage: number; + lastMessageId: string | undefined; + isPreventAutoscroll: boolean; + ticketMessagesFetchState: FetchState; } export const useMessageStore = create()( - devtools( - (set, get) => ({ - messages: [], - messagesPerPage: 10, - fetchState: "idle", - apiPage: 0, - lastMessageId: undefined, - isPreventAutoscroll: false, - ticketMessagesFetchState: "idle", - }), - { - name: "Messages", - enabled: process.env.NODE_ENV === "development", - } - ) + devtools( + (set, get) => ({ + messages: [], + messagesPerPage: 10, + fetchState: "idle", + apiPage: 0, + lastMessageId: undefined, + isPreventAutoscroll: false, + ticketMessagesFetchState: "idle", + }), + { + name: "Messages", + enabled: process.env.NODE_ENV === "development", + } + ) ); export const addOrUpdateMessages = (receivedMessages: TicketMessage[]) => { - const state = useMessageStore.getState(); - const messageIdToMessageMap: { [messageId: string]: TicketMessage; } = {}; + const state = useMessageStore.getState(); + const messageIdToMessageMap: { [messageId: string]: TicketMessage } = {}; - [...state.messages, ...receivedMessages].forEach(message => messageIdToMessageMap[message.id] = message); + [...state.messages, ...receivedMessages].forEach((message) => (messageIdToMessageMap[message.id] = message)); - const sortedMessages = Object.values(messageIdToMessageMap).sort(sortMessagesByTime); + const sortedMessages = Object.values(messageIdToMessageMap).sort(sortMessagesByTime); - useMessageStore.setState({ - messages: sortedMessages, - lastMessageId: sortedMessages.at(-1)?.id, - }); + useMessageStore.setState({ + messages: sortedMessages, + lastMessageId: sortedMessages.at(-1)?.id, + }); }; -export const clearMessageState = () => useMessageStore.setState({ - messages: [], - apiPage: 0, - lastMessageId: undefined, - fetchState: "idle", -}); +export const clearMessageState = () => + useMessageStore.setState({ + messages: [], + apiPage: 0, + lastMessageId: undefined, + fetchState: "idle", + }); -export const setMessageFetchState = (fetchState: MessageStore["fetchState"]) => useMessageStore.setState({ fetchState }); +export const setMessageFetchState = (fetchState: MessageStore["fetchState"]) => + useMessageStore.setState({ fetchState }); export const incrementMessageApiPage = () => { - const state = useMessageStore.getState(); + const state = useMessageStore.getState(); - useMessageStore.setState({ apiPage: state.apiPage + 1 }); + useMessageStore.setState({ apiPage: state.apiPage + 1 }); }; -export const setIsPreventAutoscroll = (isPreventAutoscroll: boolean) => useMessageStore.setState({ isPreventAutoscroll }); +export const setIsPreventAutoscroll = (isPreventAutoscroll: boolean) => + useMessageStore.setState({ isPreventAutoscroll }); -export const setTicketMessagesFetchState = (ticketMessagesFetchState: FetchState) => useMessageStore.setState({ ticketMessagesFetchState }); +export const setTicketMessagesFetchState = (ticketMessagesFetchState: FetchState) => + useMessageStore.setState({ ticketMessagesFetchState }); function sortMessagesByTime(ticket1: TicketMessage, ticket2: TicketMessage) { - const date1 = new Date(ticket1.created_at).getTime(); - const date2 = new Date(ticket2.created_at).getTime(); - return date1 - date2; + const date1 = new Date(ticket1.created_at).getTime(); + const date2 = new Date(ticket2.created_at).getTime(); + return date1 - date2; } diff --git a/src/stores/mocks/tickets.ts b/src/stores/mocks/tickets.ts index 7af305b..a114251 100644 --- a/src/stores/mocks/tickets.ts +++ b/src/stores/mocks/tickets.ts @@ -1,49 +1,48 @@ import { Ticket } from "@root/model/ticket"; - export const testTickets: Ticket[] = [ - { - "id": "cg5irh4vc9g7b3n3tcrg", - "user": "6407625ed01874dcffa8b008", - "sess": "6407625ed01874dcffa8b008", - "ans": "", - "state": "open", - "top_message": { - "id": "cg5irh4vc9g7b3n3tcs0", - "ticket_id": "cg5irh4vc9g7b3n3tcrg", - "user_id": "6407625ed01874dcffa8b008", - "session_id": "6407625ed01874dcffa8b008", - "message": "text", - "files": [], - "shown": {}, - "request_screenshot": "", - "created_at": "2023-03-10T13:16:52.73Z" - }, - "title": "textual ticket", - "created_at": "2023-03-10T13:16:52.73Z", - "updated_at": "2023-03-10T13:16:52.73Z", - "rate": -1 - }, - { - "id": "cg55nssvc9g7gddpnsug", - "user": "", - "sess": "", - "ans": "", - "state": "open", - "top_message": { - "id": "cg55nssvc9g7gddpnsv0", - "ticket_id": "cg55nssvc9g7gddpnsug", - "user_id": "", - "session_id": "", - "message": "text", - "files": [], - "shown": {}, - "request_screenshot": "", - "created_at": "2023-03-09T22:21:39.822Z" - }, - "title": "textual ticket", - "created_at": "2023-03-09T22:21:39.822Z", - "updated_at": "2023-03-09T22:21:39.822Z", - "rate": -1 - } -]; \ No newline at end of file + { + id: "cg5irh4vc9g7b3n3tcrg", + user: "6407625ed01874dcffa8b008", + sess: "6407625ed01874dcffa8b008", + ans: "", + state: "open", + top_message: { + id: "cg5irh4vc9g7b3n3tcs0", + ticket_id: "cg5irh4vc9g7b3n3tcrg", + user_id: "6407625ed01874dcffa8b008", + session_id: "6407625ed01874dcffa8b008", + message: "text", + files: [], + shown: {}, + request_screenshot: "", + created_at: "2023-03-10T13:16:52.73Z", + }, + title: "textual ticket", + created_at: "2023-03-10T13:16:52.73Z", + updated_at: "2023-03-10T13:16:52.73Z", + rate: -1, + }, + { + id: "cg55nssvc9g7gddpnsug", + user: "", + sess: "", + ans: "", + state: "open", + top_message: { + id: "cg55nssvc9g7gddpnsv0", + ticket_id: "cg55nssvc9g7gddpnsug", + user_id: "", + session_id: "", + message: "text", + files: [], + shown: {}, + request_screenshot: "", + created_at: "2023-03-09T22:21:39.822Z", + }, + title: "textual ticket", + created_at: "2023-03-09T22:21:39.822Z", + updated_at: "2023-03-09T22:21:39.822Z", + rate: -1, + }, +]; diff --git a/src/stores/mocks/user.ts b/src/stores/mocks/user.ts index ea6f115..e491678 100644 --- a/src/stores/mocks/user.ts +++ b/src/stores/mocks/user.ts @@ -1,8 +1,7 @@ import { User } from "@root/model/user"; - export const testUser: User = { - "ID": "buddy", - "Type": "", - "PurchasesAmount": 11000, -}; \ No newline at end of file + ID: "buddy", + Type: "", + PurchasesAmount: 11000, +}; diff --git a/src/stores/privilegesStore.ts b/src/stores/privilegesStore.ts index f0fd652..a3d40a1 100644 --- a/src/stores/privilegesStore.ts +++ b/src/stores/privilegesStore.ts @@ -2,25 +2,29 @@ import { create } from "zustand"; import { devtools } from "zustand/middleware"; import { CustomPrivilege } from "@frontend/kitui"; - interface PrivilegeStore { - privileges: CustomPrivilege[]; + privileges: CustomPrivilege[]; } export const usePrivilegeStore = create()( - devtools( - (set, get) => ({ - privileges: [], - }), - { - name: "Privileges", - enabled: process.env.NODE_ENV === "development", - } - ) + devtools( + (set, get) => ({ + privileges: [], + }), + { + name: "Privileges", + enabled: process.env.NODE_ENV === "development", + } + ) ); -export const resetPrivilegeArray = (privileges: PrivilegeStore["privileges"]) => usePrivilegeStore.setState({ privileges }); +export const resetPrivilegeArray = (privileges: PrivilegeStore["privileges"]) => + usePrivilegeStore.setState({ privileges }); export const findPrivilegeById = (privilegeId: string) => { - return usePrivilegeStore.getState().privileges.find((privilege) => privilege._id === privilegeId || privilege.privilegeId === privilegeId) ?? null; + return ( + usePrivilegeStore + .getState() + .privileges.find((privilege) => privilege._id === privilegeId || privilege.privilegeId === privilegeId) ?? null + ); }; diff --git a/src/stores/tariffs.ts b/src/stores/tariffs.ts index bddca36..f1f245c 100644 --- a/src/stores/tariffs.ts +++ b/src/stores/tariffs.ts @@ -3,60 +3,63 @@ import { devtools } from "zustand/middleware"; import { Tariff } from "@frontend/kitui"; import { GridSelectionModel } from "@mui/x-data-grid"; - interface TariffStore { - tariffs: Tariff[]; - editTariffId: string | null; - deleteTariffIds: string[] | null; - selectedTariffIds: GridSelectionModel; + tariffs: Tariff[]; + editTariffId: string | null; + deleteTariffIds: string[] | null; + selectedTariffIds: GridSelectionModel; } export const useTariffStore = create()( - devtools( - (set, get) => ({ - tariffs: [], - editTariffId: null, - deleteTariffIds: null, - selectedTariffIds: [], - }), - { - name: "Tariffs", - enabled: process.env.NODE_ENV !== "production", - } - ) + devtools( + (set, get) => ({ + tariffs: [], + editTariffId: null, + deleteTariffIds: null, + selectedTariffIds: [], + }), + { + name: "Tariffs", + enabled: process.env.NODE_ENV !== "production", + } + ) ); -export const updateTariffs = (tariffs: TariffStore["tariffs"], mode: "add" | "replace" = "replace") => useTariffStore.setState( - state => { - const tariffMap: Record = {}; +export const updateTariffs = (tariffs: TariffStore["tariffs"], mode: "add" | "replace" = "replace") => + useTariffStore.setState( + (state) => { + const tariffMap: Record = {}; - [...(mode === "add" ? state.tariffs : []), ...tariffs].forEach(tariff => tariffMap[tariff._id] = tariff); + [...(mode === "add" ? state.tariffs : []), ...tariffs].forEach((tariff) => (tariffMap[tariff._id] = tariff)); - const sortedTariffs = Object.values(tariffMap).sort(sortTariffsByCreatedAt); + const sortedTariffs = Object.values(tariffMap).sort(sortTariffsByCreatedAt); - return { tariffs: sortedTariffs }; - }, - false, - { - type: "updateTariffs", - tariffsLength: tariffs.length, - } -); + return { tariffs: sortedTariffs }; + }, + false, + { + type: "updateTariffs", + tariffsLength: tariffs.length, + } + ); -export const openEditTariffDialog = (editTariffId: TariffStore["editTariffId"]) => useTariffStore.setState({ editTariffId }); +export const openEditTariffDialog = (editTariffId: TariffStore["editTariffId"]) => + useTariffStore.setState({ editTariffId }); export const closeEditTariffDialog = () => useTariffStore.setState({ editTariffId: null }); -export const openDeleteTariffDialog = (deleteTariffId: TariffStore["deleteTariffIds"]) => useTariffStore.setState({ deleteTariffIds: deleteTariffId }); +export const openDeleteTariffDialog = (deleteTariffId: TariffStore["deleteTariffIds"]) => + useTariffStore.setState({ deleteTariffIds: deleteTariffId }); export const closeDeleteTariffDialog = () => useTariffStore.setState({ deleteTariffIds: null }); -export const setSelectedTariffIds = (selectedTariffIds: TariffStore["selectedTariffIds"]) => useTariffStore.setState({ selectedTariffIds }); +export const setSelectedTariffIds = (selectedTariffIds: TariffStore["selectedTariffIds"]) => + useTariffStore.setState({ selectedTariffIds }); function sortTariffsByCreatedAt(tariff1: Tariff, tariff2: Tariff) { - if (!tariff1.createdAt || !tariff2.createdAt) throw new Error("Trying to sort tariffs without createdAt field"); + if (!tariff1.createdAt || !tariff2.createdAt) throw new Error("Trying to sort tariffs without createdAt field"); - const date1 = new Date(tariff1.createdAt).getTime(); - const date2 = new Date(tariff2.createdAt).getTime(); - return date1 - date2; + const date1 = new Date(tariff1.createdAt).getTime(); + const date2 = new Date(tariff2.createdAt).getTime(); + return date1 - date2; } diff --git a/src/stores/tickets.ts b/src/stores/tickets.ts index 8861dac..99f2245 100644 --- a/src/stores/tickets.ts +++ b/src/stores/tickets.ts @@ -3,42 +3,41 @@ import { Ticket } from "@root/model/ticket"; import { create } from "zustand"; import { devtools } from "zustand/middleware"; - interface TicketStore { - tickets: Ticket[]; - apiPage: number; - ticketsPerPage: number; - ticketsFetchState: FetchState; + tickets: Ticket[]; + apiPage: number; + ticketsPerPage: number; + ticketsFetchState: FetchState; } export const useTicketStore = create()( - devtools( - (set, get) => ({ - tickets: [], - apiPage: 0, - ticketsPerPage: 20, - ticketsFetchState: "idle", - }), - { - name: "Tickets", - enabled: process.env.NODE_ENV === "development", - } - ) + devtools( + (set, get) => ({ + tickets: [], + apiPage: 0, + ticketsPerPage: 20, + ticketsFetchState: "idle", + }), + { + name: "Tickets", + enabled: process.env.NODE_ENV === "development", + } + ) ); export const incrementTicketsApiPage = () => { - const state = useTicketStore.getState(); + const state = useTicketStore.getState(); - useTicketStore.setState({ apiPage: state.apiPage + 1 }); + useTicketStore.setState({ apiPage: state.apiPage + 1 }); }; export const updateTickets = (receivedTickets: Ticket[]) => { - const state = useTicketStore.getState(); - const ticketIdToTicketMap: { [ticketId: string]: Ticket; } = {}; + const state = useTicketStore.getState(); + const ticketIdToTicketMap: { [ticketId: string]: Ticket } = {}; - [...state.tickets, ...receivedTickets].forEach(ticket => ticketIdToTicketMap[ticket.id] = ticket); + [...state.tickets, ...receivedTickets].forEach((ticket) => (ticketIdToTicketMap[ticket.id] = ticket)); - useTicketStore.setState({ tickets: Object.values(ticketIdToTicketMap) }); + useTicketStore.setState({ tickets: Object.values(ticketIdToTicketMap) }); }; export const clearTickets = () => useTicketStore.setState({ tickets: [] }); diff --git a/src/theme.ts b/src/theme.ts index f4f56ca..ef6752f 100644 --- a/src/theme.ts +++ b/src/theme.ts @@ -3,229 +3,229 @@ import { deepmerge } from "@mui/utils"; //import { createTheme } from "./types"; declare module "@mui/material/Button" { - interface ButtonPropsVariantOverrides { - enter: true; - } + interface ButtonPropsVariantOverrides { + enter: true; + } } declare module "@mui/material/Paper" { - interface PaperPropsVariantOverrides { - bar: true; - } + interface PaperPropsVariantOverrides { + bar: true; + } } declare module "@mui/material/styles" { - interface Theme { - palette: { - primary: { - main: string; - }; - common: { - black: string; - white: string; - }; - background: { - default: string; - paper: string; - }; - secondary: { - main: string; - }; - menu: { - main: string; - }; - content: { - main: string; - }; - hover: { - main: string; - }; - grayLight: { - main: string; - }; - grayDark: { - main: string; - }; - grayMedium: { - main: string; - }; - grayDisabled: { - main: string; - }; - golden: { - main: string; - }; - goldenDark: { - main: string; - }; - goldenMedium: { - main: string; - }; - caption: { - main: string; - }; - }; - } + interface Theme { + palette: { + primary: { + main: string; + }; + common: { + black: string; + white: string; + }; + background: { + default: string; + paper: string; + }; + secondary: { + main: string; + }; + menu: { + main: string; + }; + content: { + main: string; + }; + hover: { + main: string; + }; + grayLight: { + main: string; + }; + grayDark: { + main: string; + }; + grayMedium: { + main: string; + }; + grayDisabled: { + main: string; + }; + golden: { + main: string; + }; + goldenDark: { + main: string; + }; + goldenMedium: { + main: string; + }; + caption: { + main: string; + }; + }; + } - interface PaletteOptions { - menu?: PaletteColorOptions; - content?: PaletteColorOptions; - grayLight?: PaletteColorOptions; - grayDark?: PaletteColorOptions; - grayMedium?: PaletteColorOptions; - grayDisabled?: PaletteColorOptions; - golden?: PaletteColorOptions; - goldenDark?: PaletteColorOptions; - goldenMedium?: PaletteColorOptions; - hover?: PaletteColorOptions; - } + interface PaletteOptions { + menu?: PaletteColorOptions; + content?: PaletteColorOptions; + grayLight?: PaletteColorOptions; + grayDark?: PaletteColorOptions; + grayMedium?: PaletteColorOptions; + grayDisabled?: PaletteColorOptions; + golden?: PaletteColorOptions; + goldenDark?: PaletteColorOptions; + goldenMedium?: PaletteColorOptions; + hover?: PaletteColorOptions; + } - // allow configuration using `createTheme` - interface TypographyVariants { - body1: React.CSSProperties; - subtitle1: React.CSSProperties; - subtitle2: React.CSSProperties; - caption: React.CSSProperties; - h5: React.CSSProperties; - h6: React.CSSProperties; - button: React.CSSProperties; - } + // allow configuration using `createTheme` + interface TypographyVariants { + body1: React.CSSProperties; + subtitle1: React.CSSProperties; + subtitle2: React.CSSProperties; + caption: React.CSSProperties; + h5: React.CSSProperties; + h6: React.CSSProperties; + button: React.CSSProperties; + } } const fontFamily: string = "GilroyRegular"; const fontWeight: string = "600"; const paletteColor = { - palette: { - primary: { - main: "#111217", - }, - secondary: { - main: "#e6e8ec", - }, - menu: { - main: "#2f3339", - }, - content: { - main: "#26272c", - }, - hover: { - main: "#191a1e", - }, - grayLight: { - main: "#707070", - }, - grayDark: { - main: "#45494c", - }, - grayMedium: { - main: "#424242", - }, - grayDisabled: { - main: "#c0c1c3", - }, - golden: { - main: "#eaba5b", - }, - goldenDark: { - main: "#fe9903", - }, - goldenMedium: { - main: "#2a2b1d", - }, - }, + palette: { + primary: { + main: "#111217", + }, + secondary: { + main: "#e6e8ec", + }, + menu: { + main: "#2f3339", + }, + content: { + main: "#26272c", + }, + hover: { + main: "#191a1e", + }, + grayLight: { + main: "#707070", + }, + grayDark: { + main: "#45494c", + }, + grayMedium: { + main: "#424242", + }, + grayDisabled: { + main: "#c0c1c3", + }, + golden: { + main: "#eaba5b", + }, + goldenDark: { + main: "#fe9903", + }, + goldenMedium: { + main: "#2a2b1d", + }, + }, }; const theme: ThemeOptions = { - typography: { - body1: { - fontFamily: fontFamily, - }, - subtitle1: { - fontFamily: fontFamily, - fontWeight: fontWeight, - fontSize: 25, - }, - subtitle2: { - fontFamily: fontFamily, - fontSize: 25, - textAlign: "center", - }, - caption: { - fontFamily: fontFamily, - fontWeight: fontWeight, - fontSize: 21, - }, - h5: { - fontFamily: fontFamily, - fontWeight: fontWeight, - fontSize: 35, - }, - h6: { - fontFamily: fontFamily, - fontWeight: fontWeight, - fontSize: 18, - }, - button: { - fontFamily: fontFamily, - fontWeight: fontWeight, - fontSize: 22, - }, - h4: { - fontFamily: fontFamily, - fontWeight: fontWeight, - fontSize: 16, - }, - }, + typography: { + body1: { + fontFamily: fontFamily, + }, + subtitle1: { + fontFamily: fontFamily, + fontWeight: fontWeight, + fontSize: 25, + }, + subtitle2: { + fontFamily: fontFamily, + fontSize: 25, + textAlign: "center", + }, + caption: { + fontFamily: fontFamily, + fontWeight: fontWeight, + fontSize: 21, + }, + h5: { + fontFamily: fontFamily, + fontWeight: fontWeight, + fontSize: 35, + }, + h6: { + fontFamily: fontFamily, + fontWeight: fontWeight, + fontSize: 18, + }, + button: { + fontFamily: fontFamily, + fontWeight: fontWeight, + fontSize: 22, + }, + h4: { + fontFamily: fontFamily, + fontWeight: fontWeight, + fontSize: 16, + }, + }, - components: { - MuiButton: { - styleOverrides: { - root: { - color: paletteColor.palette.secondary.main, - backgroundColor: paletteColor.palette.menu.main, - padding: "12px", - fontSize: "13px", - "&:hover": { - backgroundColor: paletteColor.palette.hover.main, - }, - }, - }, - variants: [ - { - props: { - variant: "enter", - }, - style: { - color: paletteColor.palette.secondary.main, - backgroundColor: paletteColor.palette.content.main, - padding: "12px 48px", - "&:hover": { - backgroundColor: paletteColor.palette.hover.main, - }, - }, - }, - ], - }, - MuiPaper: { - variants: [ - { - props: { - variant: "bar", - }, - style: { - backgroundColor: paletteColor.palette.grayMedium.main, - padding: "15px", - width: "100%", - }, - }, - ], - }, - MuiButtonBase: { - styleOverrides: { - root: { - fontFamily, - fontSize: "16px", - }, - }, - }, - }, + components: { + MuiButton: { + styleOverrides: { + root: { + color: paletteColor.palette.secondary.main, + backgroundColor: paletteColor.palette.menu.main, + padding: "12px", + fontSize: "13px", + "&:hover": { + backgroundColor: paletteColor.palette.hover.main, + }, + }, + }, + variants: [ + { + props: { + variant: "enter", + }, + style: { + color: paletteColor.palette.secondary.main, + backgroundColor: paletteColor.palette.content.main, + padding: "12px 48px", + "&:hover": { + backgroundColor: paletteColor.palette.hover.main, + }, + }, + }, + ], + }, + MuiPaper: { + variants: [ + { + props: { + variant: "bar", + }, + style: { + backgroundColor: paletteColor.palette.grayMedium.main, + padding: "15px", + width: "100%", + }, + }, + ], + }, + MuiButtonBase: { + styleOverrides: { + root: { + fontFamily, + fontSize: "16px", + }, + }, + }, + }, }; export default createTheme(deepmerge(paletteColor, theme)); diff --git a/src/utils/createDiscountFromPromocode.ts b/src/utils/createDiscountFromPromocode.ts index 392b9e4..7c43642 100644 --- a/src/utils/createDiscountFromPromocode.ts +++ b/src/utils/createDiscountFromPromocode.ts @@ -1,42 +1,44 @@ import { Promocode } from "@root/model/promocodes"; - export function createDiscountFromPromocode(promocode: Promocode, userId: string) { - return { - "ID": crypto.randomUUID(), - "Name": promocode.codeword, - "Layer": promocode.bonus.discount.layer, - "Description": "", - "Condition": { - "User": userId, - "UserType": "", - "Coupon": promocode.codeword, - "PurchasesAmount": "0", - "CartPurchasesAmount": "0", - "Product": promocode.bonus.discount.target, - "Term": "0", - "Usage": "0", - "PriceFrom": "0", - "Group": promocode.bonus.discount.target - }, - "Target": { - "Products": promocode.bonus.discount.layer === 1 ? [ - { - "ID": promocode.bonus.discount.target, - "Factor": promocode.bonus.discount.factor, - "Overhelm": false - } - ] : [], - "Factor": promocode.bonus.discount.layer === 2 ? promocode.bonus.discount.factor : 0, - "TargetScope": "Sum", - "TargetGroup": promocode.bonus.discount.target, - "Overhelm": true - }, - "Audit": { - "UpdatedAt": "", - "CreatedAt": "", - "Deleted": false - }, - "Deprecated": false - }; + return { + ID: crypto.randomUUID(), + Name: promocode.codeword, + Layer: promocode.bonus.discount.layer, + Description: "", + Condition: { + User: userId, + UserType: "", + Coupon: promocode.codeword, + PurchasesAmount: "0", + CartPurchasesAmount: "0", + Product: promocode.bonus.discount.target, + Term: "0", + Usage: "0", + PriceFrom: "0", + Group: promocode.bonus.discount.target, + }, + Target: { + Products: + promocode.bonus.discount.layer === 1 + ? [ + { + ID: promocode.bonus.discount.target, + Factor: promocode.bonus.discount.factor, + Overhelm: false, + }, + ] + : [], + Factor: promocode.bonus.discount.layer === 2 ? promocode.bonus.discount.factor : 0, + TargetScope: "Sum", + TargetGroup: promocode.bonus.discount.target, + Overhelm: true, + }, + Audit: { + UpdatedAt: "", + CreatedAt: "", + Deleted: false, + }, + Deprecated: false, + }; } diff --git a/src/utils/currencyFormatter.ts b/src/utils/currencyFormatter.ts index 891d449..c440a41 100644 --- a/src/utils/currencyFormatter.ts +++ b/src/utils/currencyFormatter.ts @@ -1,9 +1,6 @@ -export const currencyFormatter = new Intl.NumberFormat( - "ru", - { - currency: "RUB", - style: "currency", - compactDisplay: "short", - minimumFractionDigits: 0 - } -); \ No newline at end of file +export const currencyFormatter = new Intl.NumberFormat("ru", { + currency: "RUB", + style: "currency", + compactDisplay: "short", + minimumFractionDigits: 0, +}); diff --git a/src/utils/discount.ts b/src/utils/discount.ts index b4601f8..b491923 100644 --- a/src/utils/discount.ts +++ b/src/utils/discount.ts @@ -2,11 +2,16 @@ import { Discount } from "@frontend/kitui"; import { DiscountType } from "@root/model/discount"; export function getDiscountTypeFromLayer(layer: Discount["Layer"]): DiscountType { - switch (layer) { - case 1: return "privilege"; - case 2: return "service"; - case 3: return "cartPurchasesAmount"; - case 4: return "purchasesAmount"; - default: throw new Error("Wrong discount layer"); - } + switch (layer) { + case 1: + return "privilege"; + case 2: + return "service"; + case 3: + return "cartPurchasesAmount"; + case 4: + return "purchasesAmount"; + default: + throw new Error("Wrong discount layer"); + } } diff --git a/src/utils/formatDiscountFactor.ts b/src/utils/formatDiscountFactor.ts index 6ed645e..1140157 100644 --- a/src/utils/formatDiscountFactor.ts +++ b/src/utils/formatDiscountFactor.ts @@ -1,3 +1,3 @@ export function formatDiscountFactor(factor: number): string { - return `${((1 - factor) * 100).toFixed(1)}%`; + return `${((1 - factor) * 100).toFixed(1)}%`; } diff --git a/src/utils/hooks/usePrivileges.ts b/src/utils/hooks/usePrivileges.ts index 8969731..fa97ae9 100644 --- a/src/utils/hooks/usePrivileges.ts +++ b/src/utils/hooks/usePrivileges.ts @@ -5,24 +5,22 @@ import { requestPrivileges } from "@root/api/privilegies"; import type { CustomPrivilege } from "@frontend/kitui"; export default function usePrivileges({ - onError, - onNewPrivileges, + onError, + onNewPrivileges, }: { - onNewPrivileges: (response: CustomPrivilege[]) => void; - onError?: (error: any) => void; + onNewPrivileges: (response: CustomPrivilege[]) => void; + onError?: (error: any) => void; }) { - useEffect(() => { - const controller = new AbortController(); + useEffect(() => { + const controller = new AbortController(); - requestPrivileges(controller.signal).then( - ([privilegesResponse, privilegesError]) => { - if (privilegesError) { - return onError?.(privilegesError); - } - onNewPrivileges(privilegesResponse); - } - ); + requestPrivileges(controller.signal).then(([privilegesResponse, privilegesError]) => { + if (privilegesError) { + return onError?.(privilegesError); + } + onNewPrivileges(privilegesResponse); + }); - return () => controller.abort(); - }, [onError, onNewPrivileges]); + return () => controller.abort(); + }, [onError, onNewPrivileges]); } diff --git a/src/utils/hooks/usePromocodeStatistics.ts b/src/utils/hooks/usePromocodeStatistics.ts index eece16b..52f81cd 100644 --- a/src/utils/hooks/usePromocodeStatistics.ts +++ b/src/utils/hooks/usePromocodeStatistics.ts @@ -6,30 +6,25 @@ import type { AllPromocodeStatistics } from "@root/api/quizStatistics/types"; import moment from "moment"; interface useStatisticProps { - to: Moment | null; - from: Moment | null; + to: Moment | null; + from: Moment | null; } export function usePromocodeStatistics({ to, from }: useStatisticProps) { - const formatTo = to?.unix(); - const formatFrom = from?.unix() || moment().unix() - 604800; + const formatTo = to?.unix(); + const formatFrom = from?.unix() || moment().unix() - 604800; - const [promocodeStatistics, setPromocodeStatistics] = useState< - Record - >({}); + const [promocodeStatistics, setPromocodeStatistics] = useState>({}); - useEffect(() => { - const requestStatistics = async () => { - const gottenData = await getStatisticPromocode( - Number(formatFrom), - Number(formatTo) - ); + useEffect(() => { + const requestStatistics = async () => { + const gottenData = await getStatisticPromocode(Number(formatFrom), Number(formatTo)); - setPromocodeStatistics(gottenData); - }; + setPromocodeStatistics(gottenData); + }; - requestStatistics(); - }, [formatTo, formatFrom]); + requestStatistics(); + }, [formatTo, formatFrom]); - return promocodeStatistics; + return promocodeStatistics; } diff --git a/src/utils/hooks/useQuizStatistic.ts b/src/utils/hooks/useQuizStatistic.ts index 8164965..3af2db0 100644 --- a/src/utils/hooks/useQuizStatistic.ts +++ b/src/utils/hooks/useQuizStatistic.ts @@ -4,31 +4,28 @@ import { QuizStatisticResponse, getStatistic } from "@root/api/quizStatistics"; import type { Moment } from "moment"; interface useQuizStatisticProps { - to: Moment | null; - from: Moment | null; + to: Moment | null; + from: Moment | null; } export function useQuizStatistic({ to, from }: useQuizStatisticProps) { - const formatTo = to?.unix(); - const formatFrom = from?.unix(); + const formatTo = to?.unix(); + const formatFrom = from?.unix(); - const [data, setData] = useState({ - Registrations: 0, - Quizes: 0, - Results: 0, - }); + const [data, setData] = useState({ + Registrations: 0, + Quizes: 0, + Results: 0, + }); - useEffect(() => { - const requestStatistics = async () => { - const gottenData = await getStatistic( - Number(formatTo), - Number(formatFrom) - ); - setData(gottenData); - }; + useEffect(() => { + const requestStatistics = async () => { + const gottenData = await getStatistic(Number(formatTo), Number(formatFrom)); + setData(gottenData); + }; - requestStatistics(); - }, [formatTo, formatFrom]); + requestStatistics(); + }, [formatTo, formatFrom]); - return { ...data }; + return { ...data }; } diff --git a/src/utils/hooks/useSchildStatistics.ts b/src/utils/hooks/useSchildStatistics.ts index 44dd2a8..b982602 100644 --- a/src/utils/hooks/useSchildStatistics.ts +++ b/src/utils/hooks/useSchildStatistics.ts @@ -7,22 +7,19 @@ import type { QuizStatisticsItem } from "@root/api/quizStatistics/types"; import moment from "moment"; export const useSchildStatistics = (from: Moment | null, to: Moment | null) => { - const formatTo = to?.unix(); - const formatFrom = from?.unix() || moment().unix() - 604800; - const [statistics, setStatistics] = useState([]); + const formatTo = to?.unix(); + const formatFrom = from?.unix() || moment().unix() - 604800; + const [statistics, setStatistics] = useState([]); - useEffect(() => { - const StatisticsShild = async () => { - const gottenData = await getStatisticSchild( - Number(formatFrom), - Number(formatTo) - ); + useEffect(() => { + const StatisticsShild = async () => { + const gottenData = await getStatisticSchild(Number(formatFrom), Number(formatTo)); - setStatistics(gottenData); - }; + setStatistics(gottenData); + }; - StatisticsShild(); - }, [formatTo, formatFrom]); + StatisticsShild(); + }, [formatTo, formatFrom]); - return statistics; + return statistics; }; diff --git a/src/utils/parse-error.ts b/src/utils/parse-error.ts index 24f16a0..bf7de87 100644 --- a/src/utils/parse-error.ts +++ b/src/utils/parse-error.ts @@ -1,52 +1,53 @@ import { AxiosError } from "axios"; export type ServerError = { - statusCode: number; - error: string; - message: string; + statusCode: number; + error: string; + message: string; }; const translateMessage: Record = { - "user not found": "Пользователь не найден", - "invalid password": "Неправильный пароль", - "field is empty": "Поле \"Пароль\" не заполнено", - "field is empty": "Поле \"Логин\" не заполнено", - "field is empty": "Поле \"E-mail\" не заполнено", - "field is empty": "Поле \"Номер телефона\" не заполнено", - "user with this email or login is exist": "Пользователь уже существует", - "user with this login is exist": "Пользователь с таким логином уже существует" -} + "user not found": "Пользователь не найден", + "invalid password": "Неправильный пароль", + "field is empty": 'Поле "Пароль" не заполнено', + "field is empty": 'Поле "Логин" не заполнено', + "field is empty": 'Поле "E-mail" не заполнено', + "field is empty": 'Поле "Номер телефона" не заполнено', + "user with this email or login is exist": "Пользователь уже существует", + "user with this login is exist": "Пользователь с таким логином уже существует", +}; export const parseAxiosError = (nativeError: unknown): [string, number?] => { - const error = nativeError as AxiosError; + const error = nativeError as AxiosError; - if ( - error.response?.data && error.response?.data !== "Not Found" && - "statusCode" in (error.response.data as ServerError) - ) { - const serverError = error.response.data as ServerError - const translatedMessage = translateMessage[serverError.message] - if (translatedMessage !== undefined) serverError.message = translatedMessage - return [serverError.message, serverError.statusCode] - } + if ( + error.response?.data && + error.response?.data !== "Not Found" && + "statusCode" in (error.response.data as ServerError) + ) { + const serverError = error.response.data as ServerError; + const translatedMessage = translateMessage[serverError.message]; + if (translatedMessage !== undefined) serverError.message = translatedMessage; + return [serverError.message, serverError.statusCode]; + } - switch (error.response?.status) { - case 404: - return ["Не найдено.", error.status]; + switch (error.response?.status) { + case 404: + return ["Не найдено.", error.status]; - case 403: - return ["Доступ ограничен.", error.status]; + case 403: + return ["Доступ ограничен.", error.status]; - case 401: - return ["Ошибка авторизации.", error.status]; + case 401: + return ["Ошибка авторизации.", error.status]; - case 500: - return ["Внутренняя ошибка сервера.", error.status]; + case 500: + return ["Внутренняя ошибка сервера.", error.status]; - case 503: - return ["Сервис недоступен.", error.status]; + case 503: + return ["Сервис недоступен.", error.status]; - default: - return ["Неизвестная ошибка сервера."]; - } + default: + return ["Неизвестная ошибка сервера."]; + } }; diff --git a/src/utils/scrollBlock.ts b/src/utils/scrollBlock.ts index 2fd6b86..beefbf3 100644 --- a/src/utils/scrollBlock.ts +++ b/src/utils/scrollBlock.ts @@ -1,10 +1,7 @@ type Coordinates = { - top?: number; - left?: number; + top?: number; + left?: number; }; -export const scrollBlock = ( - block: Element, - coordinates: Coordinates, - smooth = true -) => block.scroll({ ...coordinates, behavior: smooth ? "smooth" : "auto" }); +export const scrollBlock = (block: Element, coordinates: Coordinates, smooth = true) => + block.scroll({ ...coordinates, behavior: smooth ? "smooth" : "auto" }); diff --git a/src/utils/style/keyframes.ts b/src/utils/style/keyframes.ts index a0900a2..479c168 100644 --- a/src/utils/style/keyframes.ts +++ b/src/utils/style/keyframes.ts @@ -1,6 +1,5 @@ import { keyframes } from "@emotion/react"; - export const fadeIn = keyframes` from { opacity: 0; diff --git a/src/utils/tariffPrice.ts b/src/utils/tariffPrice.ts index f0e8b98..69a87a9 100644 --- a/src/utils/tariffPrice.ts +++ b/src/utils/tariffPrice.ts @@ -1,5 +1,5 @@ import { Tariff } from "@frontend/kitui"; export function getTariffPrice(tariff: Tariff) { - return tariff.price || tariff.privileges.reduce((sum, privilege) => sum + privilege.amount * privilege.price, 0); + return tariff.price || tariff.privileges.reduce((sum, privilege) => sum + privilege.amount * privilege.price, 0); } diff --git a/tsconfig.extend.json b/tsconfig.extend.json index 42854e7..52cc45a 100644 --- a/tsconfig.extend.json +++ b/tsconfig.extend.json @@ -1,12 +1,12 @@ { - "compilerOptions": { - "baseUrl": "./src", - "paths": { - "@theme": ["./theme.ts"], - "@root/*": ["./*"], - "@kitUI/*": ["./kitUI/*"], - "@stores/*": ["./stores/*"], - "@pages/*": ["./pages/*"] - } - } -} \ No newline at end of file + "compilerOptions": { + "baseUrl": "./src", + "paths": { + "@theme": ["./theme.ts"], + "@root/*": ["./*"], + "@kitUI/*": ["./kitUI/*"], + "@stores/*": ["./stores/*"], + "@pages/*": ["./pages/*"] + } + } +} diff --git a/tsconfig.json b/tsconfig.json index fe01ca0..22fb06e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,22 +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", - "types": ["node"] - }, - "include": ["src", "**/*.ts"] + "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", + "types": ["node"] + }, + "include": ["src", "**/*.ts"] } diff --git a/yarn.lock b/yarn.lock index dac211d..242bb80 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,11 +2,6 @@ # yarn lockfile v1 -"@aashutoshrathi/word-wrap@^1.2.3": - version "1.2.6" - resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" - integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== - "@adobe/css-tools@^4.0.1": version "4.3.3" resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.3.3.tgz#90749bde8b89cd41764224f5aac29cd4138f75ff" @@ -1479,14 +1474,14 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz#d0fce5d07b0620caa282b5131c297bb60f9d87e6" integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww== -"@eslint-community/eslint-utils@^4.2.0": +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== dependencies: eslint-visitor-keys "^3.3.0" -"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": version "4.10.0" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== @@ -1506,11 +1501,31 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@eslint/eslintrc@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.1.0.tgz#dbd3482bfd91efa663cbe7aa1f506839868207b6" + integrity sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^10.0.1" + globals "^14.0.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + "@eslint/js@8.57.0": version "8.57.0" resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== +"@eslint/js@9.3.0", "@eslint/js@^9.3.0": + version "9.3.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.3.0.tgz#2e8f65c9c55227abc4845b1513c69c32c679d8fe" + integrity sha512-niBqk8iwv96+yuTwjM6bWg8ovzAPF9qkICsGtcoa5/dmqcEMfdwNAX7+/OHcJHc7wj7XqPxH98oAHytFYlw6Sw== + "@fast-csv/format@4.3.5": version "4.3.5" resolved "https://registry.yarnpkg.com/@fast-csv/format/-/format-4.3.5.tgz#90d83d1b47b6aaf67be70d6118f84f3e12ee1ff3" @@ -1592,16 +1607,30 @@ debug "^4.3.1" minimatch "^3.0.5" +"@humanwhocodes/config-array@^0.13.0": + version "0.13.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" + integrity sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw== + dependencies: + "@humanwhocodes/object-schema" "^2.0.3" + debug "^4.3.1" + minimatch "^3.0.5" + "@humanwhocodes/module-importer@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^2.0.2": +"@humanwhocodes/object-schema@^2.0.2", "@humanwhocodes/object-schema@^2.0.3": version "2.0.3" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== +"@humanwhocodes/retry@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.0.tgz#6d86b8cb322660f03d3f0aa94b99bdd8e172d570" + integrity sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew== + "@isaacs/cliui@^8.0.2": version "8.0.2" resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" @@ -2863,6 +2892,21 @@ dependencies: "@types/node" "*" +"@typescript-eslint/eslint-plugin@7.10.0": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.10.0.tgz#07854a236f107bb45cbf4f62b89474cbea617f50" + integrity sha512-PzCr+a/KAef5ZawX7nbyNwBDtM1HdLIT53aSA2DDlxmxMngZ43O8SIePOeX8H5S+FHXeI6t97mTt/dDdzY4Fyw== + dependencies: + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "7.10.0" + "@typescript-eslint/type-utils" "7.10.0" + "@typescript-eslint/utils" "7.10.0" + "@typescript-eslint/visitor-keys" "7.10.0" + graphemer "^1.4.0" + ignore "^5.3.1" + natural-compare "^1.4.0" + ts-api-utils "^1.3.0" + "@typescript-eslint/eslint-plugin@^5.5.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" @@ -2886,6 +2930,17 @@ dependencies: "@typescript-eslint/utils" "5.62.0" +"@typescript-eslint/parser@7.10.0": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.10.0.tgz#e6ac1cba7bc0400a4459e7eb5b23115bd71accfb" + integrity sha512-2EjZMA0LUW5V5tGQiaa2Gys+nKdfrn2xiTIBLR4fxmPmVSvgPcKNW+AE/ln9k0A4zDUti0J/GZXMDupQoI+e1w== + dependencies: + "@typescript-eslint/scope-manager" "7.10.0" + "@typescript-eslint/types" "7.10.0" + "@typescript-eslint/typescript-estree" "7.10.0" + "@typescript-eslint/visitor-keys" "7.10.0" + debug "^4.3.4" + "@typescript-eslint/parser@^5.5.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" @@ -2904,6 +2959,14 @@ "@typescript-eslint/types" "5.62.0" "@typescript-eslint/visitor-keys" "5.62.0" +"@typescript-eslint/scope-manager@7.10.0": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.10.0.tgz#054a27b1090199337a39cf755f83d9f2ce26546b" + integrity sha512-7L01/K8W/VGl7noe2mgH0K7BE29Sq6KAbVmxurj8GGaPDZXPr8EEQ2seOeAS+mEV9DnzxBQB6ax6qQQ5C6P4xg== + dependencies: + "@typescript-eslint/types" "7.10.0" + "@typescript-eslint/visitor-keys" "7.10.0" + "@typescript-eslint/type-utils@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" @@ -2914,11 +2977,26 @@ debug "^4.3.4" tsutils "^3.21.0" +"@typescript-eslint/type-utils@7.10.0": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.10.0.tgz#8a75accce851d0a331aa9331268ef64e9b300270" + integrity sha512-D7tS4WDkJWrVkuzgm90qYw9RdgBcrWmbbRkrLA4d7Pg3w0ttVGDsvYGV19SH8gPR5L7OtcN5J1hTtyenO9xE9g== + dependencies: + "@typescript-eslint/typescript-estree" "7.10.0" + "@typescript-eslint/utils" "7.10.0" + debug "^4.3.4" + ts-api-utils "^1.3.0" + "@typescript-eslint/types@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== +"@typescript-eslint/types@7.10.0": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.10.0.tgz#da92309c97932a3a033762fd5faa8b067de84e3b" + integrity sha512-7fNj+Ya35aNyhuqrA1E/VayQX9Elwr8NKZ4WueClR3KwJ7Xx9jcCdOrLW04h51de/+gNbyFMs+IDxh5xIwfbNg== + "@typescript-eslint/typescript-estree@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" @@ -2932,6 +3010,20 @@ semver "^7.3.7" tsutils "^3.21.0" +"@typescript-eslint/typescript-estree@7.10.0": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.10.0.tgz#6dcdc5de3149916a6a599fa89dde5c471b88b8bb" + integrity sha512-LXFnQJjL9XIcxeVfqmNj60YhatpRLt6UhdlFwAkjNc6jSUlK8zQOl1oktAP8PlWFzPQC1jny/8Bai3/HPuvN5g== + dependencies: + "@typescript-eslint/types" "7.10.0" + "@typescript-eslint/visitor-keys" "7.10.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^1.3.0" + "@typescript-eslint/utils@5.62.0", "@typescript-eslint/utils@^5.58.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" @@ -2946,6 +3038,16 @@ eslint-scope "^5.1.1" semver "^7.3.7" +"@typescript-eslint/utils@7.10.0": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.10.0.tgz#8ee43e5608c9f439524eaaea8de5b358b15c51b3" + integrity sha512-olzif1Fuo8R8m/qKkzJqT7qwy16CzPRWBvERS0uvyc+DHd8AKbO4Jb7kpAvVzMmZm8TrHnI7hvjN4I05zow+tg== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@typescript-eslint/scope-manager" "7.10.0" + "@typescript-eslint/types" "7.10.0" + "@typescript-eslint/typescript-estree" "7.10.0" + "@typescript-eslint/visitor-keys@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" @@ -2954,6 +3056,14 @@ "@typescript-eslint/types" "5.62.0" eslint-visitor-keys "^3.3.0" +"@typescript-eslint/visitor-keys@7.10.0": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.10.0.tgz#2af2e91e73a75dd6b70b4486c48ae9d38a485a78" + integrity sha512-9ntIVgsi6gg6FIq9xjEO4VQJvwOqA3jaBFQJ/6TK5AvEup2+cECI6Fh7QiBxmfMHXU0V0J4RyPeOU1VDNzl9cg== + dependencies: + "@typescript-eslint/types" "7.10.0" + eslint-visitor-keys "^3.4.3" + "@ungap/structured-clone@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" @@ -3281,7 +3391,7 @@ acorn@^7.1.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.2.4, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: +acorn@^8.11.3, acorn@^8.2.4, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: version "8.11.3" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== @@ -6105,6 +6215,14 @@ eslint-scope@^7.2.2: esrecurse "^4.3.0" estraverse "^5.2.0" +eslint-scope@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.0.1.tgz#a9601e4b81a0b9171657c343fb13111688963cfc" + integrity sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + eslint-visitor-keys@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" @@ -6115,6 +6233,11 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4 resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== +eslint-visitor-keys@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz#e3adc021aa038a2a8e0b2f8b0ce8f66b9483b1fb" + integrity sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw== + eslint-webpack-plugin@^3.1.1: version "3.2.0" resolved "https://registry.yarnpkg.com/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz#1978cdb9edc461e4b0195a20da950cf57988347c" @@ -6170,6 +6293,55 @@ eslint@^8.3.0: strip-ansi "^6.0.1" text-table "^0.2.0" +eslint@^9.3.0: + version "9.3.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.3.0.tgz#36a96db84592618d6ed9074d677e92f4e58c08b9" + integrity sha512-5Iv4CsZW030lpUqHBapdPo3MJetAPtejVW8B84GIcIIv8+ohFaddXsrn1Gn8uD9ijDb+kcYKFUVmC8qG8B2ORQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^3.1.0" + "@eslint/js" "9.3.0" + "@humanwhocodes/config-array" "^0.13.0" + "@humanwhocodes/module-importer" "^1.0.1" + "@humanwhocodes/retry" "^0.3.0" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + escape-string-regexp "^4.0.0" + eslint-scope "^8.0.1" + eslint-visitor-keys "^4.0.0" + espree "^10.0.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^8.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.0.1.tgz#600e60404157412751ba4a6f3a2ee1a42433139f" + integrity sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww== + dependencies: + acorn "^8.11.3" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.0.0" + espree@^9.6.0, espree@^9.6.1: version "9.6.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" @@ -6546,6 +6718,13 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== + dependencies: + flat-cache "^4.0.0" + file-loader@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d" @@ -6656,6 +6835,14 @@ flat-cache@^3.0.4: keyv "^4.5.3" rimraf "^3.0.2" +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.4" + flatted@^3.2.9: version "3.3.1" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" @@ -7037,6 +7224,11 @@ globals@^13.19.0: dependencies: type-fest "^0.20.2" +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== + globalthis@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" @@ -7417,7 +7609,7 @@ iferr@^0.1.5: resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" integrity sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA== -ignore@^5.2.0: +ignore@^5.2.0, ignore@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== @@ -8712,7 +8904,7 @@ jszip@^3.10.1: readable-stream "~2.3.6" setimmediate "^1.0.5" -keyv@^4.5.3: +keyv@^4.5.3, keyv@^4.5.4: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== @@ -9272,7 +9464,7 @@ minimatch@^5.0.1, minimatch@^5.1.0: dependencies: brace-expansion "^2.0.1" -minimatch@^9.0.1: +minimatch@^9.0.1, minimatch@^9.0.4: version "9.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== @@ -9715,16 +9907,16 @@ optionator@^0.8.1: word-wrap "~1.2.3" optionator@^0.9.3: - version "0.9.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" - integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== dependencies: - "@aashutoshrathi/word-wrap" "^1.2.3" deep-is "^0.1.3" fast-levenshtein "^2.0.6" levn "^0.4.1" prelude-ls "^1.2.1" type-check "^0.4.0" + word-wrap "^1.2.5" os-browserify@^0.3.0: version "0.3.0" @@ -11563,6 +11755,11 @@ semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.5.3, semver@^7.5.4: dependencies: lru-cache "^6.0.0" +semver@^7.6.0: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + send@0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" @@ -12585,6 +12782,11 @@ tryer@^1.0.1: resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== +ts-api-utils@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" + integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== + ts-interface-checker@^0.1.9: version "0.1.13" resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" @@ -12732,6 +12934,15 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== +typescript-eslint@^7.10.0: + version "7.10.0" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-7.10.0.tgz#18637c414fcbf6ff1defac05b5c898c303a0ffed" + integrity sha512-thO8nyqptXdfWHQrMJJiJyftpW8aLmwRNs11xA8pSrXneoclFPstQZqXvDWuH1WNL4CHffqHvYUeCHTit6yfhQ== + dependencies: + "@typescript-eslint/eslint-plugin" "7.10.0" + "@typescript-eslint/parser" "7.10.0" + "@typescript-eslint/utils" "7.10.0" + typescript@^4.8.2: version "4.9.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" @@ -13333,7 +13544,7 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -word-wrap@~1.2.3: +word-wrap@^1.2.5, word-wrap@~1.2.3: version "1.2.5" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==