add husky && eslinter
This commit is contained in:
parent
69e677ccaa
commit
d69d27acb4
34
.eslintrc.json
Normal file
34
.eslintrc.json
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"es2021": true
|
||||||
|
},
|
||||||
|
"extends": [],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": "latest",
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"@typescript-eslint",
|
||||||
|
"react"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"indent": [
|
||||||
|
"error",
|
||||||
|
"tab"
|
||||||
|
],
|
||||||
|
"linebreak-style": [
|
||||||
|
"error",
|
||||||
|
"unix"
|
||||||
|
],
|
||||||
|
"quotes": [
|
||||||
|
"error",
|
||||||
|
"double"
|
||||||
|
],
|
||||||
|
"semi": [
|
||||||
|
"error",
|
||||||
|
"never"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
4
.husky/pre-commit
Executable file
4
.husky/pre-commit
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
yarn eslint . --fix
|
@ -3,4 +3,4 @@ module.exports = {
|
|||||||
["@babel/preset-env", { targets: { node: "current" } }],
|
["@babel/preset-env", { targets: { node: "current" } }],
|
||||||
"@babel/preset-typescript",
|
"@babel/preset-typescript",
|
||||||
],
|
],
|
||||||
};
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
const CracoAlias = require("craco-alias");
|
const CracoAlias = require("craco-alias")
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: [
|
plugins: [
|
||||||
@ -14,4 +14,4 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { defineConfig } from "cypress";
|
import { defineConfig } from "cypress"
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
e2e: {
|
e2e: {
|
||||||
@ -8,4 +8,4 @@ export default defineConfig({
|
|||||||
supportFile: false,
|
supportFile: false,
|
||||||
defaultCommandTimeout: 100,
|
defaultCommandTimeout: 100,
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
@ -1,125 +1,125 @@
|
|||||||
describe("Форма Входа", () => {
|
describe("Форма Входа", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.visit("http://localhost:3000");
|
cy.visit("http://localhost:3000")
|
||||||
cy.wait(1000);
|
cy.wait(1000)
|
||||||
cy.contains("Личный кабинет").click();
|
cy.contains("Личный кабинет").click()
|
||||||
});
|
})
|
||||||
|
|
||||||
it("должна успешно входить с правильными учетными данными", () => {
|
it("должна успешно входить с правильными учетными данными", () => {
|
||||||
const login = "valid_user@example.com";
|
const login = "valid_user@example.com"
|
||||||
const password = "valid_password";
|
const password = "valid_password"
|
||||||
|
|
||||||
cy.get("#login").type(login);
|
cy.get("#login").type(login)
|
||||||
cy.get("#password").type(password);
|
cy.get("#password").type(password)
|
||||||
cy.get('button[type="submit"]').click();
|
cy.get("button[type=\"submit\"]").click()
|
||||||
|
|
||||||
cy.wait(2000);
|
cy.wait(2000)
|
||||||
cy.url().should("include", "http://localhost:3000/tariffs");
|
cy.url().should("include", "http://localhost:3000/tariffs")
|
||||||
});
|
})
|
||||||
|
|
||||||
it("должна отображать два сообщение об ошибке при отсутствии полей", () => {
|
it("должна отображать два сообщение об ошибке при отсутствии полей", () => {
|
||||||
cy.get('button[type="submit"]').click();
|
cy.get("button[type=\"submit\"]").click()
|
||||||
|
|
||||||
cy.wait(2000);
|
cy.wait(2000)
|
||||||
cy.get("#password-helper-text").should("contain", "Поле обязательно");
|
cy.get("#password-helper-text").should("contain", "Поле обязательно")
|
||||||
cy.get("#login-helper-text").should("contain", "Поле обязательно");
|
cy.get("#login-helper-text").should("contain", "Поле обязательно")
|
||||||
});
|
})
|
||||||
|
|
||||||
it("должна отображать сообщение об ошибке при отсутствии пароля", () => {
|
it("должна отображать сообщение об ошибке при отсутствии пароля", () => {
|
||||||
cy.get("#login").type("valid_email@example.com");
|
cy.get("#login").type("valid_email@example.com")
|
||||||
cy.get('button[type="submit"]').click();
|
cy.get("button[type=\"submit\"]").click()
|
||||||
|
|
||||||
cy.get("#password-helper-text").should("contain", "Поле обязательно");
|
cy.get("#password-helper-text").should("contain", "Поле обязательно")
|
||||||
});
|
})
|
||||||
|
|
||||||
it("должна отображать сообщение об ошибке при отсутствии Логина", () => {
|
it("должна отображать сообщение об ошибке при отсутствии Логина", () => {
|
||||||
cy.get("#password").type("valid_password");
|
cy.get("#password").type("valid_password")
|
||||||
cy.get('button[type="submit"]').click();
|
cy.get("button[type=\"submit\"]").click()
|
||||||
|
|
||||||
cy.get("#login-helper-text").should("contain", "Поле обязательно");
|
cy.get("#login-helper-text").should("contain", "Поле обязательно")
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe("Форма регистрации", () => {
|
describe("Форма регистрации", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.visit("http://localhost:3000");
|
cy.visit("http://localhost:3000")
|
||||||
cy.wait(1000);
|
cy.wait(1000)
|
||||||
cy.contains("Личный кабинет").click();
|
cy.contains("Личный кабинет").click()
|
||||||
cy.contains("Регистрация").click();
|
cy.contains("Регистрация").click()
|
||||||
});
|
})
|
||||||
|
|
||||||
it("должна регистрировать нового пользователя с правильными данными", () => {
|
it("должна регистрировать нового пользователя с правильными данными", () => {
|
||||||
const login = Cypress._.random(1000) + "@example.com";
|
const login = Cypress._.random(1000) + "@example.com"
|
||||||
const password = "valid_password";
|
const password = "valid_password"
|
||||||
|
|
||||||
cy.get("#login").type(login);
|
cy.get("#login").type(login)
|
||||||
cy.get("#password").type(password);
|
cy.get("#password").type(password)
|
||||||
cy.get("#repeatPassword").type(password);
|
cy.get("#repeatPassword").type(password)
|
||||||
|
|
||||||
cy.get('button[type="submit"]').click();
|
cy.get("button[type=\"submit\"]").click()
|
||||||
cy.wait(5000);
|
cy.wait(5000)
|
||||||
|
|
||||||
cy.url().should("include", "http://localhost:3000/tariffs");
|
cy.url().should("include", "http://localhost:3000/tariffs")
|
||||||
});
|
})
|
||||||
|
|
||||||
it("должна отображать ошибку при отсутсвии логина", () => {
|
it("должна отображать ошибку при отсутсвии логина", () => {
|
||||||
cy.get("#password").type("valid_password");
|
cy.get("#password").type("valid_password")
|
||||||
cy.get("#repeatPassword").type("valid_password");
|
cy.get("#repeatPassword").type("valid_password")
|
||||||
|
|
||||||
cy.get('button[type="submit"]').click();
|
cy.get("button[type=\"submit\"]").click()
|
||||||
|
|
||||||
cy.get("#login-helper-text").should("contain", "Поле обязательно");
|
cy.get("#login-helper-text").should("contain", "Поле обязательно")
|
||||||
});
|
})
|
||||||
|
|
||||||
it("должна отображать ошибку при отсутствии пароля", () => {
|
it("должна отображать ошибку при отсутствии пароля", () => {
|
||||||
cy.get("#login").type("valid_login");
|
cy.get("#login").type("valid_login")
|
||||||
|
|
||||||
cy.get('button[type="submit"]').click();
|
cy.get("button[type=\"submit\"]").click()
|
||||||
|
|
||||||
cy.get("#password-helper-text").should("contain", "Поле обязательно");
|
cy.get("#password-helper-text").should("contain", "Поле обязательно")
|
||||||
});
|
})
|
||||||
|
|
||||||
it("должна отображать ошибку при отсутствии поля Повторения пароля", () => {
|
it("должна отображать ошибку при отсутствии поля Повторения пароля", () => {
|
||||||
cy.get("#login").type("valid_login");
|
cy.get("#login").type("valid_login")
|
||||||
cy.get("#password").type("valid_password");
|
cy.get("#password").type("valid_password")
|
||||||
|
|
||||||
cy.get('button[type="submit"]').click();
|
cy.get("button[type=\"submit\"]").click()
|
||||||
|
|
||||||
cy.get("#repeatPassword-helper-text").should("contain", "Повторите пароль");
|
cy.get("#repeatPassword-helper-text").should("contain", "Повторите пароль")
|
||||||
});
|
})
|
||||||
|
|
||||||
it("должна отображать ошибку при некоректном пароле", () => {
|
it("должна отображать ошибку при некоректном пароле", () => {
|
||||||
cy.get("#login").type("valid_log");
|
cy.get("#login").type("valid_log")
|
||||||
cy.get("#password").type("valid@12_-_@@password");
|
cy.get("#password").type("valid@12_-_@@password")
|
||||||
|
|
||||||
cy.get('button[type="submit"]').click();
|
cy.get("button[type=\"submit\"]").click()
|
||||||
|
|
||||||
cy.get("#password-helper-text").should("contain", "Некорректные символы");
|
cy.get("#password-helper-text").should("contain", "Некорректные символы")
|
||||||
|
|
||||||
cy.get("#repeatPassword-helper-text").should("contain", "Повторите пароль");
|
cy.get("#repeatPassword-helper-text").should("contain", "Повторите пароль")
|
||||||
});
|
})
|
||||||
|
|
||||||
it("должна отображать ошибку при несовпадении паролей", () => {
|
it("должна отображать ошибку при несовпадении паролей", () => {
|
||||||
cy.get("#login").type("valid_login");
|
cy.get("#login").type("valid_login")
|
||||||
cy.get("#password").type("valid_password");
|
cy.get("#password").type("valid_password")
|
||||||
cy.get("#repeatPassword").type("invalidPassword");
|
cy.get("#repeatPassword").type("invalidPassword")
|
||||||
|
|
||||||
cy.get('button[type="submit"]').click();
|
cy.get("button[type=\"submit\"]").click()
|
||||||
|
|
||||||
cy.get("#repeatPassword-helper-text").should("contain", "Пароли не совпадают");
|
cy.get("#repeatPassword-helper-text").should("contain", "Пароли не совпадают")
|
||||||
});
|
})
|
||||||
|
|
||||||
it("попытка отправки запроса при уже зарегистрированном пользователе", () => {
|
it("попытка отправки запроса при уже зарегистрированном пользователе", () => {
|
||||||
const login = "valid_user@example.com";
|
const login = "valid_user@example.com"
|
||||||
const password = "valid_password";
|
const password = "valid_password"
|
||||||
|
|
||||||
cy.get("#login").type(login);
|
cy.get("#login").type(login)
|
||||||
cy.get("#password").type(password);
|
cy.get("#password").type(password)
|
||||||
cy.get("#repeatPassword").type(password);
|
cy.get("#repeatPassword").type(password)
|
||||||
|
|
||||||
cy.get('button[type="submit"]').click();
|
cy.get("button[type=\"submit\"]").click()
|
||||||
|
|
||||||
cy.wait(5000);
|
cy.wait(5000)
|
||||||
cy.contains("user with this login is exist");
|
cy.contains("user with this login is exist")
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
14
package.json
14
package.json
@ -9,7 +9,8 @@
|
|||||||
"test:cart": "craco test src/utils/calcCart --transformIgnorePatterns \"node_modules/(?!@frontend)/\"",
|
"test:cart": "craco test src/utils/calcCart --transformIgnorePatterns \"node_modules/(?!@frontend)/\"",
|
||||||
"eject": "craco eject",
|
"eject": "craco eject",
|
||||||
"test:cypress": "start-server-and-test start http://localhost:3000 cypress",
|
"test:cypress": "start-server-and-test start http://localhost:3000 cypress",
|
||||||
"cypress": "cypress open"
|
"cypress": "cypress open",
|
||||||
|
"prepare": "husky install"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.10.5",
|
"@emotion/react": "^11.10.5",
|
||||||
@ -23,6 +24,7 @@
|
|||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
"cypress": "^12.17.3",
|
"cypress": "^12.17.3",
|
||||||
"formik": "^2.2.9",
|
"formik": "^2.2.9",
|
||||||
|
"husky": "^8.0.3",
|
||||||
"immer": "^10.0.2",
|
"immer": "^10.0.2",
|
||||||
"isomorphic-fetch": "^3.0.0",
|
"isomorphic-fetch": "^3.0.0",
|
||||||
"notistack": "^3.0.1",
|
"notistack": "^3.0.1",
|
||||||
@ -48,17 +50,15 @@
|
|||||||
"@types/react": "^18.0.0",
|
"@types/react": "^18.0.0",
|
||||||
"@types/react-dom": "^18.0.0",
|
"@types/react-dom": "^18.0.0",
|
||||||
"@types/react-slick": "^0.23.10",
|
"@types/react-slick": "^0.23.10",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^6.9.1",
|
||||||
|
"@typescript-eslint/parser": "^6.9.1",
|
||||||
"craco-alias": "^3.0.1",
|
"craco-alias": "^3.0.1",
|
||||||
|
"eslint": "^8.53.0",
|
||||||
|
"eslint-plugin-react": "^7.33.2",
|
||||||
"jest": "^29.5.0",
|
"jest": "^29.5.0",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"typescript": "^4.9.3"
|
"typescript": "^4.9.3"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
|
||||||
"extends": [
|
|
||||||
"react-app",
|
|
||||||
"react-app/jest"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
"production": [
|
"production": [
|
||||||
">0.2%",
|
">0.2%",
|
||||||
|
@ -2,4 +2,4 @@ export const _mocsk_: { name: string; type: "templ" | "squiz" | "reducer" }[] =
|
|||||||
{ name: "Шаблонизатор", type: "templ" },
|
{ name: "Шаблонизатор", type: "templ" },
|
||||||
{ name: "Опросник", type: "squiz" },
|
{ name: "Опросник", type: "squiz" },
|
||||||
{ name: "Сокращатель ссылок", type: "reducer" },
|
{ name: "Сокращатель ссылок", type: "reducer" },
|
||||||
];
|
]
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { showCaseTime } from "./showCaseTime";
|
import { showCaseTime } from "./showCaseTime"
|
||||||
import { showCaseVolume } from "./showCaseVolume";
|
import { showCaseVolume } from "./showCaseVolume"
|
||||||
|
|
||||||
export const showCaseObject: Record<
|
export const showCaseObject: Record<
|
||||||
string,
|
string,
|
||||||
@ -23,4 +23,4 @@ export const showCaseObject: Record<
|
|||||||
templ: { volume: showCaseVolume, time: showCaseTime },
|
templ: { volume: showCaseVolume, time: showCaseTime },
|
||||||
squiz: { volume: showCaseVolume, time: showCaseTime },
|
squiz: { volume: showCaseVolume, time: showCaseTime },
|
||||||
reducer: { volume: showCaseVolume, time: showCaseTime },
|
reducer: { volume: showCaseVolume, time: showCaseTime },
|
||||||
};
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import Infinity from "../assets/Icons/tariffs-time/Infinity.svg";
|
import Infinity from "../assets/Icons/tariffs-time/Infinity.svg"
|
||||||
import OneIcons from "../assets/Icons/tariffs-time/OneIcons.svg";
|
import OneIcons from "../assets/Icons/tariffs-time/OneIcons.svg"
|
||||||
import ThreeIcons from "../assets/Icons/tariffs-time/ThreeIcons.svg";
|
import ThreeIcons from "../assets/Icons/tariffs-time/ThreeIcons.svg"
|
||||||
import SixIcons from "../assets/Icons/tariffs-time/SixIcons.svg";
|
import SixIcons from "../assets/Icons/tariffs-time/SixIcons.svg"
|
||||||
import NineIcons from "../assets/Icons/tariffs-time/NineIcons.svg";
|
import NineIcons from "../assets/Icons/tariffs-time/NineIcons.svg"
|
||||||
|
|
||||||
export const showCaseTime = [
|
export const showCaseTime = [
|
||||||
{
|
{
|
||||||
@ -50,4 +50,4 @@ export const showCaseTime = [
|
|||||||
amount: 10,
|
amount: 10,
|
||||||
price: 1000,
|
price: 1000,
|
||||||
},
|
},
|
||||||
];
|
]
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import OneIcons from "../assets/Icons/tariffs-volume/OneIcons.svg";
|
import OneIcons from "../assets/Icons/tariffs-volume/OneIcons.svg"
|
||||||
import TwoIcons from "../assets/Icons/tariffs-volume/TwoIcons.svg";
|
import TwoIcons from "../assets/Icons/tariffs-volume/TwoIcons.svg"
|
||||||
import ThreeIcons from "../assets/Icons/tariffs-volume/ThreeIcons.svg";
|
import ThreeIcons from "../assets/Icons/tariffs-volume/ThreeIcons.svg"
|
||||||
import FourIcons from "../assets/Icons/tariffs-volume/FourIcons.svg";
|
import FourIcons from "../assets/Icons/tariffs-volume/FourIcons.svg"
|
||||||
import FiveIcons from "../assets/Icons/tariffs-volume/FiveIcons.svg";
|
import FiveIcons from "../assets/Icons/tariffs-volume/FiveIcons.svg"
|
||||||
|
|
||||||
export const showCaseVolume = [
|
export const showCaseVolume = [
|
||||||
{
|
{
|
||||||
@ -50,4 +50,4 @@ export const showCaseVolume = [
|
|||||||
amount: 10,
|
amount: 10,
|
||||||
price: 1000,
|
price: 1000,
|
||||||
},
|
},
|
||||||
];
|
]
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import { makeRequest } from "@frontend/kitui";
|
import { makeRequest } from "@frontend/kitui"
|
||||||
|
|
||||||
import { parseAxiosError } from "@root/utils/parse-error";
|
import { parseAxiosError } from "@root/utils/parse-error"
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
LoginRequest,
|
LoginRequest,
|
||||||
LoginResponse,
|
LoginResponse,
|
||||||
RegisterRequest,
|
RegisterRequest,
|
||||||
RegisterResponse,
|
RegisterResponse,
|
||||||
} from "@frontend/kitui";
|
} from "@frontend/kitui"
|
||||||
|
|
||||||
const apiUrl =
|
const apiUrl =
|
||||||
process.env.NODE_ENV === "production"
|
process.env.NODE_ENV === "production"
|
||||||
? "/auth"
|
? "/auth"
|
||||||
: "https://hub.pena.digital/auth";
|
: "https://hub.pena.digital/auth"
|
||||||
|
|
||||||
export async function register(
|
export async function register(
|
||||||
login: string,
|
login: string,
|
||||||
@ -28,13 +28,13 @@ export async function register(
|
|||||||
body: { login, password, phoneNumber },
|
body: { login, password, phoneNumber },
|
||||||
useToken: false,
|
useToken: false,
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
});
|
})
|
||||||
|
|
||||||
return [registerResponse];
|
return [registerResponse]
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
const [error] = parseAxiosError(nativeError);
|
const [error] = parseAxiosError(nativeError)
|
||||||
|
|
||||||
return [null, `Не удалось зарегестрировать аккаунт. ${error}`];
|
return [null, `Не удалось зарегестрировать аккаунт. ${error}`]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,13 +48,13 @@ export async function login(
|
|||||||
body: { login, password },
|
body: { login, password },
|
||||||
useToken: false,
|
useToken: false,
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
});
|
})
|
||||||
|
|
||||||
return [loginResponse];
|
return [loginResponse]
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
const [error] = parseAxiosError(nativeError);
|
const [error] = parseAxiosError(nativeError)
|
||||||
|
|
||||||
return [null, `Не удалось войти. ${error}`];
|
return [null, `Не удалось войти. ${error}`]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,12 +65,12 @@ export async function logout(): Promise<[unknown, string?]> {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
useToken: true,
|
useToken: true,
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
});
|
})
|
||||||
|
|
||||||
return [logoutResponse];
|
return [logoutResponse]
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
const [error] = parseAxiosError(nativeError);
|
const [error] = parseAxiosError(nativeError)
|
||||||
|
|
||||||
return [null, `Не удалось выйти. ${error}`];
|
return [null, `Не удалось выйти. ${error}`]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { UserAccount, makeRequest } from "@frontend/kitui";
|
import { UserAccount, makeRequest } from "@frontend/kitui"
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from "axios"
|
||||||
|
|
||||||
import { parseAxiosError } from "@root/utils/parse-error";
|
import { parseAxiosError } from "@root/utils/parse-error"
|
||||||
|
|
||||||
const apiUrl =
|
const apiUrl =
|
||||||
process.env.NODE_ENV === "production"
|
process.env.NODE_ENV === "production"
|
||||||
? "/customer"
|
? "/customer"
|
||||||
: "https://hub.pena.digital/customer";
|
: "https://hub.pena.digital/customer"
|
||||||
|
|
||||||
export async function patchCart(
|
export async function patchCart(
|
||||||
tariffId: string
|
tariffId: string
|
||||||
@ -16,14 +16,14 @@ export async function patchCart(
|
|||||||
url: apiUrl + `/cart?id=${tariffId}`,
|
url: apiUrl + `/cart?id=${tariffId}`,
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
useToken: true,
|
useToken: true,
|
||||||
});
|
})
|
||||||
|
|
||||||
return [patchCartResponse.cart];
|
return [patchCartResponse.cart]
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
let [error, status] = parseAxiosError(nativeError);
|
let [error, status] = parseAxiosError(nativeError)
|
||||||
if (status === 400 && error.indexOf("invalid id") !== -1) error = "Данный тариф более недоступен"
|
if (status === 400 && error.indexOf("invalid id") !== -1) error = "Данный тариф более недоступен"
|
||||||
|
|
||||||
return [[], `Не удалось добавить товар в корзину. ${error}`];
|
return [[], `Не удалось добавить товар в корзину. ${error}`]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,13 +35,13 @@ export async function deleteCart(
|
|||||||
url: apiUrl + `/cart?id=${tariffId}`,
|
url: apiUrl + `/cart?id=${tariffId}`,
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
useToken: true,
|
useToken: true,
|
||||||
});
|
})
|
||||||
|
|
||||||
return [deleteCartResponse.cart];
|
return [deleteCartResponse.cart]
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
const [error] = parseAxiosError(nativeError);
|
const [error] = parseAxiosError(nativeError)
|
||||||
|
|
||||||
return [[], `Не удалось удалить товар из корзины. ${error}`];
|
return [[], `Не удалось удалить товар из корзины. ${error}`]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,13 +51,13 @@ export async function payCart(): Promise<[UserAccount | null, string?]> {
|
|||||||
url: apiUrl + "/cart/pay",
|
url: apiUrl + "/cart/pay",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
useToken: true,
|
useToken: true,
|
||||||
});
|
})
|
||||||
|
|
||||||
return [payCartResponse];
|
return [payCartResponse]
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
const [error] = parseAxiosError(nativeError);
|
const [error] = parseAxiosError(nativeError)
|
||||||
|
|
||||||
return [null, `Не удалось оплатить товар из корзины. ${error}`];
|
return [null, `Не удалось оплатить товар из корзины. ${error}`]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,12 +75,12 @@ export async function patchCurrency(
|
|||||||
body: {
|
body: {
|
||||||
currency,
|
currency,
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
return [patchCurrencyResponse];
|
return [patchCurrencyResponse]
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
const [error] = parseAxiosError(nativeError);
|
const [error] = parseAxiosError(nativeError)
|
||||||
|
|
||||||
return [null, `Не удалось изменить валюту. ${error}`];
|
return [null, `Не удалось изменить валюту. ${error}`]
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import { Tariff, makeRequest } from "@frontend/kitui";
|
import { Tariff, makeRequest } from "@frontend/kitui"
|
||||||
import { parseAxiosError } from "@root/utils/parse-error";
|
import { parseAxiosError } from "@root/utils/parse-error"
|
||||||
|
|
||||||
export interface GetHistoryResponse {
|
export interface GetHistoryResponse {
|
||||||
totalPages: number;
|
totalPages: number;
|
||||||
@ -26,12 +26,12 @@ export async function getHistory(): Promise<[GetHistoryResponse | null, string?]
|
|||||||
url: "https://hub.pena.digital/customer/history?page=1&limit=100&type=payCart",
|
url: "https://hub.pena.digital/customer/history?page=1&limit=100&type=payCart",
|
||||||
method: "get",
|
method: "get",
|
||||||
useToken: true,
|
useToken: true,
|
||||||
});
|
})
|
||||||
|
|
||||||
return [historyResponse];
|
return [historyResponse]
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
const [error] = parseAxiosError(nativeError);
|
const [error] = parseAxiosError(nativeError)
|
||||||
|
|
||||||
return [null, `Не удалось получить историю. ${error}`];
|
return [null, `Не удалось получить историю. ${error}`]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { makeRequest } from "@frontend/kitui";
|
import { makeRequest } from "@frontend/kitui"
|
||||||
|
|
||||||
import { parseAxiosError } from "@root/utils/parse-error";
|
import { parseAxiosError } from "@root/utils/parse-error"
|
||||||
|
|
||||||
import type { GetDiscountsResponse } from "@root/model/discount";
|
import type { GetDiscountsResponse } from "@root/model/discount"
|
||||||
|
|
||||||
const apiUrl = process.env.NODE_ENV === "production" ? "/price" : "https://hub.pena.digital/price";
|
const apiUrl = process.env.NODE_ENV === "production" ? "/price" : "https://hub.pena.digital/price"
|
||||||
|
|
||||||
export async function getDiscounts(signal: AbortSignal | undefined): Promise<[GetDiscountsResponse | null, string?]> {
|
export async function getDiscounts(signal: AbortSignal | undefined): Promise<[GetDiscountsResponse | null, string?]> {
|
||||||
try {
|
try {
|
||||||
@ -13,12 +13,12 @@ export async function getDiscounts(signal: AbortSignal | undefined): Promise<[Ge
|
|||||||
method: "get",
|
method: "get",
|
||||||
useToken: true,
|
useToken: true,
|
||||||
signal,
|
signal,
|
||||||
});
|
})
|
||||||
|
|
||||||
return [discountsResponse];
|
return [discountsResponse]
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
const [error] = parseAxiosError(nativeError);
|
const [error] = parseAxiosError(nativeError)
|
||||||
|
|
||||||
return [null, `Ошибка получения списка скидок. ${error}`];
|
return [null, `Ошибка получения списка скидок. ${error}`]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { Tariff, makeRequest } from "@frontend/kitui";
|
import { Tariff, makeRequest } from "@frontend/kitui"
|
||||||
import { CreateTariffBody } from "@root/model/customTariffs";
|
import { CreateTariffBody } from "@root/model/customTariffs"
|
||||||
import { parseAxiosError } from "@root/utils/parse-error";
|
import { parseAxiosError } from "@root/utils/parse-error"
|
||||||
|
|
||||||
import type { ServiceKeyToPrivilegesMap } from "@root/model/privilege";
|
import type { ServiceKeyToPrivilegesMap } from "@root/model/privilege"
|
||||||
import type { GetTariffsResponse } from "@root/model/tariff";
|
import type { GetTariffsResponse } from "@root/model/tariff"
|
||||||
|
|
||||||
const apiUrl = process.env.NODE_ENV === "production" ? "/strator" : "https://hub.pena.digital/strator";
|
const apiUrl = process.env.NODE_ENV === "production" ? "/strator" : "https://hub.pena.digital/strator"
|
||||||
|
|
||||||
export async function getTariffs(
|
export async function getTariffs(
|
||||||
apiPage: number,
|
apiPage: number,
|
||||||
@ -18,13 +18,13 @@ export async function getTariffs(
|
|||||||
method: "get",
|
method: "get",
|
||||||
useToken: true,
|
useToken: true,
|
||||||
signal,
|
signal,
|
||||||
});
|
})
|
||||||
|
|
||||||
return [tariffsResponse];
|
return [tariffsResponse]
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
const [error] = parseAxiosError(nativeError);
|
const [error] = parseAxiosError(nativeError)
|
||||||
|
|
||||||
return [null, `Не удалось получить список тарифов. ${error}`];
|
return [null, `Не удалось получить список тарифов. ${error}`]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,13 +35,13 @@ export async function createTariff(tariff: CreateTariffBody): Promise<[Tariff |
|
|||||||
method: "post",
|
method: "post",
|
||||||
useToken: true,
|
useToken: true,
|
||||||
body: tariff,
|
body: tariff,
|
||||||
});
|
})
|
||||||
|
|
||||||
return [createTariffResponse];
|
return [createTariffResponse]
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
const [error] = parseAxiosError(nativeError);
|
const [error] = parseAxiosError(nativeError)
|
||||||
|
|
||||||
return [null, `Не удалось создать тариф. ${error}`];
|
return [null, `Не удалось создать тариф. ${error}`]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,13 +51,13 @@ export async function getTariffById(tariffId: string): Promise<[Tariff | null, s
|
|||||||
url: `${apiUrl}/tariff/${tariffId}`,
|
url: `${apiUrl}/tariff/${tariffId}`,
|
||||||
method: "get",
|
method: "get",
|
||||||
useToken: true,
|
useToken: true,
|
||||||
});
|
})
|
||||||
|
|
||||||
return [getTariffByIdResponse];
|
return [getTariffByIdResponse]
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
const [error, status] = parseAxiosError(nativeError);
|
const [error, status] = parseAxiosError(nativeError)
|
||||||
|
|
||||||
return [null, `Не удалось получить тарифы. ${error}`, status];
|
return [null, `Не удалось получить тарифы. ${error}`, status]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,12 +70,12 @@ export async function getCustomTariffs(
|
|||||||
signal,
|
signal,
|
||||||
method: "get",
|
method: "get",
|
||||||
useToken: true,
|
useToken: true,
|
||||||
});
|
})
|
||||||
|
|
||||||
return [getCustomTariffsResponse];
|
return [getCustomTariffsResponse]
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
const [error] = parseAxiosError(nativeError);
|
const [error] = parseAxiosError(nativeError)
|
||||||
|
|
||||||
return [null, `Не удалось получить кастомные тарифы. ${error}`];
|
return [null, `Не удалось получить кастомные тарифы. ${error}`]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { makeRequest } from "@frontend/kitui";
|
import { makeRequest } from "@frontend/kitui"
|
||||||
import { parseAxiosError } from "@root/utils/parse-error";
|
import { parseAxiosError } from "@root/utils/parse-error"
|
||||||
|
|
||||||
import { SendTicketMessageRequest } from "@frontend/kitui";
|
import { SendTicketMessageRequest } from "@frontend/kitui"
|
||||||
|
|
||||||
const apiUrl =
|
const apiUrl =
|
||||||
process.env.NODE_ENV === "production"
|
process.env.NODE_ENV === "production"
|
||||||
? "/heruvym"
|
? "/heruvym"
|
||||||
: "https://hub.pena.digital/heruvym";
|
: "https://hub.pena.digital/heruvym"
|
||||||
|
|
||||||
export async function sendTicketMessage(
|
export async function sendTicketMessage(
|
||||||
ticketId: string,
|
ticketId: string,
|
||||||
@ -21,13 +21,13 @@ export async function sendTicketMessage(
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
useToken: true,
|
useToken: true,
|
||||||
body: { ticket: ticketId, message: message, lang: "ru", files: [] },
|
body: { ticket: ticketId, message: message, lang: "ru", files: [] },
|
||||||
});
|
})
|
||||||
|
|
||||||
return [sendTicketMessageResponse];
|
return [sendTicketMessageResponse]
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
const [error] = parseAxiosError(nativeError);
|
const [error] = parseAxiosError(nativeError)
|
||||||
|
|
||||||
return [null, `Не удалось отправить сообщение. ${error}`];
|
return [null, `Не удалось отправить сообщение. ${error}`]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,12 +38,12 @@ export async function shownMessage(id: string): Promise<[null, string?]> {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
useToken: true,
|
useToken: true,
|
||||||
body: { id },
|
body: { id },
|
||||||
});
|
})
|
||||||
|
|
||||||
return [shownMessageResponse];
|
return [shownMessageResponse]
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
const [error] = parseAxiosError(nativeError);
|
const [error] = parseAxiosError(nativeError)
|
||||||
|
|
||||||
return [null, `Не удалось прочесть сообщение. ${error}`];
|
return [null, `Не удалось прочесть сообщение. ${error}`]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { User, makeRequest } from "@frontend/kitui";
|
import { User, makeRequest } from "@frontend/kitui"
|
||||||
import { PatchUserRequest } from "@root/model/user";
|
import { PatchUserRequest } from "@root/model/user"
|
||||||
import { parseAxiosError } from "@root/utils/parse-error";
|
import { parseAxiosError } from "@root/utils/parse-error"
|
||||||
|
|
||||||
const apiUrl =
|
const apiUrl =
|
||||||
process.env.NODE_ENV === "production"
|
process.env.NODE_ENV === "production"
|
||||||
? "/user"
|
? "/user"
|
||||||
: "https://hub.pena.digital/user";
|
: "https://hub.pena.digital/user"
|
||||||
|
|
||||||
export async function patchUser(
|
export async function patchUser(
|
||||||
user: PatchUserRequest
|
user: PatchUserRequest
|
||||||
@ -18,12 +18,12 @@ export async function patchUser(
|
|||||||
useToken: true,
|
useToken: true,
|
||||||
withCredentials: false,
|
withCredentials: false,
|
||||||
body: user,
|
body: user,
|
||||||
});
|
})
|
||||||
|
|
||||||
return [patchUserResponse];
|
return [patchUserResponse]
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
const [error] = parseAxiosError(nativeError);
|
const [error] = parseAxiosError(nativeError)
|
||||||
|
|
||||||
return [null, `Не удалось изменить пользователя. ${error}`];
|
return [null, `Не удалось изменить пользователя. ${error}`]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import { makeRequest } from "@frontend/kitui";
|
import { makeRequest } from "@frontend/kitui"
|
||||||
|
|
||||||
import { jsonToFormdata } from "@root/utils/jsonToFormdata";
|
import { jsonToFormdata } from "@root/utils/jsonToFormdata"
|
||||||
import { parseAxiosError } from "@root/utils/parse-error";
|
import { parseAxiosError } from "@root/utils/parse-error"
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
Verification,
|
Verification,
|
||||||
SendDocumentsArgs,
|
SendDocumentsArgs,
|
||||||
UpdateDocumentsArgs,
|
UpdateDocumentsArgs,
|
||||||
} from "@root/model/auth";
|
} from "@root/model/auth"
|
||||||
|
|
||||||
const apiUrl =
|
const apiUrl =
|
||||||
process.env.NODE_ENV === "production"
|
process.env.NODE_ENV === "production"
|
||||||
? "/verification"
|
? "/verification"
|
||||||
: "https://hub.pena.digital/verification";
|
: "https://hub.pena.digital/verification"
|
||||||
|
|
||||||
export async function verification(
|
export async function verification(
|
||||||
userId: string
|
userId: string
|
||||||
@ -23,13 +23,13 @@ export async function verification(
|
|||||||
method: "GET",
|
method: "GET",
|
||||||
useToken: true,
|
useToken: true,
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
});
|
})
|
||||||
|
|
||||||
return [verificationResponse];
|
return [verificationResponse]
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
const [error] = parseAxiosError(nativeError);
|
const [error] = parseAxiosError(nativeError)
|
||||||
|
|
||||||
return [null, `Ошибка запроса верификации. ${error}`];
|
return [null, `Ошибка запроса верификации. ${error}`]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,13 +43,13 @@ export async function sendDocuments(
|
|||||||
useToken: true,
|
useToken: true,
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
body: jsonToFormdata({ ...documents, egrule: documents.inn }),
|
body: jsonToFormdata({ ...documents, egrule: documents.inn }),
|
||||||
});
|
})
|
||||||
|
|
||||||
return [sendDocumentsResponse];
|
return [sendDocumentsResponse]
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
const [error] = parseAxiosError(nativeError);
|
const [error] = parseAxiosError(nativeError)
|
||||||
|
|
||||||
return [null, `Ошибка отправки документов. ${error}`];
|
return [null, `Ошибка отправки документов. ${error}`]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,12 +65,12 @@ export async function updateDocuments(
|
|||||||
body: jsonToFormdata(
|
body: jsonToFormdata(
|
||||||
documents.inn ? { ...documents, egrule: documents.inn } : documents
|
documents.inn ? { ...documents, egrule: documents.inn } : documents
|
||||||
),
|
),
|
||||||
});
|
})
|
||||||
|
|
||||||
return [updateDocumentsResponse];
|
return [updateDocumentsResponse]
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
const [error] = parseAxiosError(nativeError);
|
const [error] = parseAxiosError(nativeError)
|
||||||
|
|
||||||
return [null, `Ошибка обновления документов. ${error}`];
|
return [null, `Ошибка обновления документов. ${error}`]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { makeRequest } from "@frontend/kitui";
|
import { makeRequest } from "@frontend/kitui"
|
||||||
import { SendPaymentRequest, SendPaymentResponse } from "@root/model/wallet";
|
import { SendPaymentRequest, SendPaymentResponse } from "@root/model/wallet"
|
||||||
import { parseAxiosError } from "@root/utils/parse-error";
|
import { parseAxiosError } from "@root/utils/parse-error"
|
||||||
|
|
||||||
const apiUrl =
|
const apiUrl =
|
||||||
process.env.NODE_ENV === "production"
|
process.env.NODE_ENV === "production"
|
||||||
? "/customer"
|
? "/customer"
|
||||||
: "https://hub.pena.digital/customer";
|
: "https://hub.pena.digital/customer"
|
||||||
|
|
||||||
const testPaymentBody: SendPaymentRequest = {
|
const testPaymentBody: SendPaymentRequest = {
|
||||||
type: "bankCard",
|
type: "bankCard",
|
||||||
@ -21,7 +21,7 @@ const testPaymentBody: SendPaymentRequest = {
|
|||||||
phoneNumber: "79000000000",
|
phoneNumber: "79000000000",
|
||||||
login: "login_test",
|
login: "login_test",
|
||||||
returnUrl: window.location.origin + "/wallet",
|
returnUrl: window.location.origin + "/wallet",
|
||||||
};
|
}
|
||||||
|
|
||||||
export async function sendPayment(
|
export async function sendPayment(
|
||||||
body: SendPaymentRequest = testPaymentBody
|
body: SendPaymentRequest = testPaymentBody
|
||||||
@ -37,12 +37,12 @@ export async function sendPayment(
|
|||||||
useToken: true,
|
useToken: true,
|
||||||
withCredentials: false,
|
withCredentials: false,
|
||||||
body,
|
body,
|
||||||
});
|
})
|
||||||
|
|
||||||
return [sendPaymentResponse];
|
return [sendPaymentResponse]
|
||||||
} catch (nativeError) {
|
} catch (nativeError) {
|
||||||
const [error] = parseAxiosError(nativeError);
|
const [error] = parseAxiosError(nativeError)
|
||||||
|
|
||||||
return [null, `Ошибка оплаты. ${error}`];
|
return [null, `Ошибка оплаты. ${error}`]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Box, SxProps, Theme, Typography, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, SxProps, Theme, Typography, useMediaQuery, useTheme } from "@mui/material"
|
||||||
import { PenaLink } from "@frontend/kitui";
|
import { PenaLink } from "@frontend/kitui"
|
||||||
import { Link as RouterLink } from "react-router-dom";
|
import { Link as RouterLink } from "react-router-dom"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
image?: string;
|
image?: string;
|
||||||
@ -12,7 +12,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function CardWithLink({ image, headerText, text, linkHref, isHighlighted = false, sx }: Props) {
|
export default function CardWithLink({ image, headerText, text, linkHref, isHighlighted = false, sx }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@ -67,5 +67,5 @@ export default function CardWithLink({ image, headerText, text, linkHref, isHigh
|
|||||||
Подробнее
|
Подробнее
|
||||||
</PenaLink>
|
</PenaLink>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material"
|
||||||
import { isDateToday } from "@root/utils/date";
|
import { isDateToday } from "@root/utils/date"
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -10,17 +10,17 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function ChatMessage({ unAuthenticated = false, isSelf, text, createdAt }: Props) {
|
export default function ChatMessage({ unAuthenticated = false, isSelf, text, createdAt }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
|
|
||||||
const messageBackgroundColor = isSelf ? "white" : unAuthenticated ? "#EFF0F5" : theme.palette.gray.main;
|
const messageBackgroundColor = isSelf ? "white" : unAuthenticated ? "#EFF0F5" : theme.palette.gray.main
|
||||||
|
|
||||||
const date = new Date(createdAt);
|
const date = new Date(createdAt)
|
||||||
const time = date.toLocaleString([], {
|
const time = date.toLocaleString([], {
|
||||||
hour: "2-digit",
|
hour: "2-digit",
|
||||||
minute: "2-digit",
|
minute: "2-digit",
|
||||||
...(!isDateToday(date) && { year: "2-digit", month: "2-digit", day: "2-digit" })
|
...(!isDateToday(date) && { year: "2-digit", month: "2-digit", day: "2-digit" })
|
||||||
});
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@ -47,7 +47,7 @@ export default function ChatMessage({ unAuthenticated = false, isSelf, text, cre
|
|||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: messageBackgroundColor,
|
backgroundColor: messageBackgroundColor,
|
||||||
border: unAuthenticated ? `1px solid #E3E3E3` : `1px solid ${theme.palette.gray.main}`,
|
border: unAuthenticated ? "1px solid #E3E3E3" : `1px solid ${theme.palette.gray.main}`,
|
||||||
order: isSelf ? 2 : 1,
|
order: isSelf ? 2 : 1,
|
||||||
p: upMd ? "18px" : "12px",
|
p: upMd ? "18px" : "12px",
|
||||||
borderRadius: "8px",
|
borderRadius: "8px",
|
||||||
@ -86,5 +86,5 @@ export default function ChatMessage({ unAuthenticated = false, isSelf, text, cre
|
|||||||
>{text}</Typography>
|
>{text}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
</Box >
|
</Box >
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { SxProps, Theme, Typography, useTheme } from "@mui/material";
|
import { SxProps, Theme, Typography, useTheme } from "@mui/material"
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -8,7 +8,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function ComplexHeader({ text1, text2, sx }: Props) {
|
export default function ComplexHeader({ text1, text2, sx }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Typography variant="h4" sx={sx}>
|
<Typography variant="h4" sx={sx}>
|
||||||
@ -27,5 +27,5 @@ export default function ComplexHeader({ text1, text2, sx }: Props) {
|
|||||||
</Typography>
|
</Typography>
|
||||||
}
|
}
|
||||||
</Typography>
|
</Typography>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material"
|
||||||
import { useState } from "react";
|
import { useState } from "react"
|
||||||
import ExpandIcon from "./icons/ExpandIcon";
|
import ExpandIcon from "./icons/ExpandIcon"
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
header: ReactNode;
|
header: ReactNode;
|
||||||
@ -13,10 +13,10 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function CustomAccordion({ header, text, divide = false, price, last, first }: Props) {
|
export default function CustomAccordion({ header, text, divide = false, price, last, first }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
const upXs = useMediaQuery(theme.breakpoints.up("xs"));
|
const upXs = useMediaQuery(theme.breakpoints.up("xs"))
|
||||||
const [isExpanded, setIsExpanded] = useState<boolean>(false);
|
const [isExpanded, setIsExpanded] = useState<boolean>(false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@ -99,5 +99,5 @@ export default function CustomAccordion({ header, text, divide = false, price, l
|
|||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Box, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, useMediaQuery, useTheme } from "@mui/material"
|
||||||
import { useState } from "react";
|
import { useState } from "react"
|
||||||
import ExpandIcon from "./icons/ExpandIcon";
|
import ExpandIcon from "./icons/ExpandIcon"
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
header: ReactNode;
|
header: ReactNode;
|
||||||
@ -12,10 +12,10 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function CustomSaveAccordion({ header, divide = false, privilege, last, first }: Props) {
|
export default function CustomSaveAccordion({ header, divide = false, privilege, last, first }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
const upXs = useMediaQuery(theme.breakpoints.up("xs"));
|
const upXs = useMediaQuery(theme.breakpoints.up("xs"))
|
||||||
const [isExpanded, setIsExpanded] = useState<boolean>(false);
|
const [isExpanded, setIsExpanded] = useState<boolean>(false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@ -73,5 +73,5 @@ export default function CustomSaveAccordion({ header, divide = false, privilege,
|
|||||||
</Box>
|
</Box>
|
||||||
{isExpanded && privilege}
|
{isExpanded && privilege}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react"
|
||||||
import { Slider, useTheme } from "@mui/material";
|
import { Slider, useTheme } from "@mui/material"
|
||||||
|
|
||||||
type CustomSliderProps = {
|
type CustomSliderProps = {
|
||||||
value: number;
|
value: number;
|
||||||
@ -14,31 +14,31 @@ export const CustomSlider = ({
|
|||||||
max = 100,
|
max = 100,
|
||||||
onChange,
|
onChange,
|
||||||
}: CustomSliderProps) => {
|
}: CustomSliderProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const [step, setStep] = useState<number>(1);
|
const [step, setStep] = useState<number>(1)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (value < 100) {
|
if (value < 100) {
|
||||||
return setStep(10);
|
return setStep(10)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value < 500) {
|
if (value < 500) {
|
||||||
return setStep(20);
|
return setStep(20)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value < 2000) {
|
if (value < 2000) {
|
||||||
return setStep(50);
|
return setStep(50)
|
||||||
}
|
}
|
||||||
|
|
||||||
setStep(150);
|
setStep(150)
|
||||||
}, [value]);
|
}, [value])
|
||||||
|
|
||||||
const handleChange = ({ type }: Event, newValue: number | number[]) => {
|
const handleChange = ({ type }: Event, newValue: number | number[]) => {
|
||||||
// Для корректной работы слайдера в FireFox
|
// Для корректной работы слайдера в FireFox
|
||||||
if (type !== "change") {
|
if (type !== "change") {
|
||||||
onChange(newValue);
|
onChange(newValue)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Slider
|
<Slider
|
||||||
@ -72,5 +72,5 @@ export const CustomSlider = ({
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Tab } from "@mui/material";
|
import { Tab } from "@mui/material"
|
||||||
import { styled } from "@mui/material";
|
import { styled } from "@mui/material"
|
||||||
|
|
||||||
|
|
||||||
export const CustomTab = styled(Tab)(({ theme }) => ({
|
export const CustomTab = styled(Tab)(({ theme }) => ({
|
||||||
@ -16,4 +16,4 @@ export const CustomTab = styled(Tab)(({ theme }) => ({
|
|||||||
"&:first-of-type": {
|
"&:first-of-type": {
|
||||||
paddingLeft: 0,
|
paddingLeft: 0,
|
||||||
}
|
}
|
||||||
}));
|
}))
|
||||||
|
@ -1,38 +1,38 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react"
|
||||||
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material"
|
||||||
import ExpandIcon from "@components/icons/ExpandIcon";
|
import ExpandIcon from "@components/icons/ExpandIcon"
|
||||||
import { currencyFormatter } from "@root/utils/currencyFormatter";
|
import { currencyFormatter } from "@root/utils/currencyFormatter"
|
||||||
import { removeTariffFromCart } from "@root/stores/user";
|
import { removeTariffFromCart } from "@root/stores/user"
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack"
|
||||||
import {
|
import {
|
||||||
CloseButton,
|
CloseButton,
|
||||||
TariffCartData,
|
TariffCartData,
|
||||||
getMessageFromFetchError,
|
getMessageFromFetchError,
|
||||||
} from "@frontend/kitui";
|
} from "@frontend/kitui"
|
||||||
|
|
||||||
import type { MouseEvent } from "react";
|
import type { MouseEvent } from "react"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
tariffCartData: TariffCartData;
|
tariffCartData: TariffCartData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function CustomTariffAccordion({ tariffCartData }: Props) {
|
export default function CustomTariffAccordion({ tariffCartData }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
const upSm = useMediaQuery(theme.breakpoints.up("sm"));
|
const upSm = useMediaQuery(theme.breakpoints.up("sm"))
|
||||||
const [isExpanded, setIsExpanded] = useState<boolean>(false);
|
const [isExpanded, setIsExpanded] = useState<boolean>(false)
|
||||||
|
|
||||||
function handleDeleteClick(event: MouseEvent<HTMLButtonElement>) {
|
function handleDeleteClick(event: MouseEvent<HTMLButtonElement>) {
|
||||||
event.stopPropagation();
|
event.stopPropagation()
|
||||||
|
|
||||||
removeTariffFromCart(tariffCartData.id)
|
removeTariffFromCart(tariffCartData.id)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
enqueueSnackbar("Тариф удален");
|
enqueueSnackbar("Тариф удален")
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
const message = getMessageFromFetchError(error);
|
const message = getMessageFromFetchError(error)
|
||||||
if (message) enqueueSnackbar(message);
|
if (message) enqueueSnackbar(message)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -155,5 +155,5 @@ export default function CustomTariffAccordion({ tariffCartData }: Props) {
|
|||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react"
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
SvgIcon,
|
SvgIcon,
|
||||||
@ -7,72 +7,72 @@ import {
|
|||||||
CircularProgress,
|
CircularProgress,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material"
|
||||||
|
|
||||||
import ClearIcon from "@mui/icons-material/Clear";
|
import ClearIcon from "@mui/icons-material/Clear"
|
||||||
import { currencyFormatter } from "@root/utils/currencyFormatter";
|
import { currencyFormatter } from "@root/utils/currencyFormatter"
|
||||||
import { useUserStore, removeTariffFromCart, setCart } from "@root/stores/user";
|
import { useUserStore, removeTariffFromCart, setCart } from "@root/stores/user"
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack"
|
||||||
import { ServiceCartData, getMessageFromFetchError } from "@frontend/kitui";
|
import { ServiceCartData, getMessageFromFetchError } from "@frontend/kitui"
|
||||||
import ExpandIcon from "@components/icons/ExpandIcon";
|
import ExpandIcon from "@components/icons/ExpandIcon"
|
||||||
import { deleteCart } from "@root/api/cart";
|
import { deleteCart } from "@root/api/cart"
|
||||||
|
|
||||||
import { ReactComponent as CrossIcon } from "@root/assets/Icons/cross.svg";
|
import { ReactComponent as CrossIcon } from "@root/assets/Icons/cross.svg"
|
||||||
|
|
||||||
import type { MouseEvent } from "react";
|
import type { MouseEvent } from "react"
|
||||||
import CustomTariffAccordion from "@root/components/CustomTariffAccordion";
|
import CustomTariffAccordion from "@root/components/CustomTariffAccordion"
|
||||||
|
|
||||||
const name: Record<string, string> = {
|
const name: Record<string, string> = {
|
||||||
templategen: "Шаблонизатор",
|
templategen: "Шаблонизатор",
|
||||||
squiz: "Опросник",
|
squiz: "Опросник",
|
||||||
reducer: "Скоращатель ссылок",
|
reducer: "Скоращатель ссылок",
|
||||||
custom: "Кастомные тарифы",
|
custom: "Кастомные тарифы",
|
||||||
};
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
serviceData: ServiceCartData;
|
serviceData: ServiceCartData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function CustomWrapperDrawer({ serviceData }: Props) {
|
export default function CustomWrapperDrawer({ serviceData }: Props) {
|
||||||
const [isExpanded, setIsExpanded] = useState<boolean>(false);
|
const [isExpanded, setIsExpanded] = useState<boolean>(false)
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
const [isLoading, setIsLoading] = useState<boolean>(false)
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
const upSm = useMediaQuery(theme.breakpoints.up("sm"));
|
const upSm = useMediaQuery(theme.breakpoints.up("sm"))
|
||||||
const userAccount = useUserStore((state) => state.userAccount);
|
const userAccount = useUserStore((state) => state.userAccount)
|
||||||
|
|
||||||
function handleItemDeleteClick(tariffId: string) {
|
function handleItemDeleteClick(tariffId: string) {
|
||||||
removeTariffFromCart(tariffId)
|
removeTariffFromCart(tariffId)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
enqueueSnackbar("Тариф удален");
|
enqueueSnackbar("Тариф удален")
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
const message = getMessageFromFetchError(error);
|
const message = getMessageFromFetchError(error)
|
||||||
if (message) enqueueSnackbar(message);
|
if (message) enqueueSnackbar(message)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteService = async (event: MouseEvent<HTMLButtonElement>) => {
|
const deleteService = async (event: MouseEvent<HTMLButtonElement>) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation()
|
||||||
let cartItems: string[] = userAccount?.cart || [];
|
let cartItems: string[] = userAccount?.cart || []
|
||||||
const errors: string[] = [];
|
const errors: string[] = []
|
||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true)
|
||||||
|
|
||||||
for (const { id } of serviceData.tariffs) {
|
for (const { id } of serviceData.tariffs) {
|
||||||
const [cartItemsResponse, deleteError] = await deleteCart(id);
|
const [cartItemsResponse, deleteError] = await deleteCart(id)
|
||||||
|
|
||||||
if (deleteError) {
|
if (deleteError) {
|
||||||
errors.push(deleteError);
|
errors.push(deleteError)
|
||||||
} else {
|
} else {
|
||||||
cartItems = cartItemsResponse;
|
cartItems = cartItemsResponse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setCart(cartItems);
|
setCart(cartItems)
|
||||||
setIsLoading(false);
|
setIsLoading(false)
|
||||||
errors.forEach(enqueueSnackbar);
|
errors.forEach(enqueueSnackbar)
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||||||
@ -174,7 +174,7 @@ export default function CustomWrapperDrawer({ serviceData }: Props) {
|
|||||||
</Box>
|
</Box>
|
||||||
{isExpanded &&
|
{isExpanded &&
|
||||||
serviceData.tariffs.map((tariff) => {
|
serviceData.tariffs.map((tariff) => {
|
||||||
const privilege = tariff.privileges[0];
|
const privilege = tariff.privileges[0]
|
||||||
|
|
||||||
return tariff.privileges.length > 1 ? (
|
return tariff.privileges.length > 1 ? (
|
||||||
<CustomTariffAccordion
|
<CustomTariffAccordion
|
||||||
@ -235,10 +235,10 @@ export default function CustomWrapperDrawer({ serviceData }: Props) {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
})}
|
})}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useState, useRef } from "react";
|
import { useState, useRef } from "react"
|
||||||
import {
|
import {
|
||||||
Typography,
|
Typography,
|
||||||
Drawer,
|
Drawer,
|
||||||
@ -8,81 +8,81 @@ import {
|
|||||||
IconButton,
|
IconButton,
|
||||||
Badge,
|
Badge,
|
||||||
Button,
|
Button,
|
||||||
} from "@mui/material";
|
} from "@mui/material"
|
||||||
import SectionWrapper from "./SectionWrapper";
|
import SectionWrapper from "./SectionWrapper"
|
||||||
import CustomWrapperDrawer from "./CustomWrapperDrawer";
|
import CustomWrapperDrawer from "./CustomWrapperDrawer"
|
||||||
import { NotificationsModal } from "./NotificationsModal";
|
import { NotificationsModal } from "./NotificationsModal"
|
||||||
import { Loader } from "./Loader";
|
import { Loader } from "./Loader"
|
||||||
import { useCart } from "@root/utils/hooks/useCart";
|
import { useCart } from "@root/utils/hooks/useCart"
|
||||||
import { currencyFormatter } from "@root/utils/currencyFormatter";
|
import { currencyFormatter } from "@root/utils/currencyFormatter"
|
||||||
import {
|
import {
|
||||||
closeCartDrawer,
|
closeCartDrawer,
|
||||||
openCartDrawer,
|
openCartDrawer,
|
||||||
useCartStore,
|
useCartStore,
|
||||||
} from "@root/stores/cart";
|
} from "@root/stores/cart"
|
||||||
import { setUserAccount, useUserStore } from "@root/stores/user";
|
import { setUserAccount, useUserStore } from "@root/stores/user"
|
||||||
import { useTicketStore } from "@root/stores/tickets";
|
import { useTicketStore } from "@root/stores/tickets"
|
||||||
|
|
||||||
import { ReactComponent as BellIcon } from "@root/assets/Icons/bell.svg";
|
import { ReactComponent as BellIcon } from "@root/assets/Icons/bell.svg"
|
||||||
import { ReactComponent as CartIcon } from "@root/assets/Icons/cart.svg";
|
import { ReactComponent as CartIcon } from "@root/assets/Icons/cart.svg"
|
||||||
import { ReactComponent as CrossIcon } from "@root/assets/Icons/cross.svg";
|
import { ReactComponent as CrossIcon } from "@root/assets/Icons/cross.svg"
|
||||||
|
|
||||||
import { payCart } from "@root/api/cart";
|
import { payCart } from "@root/api/cart"
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack"
|
||||||
import { Link, useNavigate } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom"
|
||||||
import { withErrorBoundary } from "react-error-boundary";
|
import { withErrorBoundary } from "react-error-boundary"
|
||||||
import { handleComponentError } from "@root/utils/handleComponentError";
|
import { handleComponentError } from "@root/utils/handleComponentError"
|
||||||
import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';
|
import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline"
|
||||||
|
|
||||||
function Drawers() {
|
function Drawers() {
|
||||||
const [openNotificationsModal, setOpenNotificationsModal] =
|
const [openNotificationsModal, setOpenNotificationsModal] =
|
||||||
useState<boolean>(false);
|
useState<boolean>(false)
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState<boolean>(false)
|
||||||
const bellRef = useRef<HTMLButtonElement | null>(null);
|
const bellRef = useRef<HTMLButtonElement | null>(null)
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate()
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
const isTablet = useMediaQuery(theme.breakpoints.down(1000))
|
||||||
const isDrawerOpen = useCartStore((state) => state.isDrawerOpen);
|
const isDrawerOpen = useCartStore((state) => state.isDrawerOpen)
|
||||||
const cart = useCart();
|
const cart = useCart()
|
||||||
const userAccount = useUserStore((state) => state.userAccount);
|
const userAccount = useUserStore((state) => state.userAccount)
|
||||||
const tickets = useTicketStore((state) => state.tickets);
|
const tickets = useTicketStore((state) => state.tickets)
|
||||||
const [notEnoughMoneyAmount, setNotEnoughMoneyAmount] = useState<number>(0);
|
const [notEnoughMoneyAmount, setNotEnoughMoneyAmount] = useState<number>(0)
|
||||||
|
|
||||||
const notificationsCount = tickets.filter(
|
const notificationsCount = tickets.filter(
|
||||||
({ user, top_message }) =>
|
({ user, top_message }) =>
|
||||||
user !== top_message.user_id && top_message.shown.me !== 1
|
user !== top_message.user_id && top_message.shown.me !== 1
|
||||||
).length;
|
).length
|
||||||
|
|
||||||
async function handlePayClick() {
|
async function handlePayClick() {
|
||||||
setLoading(true);
|
setLoading(true)
|
||||||
|
|
||||||
const [payCartResponse, payCartError] = await payCart();
|
const [payCartResponse, payCartError] = await payCart()
|
||||||
|
|
||||||
if (payCartError) {
|
if (payCartError) {
|
||||||
if (payCartError.includes("insufficient funds: ")) {
|
if (payCartError.includes("insufficient funds: ")) {
|
||||||
const notEnoughMoneyAmount = parseInt(
|
const notEnoughMoneyAmount = parseInt(
|
||||||
payCartError.replace(/^.*insufficient\sfunds:\s(?=\d+$)/, "")
|
payCartError.replace(/^.*insufficient\sfunds:\s(?=\d+$)/, "")
|
||||||
);
|
)
|
||||||
|
|
||||||
setNotEnoughMoneyAmount(notEnoughMoneyAmount);
|
setNotEnoughMoneyAmount(notEnoughMoneyAmount)
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(false);
|
setLoading(false)
|
||||||
|
|
||||||
return enqueueSnackbar(payCartError);
|
return enqueueSnackbar(payCartError)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payCartResponse) {
|
if (payCartResponse) {
|
||||||
setUserAccount(payCartResponse);
|
setUserAccount(payCartResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(false);
|
setLoading(false)
|
||||||
closeCartDrawer();
|
closeCartDrawer()
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleReplenishWallet() {
|
function handleReplenishWallet() {
|
||||||
navigate("/payment", { state: { notEnoughMoneyAmount } });
|
navigate("/payment", { state: { notEnoughMoneyAmount } })
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -325,7 +325,7 @@ function Drawers() {
|
|||||||
</SectionWrapper>
|
</SectionWrapper>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withErrorBoundary(Drawers, {
|
export default withErrorBoundary(Drawers, {
|
||||||
|
@ -9,8 +9,8 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material"
|
||||||
import { TicketMessage } from "@frontend/kitui";
|
import { TicketMessage } from "@frontend/kitui"
|
||||||
import {
|
import {
|
||||||
addOrUpdateUnauthMessages,
|
addOrUpdateUnauthMessages,
|
||||||
useUnauthTicketStore,
|
useUnauthTicketStore,
|
||||||
@ -19,47 +19,47 @@ import {
|
|||||||
setUnauthSessionData,
|
setUnauthSessionData,
|
||||||
setIsMessageSending,
|
setIsMessageSending,
|
||||||
setUnauthTicketMessageFetchState,
|
setUnauthTicketMessageFetchState,
|
||||||
} from "@root/stores/unauthTicket";
|
} from "@root/stores/unauthTicket"
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack"
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
||||||
import ChatMessage from "../ChatMessage";
|
import ChatMessage from "../ChatMessage"
|
||||||
import SendIcon from "../icons/SendIcon";
|
import SendIcon from "../icons/SendIcon"
|
||||||
import UserCircleIcon from "./UserCircleIcon";
|
import UserCircleIcon from "./UserCircleIcon"
|
||||||
import { throttle } from "@frontend/kitui";
|
import { throttle } from "@frontend/kitui"
|
||||||
import {
|
import {
|
||||||
useTicketMessages,
|
useTicketMessages,
|
||||||
getMessageFromFetchError,
|
getMessageFromFetchError,
|
||||||
useSSESubscription,
|
useSSESubscription,
|
||||||
useEventListener,
|
useEventListener,
|
||||||
createTicket,
|
createTicket,
|
||||||
} from "@frontend/kitui";
|
} from "@frontend/kitui"
|
||||||
import { sendTicketMessage } from "@root/api/ticket";
|
import { sendTicketMessage } from "@root/api/ticket"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
sx?: SxProps<Theme>;
|
sx?: SxProps<Theme>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Chat({ sx }: Props) {
|
export default function Chat({ sx }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
const [messageField, setMessageField] = useState<string>("");
|
const [messageField, setMessageField] = useState<string>("")
|
||||||
const sessionData = useUnauthTicketStore((state) => state.sessionData);
|
const sessionData = useUnauthTicketStore((state) => state.sessionData)
|
||||||
const messages = useUnauthTicketStore((state) => state.messages);
|
const messages = useUnauthTicketStore((state) => state.messages)
|
||||||
const messageApiPage = useUnauthTicketStore((state) => state.apiPage);
|
const messageApiPage = useUnauthTicketStore((state) => state.apiPage)
|
||||||
const messagesPerPage = useUnauthTicketStore(
|
const messagesPerPage = useUnauthTicketStore(
|
||||||
(state) => state.messagesPerPage
|
(state) => state.messagesPerPage
|
||||||
);
|
)
|
||||||
const isMessageSending = useUnauthTicketStore(
|
const isMessageSending = useUnauthTicketStore(
|
||||||
(state) => state.isMessageSending
|
(state) => state.isMessageSending
|
||||||
);
|
)
|
||||||
const isPreventAutoscroll = useUnauthTicketStore(
|
const isPreventAutoscroll = useUnauthTicketStore(
|
||||||
(state) => state.isPreventAutoscroll
|
(state) => state.isPreventAutoscroll
|
||||||
);
|
)
|
||||||
const lastMessageId = useUnauthTicketStore((state) => state.lastMessageId);
|
const lastMessageId = useUnauthTicketStore((state) => state.lastMessageId)
|
||||||
const fetchState = useUnauthTicketStore(
|
const fetchState = useUnauthTicketStore(
|
||||||
(state) => state.unauthTicketMessageFetchState
|
(state) => state.unauthTicketMessageFetchState
|
||||||
);
|
)
|
||||||
const chatBoxRef = useRef<HTMLDivElement>(null);
|
const chatBoxRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
useTicketMessages({
|
useTicketMessages({
|
||||||
url: "https://hub.pena.digital/heruvym/getMessages",
|
url: "https://hub.pena.digital/heruvym/getMessages",
|
||||||
@ -69,67 +69,67 @@ export default function Chat({ sx }: Props) {
|
|||||||
messageApiPage,
|
messageApiPage,
|
||||||
onSuccess: useCallback((messages) => {
|
onSuccess: useCallback((messages) => {
|
||||||
if (chatBoxRef.current && chatBoxRef.current.scrollTop < 1)
|
if (chatBoxRef.current && chatBoxRef.current.scrollTop < 1)
|
||||||
chatBoxRef.current.scrollTop = 1;
|
chatBoxRef.current.scrollTop = 1
|
||||||
addOrUpdateUnauthMessages(messages);
|
addOrUpdateUnauthMessages(messages)
|
||||||
}, []),
|
}, []),
|
||||||
onError: useCallback((error: Error) => {
|
onError: useCallback((error: Error) => {
|
||||||
const message = getMessageFromFetchError(error);
|
const message = getMessageFromFetchError(error)
|
||||||
if (message) enqueueSnackbar(message);
|
if (message) enqueueSnackbar(message)
|
||||||
}, []),
|
}, []),
|
||||||
onFetchStateChange: setUnauthTicketMessageFetchState,
|
onFetchStateChange: setUnauthTicketMessageFetchState,
|
||||||
});
|
})
|
||||||
|
|
||||||
useSSESubscription<TicketMessage>({
|
useSSESubscription<TicketMessage>({
|
||||||
enabled: Boolean(sessionData),
|
enabled: Boolean(sessionData),
|
||||||
url: `https://hub.pena.digital/heruvym/ticket?ticket=${sessionData?.ticketId}&s=${sessionData?.sessionId}`,
|
url: `https://hub.pena.digital/heruvym/ticket?ticket=${sessionData?.ticketId}&s=${sessionData?.sessionId}`,
|
||||||
onNewData: addOrUpdateUnauthMessages,
|
onNewData: addOrUpdateUnauthMessages,
|
||||||
onDisconnect: useCallback(() => {
|
onDisconnect: useCallback(() => {
|
||||||
setUnauthIsPreventAutoscroll(false);
|
setUnauthIsPreventAutoscroll(false)
|
||||||
}, []),
|
}, []),
|
||||||
marker: "ticket",
|
marker: "ticket",
|
||||||
});
|
})
|
||||||
|
|
||||||
const throttledScrollHandler = useMemo(
|
const throttledScrollHandler = useMemo(
|
||||||
() =>
|
() =>
|
||||||
throttle(() => {
|
throttle(() => {
|
||||||
const chatBox = chatBoxRef.current;
|
const chatBox = chatBoxRef.current
|
||||||
if (!chatBox) return;
|
if (!chatBox) return
|
||||||
|
|
||||||
const scrollBottom =
|
const scrollBottom =
|
||||||
chatBox.scrollHeight - chatBox.scrollTop - chatBox.clientHeight;
|
chatBox.scrollHeight - chatBox.scrollTop - chatBox.clientHeight
|
||||||
const isPreventAutoscroll = scrollBottom > chatBox.clientHeight;
|
const isPreventAutoscroll = scrollBottom > chatBox.clientHeight
|
||||||
setUnauthIsPreventAutoscroll(isPreventAutoscroll);
|
setUnauthIsPreventAutoscroll(isPreventAutoscroll)
|
||||||
|
|
||||||
if (fetchState !== "idle") return;
|
if (fetchState !== "idle") return
|
||||||
|
|
||||||
if (chatBox.scrollTop < chatBox.clientHeight) {
|
if (chatBox.scrollTop < chatBox.clientHeight) {
|
||||||
incrementUnauthMessageApiPage();
|
incrementUnauthMessageApiPage()
|
||||||
}
|
}
|
||||||
}, 200),
|
}, 200),
|
||||||
[fetchState]
|
[fetchState]
|
||||||
);
|
)
|
||||||
|
|
||||||
useEventListener("scroll", throttledScrollHandler, chatBoxRef);
|
useEventListener("scroll", throttledScrollHandler, chatBoxRef)
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
function scrollOnNewMessage() {
|
function scrollOnNewMessage() {
|
||||||
if (!chatBoxRef.current) return;
|
if (!chatBoxRef.current) return
|
||||||
|
|
||||||
if (!isPreventAutoscroll) {
|
if (!isPreventAutoscroll) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
scrollToBottom();
|
scrollToBottom()
|
||||||
}, 50);
|
}, 50)
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
},
|
},
|
||||||
[lastMessageId]
|
[lastMessageId]
|
||||||
);
|
)
|
||||||
|
|
||||||
async function handleSendMessage() {
|
async function handleSendMessage() {
|
||||||
if (!messageField || isMessageSending) return;
|
if (!messageField || isMessageSending) return
|
||||||
|
|
||||||
if (!sessionData) {
|
if (!sessionData) {
|
||||||
setIsMessageSending(true);
|
setIsMessageSending(true)
|
||||||
createTicket({
|
createTicket({
|
||||||
url: "https://hub.pena.digital/heruvym/create",
|
url: "https://hub.pena.digital/heruvym/create",
|
||||||
body: {
|
body: {
|
||||||
@ -142,52 +142,52 @@ export default function Chat({ sx }: Props) {
|
|||||||
setUnauthSessionData({
|
setUnauthSessionData({
|
||||||
ticketId: response.Ticket,
|
ticketId: response.Ticket,
|
||||||
sessionId: response.sess,
|
sessionId: response.sess,
|
||||||
});
|
})
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
const errorMessage = getMessageFromFetchError(error);
|
const errorMessage = getMessageFromFetchError(error)
|
||||||
if (errorMessage) enqueueSnackbar(errorMessage);
|
if (errorMessage) enqueueSnackbar(errorMessage)
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setMessageField("");
|
setMessageField("")
|
||||||
setIsMessageSending(false);
|
setIsMessageSending(false)
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
setIsMessageSending(true);
|
setIsMessageSending(true)
|
||||||
|
|
||||||
const [_, sendTicketMessageError] = await sendTicketMessage(
|
const [_, sendTicketMessageError] = await sendTicketMessage(
|
||||||
sessionData.ticketId,
|
sessionData.ticketId,
|
||||||
messageField
|
messageField
|
||||||
);
|
)
|
||||||
|
|
||||||
if (sendTicketMessageError) {
|
if (sendTicketMessageError) {
|
||||||
enqueueSnackbar(sendTicketMessageError);
|
enqueueSnackbar(sendTicketMessageError)
|
||||||
}
|
}
|
||||||
|
|
||||||
setMessageField("");
|
setMessageField("")
|
||||||
setIsMessageSending(false);
|
setIsMessageSending(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function scrollToBottom(behavior?: ScrollBehavior) {
|
function scrollToBottom(behavior?: ScrollBehavior) {
|
||||||
if (!chatBoxRef.current) return;
|
if (!chatBoxRef.current) return
|
||||||
|
|
||||||
const chatBox = chatBoxRef.current;
|
const chatBox = chatBoxRef.current
|
||||||
chatBox.scroll({
|
chatBox.scroll({
|
||||||
left: 0,
|
left: 0,
|
||||||
top: chatBox.scrollHeight,
|
top: chatBox.scrollHeight,
|
||||||
behavior,
|
behavior,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleTextfieldKeyPress: React.KeyboardEventHandler<
|
const handleTextfieldKeyPress: React.KeyboardEventHandler<
|
||||||
HTMLInputElement | HTMLTextAreaElement
|
HTMLInputElement | HTMLTextAreaElement
|
||||||
> = (e) => {
|
> = (e) => {
|
||||||
if (e.key === "Enter" && !e.shiftKey) {
|
if (e.key === "Enter" && !e.shiftKey) {
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
handleSendMessage();
|
handleSendMessage()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@ -315,5 +315,5 @@ export default function Chat({ sx }: Props) {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Box } from "@mui/material";
|
import { Box } from "@mui/material"
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -22,5 +22,5 @@ export default function CircleDoubleDown({ isUp = false }: Props) {
|
|||||||
<path d="M12.9004 14L16.9004 10L20.9004 14" stroke="#252734" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
<path d="M12.9004 14L16.9004 10L20.9004 14" stroke="#252734" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
</svg>
|
</svg>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
@ -1,10 +1,10 @@
|
|||||||
import { Box, Fab, Typography } from "@mui/material";
|
import { Box, Fab, Typography } from "@mui/material"
|
||||||
import { useState } from "react";
|
import { useState } from "react"
|
||||||
import CircleDoubleDown from "./CircleDoubleDownIcon";
|
import CircleDoubleDown from "./CircleDoubleDownIcon"
|
||||||
import Chat from "./Chat";
|
import Chat from "./Chat"
|
||||||
|
|
||||||
export default function FloatingSupportChat() {
|
export default function FloatingSupportChat() {
|
||||||
const [isChatOpened, setIsChatOpened] = useState<boolean>(false);
|
const [isChatOpened, setIsChatOpened] = useState<boolean>(false)
|
||||||
|
|
||||||
const animation = {
|
const animation = {
|
||||||
"@keyframes runningStripe": {
|
"@keyframes runningStripe": {
|
||||||
@ -29,7 +29,7 @@ export default function FloatingSupportChat() {
|
|||||||
left: "100%",
|
left: "100%",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -90,5 +90,5 @@ export default function FloatingSupportChat() {
|
|||||||
{!isChatOpened && <Typography sx={{ zIndex: "10000" }}>Задайте нам вопрос</Typography>}
|
{!isChatOpened && <Typography sx={{ zIndex: "10000" }}>Задайте нам вопрос</Typography>}
|
||||||
</Fab>
|
</Fab>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Box } from "@mui/material";
|
import { Box } from "@mui/material"
|
||||||
|
|
||||||
|
|
||||||
export default function UserCircleIcon() {
|
export default function UserCircleIcon() {
|
||||||
@ -18,5 +18,5 @@ export default function UserCircleIcon() {
|
|||||||
<path d="M7.97461 25.425C8.727 23.943 9.87506 22.6983 11.2915 21.8289C12.708 20.9595 14.3376 20.4992 15.9996 20.4992C17.6616 20.4992 19.2912 20.9595 20.7077 21.8289C22.1242 22.6983 23.2722 23.943 24.0246 25.425" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
<path d="M7.97461 25.425C8.727 23.943 9.87506 22.6983 11.2915 21.8289C12.708 20.9595 14.3376 20.4992 15.9996 20.4992C17.6616 20.4992 19.2912 20.9595 20.7077 21.8289C22.1242 22.6983 23.2722 23.943 24.0246 25.425" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
</svg>
|
</svg>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
@ -1,11 +1,11 @@
|
|||||||
import { Box, Button, Divider, Typography, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, Button, Divider, Typography, useMediaQuery, useTheme } from "@mui/material"
|
||||||
import PenaLogo from "./PenaLogo";
|
import PenaLogo from "./PenaLogo"
|
||||||
import SectionWrapper from "./SectionWrapper";
|
import SectionWrapper from "./SectionWrapper"
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom"
|
||||||
|
|
||||||
export default function Footer() {
|
export default function Footer() {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionWrapper
|
<SectionWrapper
|
||||||
@ -81,5 +81,5 @@ export default function Footer() {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
</SectionWrapper>
|
</SectionWrapper>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,9 @@ import {
|
|||||||
Theme,
|
Theme,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material"
|
||||||
|
|
||||||
import "./text-input.css";
|
import "./text-input.css"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
id: string;
|
id: string;
|
||||||
@ -32,16 +32,16 @@ export default function InputTextfield({
|
|||||||
color,
|
color,
|
||||||
FormInputSx,
|
FormInputSx,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
|
|
||||||
const labelFont = upMd
|
const labelFont = upMd
|
||||||
? bold
|
? bold
|
||||||
? theme.typography.p1
|
? theme.typography.p1
|
||||||
: { ...theme.typography.body1, fontWeight: 500 }
|
: { ...theme.typography.body1, fontWeight: 500 }
|
||||||
: theme.typography.body2;
|
: theme.typography.body2
|
||||||
|
|
||||||
const placeholderFont = upMd ? undefined : { fontWeight: 400, fontSize: "16px", lineHeight: "19px" };
|
const placeholderFont = upMd ? undefined : { fontWeight: 400, fontSize: "16px", lineHeight: "19px" }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormControl
|
<FormControl
|
||||||
@ -92,5 +92,5 @@ export default function InputTextfield({
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { Box, useTheme } from "@mui/material";
|
import { Box, useTheme } from "@mui/material"
|
||||||
|
|
||||||
type LoaderProps = {
|
type LoaderProps = {
|
||||||
size?: number;
|
size?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Loader = ({ size = 40 }: LoaderProps) => {
|
export const Loader = ({ size = 40 }: LoaderProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@ -26,5 +26,5 @@ export const Loader = ({ size = 40 }: LoaderProps) => {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react"
|
||||||
import { Box, Typography, useTheme } from "@mui/material";
|
import { Box, Typography, useTheme } from "@mui/material"
|
||||||
import { Link, useLocation } from "react-router-dom";
|
import { Link, useLocation } from "react-router-dom"
|
||||||
|
|
||||||
type MenuItem = {
|
type MenuItem = {
|
||||||
name: string;
|
name: string;
|
||||||
@ -9,11 +9,11 @@ type MenuItem = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function Menu() {
|
export default function Menu() {
|
||||||
const [activeSubMenu, setActiveSubMenu] = useState<MenuItem[]>([]);
|
const [activeSubMenu, setActiveSubMenu] = useState<MenuItem[]>([])
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const location = useLocation();
|
const location = useLocation()
|
||||||
|
|
||||||
const color = location.pathname === "/" ? "white" : "black";
|
const color = location.pathname === "/" ? "white" : "black"
|
||||||
|
|
||||||
const arrayMenu: MenuItem[] = location.pathname === "/" ? [
|
const arrayMenu: MenuItem[] = location.pathname === "/" ? [
|
||||||
{ name: "Наши продукты", url: "/faq" },
|
{ name: "Наши продукты", url: "/faq" },
|
||||||
@ -32,7 +32,7 @@ export default function Menu() {
|
|||||||
{ name: "Корзина", url: "/cart" },
|
{ name: "Корзина", url: "/cart" },
|
||||||
{ name: "Поддержка", url: "/support" },
|
{ name: "Поддержка", url: "/support" },
|
||||||
{ name: "История", url: "/history" },
|
{ name: "История", url: "/history" },
|
||||||
];
|
]
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -127,5 +127,5 @@ export default function Menu() {
|
|||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { Avatar, Box, IconButton, SxProps, Theme, Typography, useTheme } from "@mui/material";
|
import { Avatar, Box, IconButton, SxProps, Theme, Typography, useTheme } from "@mui/material"
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom"
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
sx?: SxProps<Theme>;
|
sx?: SxProps<Theme>;
|
||||||
}
|
}
|
||||||
export default function CustomAvatar({ sx }: Props) {
|
export default function CustomAvatar({ sx }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -38,5 +38,5 @@ export default function CustomAvatar({ sx }: Props) {
|
|||||||
</Box>
|
</Box>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
);
|
)
|
||||||
}
|
}
|
@ -1,18 +1,18 @@
|
|||||||
import { TransitionProps } from "@mui/material/transitions";
|
import { TransitionProps } from "@mui/material/transitions"
|
||||||
import logotip from "../../assets/Icons/logoPenaHab.svg";
|
import logotip from "../../assets/Icons/logoPenaHab.svg"
|
||||||
import logotipBlack from "../../assets/Icons/black_logo_PenaHab.svg";
|
import logotipBlack from "../../assets/Icons/black_logo_PenaHab.svg"
|
||||||
import CustomAvatar from "./Avatar";
|
import CustomAvatar from "./Avatar"
|
||||||
import React from "react";
|
import React from "react"
|
||||||
import { Box, Button, Dialog, List, ListItem, Slide, Typography, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, Button, Dialog, List, ListItem, Slide, Typography, useMediaQuery, useTheme } from "@mui/material"
|
||||||
import { Link, useLocation } from "react-router-dom";
|
import { Link, useLocation } from "react-router-dom"
|
||||||
import { useUserStore } from "@root/stores/user";
|
import { useUserStore } from "@root/stores/user"
|
||||||
import { currencyFormatter } from "@root/utils/currencyFormatter";
|
import { currencyFormatter } from "@root/utils/currencyFormatter"
|
||||||
|
|
||||||
|
|
||||||
const arrayMenu = [
|
const arrayMenu = [
|
||||||
{ name: "Наши продукты", url: "/" },
|
{ name: "Наши продукты", url: "/" },
|
||||||
{ name: "Наши услуги", url: "/" },
|
{ name: "Наши услуги", url: "/" },
|
||||||
];
|
]
|
||||||
|
|
||||||
const Transition = React.forwardRef(function Transition(
|
const Transition = React.forwardRef(function Transition(
|
||||||
props: TransitionProps & {
|
props: TransitionProps & {
|
||||||
@ -21,8 +21,8 @@ const Transition = React.forwardRef(function Transition(
|
|||||||
|
|
||||||
ref: React.Ref<null>
|
ref: React.Ref<null>
|
||||||
) {
|
) {
|
||||||
return <Slide direction={"left"} ref={ref} {...props} />;
|
return <Slide direction={"left"} ref={ref} {...props} />
|
||||||
});
|
})
|
||||||
|
|
||||||
interface DialogMenuProps {
|
interface DialogMenuProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@ -30,13 +30,13 @@ interface DialogMenuProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function DialogMenu({ open, handleClose }: DialogMenuProps) {
|
export default function DialogMenu({ open, handleClose }: DialogMenuProps) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const location = useLocation();
|
const location = useLocation()
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(900));
|
const isTablet = useMediaQuery(theme.breakpoints.down(900))
|
||||||
const user = useUserStore((state) => state.user);
|
const user = useUserStore((state) => state.user)
|
||||||
const cash = useUserStore((state) => state.userAccount?.wallet.cash) ?? 0;
|
const cash = useUserStore((state) => state.userAccount?.wallet.cash) ?? 0
|
||||||
|
|
||||||
const isMobileHeight = useMediaQuery("(max-height: 400px)");
|
const isMobileHeight = useMediaQuery("(max-height: 400px)")
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
@ -154,5 +154,5 @@ export default function DialogMenu({ open, handleClose }: DialogMenuProps) {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useMediaQuery, useTheme } from "@mui/material";
|
import { useMediaQuery, useTheme } from "@mui/material"
|
||||||
import NavbarCollapsed from "./NavbarCollapsed";
|
import NavbarCollapsed from "./NavbarCollapsed"
|
||||||
import NavbarFull from "./NavbarFull";
|
import NavbarFull from "./NavbarFull"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isCollapsed?: boolean;
|
isCollapsed?: boolean;
|
||||||
@ -8,8 +8,8 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Navbar({ isLoggedIn, isCollapsed = false }: Props) {
|
export default function Navbar({ isLoggedIn, isCollapsed = false }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
|
|
||||||
return upMd ? <NavbarFull isLoggedIn={isLoggedIn} /> : <NavbarCollapsed isLoggedIn={isLoggedIn} />;
|
return upMd ? <NavbarFull isLoggedIn={isLoggedIn} /> : <NavbarCollapsed isLoggedIn={isLoggedIn} />
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react"
|
||||||
import { Button, useMediaQuery, useTheme } from "@mui/material";
|
import { Button, useMediaQuery, useTheme } from "@mui/material"
|
||||||
import { Link, useLocation } from "react-router-dom";
|
import { Link, useLocation } from "react-router-dom"
|
||||||
|
|
||||||
import SectionWrapper from "../SectionWrapper";
|
import SectionWrapper from "../SectionWrapper"
|
||||||
|
|
||||||
import PenaLogo from "../PenaLogo";
|
import PenaLogo from "../PenaLogo"
|
||||||
import DialogMenu from "./DialogMenu";
|
import DialogMenu from "./DialogMenu"
|
||||||
import { BurgerButton } from "@frontend/kitui";
|
import { BurgerButton } from "@frontend/kitui"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isLoggedIn: boolean;
|
isLoggedIn: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NavbarCollapsed({ isLoggedIn }: Props) {
|
export default function NavbarCollapsed({ isLoggedIn }: Props) {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false)
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionWrapper
|
<SectionWrapper
|
||||||
@ -42,5 +42,5 @@ export default function NavbarCollapsed({ isLoggedIn }: Props) {
|
|||||||
<BurgerButton onClick={() => setOpen(!open)} sx={{ color: "white" }} />
|
<BurgerButton onClick={() => setOpen(!open)} sx={{ color: "white" }} />
|
||||||
<DialogMenu open={open} handleClose={() => setOpen(false)} />
|
<DialogMenu open={open} handleClose={() => setOpen(false)} />
|
||||||
</SectionWrapper>
|
</SectionWrapper>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react"
|
||||||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
import { Link, useLocation, useNavigate } from "react-router-dom"
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
@ -8,52 +8,52 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material"
|
||||||
import SectionWrapper from "../SectionWrapper";
|
import SectionWrapper from "../SectionWrapper"
|
||||||
import LogoutIcon from "../icons/LogoutIcon";
|
import LogoutIcon from "../icons/LogoutIcon"
|
||||||
import DialogMenu from "./DialogMenu";
|
import DialogMenu from "./DialogMenu"
|
||||||
import WalletIcon from "../icons/WalletIcon";
|
import WalletIcon from "../icons/WalletIcon"
|
||||||
import CustomAvatar from "./Avatar";
|
import CustomAvatar from "./Avatar"
|
||||||
import Drawers from "../Drawers";
|
import Drawers from "../Drawers"
|
||||||
import PenaLogo from "../PenaLogo";
|
import PenaLogo from "../PenaLogo"
|
||||||
import Menu from "../Menu";
|
import Menu from "../Menu"
|
||||||
import { logout } from "@root/api/auth";
|
import { logout } from "@root/api/auth"
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack"
|
||||||
import { clearUserData, useUserStore } from "@root/stores/user";
|
import { clearUserData, useUserStore } from "@root/stores/user"
|
||||||
import {
|
import {
|
||||||
BurgerButton,
|
BurgerButton,
|
||||||
clearAuthToken,
|
clearAuthToken,
|
||||||
getMessageFromFetchError,
|
getMessageFromFetchError,
|
||||||
} from "@frontend/kitui";
|
} from "@frontend/kitui"
|
||||||
import { currencyFormatter } from "@root/utils/currencyFormatter";
|
import { currencyFormatter } from "@root/utils/currencyFormatter"
|
||||||
import { clearCustomTariffs } from "@root/stores/customTariffs";
|
import { clearCustomTariffs } from "@root/stores/customTariffs"
|
||||||
import { clearTickets } from "@root/stores/tickets";
|
import { clearTickets } from "@root/stores/tickets"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isLoggedIn: boolean;
|
isLoggedIn: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NavbarFull({ isLoggedIn }: Props) {
|
export default function NavbarFull({ isLoggedIn }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const location = useLocation();
|
const location = useLocation()
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate()
|
||||||
const user = useUserStore((state) => state.user);
|
const user = useUserStore((state) => state.user)
|
||||||
const cash = useUserStore((state) => state.userAccount?.wallet.cash) ?? 0;
|
const cash = useUserStore((state) => state.userAccount?.wallet.cash) ?? 0
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.up(1000));
|
const isTablet = useMediaQuery(theme.breakpoints.up(1000))
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false)
|
||||||
|
|
||||||
async function handleLogoutClick() {
|
async function handleLogoutClick() {
|
||||||
const [_, logoutError] = await logout();
|
const [_, logoutError] = await logout()
|
||||||
|
|
||||||
if (logoutError) {
|
if (logoutError) {
|
||||||
return enqueueSnackbar(logoutError);
|
return enqueueSnackbar(logoutError)
|
||||||
}
|
}
|
||||||
|
|
||||||
clearAuthToken();
|
clearAuthToken()
|
||||||
clearUserData();
|
clearUserData()
|
||||||
clearCustomTariffs();
|
clearCustomTariffs()
|
||||||
clearTickets();
|
clearTickets()
|
||||||
navigate("/");
|
navigate("/")
|
||||||
}
|
}
|
||||||
|
|
||||||
return isLoggedIn ? (
|
return isLoggedIn ? (
|
||||||
@ -160,5 +160,5 @@ export default function NavbarFull({ isLoggedIn }: Props) {
|
|||||||
</SectionWrapper>
|
</SectionWrapper>
|
||||||
<DialogMenu open={open} handleClose={() => setOpen(false)} />
|
<DialogMenu open={open} handleClose={() => setOpen(false)} />
|
||||||
</>
|
</>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react"
|
||||||
import { Link, useLocation } from "react-router-dom";
|
import { Link, useLocation } from "react-router-dom"
|
||||||
import { Box, Button, List, ListItem, Typography, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, Button, List, ListItem, Typography, useMediaQuery, useTheme } from "@mui/material"
|
||||||
import { useUserStore } from "@root/stores/user";
|
import { useUserStore } from "@root/stores/user"
|
||||||
import { currencyFormatter } from "@root/utils/currencyFormatter";
|
import { currencyFormatter } from "@root/utils/currencyFormatter"
|
||||||
import { cardShadow } from "@root/utils/theme";
|
import { cardShadow } from "@root/utils/theme"
|
||||||
import { AvatarButton } from "@frontend/kitui";
|
import { AvatarButton } from "@frontend/kitui"
|
||||||
|
|
||||||
|
|
||||||
type MenuItem = {
|
type MenuItem = {
|
||||||
@ -28,29 +28,29 @@ const arrayMenu: MenuItem[] = [
|
|||||||
{ name: "Корзина", url: "/cart" },
|
{ name: "Корзина", url: "/cart" },
|
||||||
{ name: "Поддержка", url: "/support" },
|
{ name: "Поддержка", url: "/support" },
|
||||||
{ name: "История", url: "/history" },
|
{ name: "История", url: "/history" },
|
||||||
];
|
]
|
||||||
|
|
||||||
interface DialogMenuProps {
|
interface DialogMenuProps {
|
||||||
handleClose: () => void;
|
handleClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DialogMenu({ handleClose }: DialogMenuProps) {
|
export default function DialogMenu({ handleClose }: DialogMenuProps) {
|
||||||
const [activeSubMenuIndex, setActiveSubMenuIndex] = useState<number>(-1);
|
const [activeSubMenuIndex, setActiveSubMenuIndex] = useState<number>(-1)
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const location = useLocation();
|
const location = useLocation()
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
const isTablet = useMediaQuery(theme.breakpoints.down(1000))
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
const isMobile = useMediaQuery(theme.breakpoints.down(600))
|
||||||
const user = useUserStore((state) => state.user);
|
const user = useUserStore((state) => state.user)
|
||||||
const cash = useUserStore((state) => state.userAccount?.wallet.cash) ?? 0;
|
const cash = useUserStore((state) => state.userAccount?.wallet.cash) ?? 0
|
||||||
const initials = useUserStore(state => state.initials);
|
const initials = useUserStore(state => state.initials)
|
||||||
|
|
||||||
const closeDialogMenu = () => {
|
const closeDialogMenu = () => {
|
||||||
setActiveSubMenuIndex(-1);
|
setActiveSubMenuIndex(-1)
|
||||||
|
|
||||||
handleClose();
|
handleClose()
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSubMenu = (index: number) => setActiveSubMenuIndex((activeIndex) => (activeIndex !== index ? index : -1));
|
const handleSubMenu = (index: number) => setActiveSubMenuIndex((activeIndex) => (activeIndex !== index ? index : -1))
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -195,5 +195,5 @@ export default function DialogMenu({ handleClose }: DialogMenuProps) {
|
|||||||
)}
|
)}
|
||||||
</List>
|
</List>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { Box, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, useMediaQuery, useTheme } from "@mui/material"
|
||||||
import NavbarCollapsed from "./NavbarCollapsed";
|
import NavbarCollapsed from "./NavbarCollapsed"
|
||||||
import NavbarFull from "./NavbarFull";
|
import NavbarFull from "./NavbarFull"
|
||||||
|
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
export default function Navbar({ children }: Props) {
|
export default function Navbar({ children }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up(1000));
|
const upMd = useMediaQuery(theme.breakpoints.up(1000))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@ -24,5 +24,5 @@ export default function Navbar({ children }: Props) {
|
|||||||
<NavbarCollapsed>{children}</NavbarCollapsed>
|
<NavbarCollapsed>{children}</NavbarCollapsed>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useState, useRef, useEffect } from "react";
|
import { useState, useRef, useEffect } from "react"
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Badge,
|
Badge,
|
||||||
@ -6,60 +6,60 @@ import {
|
|||||||
IconButton,
|
IconButton,
|
||||||
useTheme,
|
useTheme,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
} from "@mui/material";
|
} from "@mui/material"
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom"
|
||||||
|
|
||||||
import SectionWrapper from "../SectionWrapper";
|
import SectionWrapper from "../SectionWrapper"
|
||||||
import { NotificationsModal } from "../NotificationsModal";
|
import { NotificationsModal } from "../NotificationsModal"
|
||||||
import { NavbarPanel } from "./NavbarPanel";
|
import { NavbarPanel } from "./NavbarPanel"
|
||||||
|
|
||||||
import { useUserStore } from "@root/stores/user";
|
import { useUserStore } from "@root/stores/user"
|
||||||
import {
|
import {
|
||||||
useTicketStore
|
useTicketStore
|
||||||
} from "@root/stores/tickets";
|
} from "@root/stores/tickets"
|
||||||
|
|
||||||
import { ReactComponent as CartIcon } from "@root/assets/Icons/cart.svg";
|
import { ReactComponent as CartIcon } from "@root/assets/Icons/cart.svg"
|
||||||
import { ReactComponent as BellIcon } from "@root/assets/Icons/bell.svg";
|
import { ReactComponent as BellIcon } from "@root/assets/Icons/bell.svg"
|
||||||
|
|
||||||
import DialogMenu from "./DialogMenu";
|
import DialogMenu from "./DialogMenu"
|
||||||
import PenaLogo from "../PenaLogo";
|
import PenaLogo from "../PenaLogo"
|
||||||
import CloseIcon from "../icons/CloseIcons";
|
import CloseIcon from "../icons/CloseIcons"
|
||||||
import MenuIcon from "@mui/icons-material/Menu";
|
import MenuIcon from "@mui/icons-material/Menu"
|
||||||
|
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NavbarCollapsed({ children }: Props) {
|
export default function NavbarCollapsed({ children }: Props) {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false)
|
||||||
const [openNotificationsModal, setOpenNotificationsModal] =
|
const [openNotificationsModal, setOpenNotificationsModal] =
|
||||||
useState<boolean>(false);
|
useState<boolean>(false)
|
||||||
const bellRef = useRef<HTMLButtonElement | null>(null);
|
const bellRef = useRef<HTMLButtonElement | null>(null)
|
||||||
const userAccount = useUserStore((state) => state.userAccount);
|
const userAccount = useUserStore((state) => state.userAccount)
|
||||||
const tickets = useTicketStore((state) => state.tickets);
|
const tickets = useTicketStore((state) => state.tickets)
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(550));
|
const isMobile = useMediaQuery(theme.breakpoints.down(550))
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setOpen(false);
|
setOpen(false)
|
||||||
};
|
}
|
||||||
|
|
||||||
const notificationsCount = tickets.filter(
|
const notificationsCount = tickets.filter(
|
||||||
({ user, top_message }) =>
|
({ user, top_message }) =>
|
||||||
user !== top_message.user_id && top_message.shown.me !== 1
|
user !== top_message.user_id && top_message.shown.me !== 1
|
||||||
).length;
|
).length
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open) {
|
if (open) {
|
||||||
document.body.style.overflow = "hidden";
|
document.body.style.overflow = "hidden"
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
document.body.style.overflow = "unset";
|
document.body.style.overflow = "unset"
|
||||||
}, [open]);
|
}, [open])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionWrapper
|
<SectionWrapper
|
||||||
@ -244,5 +244,5 @@ export default function NavbarCollapsed({ children }: Props) {
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</SectionWrapper>
|
</SectionWrapper>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,45 +1,45 @@
|
|||||||
import { Link, useNavigate } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom"
|
||||||
import { Box, Container, Typography, useTheme } from "@mui/material";
|
import { Box, Container, Typography, useTheme } from "@mui/material"
|
||||||
import Drawers from "../Drawers";
|
import Drawers from "../Drawers"
|
||||||
import PenaLogo from "../PenaLogo";
|
import PenaLogo from "../PenaLogo"
|
||||||
import Menu from "../Menu";
|
import Menu from "../Menu"
|
||||||
import { logout } from "@root/api/auth";
|
import { logout } from "@root/api/auth"
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack"
|
||||||
import { clearUserData, useUserStore } from "@root/stores/user";
|
import { clearUserData, useUserStore } from "@root/stores/user"
|
||||||
import {
|
import {
|
||||||
AvatarButton,
|
AvatarButton,
|
||||||
LogoutButton,
|
LogoutButton,
|
||||||
WalletButton,
|
WalletButton,
|
||||||
clearAuthToken,
|
clearAuthToken,
|
||||||
} from "@frontend/kitui";
|
} from "@frontend/kitui"
|
||||||
import { clearCustomTariffs } from "@root/stores/customTariffs";
|
import { clearCustomTariffs } from "@root/stores/customTariffs"
|
||||||
import { currencyFormatter } from "@root/utils/currencyFormatter";
|
import { currencyFormatter } from "@root/utils/currencyFormatter"
|
||||||
import { clearTickets } from "@root/stores/tickets";
|
import { clearTickets } from "@root/stores/tickets"
|
||||||
|
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NavbarFull({ children }: Props) {
|
export default function NavbarFull({ children }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate()
|
||||||
const cash = useUserStore((state) => state.userAccount?.wallet.cash) ?? 0;
|
const cash = useUserStore((state) => state.userAccount?.wallet.cash) ?? 0
|
||||||
const initials = useUserStore((state) => state.initials);
|
const initials = useUserStore((state) => state.initials)
|
||||||
|
|
||||||
async function handleLogoutClick() {
|
async function handleLogoutClick() {
|
||||||
const [_, logoutError] = await logout();
|
const [_, logoutError] = await logout()
|
||||||
|
|
||||||
if (logoutError) {
|
if (logoutError) {
|
||||||
return enqueueSnackbar(logoutError);
|
return enqueueSnackbar(logoutError)
|
||||||
}
|
}
|
||||||
|
|
||||||
clearAuthToken();
|
clearAuthToken()
|
||||||
clearUserData();
|
clearUserData()
|
||||||
clearCustomTariffs();
|
clearCustomTariffs()
|
||||||
clearTickets();
|
clearTickets()
|
||||||
navigate("/");
|
navigate("/")
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -90,5 +90,5 @@ export default function NavbarFull({ children }: Props) {
|
|||||||
</Container>
|
</Container>
|
||||||
<Box>{children}</Box>
|
<Box>{children}</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,43 +1,43 @@
|
|||||||
import { Link, useNavigate } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom"
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
IconButton,
|
IconButton,
|
||||||
Typography,
|
Typography,
|
||||||
useTheme,
|
useTheme,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
} from "@mui/material";
|
} from "@mui/material"
|
||||||
import LogoutIcon from "../icons/LogoutIcon";
|
import LogoutIcon from "../icons/LogoutIcon"
|
||||||
import Drawers from "../Drawers";
|
import Drawers from "../Drawers"
|
||||||
import { logout } from "@root/api/auth";
|
import { logout } from "@root/api/auth"
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack"
|
||||||
import { clearUserData, useUserStore } from "@root/stores/user";
|
import { clearUserData, useUserStore } from "@root/stores/user"
|
||||||
import { AvatarButton, clearAuthToken } from "@frontend/kitui";
|
import { AvatarButton, clearAuthToken } from "@frontend/kitui"
|
||||||
import { clearCustomTariffs } from "@root/stores/customTariffs";
|
import { clearCustomTariffs } from "@root/stores/customTariffs"
|
||||||
import { clearTickets } from "@root/stores/tickets";
|
import { clearTickets } from "@root/stores/tickets"
|
||||||
|
|
||||||
import { currencyFormatter } from "@root/utils/currencyFormatter";
|
import { currencyFormatter } from "@root/utils/currencyFormatter"
|
||||||
|
|
||||||
import walletIcon from "@root/assets/Icons/wallet_icon.svg";
|
import walletIcon from "@root/assets/Icons/wallet_icon.svg"
|
||||||
|
|
||||||
export const NavbarPanel = () => {
|
export const NavbarPanel = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate()
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
const isTablet = useMediaQuery(theme.breakpoints.down(1000))
|
||||||
const cash = useUserStore((state) => state.userAccount?.wallet.cash) ?? 0;
|
const cash = useUserStore((state) => state.userAccount?.wallet.cash) ?? 0
|
||||||
const initials = useUserStore((state) => state.initials);
|
const initials = useUserStore((state) => state.initials)
|
||||||
|
|
||||||
async function handleLogoutClick() {
|
async function handleLogoutClick() {
|
||||||
const [_, logoutError] = await logout();
|
const [_, logoutError] = await logout()
|
||||||
|
|
||||||
if (logoutError) {
|
if (logoutError) {
|
||||||
return enqueueSnackbar(logoutError);
|
return enqueueSnackbar(logoutError)
|
||||||
}
|
}
|
||||||
|
|
||||||
clearAuthToken();
|
clearAuthToken()
|
||||||
clearUserData();
|
clearUserData()
|
||||||
clearCustomTariffs();
|
clearCustomTariffs()
|
||||||
clearTickets();
|
clearTickets()
|
||||||
navigate("/");
|
navigate("/")
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -87,5 +87,5 @@ export const NavbarPanel = () => {
|
|||||||
<LogoutIcon />
|
<LogoutIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
@ -5,8 +5,8 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
useTheme,
|
useTheme,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
} from "@mui/material";
|
} from "@mui/material"
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom"
|
||||||
|
|
||||||
type Notification = {
|
type Notification = {
|
||||||
text: string;
|
text: string;
|
||||||
@ -28,8 +28,8 @@ export const NotificationsModal = ({
|
|||||||
anchorElement,
|
anchorElement,
|
||||||
notifications,
|
notifications,
|
||||||
}: NotificationsModalProps) => {
|
}: NotificationsModalProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
const isMobile = useMediaQuery(theme.breakpoints.down(650))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover
|
<Popover
|
||||||
@ -97,7 +97,7 @@ export const NotificationsModal = ({
|
|||||||
paddingLeft: watched ? "0" : "35px",
|
paddingLeft: watched ? "0" : "35px",
|
||||||
fontWeight: watched ? "normal" : "bold",
|
fontWeight: watched ? "normal" : "bold",
|
||||||
"&::before": {
|
"&::before": {
|
||||||
content: '""',
|
content: "\"\"",
|
||||||
display: watched ? "none" : "block",
|
display: watched ? "none" : "block",
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
left: "10px",
|
left: "10px",
|
||||||
@ -141,5 +141,5 @@ export const NotificationsModal = ({
|
|||||||
)}
|
)}
|
||||||
</List>
|
</List>
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Box, SxProps, Theme } from "@mui/material";
|
import { Box, SxProps, Theme } from "@mui/material"
|
||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react"
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -10,20 +10,20 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function NumberIcon({ number, backgroundColor = "rgb(0 0 0 / 0)", color, sx }: Props) {
|
export default function NumberIcon({ number, backgroundColor = "rgb(0 0 0 / 0)", color, sx }: Props) {
|
||||||
number = number % 100;
|
number = number % 100
|
||||||
|
|
||||||
const firstDigit = Math.floor(number / 10);
|
const firstDigit = Math.floor(number / 10)
|
||||||
const secondDigit = number % 10;
|
const secondDigit = number % 10
|
||||||
|
|
||||||
const firstDigitTranslateX = 6;
|
const firstDigitTranslateX = 6
|
||||||
const secondDigitTranslateX = number < 10
|
const secondDigitTranslateX = number < 10
|
||||||
? 9
|
? 9
|
||||||
: number < 20
|
: number < 20
|
||||||
? 11
|
? 11
|
||||||
: 12;
|
: 12
|
||||||
|
|
||||||
const firstDigitElement = digitSvgs[firstDigit](firstDigitTranslateX);
|
const firstDigitElement = digitSvgs[firstDigit](firstDigitTranslateX)
|
||||||
const secondDigitElement = digitSvgs[secondDigit](secondDigitTranslateX);
|
const secondDigitElement = digitSvgs[secondDigit](secondDigitTranslateX)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{
|
<Box sx={{
|
||||||
@ -44,10 +44,10 @@ export default function NumberIcon({ number, backgroundColor = "rgb(0 0 0 / 0)",
|
|||||||
{secondDigitElement}
|
{secondDigitElement}
|
||||||
</svg>
|
</svg>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const circleSvg = <path d="M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21Z" stroke="currentColor" strokeWidth="1.5" strokeMiterlimit="10" />;
|
const circleSvg = <path d="M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21Z" stroke="currentColor" strokeWidth="1.5" strokeMiterlimit="10" />
|
||||||
|
|
||||||
const digitSvgs: Record<number, (translateX: number) => ReactElement> = {
|
const digitSvgs: Record<number, (translateX: number) => ReactElement> = {
|
||||||
0: (translateX: number) => (
|
0: (translateX: number) => (
|
||||||
@ -92,4 +92,4 @@ const digitSvgs: Record<number, (translateX: number) => ReactElement> = {
|
|||||||
<path transform={`translate(${translateX} 7)`} d="M3.04688 5.6189C4.3154 5.6189 5.34375 4.59055 5.34375 3.32202C5.34375 2.05349 4.3154 1.02515 3.04688 1.02515C1.77835 1.02515 0.75 2.05349 0.75 3.32202C0.75 4.59055 1.77835 5.6189 3.04688 5.6189Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
<path transform={`translate(${translateX} 7)`} d="M3.04688 5.6189C4.3154 5.6189 5.34375 4.59055 5.34375 3.32202C5.34375 2.05349 4.3154 1.02515 3.04688 1.02515C1.77835 1.02515 0.75 2.05349 0.75 3.32202C0.75 4.59055 1.77835 5.6189 3.04688 5.6189Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
};
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react"
|
||||||
import { InputAdornment, TextField, Typography, useTheme } from "@mui/material";
|
import { InputAdornment, TextField, Typography, useTheme } from "@mui/material"
|
||||||
|
|
||||||
import type { ChangeEvent } from "react";
|
import type { ChangeEvent } from "react"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
id: string;
|
id: string;
|
||||||
@ -11,8 +11,8 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function NumberInputWithUnitAdornment({ id, value, adornmentText, onChange }: Props) {
|
export default function NumberInputWithUnitAdornment({ id, value, adornmentText, onChange }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const [changed, setChanged] = useState<boolean>(false);
|
const [changed, setChanged] = useState<boolean>(false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TextField
|
<TextField
|
||||||
@ -23,23 +23,23 @@ export default function NumberInputWithUnitAdornment({ id, value, adornmentText,
|
|||||||
value={changed ? (value !== 0 ? value : "") : ""}
|
value={changed ? (value !== 0 ? value : "") : ""}
|
||||||
onChange={({ target }: ChangeEvent<HTMLInputElement>) => {
|
onChange={({ target }: ChangeEvent<HTMLInputElement>) => {
|
||||||
if (!changed) {
|
if (!changed) {
|
||||||
setChanged(true);
|
setChanged(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Number(target.value) > 999999) {
|
if (Number(target.value) > 999999) {
|
||||||
target.value = "999999";
|
target.value = "999999"
|
||||||
}
|
}
|
||||||
|
|
||||||
const newNumber = parseInt(target.value);
|
const newNumber = parseInt(target.value)
|
||||||
|
|
||||||
|
|
||||||
if (!isFinite(newNumber) || newNumber < 0) {
|
if (!isFinite(newNumber) || newNumber < 0) {
|
||||||
onChange(0);
|
onChange(0)
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange(newNumber);
|
onChange(newNumber)
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
maxWidth: "200px",
|
maxWidth: "200px",
|
||||||
@ -98,5 +98,5 @@ export default function NumberInputWithUnitAdornment({ id, value, adornmentText,
|
|||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useTheme } from "@mui/material";
|
import { useTheme } from "@mui/material"
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -7,7 +7,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function PenaLogo({ width, color }: Props) {
|
export default function PenaLogo({ width, color }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<svg style={{ minWidth: width }} width={width} viewBox="0 0 180 70" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg style={{ minWidth: width }} width={width} viewBox="0 0 180 70" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
@ -31,5 +31,5 @@ export default function PenaLogo({ width, color }: Props) {
|
|||||||
</clipPath>
|
</clipPath>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Outlet } from "react-router-dom";
|
import { Outlet } from "react-router-dom"
|
||||||
import Navbar from "./NavbarSite/Navbar";
|
import Navbar from "./NavbarSite/Navbar"
|
||||||
import {
|
import {
|
||||||
Ticket,
|
Ticket,
|
||||||
getMessageFromFetchError,
|
getMessageFromFetchError,
|
||||||
@ -8,78 +8,78 @@ import {
|
|||||||
useSSESubscription,
|
useSSESubscription,
|
||||||
useTicketsFetcher,
|
useTicketsFetcher,
|
||||||
useToken,
|
useToken,
|
||||||
} from "@frontend/kitui";
|
} from "@frontend/kitui"
|
||||||
import { updateTickets, setTicketCount, useTicketStore, setTicketsFetchState } from "@root/stores/tickets";
|
import { updateTickets, setTicketCount, useTicketStore, setTicketsFetchState } from "@root/stores/tickets"
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack"
|
||||||
import { updateTariffs } from "@root/stores/tariffs";
|
import { updateTariffs } from "@root/stores/tariffs"
|
||||||
import { useCustomTariffs } from "@root/utils/hooks/useCustomTariffs";
|
import { useCustomTariffs } from "@root/utils/hooks/useCustomTariffs"
|
||||||
import { setCustomTariffs } from "@root/stores/customTariffs";
|
import { setCustomTariffs } from "@root/stores/customTariffs"
|
||||||
import { useDiscounts } from "@root/utils/hooks/useDiscounts";
|
import { useDiscounts } from "@root/utils/hooks/useDiscounts"
|
||||||
import { setDiscounts } from "@root/stores/discounts";
|
import { setDiscounts } from "@root/stores/discounts"
|
||||||
import { setPrivileges } from "@root/stores/privileges";
|
import { setPrivileges } from "@root/stores/privileges"
|
||||||
|
|
||||||
export default function ProtectedLayout() {
|
export default function ProtectedLayout() {
|
||||||
const token = useToken();
|
const token = useToken()
|
||||||
const ticketApiPage = useTicketStore((state) => state.apiPage);
|
const ticketApiPage = useTicketStore((state) => state.apiPage)
|
||||||
const ticketsPerPage = useTicketStore((state) => state.ticketsPerPage);
|
const ticketsPerPage = useTicketStore((state) => state.ticketsPerPage)
|
||||||
|
|
||||||
useSSESubscription<Ticket>({
|
useSSESubscription<Ticket>({
|
||||||
url: `https://hub.pena.digital/heruvym/subscribe?Authorization=${token}`,
|
url: `https://hub.pena.digital/heruvym/subscribe?Authorization=${token}`,
|
||||||
onNewData: (data) => {
|
onNewData: (data) => {
|
||||||
updateTickets(data.filter((d) => Boolean(d.id)));
|
updateTickets(data.filter((d) => Boolean(d.id)))
|
||||||
setTicketCount(data.length);
|
setTicketCount(data.length)
|
||||||
},
|
},
|
||||||
marker: "ticket",
|
marker: "ticket",
|
||||||
});
|
})
|
||||||
|
|
||||||
useTicketsFetcher({
|
useTicketsFetcher({
|
||||||
url: "https://hub.pena.digital/heruvym/getTickets",
|
url: "https://hub.pena.digital/heruvym/getTickets",
|
||||||
ticketsPerPage,
|
ticketsPerPage,
|
||||||
ticketApiPage,
|
ticketApiPage,
|
||||||
onSuccess: (result) => {
|
onSuccess: (result) => {
|
||||||
if (result.data) updateTickets(result.data);
|
if (result.data) updateTickets(result.data)
|
||||||
setTicketCount(result.count);
|
setTicketCount(result.count)
|
||||||
},
|
},
|
||||||
onError: (error: Error) => {
|
onError: (error: Error) => {
|
||||||
const message = getMessageFromFetchError(error);
|
const message = getMessageFromFetchError(error)
|
||||||
if (message) enqueueSnackbar(message);
|
if (message) enqueueSnackbar(message)
|
||||||
},
|
},
|
||||||
onFetchStateChange: setTicketsFetchState,
|
onFetchStateChange: setTicketsFetchState,
|
||||||
});
|
})
|
||||||
|
|
||||||
useAllTariffsFetcher({
|
useAllTariffsFetcher({
|
||||||
onSuccess: updateTariffs,
|
onSuccess: updateTariffs,
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
const errorMessage = getMessageFromFetchError(error);
|
const errorMessage = getMessageFromFetchError(error)
|
||||||
if (errorMessage) enqueueSnackbar(errorMessage);
|
if (errorMessage) enqueueSnackbar(errorMessage)
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
useCustomTariffs({
|
useCustomTariffs({
|
||||||
onNewUser: setCustomTariffs,
|
onNewUser: setCustomTariffs,
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
if (error) enqueueSnackbar(error);
|
if (error) enqueueSnackbar(error)
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
useDiscounts({
|
useDiscounts({
|
||||||
onNewDiscounts: setDiscounts,
|
onNewDiscounts: setDiscounts,
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
const message = getMessageFromFetchError(error);
|
const message = getMessageFromFetchError(error)
|
||||||
if (message) enqueueSnackbar(message);
|
if (message) enqueueSnackbar(message)
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
usePrivilegeFetcher({
|
usePrivilegeFetcher({
|
||||||
onSuccess: setPrivileges,
|
onSuccess: setPrivileges,
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.log("usePrivilegeFetcher error :>> ", error);
|
console.log("usePrivilegeFetcher error :>> ", error)
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Navbar>
|
<Navbar>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</Navbar>
|
</Navbar>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Breakpoint, Container, SxProps, Theme, useMediaQuery, useTheme } from "@mui/material";
|
import { Breakpoint, Container, SxProps, Theme, useMediaQuery, useTheme } from "@mui/material"
|
||||||
import React, { ElementType } from "react";
|
import React, { ElementType } from "react"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
component?: ElementType;
|
component?: ElementType;
|
||||||
@ -11,10 +11,10 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function SectionWrapper({ component, outerContainerSx: sx, sx: innerSx, children, maxWidth }: Props) {
|
export default function SectionWrapper({ component, outerContainerSx: sx, sx: innerSx, children, maxWidth }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const matchMd = useMediaQuery(theme.breakpoints.up("md"));
|
const matchMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
|
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(380));
|
const isMobile = useMediaQuery(theme.breakpoints.down(380))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container component={component || "div"} maxWidth={false} disableGutters sx={sx}>
|
<Container component={component || "div"} maxWidth={false} disableGutters sx={sx}>
|
||||||
@ -29,5 +29,5 @@ export default function SectionWrapper({ component, outerContainerSx: sx, sx: in
|
|||||||
{children}
|
{children}
|
||||||
</Container>
|
</Container>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { useState, useRef } from "react";
|
import { useState, useRef } from "react"
|
||||||
import { Select as MuiSelect, MenuItem, Box, Typography, useTheme } from "@mui/material";
|
import { Select as MuiSelect, MenuItem, Box, Typography, useTheme } from "@mui/material"
|
||||||
import classnames from "classnames";
|
import classnames from "classnames"
|
||||||
|
|
||||||
import checkIcon from "@root/assets/Icons/check.svg";
|
import checkIcon from "@root/assets/Icons/check.svg"
|
||||||
|
|
||||||
import "./select.css";
|
import "./select.css"
|
||||||
|
|
||||||
import type { SelectChangeEvent } from "@mui/material";
|
import type { SelectChangeEvent } from "@mui/material"
|
||||||
|
|
||||||
type SelectProps = {
|
type SelectProps = {
|
||||||
items: string[];
|
items: string[];
|
||||||
@ -15,15 +15,15 @@ type SelectProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Select = ({ items, selectedItem, setSelectedItem }: SelectProps) => {
|
export const Select = ({ items, selectedItem, setSelectedItem }: SelectProps) => {
|
||||||
const [opened, setOpened] = useState<boolean>(false);
|
const [opened, setOpened] = useState<boolean>(false)
|
||||||
const [currentValue, setCurrentValue] = useState<string>(items[selectedItem]);
|
const [currentValue, setCurrentValue] = useState<string>(items[selectedItem])
|
||||||
const ref = useRef<HTMLDivElement | null>(null);
|
const ref = useRef<HTMLDivElement | null>(null)
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
|
|
||||||
const selectItem = (event: SelectChangeEvent<HTMLDivElement>) => {
|
const selectItem = (event: SelectChangeEvent<HTMLDivElement>) => {
|
||||||
setCurrentValue(items[Number(event.target.value)]);
|
setCurrentValue(items[Number(event.target.value)])
|
||||||
setSelectedItem(Number(event.target.value));
|
setSelectedItem(Number(event.target.value))
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
@ -84,5 +84,5 @@ export const Select = ({ items, selectedItem, setSelectedItem }: SelectProps) =>
|
|||||||
))}
|
))}
|
||||||
</MuiSelect>
|
</MuiSelect>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Tabs as MuiTabs } from "@mui/material";
|
import { Tabs as MuiTabs } from "@mui/material"
|
||||||
import { CustomTab } from "@root/components/CustomTab";
|
import { CustomTab } from "@root/components/CustomTab"
|
||||||
|
|
||||||
type TabsProps = {
|
type TabsProps = {
|
||||||
items: string[];
|
items: string[];
|
||||||
@ -19,4 +19,4 @@ export const Tabs = ({ items, selectedItem, setSelectedItem }: TabsProps) => (
|
|||||||
<CustomTab key={item + index} value={index} label={item} />
|
<CustomTab key={item + index} value={index} label={item} />
|
||||||
))}
|
))}
|
||||||
</MuiTabs>
|
</MuiTabs>
|
||||||
);
|
)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react"
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom"
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack"
|
||||||
import {
|
import {
|
||||||
Alert,
|
Alert,
|
||||||
Box,
|
Box,
|
||||||
@ -8,13 +8,13 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material"
|
||||||
|
|
||||||
import { Loader } from "./Loader";
|
import { Loader } from "./Loader"
|
||||||
|
|
||||||
import { currencyFormatter } from "@root/utils/currencyFormatter";
|
import { currencyFormatter } from "@root/utils/currencyFormatter"
|
||||||
import { payCart } from "@root/api/cart";
|
import { payCart } from "@root/api/cart"
|
||||||
import { setUserAccount } from "@root/stores/user";
|
import { setUserAccount } from "@root/stores/user"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
priceBeforeDiscounts: number;
|
priceBeforeDiscounts: number;
|
||||||
@ -25,41 +25,41 @@ export default function TotalPrice({
|
|||||||
priceAfterDiscounts,
|
priceAfterDiscounts,
|
||||||
priceBeforeDiscounts,
|
priceBeforeDiscounts,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(550));
|
const isMobile = useMediaQuery(theme.breakpoints.down(550))
|
||||||
const [notEnoughMoneyAmount, setNotEnoughMoneyAmount] = useState<number>(0);
|
const [notEnoughMoneyAmount, setNotEnoughMoneyAmount] = useState<number>(0)
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState<boolean>(false)
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate()
|
||||||
|
|
||||||
async function handlePayClick() {
|
async function handlePayClick() {
|
||||||
setLoading(true);
|
setLoading(true)
|
||||||
|
|
||||||
const [payCartResponse, payCartError] = await payCart();
|
const [payCartResponse, payCartError] = await payCart()
|
||||||
|
|
||||||
if (payCartError) {
|
if (payCartError) {
|
||||||
if (payCartError.includes("insufficient funds: ")) {
|
if (payCartError.includes("insufficient funds: ")) {
|
||||||
const notEnoughMoneyAmount = parseInt(
|
const notEnoughMoneyAmount = parseInt(
|
||||||
payCartError.replace(/^.*insufficient\sfunds:\s(?=\d+$)/, "")
|
payCartError.replace(/^.*insufficient\sfunds:\s(?=\d+$)/, "")
|
||||||
);
|
)
|
||||||
|
|
||||||
setNotEnoughMoneyAmount(notEnoughMoneyAmount);
|
setNotEnoughMoneyAmount(notEnoughMoneyAmount)
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(false);
|
setLoading(false)
|
||||||
|
|
||||||
return enqueueSnackbar(payCartError);
|
return enqueueSnackbar(payCartError)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payCartResponse) {
|
if (payCartResponse) {
|
||||||
setUserAccount(payCartResponse);
|
setUserAccount(payCartResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(false);
|
setLoading(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleReplenishWallet() {
|
function handleReplenishWallet() {
|
||||||
navigate("/payment", { state: { notEnoughMoneyAmount } });
|
navigate("/payment", { state: { notEnoughMoneyAmount } })
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -146,5 +146,5 @@ export default function TotalPrice({
|
|||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Button, ButtonProps, SxProps, Theme, useMediaQuery, useTheme } from "@mui/material";
|
import { Button, ButtonProps, SxProps, Theme, useMediaQuery, useTheme } from "@mui/material"
|
||||||
import { MouseEventHandler, ReactNode } from "react";
|
import { MouseEventHandler, ReactNode } from "react"
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -11,8 +11,8 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function UnderlinedButtonWithIcon({ ButtonProps, icon, children, sx, onClick }: Props) {
|
export default function UnderlinedButtonWithIcon({ ButtonProps, icon, children, sx, onClick }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
@ -41,5 +41,5 @@ export default function UnderlinedButtonWithIcon({ ButtonProps, icon, children,
|
|||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Box } from "@mui/material";
|
import { Box } from "@mui/material"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
color: string;
|
color: string;
|
||||||
@ -64,5 +64,5 @@ export default function CalendarIcon({ color, bgcolor }: Props) {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Box } from "@mui/material";
|
import { Box } from "@mui/material"
|
||||||
|
|
||||||
export default function CloseIcon() {
|
export default function CloseIcon() {
|
||||||
return (
|
return (
|
||||||
@ -19,5 +19,5 @@ export default function CloseIcon() {
|
|||||||
<path d="M1 1L25 25M1 25L25 1" stroke="black" />
|
<path d="M1 1L25 25M1 25L25 1" stroke="black" />
|
||||||
</svg>
|
</svg>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Box } from "@mui/material";
|
import { Box } from "@mui/material"
|
||||||
|
|
||||||
|
|
||||||
export default function CloseSmallIcon() {
|
export default function CloseSmallIcon() {
|
||||||
@ -17,5 +17,5 @@ export default function CloseSmallIcon() {
|
|||||||
<path d="M6 6L18 18" stroke="#A9AAB1" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
<path d="M6 6L18 18" stroke="#A9AAB1" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
</svg>
|
</svg>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { Box } from "@mui/material";
|
import { Box } from "@mui/material"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
color: string;
|
color: string;
|
||||||
@ -56,5 +56,5 @@ export default function CustomIcon({ color, bgcolor }: Props) {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useTheme , Box} from "@mui/material";
|
import { useTheme , Box} from "@mui/material"
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -6,7 +6,7 @@ interface Props {
|
|||||||
|
|
||||||
}
|
}
|
||||||
export default function ExpandIcon({ isExpanded }: Props) {
|
export default function ExpandIcon({ isExpanded }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{
|
<Box sx={{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Box } from "@mui/material";
|
import { Box } from "@mui/material"
|
||||||
|
|
||||||
|
|
||||||
export default function EyeIcon() {
|
export default function EyeIcon() {
|
||||||
@ -17,5 +17,5 @@ export default function EyeIcon() {
|
|||||||
<circle cx="10.9495" cy="7.50033" r="3.58333" stroke="#7E2AEA" strokeWidth="1.5" />
|
<circle cx="10.9495" cy="7.50033" r="3.58333" stroke="#7E2AEA" strokeWidth="1.5" />
|
||||||
</svg>
|
</svg>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { Box } from "@mui/material";
|
import { Box } from "@mui/material"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
color?: string;
|
color?: string;
|
||||||
@ -26,5 +26,5 @@ export default function CustomIcon() {
|
|||||||
<path d="M7.65039 17.4375H12.1004" strokeWidth="1.5" strokeLinecap="round"/>
|
<path d="M7.65039 17.4375H12.1004" strokeWidth="1.5" strokeLinecap="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { useTheme } from "@mui/material";
|
import { useTheme } from "@mui/material"
|
||||||
|
|
||||||
|
|
||||||
export default function LogoutIcon() {
|
export default function LogoutIcon() {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
@ -10,5 +10,5 @@ export default function LogoutIcon() {
|
|||||||
<path d="M15.067 11.475L16.8532 9.71165C17.2498 9.32011 17.2498 8.6799 16.8532 8.28836L15.067 6.52501" stroke={theme.palette.gray.main} strokeWidth="1.5" strokeLinecap="round" />
|
<path d="M15.067 11.475L16.8532 9.71165C17.2498 9.32011 17.2498 8.6799 16.8532 8.28836L15.067 6.52501" stroke={theme.palette.gray.main} strokeWidth="1.5" strokeLinecap="round" />
|
||||||
<path d="M16.7384 9L6.70996 9" stroke={theme.palette.gray.main} strokeWidth="1.5" strokeLinecap="round" />
|
<path d="M16.7384 9L6.70996 9" stroke={theme.palette.gray.main} strokeWidth="1.5" strokeLinecap="round" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Box } from "@mui/material";
|
import { Box } from "@mui/material"
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -20,5 +20,5 @@ export default function PaperClipIcon({ color = "#7E2AEA" }: Props) {
|
|||||||
<path d="M15.7541 9.80636C13.714 11.8464 9.42986 16.1306 8.61382 16.9467C7.59378 17.9667 5.14568 19.0548 3.10559 17.0147C1.0655 14.9746 1.47352 12.5265 2.83358 11.1664C4.19364 9.80636 10.9939 3.00608 11.674 2.32605C12.694 1.30601 14.1901 1.44201 15.2101 2.46205C16.2301 3.4821 16.5686 4.91167 15.4141 6.06621C14.0541 7.42626 7.45777 14.1585 6.77775 14.8386C6.09772 15.5186 5.31107 15.276 4.90767 14.8726C4.50426 14.4692 4.26164 13.6825 4.94167 13.0025C5.48569 12.4585 9.79254 8.15163 11.946 5.9982" stroke={color} strokeWidth="1.5" strokeLinecap="round" />
|
<path d="M15.7541 9.80636C13.714 11.8464 9.42986 16.1306 8.61382 16.9467C7.59378 17.9667 5.14568 19.0548 3.10559 17.0147C1.0655 14.9746 1.47352 12.5265 2.83358 11.1664C4.19364 9.80636 10.9939 3.00608 11.674 2.32605C12.694 1.30601 14.1901 1.44201 15.2101 2.46205C16.2301 3.4821 16.5686 4.91167 15.4141 6.06621C14.0541 7.42626 7.45777 14.1585 6.77775 14.8386C6.09772 15.5186 5.31107 15.276 4.90767 14.8726C4.50426 14.4692 4.26164 13.6825 4.94167 13.0025C5.48569 12.4585 9.79254 8.15163 11.946 5.9982" stroke={color} strokeWidth="1.5" strokeLinecap="round" />
|
||||||
</svg>
|
</svg>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { Box } from "@mui/material";
|
import { Box } from "@mui/material"
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -28,5 +28,5 @@ export default function PieChartIcon({ color, bgcolor }: Props) {
|
|||||||
<path d="M10.0025 1.00455C11.5795 1.00473 13.1288 1.41928 14.4952 2.2067C15.8616 2.99411 16.9971 4.12674 17.7879 5.49112C18.5788 6.85551 18.9973 8.40375 19.0014 9.98078C19.0056 11.5578 18.5953 13.1082 17.8117 14.4768C17.028 15.8453 15.8985 16.9839 14.5363 17.7785C13.1741 18.5732 11.627 18.9959 10.05 19.0044C8.47303 19.0129 6.92147 18.6069 5.55077 17.827C4.18007 17.0472 3.03836 15.9208 2.23999 14.5608" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
<path d="M10.0025 1.00455C11.5795 1.00473 13.1288 1.41928 14.4952 2.2067C15.8616 2.99411 16.9971 4.12674 17.7879 5.49112C18.5788 6.85551 18.9973 8.40375 19.0014 9.98078C19.0056 11.5578 18.5953 13.1082 17.8117 14.4768C17.028 15.8453 15.8985 16.9839 14.5363 17.7785C13.1741 18.5732 11.627 18.9959 10.05 19.0044C8.47303 19.0129 6.92147 18.6069 5.55077 17.827C4.18007 17.0472 3.03836 15.9208 2.23999 14.5608" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
</svg>
|
</svg>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { CSSProperties } from "react";
|
import { CSSProperties } from "react"
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -13,5 +13,5 @@ export default function SendIcon({ style }: Props) {
|
|||||||
<path d="M33.8489 22.1816L15.9232 12.1415C15.7722 12.0581 15.5994 12.0227 15.4277 12.0399C15.2561 12.0571 15.0938 12.1263 14.9624 12.2381C14.831 12.3498 14.7368 12.499 14.6923 12.6657C14.6478 12.8323 14.6551 13.0086 14.7133 13.171L18.0883 22.638C18.1627 22.8218 18.1627 23.0273 18.0883 23.2111L14.7133 32.6781C14.6551 32.8405 14.6478 33.0167 14.6923 33.1834C14.7368 33.3501 14.831 33.4992 14.9624 33.611C15.0938 33.7228 15.2561 33.7919 15.4277 33.8092C15.5994 33.8264 15.7722 33.791 15.9232 33.7076L33.8489 23.6675C33.9816 23.594 34.0922 23.4864 34.1693 23.3558C34.2463 23.2251 34.2869 23.0762 34.2869 22.9245C34.2869 22.7729 34.2463 22.624 34.1693 22.4933C34.0922 22.3627 33.9816 22.255 33.8489 22.1816V22.1816Z" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
<path d="M33.8489 22.1816L15.9232 12.1415C15.7722 12.0581 15.5994 12.0227 15.4277 12.0399C15.2561 12.0571 15.0938 12.1263 14.9624 12.2381C14.831 12.3498 14.7368 12.499 14.6923 12.6657C14.6478 12.8323 14.6551 13.0086 14.7133 13.171L18.0883 22.638C18.1627 22.8218 18.1627 23.0273 18.0883 23.2111L14.7133 32.6781C14.6551 32.8405 14.6478 33.0167 14.6923 33.1834C14.7368 33.3501 14.831 33.4992 14.9624 33.611C15.0938 33.7228 15.2561 33.7919 15.4277 33.8092C15.5994 33.8264 15.7722 33.791 15.9232 33.7076L33.8489 23.6675C33.9816 23.594 34.0922 23.4864 34.1693 23.3558C34.2463 23.2251 34.2869 23.0762 34.2869 22.9245C34.2869 22.7729 34.2463 22.624 34.1693 22.4933C34.0922 22.3627 33.9816 22.255 33.8489 22.1816V22.1816Z" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
<path d="M18.1943 22.9248H24.9868" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
<path d="M18.1943 22.9248H24.9868" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
)
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { Box } from "@mui/material";
|
import { Box } from "@mui/material"
|
||||||
|
|
||||||
|
|
||||||
export default function SendIcon() {
|
export default function SendIcon() {
|
||||||
@ -18,5 +18,5 @@ export default function SendIcon() {
|
|||||||
<path d="M20.25 14.4761V19.8094C20.25 20.0115 20.171 20.2053 20.0303 20.3482C19.8897 20.491 19.6989 20.5713 19.5 20.5713H4.5C4.30109 20.5713 4.11032 20.491 3.96967 20.3482C3.82902 20.2053 3.75 20.0115 3.75 19.8094V14.4761" stroke="#7E2AEA" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
<path d="M20.25 14.4761V19.8094C20.25 20.0115 20.171 20.2053 20.0303 20.3482C19.8897 20.491 19.6989 20.5713 19.5 20.5713H4.5C4.30109 20.5713 4.11032 20.491 3.96967 20.3482C3.82902 20.2053 3.75 20.0115 3.75 19.8094V14.4761" stroke="#7E2AEA" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
</svg>
|
</svg>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { Box } from "@mui/material";
|
import { Box } from "@mui/material"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
color: string;
|
color: string;
|
||||||
@ -33,5 +33,5 @@ export default function SendIcon({ color, bgcolor }: Props) {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -8,11 +8,11 @@ import {
|
|||||||
Theme,
|
Theme,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material"
|
||||||
import * as React from "react";
|
import * as React from "react"
|
||||||
import InputAdornment from "@mui/material/InputAdornment";
|
import InputAdornment from "@mui/material/InputAdornment"
|
||||||
import Visibility from "@mui/icons-material/Visibility";
|
import Visibility from "@mui/icons-material/Visibility"
|
||||||
import VisibilityOff from "@mui/icons-material/VisibilityOff";
|
import VisibilityOff from "@mui/icons-material/VisibilityOff"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
id: string;
|
id: string;
|
||||||
@ -35,24 +35,24 @@ export default function PasswordInput({
|
|||||||
color,
|
color,
|
||||||
FormInputSx,
|
FormInputSx,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
|
|
||||||
const labelFont = upMd
|
const labelFont = upMd
|
||||||
? bold
|
? bold
|
||||||
? theme.typography.p1
|
? theme.typography.p1
|
||||||
: { ...theme.typography.body1, fontWeight: 500 }
|
: { ...theme.typography.body1, fontWeight: 500 }
|
||||||
: theme.typography.body2;
|
: theme.typography.body2
|
||||||
|
|
||||||
const placeholderFont = upMd ? undefined : { fontWeight: 400, fontSize: "16px", lineHeight: "19px" };
|
const placeholderFont = upMd ? undefined : { fontWeight: 400, fontSize: "16px", lineHeight: "19px" }
|
||||||
|
|
||||||
const [showPassword, setShowPassword] = React.useState(false);
|
const [showPassword, setShowPassword] = React.useState(false)
|
||||||
|
|
||||||
const handleClickShowPassword = () => setShowPassword((show) => !show);
|
const handleClickShowPassword = () => setShowPassword((show) => !show)
|
||||||
|
|
||||||
const handleMouseDownPassword = (event: React.MouseEvent<HTMLButtonElement>) => {
|
const handleMouseDownPassword = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormControl
|
<FormControl
|
||||||
@ -124,5 +124,5 @@ export default function PasswordInput({
|
|||||||
type={showPassword ? "text" : "password"}
|
type={showPassword ? "text" : "password"}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { Box, Button, Typography, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, Button, Typography, useMediaQuery, useTheme } from "@mui/material"
|
||||||
import card1Image from "@root/assets/landing/card1.png";
|
import card1Image from "@root/assets/landing/card1.png"
|
||||||
|
|
||||||
export default function TemplCardPhoneLight() {
|
export default function TemplCardPhoneLight() {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
const isMobile = useMediaQuery(theme.breakpoints.down(600))
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -52,5 +52,5 @@ export default function TemplCardPhoneLight() {
|
|||||||
<Button variant="pena-contained-light">Подробнее</Button>
|
<Button variant="pena-contained-light">Подробнее</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { Box, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, useMediaQuery, useTheme } from "@mui/material"
|
||||||
import CardWithLink from "@components/CardWithLink";
|
import CardWithLink from "@components/CardWithLink"
|
||||||
import card1Image from "@root/assets/landing/card1.png";
|
import card1Image from "@root/assets/landing/card1.png"
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@ -15,7 +15,7 @@ export default function () {
|
|||||||
justifyContent: "space-evenly",
|
justifyContent: "space-evenly",
|
||||||
columnGap: "40px",
|
columnGap: "40px",
|
||||||
rowGap: "50px",
|
rowGap: "50px",
|
||||||
backgroundColor: '"#E6E6EB',
|
backgroundColor: "\"#E6E6EB",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CardWithLink
|
<CardWithLink
|
||||||
@ -26,5 +26,5 @@ export default function () {
|
|||||||
isHighlighted={!upMd}
|
isHighlighted={!upMd}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Box, Typography, SxProps, Theme, Button, useTheme, useMediaQuery } from "@mui/material";
|
import { Box, Typography, SxProps, Theme, Button, useTheme, useMediaQuery } from "@mui/material"
|
||||||
import cardImageBig from "@root/assets/landing/card1big.png";
|
import cardImageBig from "@root/assets/landing/card1big.png"
|
||||||
import { PenaLink } from "@frontend/kitui";
|
import { PenaLink } from "@frontend/kitui"
|
||||||
import { Link as RouterLink } from "react-router-dom";
|
import { Link as RouterLink } from "react-router-dom"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
light?: boolean;
|
light?: boolean;
|
||||||
@ -11,8 +11,8 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function WideTemplCard({ light = true, sx, name="Шаблонизатор", desc="тект заполнитель это текст который имеет" }: Props) {
|
export default function WideTemplCard({ light = true, sx, name="Шаблонизатор", desc="тект заполнитель это текст который имеет" }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
const isTablet = useMediaQuery(theme.breakpoints.down(1000))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@ -52,5 +52,5 @@ export default function WideTemplCard({ light = true, sx, name="Шаблониз
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { UserDocumentTypes } from "@root/model/user";
|
import type { UserDocumentTypes } from "@root/model/user"
|
||||||
|
|
||||||
export const DOCUMENT_TYPE_MAP: Record<
|
export const DOCUMENT_TYPE_MAP: Record<
|
||||||
"inn" | "rule" | "egrule" | "certificate",
|
"inn" | "rule" | "egrule" | "certificate",
|
||||||
@ -8,4 +8,4 @@ export const DOCUMENT_TYPE_MAP: Record<
|
|||||||
rule: "Устав",
|
rule: "Устав",
|
||||||
certificate: "Свидетельство о регистрации НКО",
|
certificate: "Свидетельство о регистрации НКО",
|
||||||
egrule: "ИНН",
|
egrule: "ИНН",
|
||||||
};
|
}
|
||||||
|
@ -1,70 +1,70 @@
|
|||||||
import React from "react";
|
import React from "react"
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client"
|
||||||
import { BrowserRouter, Navigate, Route, Routes, useLocation, useNavigate } from "react-router-dom";
|
import { BrowserRouter, Navigate, Route, Routes, useLocation, useNavigate } from "react-router-dom"
|
||||||
import { CssBaseline, ThemeProvider } from "@mui/material";
|
import { CssBaseline, ThemeProvider } from "@mui/material"
|
||||||
import Faq from "./pages/Faq/Faq";
|
import Faq from "./pages/Faq/Faq"
|
||||||
import Wallet from "./pages/Wallet";
|
import Wallet from "./pages/Wallet"
|
||||||
import Payment from "./pages/Payment/Payment";
|
import Payment from "./pages/Payment/Payment"
|
||||||
import Support from "./pages/Support/Support";
|
import Support from "./pages/Support/Support"
|
||||||
import AccountSettings from "./pages/AccountSettings/AccountSettings";
|
import AccountSettings from "./pages/AccountSettings/AccountSettings"
|
||||||
import Landing from "./pages/Landing/Landing";
|
import Landing from "./pages/Landing/Landing"
|
||||||
import Tariffs from "./pages/Tariffs/Tariffs";
|
import Tariffs from "./pages/Tariffs/Tariffs"
|
||||||
import SigninDialog from "./pages/auth/Signin";
|
import SigninDialog from "./pages/auth/Signin"
|
||||||
import SignupDialog from "./pages/auth/Signup";
|
import SignupDialog from "./pages/auth/Signup"
|
||||||
import History from "./pages/History";
|
import History from "./pages/History"
|
||||||
import Cart from "./pages/Cart/Cart";
|
import Cart from "./pages/Cart/Cart"
|
||||||
import TariffPage from "./pages/Tariffs/TariffsPage";
|
import TariffPage from "./pages/Tariffs/TariffsPage"
|
||||||
import SavedTariffs from "./pages/SavedTariffs";
|
import SavedTariffs from "./pages/SavedTariffs"
|
||||||
import PrivateRoute from "@root/utils/routes/ProtectedRoute";
|
import PrivateRoute from "@root/utils/routes/ProtectedRoute"
|
||||||
import reportWebVitals from "./reportWebVitals";
|
import reportWebVitals from "./reportWebVitals"
|
||||||
import { SnackbarProvider, enqueueSnackbar } from "notistack";
|
import { SnackbarProvider, enqueueSnackbar } from "notistack"
|
||||||
import "./index.css";
|
import "./index.css"
|
||||||
import ProtectedLayout from "./components/ProtectedLayout";
|
import ProtectedLayout from "./components/ProtectedLayout"
|
||||||
import { clearUserData, setUser, setUserAccount, useUserStore } from "./stores/user";
|
import { clearUserData, setUser, setUserAccount, useUserStore } from "./stores/user"
|
||||||
import TariffConstructor from "./pages/TariffConstructor/TariffConstructor";
|
import TariffConstructor from "./pages/TariffConstructor/TariffConstructor"
|
||||||
import { clearAuthToken, getMessageFromFetchError, useUserAccountFetcher, useUserFetcher } from "@frontend/kitui";
|
import { clearAuthToken, getMessageFromFetchError, useUserAccountFetcher, useUserFetcher } from "@frontend/kitui"
|
||||||
import { pdfjs } from "react-pdf";
|
import { pdfjs } from "react-pdf"
|
||||||
import { theme } from "./utils/theme";
|
import { theme } from "./utils/theme"
|
||||||
|
|
||||||
pdfjs.GlobalWorkerOptions.workerSrc = new URL("pdfjs-dist/build/pdf.worker.min.js", import.meta.url).toString();
|
pdfjs.GlobalWorkerOptions.workerSrc = new URL("pdfjs-dist/build/pdf.worker.min.js", import.meta.url).toString()
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
console.log("render app")
|
console.log("render app")
|
||||||
const location = useLocation();
|
const location = useLocation()
|
||||||
const userId = useUserStore((state) => state.userId);
|
const userId = useUserStore((state) => state.userId)
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate()
|
||||||
|
|
||||||
useUserFetcher({
|
useUserFetcher({
|
||||||
url: `https://hub.pena.digital/user/${userId}`,
|
url: `https://hub.pena.digital/user/${userId}`,
|
||||||
userId,
|
userId,
|
||||||
onNewUser: setUser,
|
onNewUser: setUser,
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
const errorMessage = getMessageFromFetchError(error);
|
const errorMessage = getMessageFromFetchError(error)
|
||||||
if (errorMessage) {
|
if (errorMessage) {
|
||||||
enqueueSnackbar(errorMessage);
|
enqueueSnackbar(errorMessage)
|
||||||
clearUserData();
|
clearUserData()
|
||||||
clearAuthToken();
|
clearAuthToken()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
useUserAccountFetcher({
|
useUserAccountFetcher({
|
||||||
url: "https://hub.pena.digital/customer/account",
|
url: "https://hub.pena.digital/customer/account",
|
||||||
userId,
|
userId,
|
||||||
onNewUserAccount: setUserAccount,
|
onNewUserAccount: setUserAccount,
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
const errorMessage = getMessageFromFetchError(error);
|
const errorMessage = getMessageFromFetchError(error)
|
||||||
if (errorMessage) {
|
if (errorMessage) {
|
||||||
enqueueSnackbar(errorMessage);
|
enqueueSnackbar(errorMessage)
|
||||||
clearUserData();
|
clearUserData()
|
||||||
clearAuthToken();
|
clearAuthToken()
|
||||||
navigate("/signin");
|
navigate("/signin")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
if (location.state?.redirectTo)
|
if (location.state?.redirectTo)
|
||||||
return <Navigate to={location.state.redirectTo} replace state={{ backgroundLocation: location }} />;
|
return <Navigate to={location.state.redirectTo} replace state={{ backgroundLocation: location }} />
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -97,10 +97,10 @@ const App = () => {
|
|||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
</>
|
</>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
|
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement)
|
||||||
root.render(
|
root.render(
|
||||||
// <React.StrictMode>
|
// <React.StrictMode>
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
@ -111,9 +111,9 @@ root.render(
|
|||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
// </React.StrictMode>
|
// </React.StrictMode>
|
||||||
);
|
)
|
||||||
|
|
||||||
// If you want to start measuring performance in your app, pass a function
|
// If you want to start measuring performance in your app, pass a function
|
||||||
// to log results (for example: reportWebVitals(console.log))
|
// to log results (for example: reportWebVitals(console.log))
|
||||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
||||||
reportWebVitals();
|
reportWebVitals()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { UserName } from "@frontend/kitui";
|
import { UserName } from "@frontend/kitui"
|
||||||
|
|
||||||
|
|
||||||
export enum VerificationStatus {
|
export enum VerificationStatus {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { Attachment } from "@root/model/attachment";
|
import type { Attachment } from "@root/model/attachment"
|
||||||
|
|
||||||
export type File = {
|
export type File = {
|
||||||
name: "inn" | "rule" | "egrule" | "certificate";
|
name: "inn" | "rule" | "egrule" | "certificate";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { PrivilegeWithAmount } from "@frontend/kitui";
|
import { PrivilegeWithAmount } from "@frontend/kitui"
|
||||||
import { PrivilegeWithoutPrice } from "./privilege";
|
import { PrivilegeWithoutPrice } from "./privilege"
|
||||||
|
|
||||||
|
|
||||||
type ServiceKey = string;
|
type ServiceKey = string;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Discount } from "@frontend/kitui";
|
import { Discount } from "@frontend/kitui"
|
||||||
|
|
||||||
|
|
||||||
export interface GetDiscountsResponse {
|
export interface GetDiscountsResponse {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Privilege, PrivilegeWithAmount } from "@frontend/kitui";
|
import { Privilege, PrivilegeWithAmount } from "@frontend/kitui"
|
||||||
|
|
||||||
|
|
||||||
export type ServiceKeyToPrivilegesMap = Record<string, Privilege[]>;
|
export type ServiceKeyToPrivilegesMap = Record<string, Privilege[]>;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Tariff } from "@frontend/kitui";
|
import { Tariff } from "@frontend/kitui"
|
||||||
|
|
||||||
|
|
||||||
export interface GetTariffsResponse {
|
export interface GetTariffsResponse {
|
||||||
|
@ -1,54 +1,54 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react"
|
||||||
import { Box, Button, SxProps, Theme, Typography, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, Button, SxProps, Theme, Typography, useMediaQuery, useTheme } from "@mui/material"
|
||||||
import InputTextfield from "@components/InputTextfield";
|
import InputTextfield from "@components/InputTextfield"
|
||||||
import PasswordInput from "@components/passwordInput";
|
import PasswordInput from "@components/passwordInput"
|
||||||
import UserFields from "./UserFields";
|
import UserFields from "./UserFields"
|
||||||
import SectionWrapper from "@components/SectionWrapper";
|
import SectionWrapper from "@components/SectionWrapper"
|
||||||
import { openDocumentsDialog, sendUserData, setSettingsField, useUserStore } from "@root/stores/user";
|
import { openDocumentsDialog, sendUserData, setSettingsField, useUserStore } from "@root/stores/user"
|
||||||
import UnderlinedButtonWithIcon from "@root/components/UnderlinedButtonWithIcon";
|
import UnderlinedButtonWithIcon from "@root/components/UnderlinedButtonWithIcon"
|
||||||
import UploadIcon from "@root/components/icons/UploadIcon";
|
import UploadIcon from "@root/components/icons/UploadIcon"
|
||||||
import DocumentsDialog from "./DocumentsDialog/DocumentsDialog";
|
import DocumentsDialog from "./DocumentsDialog/DocumentsDialog"
|
||||||
import EyeIcon from "@root/components/icons/EyeIcon";
|
import EyeIcon from "@root/components/icons/EyeIcon"
|
||||||
import { cardShadow } from "@root/utils/theme";
|
import { cardShadow } from "@root/utils/theme"
|
||||||
import { getMessageFromFetchError } from "@frontend/kitui";
|
import { getMessageFromFetchError } from "@frontend/kitui"
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack"
|
||||||
import { VerificationStatus } from "@root/model/account";
|
import { VerificationStatus } from "@root/model/account"
|
||||||
import { verify } from "./helper";
|
import { verify } from "./helper"
|
||||||
import { withErrorBoundary } from "react-error-boundary";
|
import { withErrorBoundary } from "react-error-boundary"
|
||||||
import { handleComponentError } from "@root/utils/handleComponentError";
|
import { handleComponentError } from "@root/utils/handleComponentError"
|
||||||
|
|
||||||
function AccountSettings() {
|
function AccountSettings() {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
const upSm = useMediaQuery(theme.breakpoints.up("sm"));
|
const upSm = useMediaQuery(theme.breakpoints.up("sm"))
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
const isTablet = useMediaQuery(theme.breakpoints.down(1000))
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
const isMobile = useMediaQuery(theme.breakpoints.down(600))
|
||||||
|
|
||||||
const fields = useUserStore((state) => state.settingsFields);
|
const fields = useUserStore((state) => state.settingsFields)
|
||||||
const verificationStatus = useUserStore((state) => state.verificationStatus);
|
const verificationStatus = useUserStore((state) => state.verificationStatus)
|
||||||
const verificationType = useUserStore((state) => state.verificationType);
|
const verificationType = useUserStore((state) => state.verificationType)
|
||||||
const comment = useUserStore((state) => state.comment);
|
const comment = useUserStore((state) => state.comment)
|
||||||
const userId = useUserStore((state) => state.userId) ?? "";
|
const userId = useUserStore((state) => state.userId) ?? ""
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
verify(userId);
|
verify(userId)
|
||||||
}, []);
|
}, [])
|
||||||
|
|
||||||
const textFieldProps = {
|
const textFieldProps = {
|
||||||
gap: upMd ? "16px" : "10px",
|
gap: upMd ? "16px" : "10px",
|
||||||
color: "#F2F3F7",
|
color: "#F2F3F7",
|
||||||
bold: true,
|
bold: true,
|
||||||
};
|
}
|
||||||
|
|
||||||
function handleSendDataClick() {
|
function handleSendDataClick() {
|
||||||
sendUserData()
|
sendUserData()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
enqueueSnackbar("Информация обновлена");
|
enqueueSnackbar("Информация обновлена")
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
const message = getMessageFromFetchError(error);
|
const message = getMessageFromFetchError(error)
|
||||||
if (message) enqueueSnackbar(message);
|
if (message) enqueueSnackbar(message)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -139,7 +139,7 @@ function AccountSettings() {
|
|||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</SectionWrapper>
|
</SectionWrapper>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withErrorBoundary(AccountSettings, {
|
export default withErrorBoundary(AccountSettings, {
|
||||||
@ -151,7 +151,7 @@ const verificationStatusData: Record<VerificationStatus, { text: string; color:
|
|||||||
verificated: { text: "Верификация пройдена", color: "#0D9F00" },
|
verificated: { text: "Верификация пройдена", color: "#0D9F00" },
|
||||||
waiting: { text: "В ожидании верификации", color: "#F18956" },
|
waiting: { text: "В ожидании верификации", color: "#F18956" },
|
||||||
notVerificated: { text: "Не верифицирован", color: "#E02C2C" },
|
notVerificated: { text: "Не верифицирован", color: "#E02C2C" },
|
||||||
};
|
}
|
||||||
|
|
||||||
function VerificationIndicator({
|
function VerificationIndicator({
|
||||||
verificationStatus,
|
verificationStatus,
|
||||||
@ -176,5 +176,5 @@ function VerificationIndicator({
|
|||||||
>
|
>
|
||||||
<Typography lineHeight="100%">{verificationStatusData[verificationStatus].text}</Typography>
|
<Typography lineHeight="100%">{verificationStatusData[verificationStatus].text}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
@ -1,8 +1,8 @@
|
|||||||
import axios from "axios";
|
import axios from "axios"
|
||||||
import { Box, SxProps, Theme, Typography, useTheme } from "@mui/material";
|
import { Box, SxProps, Theme, Typography, useTheme } from "@mui/material"
|
||||||
import { Document, Page } from "react-pdf";
|
import { Document, Page } from "react-pdf"
|
||||||
import { Buffer } from "buffer";
|
import { Buffer } from "buffer"
|
||||||
import { downloadFileToDevice } from "@root/utils/downloadFileToDevice";
|
import { downloadFileToDevice } from "@root/utils/downloadFileToDevice"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
text: string;
|
text: string;
|
||||||
@ -11,24 +11,24 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function DocumentItem({ text, documentUrl = "", sx }: Props) {
|
export default function DocumentItem({ text, documentUrl = "", sx }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
|
|
||||||
const downloadFile = async () => {
|
const downloadFile = async () => {
|
||||||
const { data } = await axios.get<ArrayBuffer>(documentUrl, {
|
const { data } = await axios.get<ArrayBuffer>(documentUrl, {
|
||||||
responseType: "arraybuffer",
|
responseType: "arraybuffer",
|
||||||
});
|
})
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadFileToDevice(
|
downloadFileToDevice(
|
||||||
`${documentUrl.split("/").pop()?.split(".")?.[0] || "document"}.pdf`,
|
`${documentUrl.split("/").pop()?.split(".")?.[0] || "document"}.pdf`,
|
||||||
Buffer.from(data)
|
Buffer.from(data)
|
||||||
);
|
)
|
||||||
|
|
||||||
return;
|
return
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@ -68,5 +68,5 @@ export default function DocumentItem({ text, documentUrl = "", sx }: Props) {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import { ChangeEvent, useRef } from "react";
|
import { ChangeEvent, useRef } from "react"
|
||||||
import axios from "axios";
|
import axios from "axios"
|
||||||
import { Document, Page, pdfjs } from "react-pdf";
|
import { Document, Page, pdfjs } from "react-pdf"
|
||||||
import { Box, SxProps, Theme, Typography } from "@mui/material";
|
import { Box, SxProps, Theme, Typography } from "@mui/material"
|
||||||
import { Buffer } from "buffer";
|
import { Buffer } from "buffer"
|
||||||
|
|
||||||
import UnderlinedButtonWithIcon from "@root/components/UnderlinedButtonWithIcon";
|
import UnderlinedButtonWithIcon from "@root/components/UnderlinedButtonWithIcon"
|
||||||
import PaperClipIcon from "@root/components/icons/PaperClipIcon";
|
import PaperClipIcon from "@root/components/icons/PaperClipIcon"
|
||||||
|
|
||||||
import { UserDocument } from "@root/model/user";
|
import { UserDocument } from "@root/model/user"
|
||||||
|
|
||||||
import { readFile } from "@root/utils/readFile";
|
import { readFile } from "@root/utils/readFile"
|
||||||
import { downloadFileToDevice } from "@root/utils/downloadFileToDevice";
|
import { downloadFileToDevice } from "@root/utils/downloadFileToDevice"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
text: string;
|
text: string;
|
||||||
@ -24,7 +24,7 @@ interface Props {
|
|||||||
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
|
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
|
||||||
"pdfjs-dist/build/pdf.worker.min.js",
|
"pdfjs-dist/build/pdf.worker.min.js",
|
||||||
import.meta.url
|
import.meta.url
|
||||||
).toString();
|
).toString()
|
||||||
|
|
||||||
export default function DocumentUploadItem({
|
export default function DocumentUploadItem({
|
||||||
text,
|
text,
|
||||||
@ -34,33 +34,33 @@ export default function DocumentUploadItem({
|
|||||||
sx,
|
sx,
|
||||||
accept = "image/*",
|
accept = "image/*",
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||||
|
|
||||||
function handleChooseFileClick() {
|
function handleChooseFileClick() {
|
||||||
fileInputRef.current?.click();
|
fileInputRef.current?.click()
|
||||||
}
|
}
|
||||||
|
|
||||||
const downloadFile = async () => {
|
const downloadFile = async () => {
|
||||||
if (!document.file && documentUrl) {
|
if (!document.file && documentUrl) {
|
||||||
const { data } = await axios.get<ArrayBuffer>(documentUrl, {
|
const { data } = await axios.get<ArrayBuffer>(documentUrl, {
|
||||||
responseType: "arraybuffer",
|
responseType: "arraybuffer",
|
||||||
});
|
})
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadFileToDevice("document.pdf", Buffer.from(data));
|
downloadFileToDevice("document.pdf", Buffer.from(data))
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document.file) {
|
if (document.file) {
|
||||||
const fileArrayBuffer = await readFile(document.file, "array");
|
const fileArrayBuffer = await readFile(document.file, "array")
|
||||||
|
|
||||||
downloadFileToDevice(document.file.name, Buffer.from(fileArrayBuffer));
|
downloadFileToDevice(document.file.name, Buffer.from(fileArrayBuffer))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@ -106,5 +106,5 @@ export default function DocumentUploadItem({
|
|||||||
/>
|
/>
|
||||||
</Document>
|
</Document>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { useUserStore } from "@root/stores/user";
|
import { useUserStore } from "@root/stores/user"
|
||||||
import NkoDocumentsDialog from "./NkoDocumentsDialog";
|
import NkoDocumentsDialog from "./NkoDocumentsDialog"
|
||||||
import JuridicalDocumentsDialog from "./JuridicalDocumentsDialog";
|
import JuridicalDocumentsDialog from "./JuridicalDocumentsDialog"
|
||||||
|
|
||||||
|
|
||||||
export default function DocumentsDialog() {
|
export default function DocumentsDialog() {
|
||||||
switch(useUserStore(state => state.dialogType)) {
|
switch(useUserStore(state => state.dialogType)) {
|
||||||
case 'juridical':
|
case "juridical":
|
||||||
return <JuridicalDocumentsDialog />
|
return <JuridicalDocumentsDialog />
|
||||||
|
|
||||||
case "nko":
|
case "nko":
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
import { Box, Button, Dialog, IconButton, Typography } from "@mui/material";
|
import { Box, Button, Dialog, IconButton, Typography } from "@mui/material"
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack"
|
||||||
import CloseSmallIcon from "@root/components/icons/CloseSmallIcon";
|
import CloseSmallIcon from "@root/components/icons/CloseSmallIcon"
|
||||||
import {
|
import {
|
||||||
closeDocumentsDialog,
|
closeDocumentsDialog,
|
||||||
setDocument,
|
setDocument,
|
||||||
useUserStore,
|
useUserStore,
|
||||||
} from "@root/stores/user";
|
} from "@root/stores/user"
|
||||||
import DocumentUploadItem from "./DocumentUploadItem";
|
import DocumentUploadItem from "./DocumentUploadItem"
|
||||||
import DocumentItem from "./DocumentItem";
|
import DocumentItem from "./DocumentItem"
|
||||||
import { VerificationStatus } from "@root/model/account";
|
import { VerificationStatus } from "@root/model/account"
|
||||||
import { sendDocuments, updateDocuments } from "@root/api/verification";
|
import { sendDocuments, updateDocuments } from "@root/api/verification"
|
||||||
import { readFile } from "@root/utils/readFile";
|
import { readFile } from "@root/utils/readFile"
|
||||||
import { deleteEmptyKeys } from "@root/utils/deleteEmptyKeys";
|
import { deleteEmptyKeys } from "@root/utils/deleteEmptyKeys"
|
||||||
import { verify } from "../helper";
|
import { verify } from "../helper"
|
||||||
import { useState } from "react";
|
import { useState } from "react"
|
||||||
import { theme } from "@root/utils/theme";
|
import { theme } from "@root/utils/theme"
|
||||||
|
|
||||||
const dialogContainerStyle = {
|
const dialogContainerStyle = {
|
||||||
height: "100%",
|
height: "100%",
|
||||||
@ -22,50 +22,50 @@ const dialogContainerStyle = {
|
|||||||
"::-webkit-scrollbar": {
|
"::-webkit-scrollbar": {
|
||||||
display: "none",
|
display: "none",
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
|
||||||
export default function JuridicalDocumentsDialog() {
|
export default function JuridicalDocumentsDialog() {
|
||||||
const isOpen = useUserStore((state) => state.isDocumentsDialogOpen);
|
const isOpen = useUserStore((state) => state.isDocumentsDialogOpen)
|
||||||
const verificationStatus = useUserStore((state) => state.verificationStatus);
|
const verificationStatus = useUserStore((state) => state.verificationStatus)
|
||||||
const documents = useUserStore((state) => state.documents);//загруженные юзером файлы
|
const documents = useUserStore((state) => state.documents)//загруженные юзером файлы
|
||||||
const documentsUrl = useUserStore((state) => state.documentsUrl);//ссылки с бекенда
|
const documentsUrl = useUserStore((state) => state.documentsUrl)//ссылки с бекенда
|
||||||
const userId = useUserStore((state) => state.userId) ?? "";
|
const userId = useUserStore((state) => state.userId) ?? ""
|
||||||
|
|
||||||
const sendUploadedDocuments = async () => {
|
const sendUploadedDocuments = async () => {
|
||||||
|
|
||||||
if (documents["ИНН"].file && documents["Устав"].file && !documentsUrl["ИНН"] && !documentsUrl["Устав"]) {
|
if (documents["ИНН"].file && documents["Устав"].file && !documentsUrl["ИНН"] && !documentsUrl["Устав"]) {
|
||||||
closeDocumentsDialog();
|
closeDocumentsDialog()
|
||||||
//Пользователь заполнил все поля и на беке пусто
|
//Пользователь заполнил все поля и на беке пусто
|
||||||
const inn = await readFile(documents["ИНН"].file, "binary");
|
const inn = await readFile(documents["ИНН"].file, "binary")
|
||||||
const rule = await readFile(documents["Устав"].file, "binary");
|
const rule = await readFile(documents["Устав"].file, "binary")
|
||||||
|
|
||||||
const [_, sendDocumentsError] = await sendDocuments({
|
const [_, sendDocumentsError] = await sendDocuments({
|
||||||
status: "org",
|
status: "org",
|
||||||
inn,
|
inn,
|
||||||
rule,
|
rule,
|
||||||
});
|
})
|
||||||
|
|
||||||
if (sendDocumentsError) {
|
if (sendDocumentsError) {
|
||||||
enqueueSnackbar(sendDocumentsError);
|
enqueueSnackbar(sendDocumentsError)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
if (_ === "OK") {
|
if (_ === "OK") {
|
||||||
enqueueSnackbar("Информация доставлена")
|
enqueueSnackbar("Информация доставлена")
|
||||||
}
|
}
|
||||||
|
|
||||||
setDocument("ИНН", null);
|
setDocument("ИНН", null)
|
||||||
setDocument("Устав", null);
|
setDocument("Устав", null)
|
||||||
|
|
||||||
await verify(userId);
|
await verify(userId)
|
||||||
} else { //Пользователь заполнил не все, или на беке что-то есть
|
} else { //Пользователь заполнил не все, или на беке что-то есть
|
||||||
if ((documents["ИНН"].file || documents["Устав"].file) && (documentsUrl["ИНН"] || documentsUrl["Устав"])) { //минимум 1 поле заполнено на фронте и минимум 1 поле на беке записано
|
if ((documents["ИНН"].file || documents["Устав"].file) && (documentsUrl["ИНН"] || documentsUrl["Устав"])) { //минимум 1 поле заполнено на фронте и минимум 1 поле на беке записано
|
||||||
closeDocumentsDialog();
|
closeDocumentsDialog()
|
||||||
const inn = documents["ИНН"].file
|
const inn = documents["ИНН"].file
|
||||||
? await readFile(documents["ИНН"].file, "binary")
|
? await readFile(documents["ИНН"].file, "binary")
|
||||||
: undefined;
|
: undefined
|
||||||
const rule = documents["Устав"].file
|
const rule = documents["Устав"].file
|
||||||
? await readFile(documents["Устав"].file, "binary")
|
? await readFile(documents["Устав"].file, "binary")
|
||||||
: undefined;
|
: undefined
|
||||||
|
|
||||||
const [_, updateDocumentsError] = await updateDocuments(
|
const [_, updateDocumentsError] = await updateDocuments(
|
||||||
deleteEmptyKeys({
|
deleteEmptyKeys({
|
||||||
@ -73,24 +73,24 @@ export default function JuridicalDocumentsDialog() {
|
|||||||
inn,
|
inn,
|
||||||
rule,
|
rule,
|
||||||
})
|
})
|
||||||
);
|
)
|
||||||
|
|
||||||
if (updateDocumentsError) {
|
if (updateDocumentsError) {
|
||||||
enqueueSnackbar(updateDocumentsError);
|
enqueueSnackbar(updateDocumentsError)
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
if (_ === "OK") {
|
if (_ === "OK") {
|
||||||
enqueueSnackbar("Информация доставлена")
|
enqueueSnackbar("Информация доставлена")
|
||||||
}
|
}
|
||||||
|
|
||||||
setDocument("ИНН", null);
|
setDocument("ИНН", null)
|
||||||
setDocument("Устав", null);
|
setDocument("Устав", null)
|
||||||
|
|
||||||
await verify(userId);
|
await verify(userId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const disbutt = () => {
|
const disbutt = () => {
|
||||||
if (documents["ИНН"].file && documents["Устав"].file && !documentsUrl["ИНН"] && !documentsUrl["Устав"]) { //post
|
if (documents["ИНН"].file && documents["Устав"].file && !documentsUrl["ИНН"] && !documentsUrl["Устав"]) { //post
|
||||||
@ -136,7 +136,7 @@ export default function JuridicalDocumentsDialog() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
@ -223,5 +223,5 @@ export default function JuridicalDocumentsDialog() {
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
import { Box, Button, Dialog, IconButton, Typography } from "@mui/material";
|
import { Box, Button, Dialog, IconButton, Typography } from "@mui/material"
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack"
|
||||||
import CloseSmallIcon from "@root/components/icons/CloseSmallIcon";
|
import CloseSmallIcon from "@root/components/icons/CloseSmallIcon"
|
||||||
import {
|
import {
|
||||||
closeDocumentsDialog,
|
closeDocumentsDialog,
|
||||||
setDocument,
|
setDocument,
|
||||||
useUserStore,
|
useUserStore,
|
||||||
} from "@root/stores/user";
|
} from "@root/stores/user"
|
||||||
import DocumentUploadItem from "./DocumentUploadItem";
|
import DocumentUploadItem from "./DocumentUploadItem"
|
||||||
import DocumentItem from "./DocumentItem";
|
import DocumentItem from "./DocumentItem"
|
||||||
import { verify } from "../helper";
|
import { verify } from "../helper"
|
||||||
import { VerificationStatus } from "@root/model/account";
|
import { VerificationStatus } from "@root/model/account"
|
||||||
import { sendDocuments, updateDocuments } from "@root/api/verification";
|
import { sendDocuments, updateDocuments } from "@root/api/verification"
|
||||||
import { readFile } from "@root/utils/readFile";
|
import { readFile } from "@root/utils/readFile"
|
||||||
import { deleteEmptyKeys } from "@root/utils/deleteEmptyKeys";
|
import { deleteEmptyKeys } from "@root/utils/deleteEmptyKeys"
|
||||||
import { useState } from "react";
|
import { useState } from "react"
|
||||||
import { theme } from "@root/utils/theme";
|
import { theme } from "@root/utils/theme"
|
||||||
|
|
||||||
const dialogContainerStyle = {
|
const dialogContainerStyle = {
|
||||||
height: "100%",
|
height: "100%",
|
||||||
@ -22,14 +22,14 @@ const dialogContainerStyle = {
|
|||||||
"::-webkit-scrollbar": {
|
"::-webkit-scrollbar": {
|
||||||
display: "none",
|
display: "none",
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
|
||||||
export default function NkoDocumentsDialog() {
|
export default function NkoDocumentsDialog() {
|
||||||
const isOpen = useUserStore((state) => state.isDocumentsDialogOpen);
|
const isOpen = useUserStore((state) => state.isDocumentsDialogOpen)
|
||||||
const verificationStatus = useUserStore((state) => state.verificationStatus);
|
const verificationStatus = useUserStore((state) => state.verificationStatus)
|
||||||
const documents = useUserStore((state) => state.documents);
|
const documents = useUserStore((state) => state.documents)
|
||||||
const documentsUrl = useUserStore((state) => state.documentsUrl);
|
const documentsUrl = useUserStore((state) => state.documentsUrl)
|
||||||
const userId = useUserStore((state) => state.userId) ?? "";
|
const userId = useUserStore((state) => state.userId) ?? ""
|
||||||
|
|
||||||
const sendUploadedDocuments = async () => {
|
const sendUploadedDocuments = async () => {
|
||||||
if (
|
if (
|
||||||
@ -38,51 +38,51 @@ export default function NkoDocumentsDialog() {
|
|||||||
documents["Свидетельство о регистрации НКО"].file
|
documents["Свидетельство о регистрации НКО"].file
|
||||||
&& !documentsUrl["ИНН"] && !documentsUrl["Устав"] && !documentsUrl["Свидетельство о регистрации НКО"]
|
&& !documentsUrl["ИНН"] && !documentsUrl["Устав"] && !documentsUrl["Свидетельство о регистрации НКО"]
|
||||||
) {
|
) {
|
||||||
closeDocumentsDialog();
|
closeDocumentsDialog()
|
||||||
//Пользователь заполнил все поля и на беке пусто
|
//Пользователь заполнил все поля и на беке пусто
|
||||||
const inn = await readFile(documents["ИНН"].file, "binary");
|
const inn = await readFile(documents["ИНН"].file, "binary")
|
||||||
const rule = await readFile(documents["Устав"].file, "binary");
|
const rule = await readFile(documents["Устав"].file, "binary")
|
||||||
const certificate = await readFile(
|
const certificate = await readFile(
|
||||||
documents["Свидетельство о регистрации НКО"].file,
|
documents["Свидетельство о регистрации НКО"].file,
|
||||||
"binary"
|
"binary"
|
||||||
);
|
)
|
||||||
|
|
||||||
const [_, sendDocumentsError] = await sendDocuments({
|
const [_, sendDocumentsError] = await sendDocuments({
|
||||||
status: "nko",
|
status: "nko",
|
||||||
inn,
|
inn,
|
||||||
rule,
|
rule,
|
||||||
certificate,
|
certificate,
|
||||||
});
|
})
|
||||||
|
|
||||||
if (sendDocumentsError) {
|
if (sendDocumentsError) {
|
||||||
enqueueSnackbar(sendDocumentsError);
|
enqueueSnackbar(sendDocumentsError)
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
if (_ === "OK") {
|
if (_ === "OK") {
|
||||||
enqueueSnackbar("Информация доставлена")
|
enqueueSnackbar("Информация доставлена")
|
||||||
}
|
}
|
||||||
|
|
||||||
setDocument("ИНН", null);
|
setDocument("ИНН", null)
|
||||||
setDocument("Устав", null);
|
setDocument("Устав", null)
|
||||||
setDocument("Свидетельство о регистрации НКО", null);
|
setDocument("Свидетельство о регистрации НКО", null)
|
||||||
|
|
||||||
await verify(userId);
|
await verify(userId)
|
||||||
} else { //Пользователь заполнил не все, или на беке что-то есть
|
} else { //Пользователь заполнил не все, или на беке что-то есть
|
||||||
if ((documents["ИНН"].file || documents["Устав"].file || documents["Свидетельство о регистрации НКО"].file) && (documentsUrl["ИНН"] || documentsUrl["Устав"] || documentsUrl["Свидетельство о регистрации НКО"])) { //минимум 1 поле заполнено на фронте и минимум 1 поле на беке записано
|
if ((documents["ИНН"].file || documents["Устав"].file || documents["Свидетельство о регистрации НКО"].file) && (documentsUrl["ИНН"] || documentsUrl["Устав"] || documentsUrl["Свидетельство о регистрации НКО"])) { //минимум 1 поле заполнено на фронте и минимум 1 поле на беке записано
|
||||||
closeDocumentsDialog();
|
closeDocumentsDialog()
|
||||||
const inn = documents["ИНН"].file
|
const inn = documents["ИНН"].file
|
||||||
? await readFile(documents["ИНН"].file, "binary")
|
? await readFile(documents["ИНН"].file, "binary")
|
||||||
: undefined;
|
: undefined
|
||||||
const rule = documents["Устав"].file
|
const rule = documents["Устав"].file
|
||||||
? await readFile(documents["Устав"].file, "binary")
|
? await readFile(documents["Устав"].file, "binary")
|
||||||
: undefined;
|
: undefined
|
||||||
const certificate = documents["Свидетельство о регистрации НКО"].file
|
const certificate = documents["Свидетельство о регистрации НКО"].file
|
||||||
? await readFile(
|
? await readFile(
|
||||||
documents["Свидетельство о регистрации НКО"].file,
|
documents["Свидетельство о регистрации НКО"].file,
|
||||||
"binary"
|
"binary"
|
||||||
)
|
)
|
||||||
: undefined;
|
: undefined
|
||||||
|
|
||||||
const [_, updateDocumentsError] = await updateDocuments(
|
const [_, updateDocumentsError] = await updateDocuments(
|
||||||
deleteEmptyKeys({
|
deleteEmptyKeys({
|
||||||
@ -91,26 +91,26 @@ export default function NkoDocumentsDialog() {
|
|||||||
rule,
|
rule,
|
||||||
certificate,
|
certificate,
|
||||||
})
|
})
|
||||||
);
|
)
|
||||||
|
|
||||||
if (updateDocumentsError) {
|
if (updateDocumentsError) {
|
||||||
enqueueSnackbar(updateDocumentsError);
|
enqueueSnackbar(updateDocumentsError)
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
if (_ === "OK") {
|
if (_ === "OK") {
|
||||||
enqueueSnackbar("Информация доставлена")
|
enqueueSnackbar("Информация доставлена")
|
||||||
}
|
}
|
||||||
|
|
||||||
setDocument("ИНН", null);
|
setDocument("ИНН", null)
|
||||||
setDocument("Устав", null);
|
setDocument("Устав", null)
|
||||||
setDocument("Свидетельство о регистрации НКО", null);
|
setDocument("Свидетельство о регистрации НКО", null)
|
||||||
|
|
||||||
await verify(userId);
|
await verify(userId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
}
|
||||||
|
|
||||||
const disbutt = () => {
|
const disbutt = () => {
|
||||||
if (documents["ИНН"].file && documents["Устав"].file && documents["Свидетельство о регистрации НКО"].file && !documentsUrl["ИНН"] && !documentsUrl["Устав"] && !documentsUrl["Свидетельство о регистрации НКО"]) { //post
|
if (documents["ИНН"].file && documents["Устав"].file && documents["Свидетельство о регистрации НКО"].file && !documentsUrl["ИНН"] && !documentsUrl["Устав"] && !documentsUrl["Свидетельство о регистрации НКО"]) { //post
|
||||||
@ -172,7 +172,7 @@ export default function NkoDocumentsDialog() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
@ -259,5 +259,5 @@ export default function NkoDocumentsDialog() {
|
|||||||
)}
|
)}
|
||||||
<></>
|
<></>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
)
|
||||||
}
|
}
|
@ -1,15 +1,15 @@
|
|||||||
import { Box, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, useMediaQuery, useTheme } from "@mui/material"
|
||||||
import InputTextfield from "@components/InputTextfield";
|
import InputTextfield from "@components/InputTextfield"
|
||||||
import PasswordInput from "@components/passwordInput";
|
import PasswordInput from "@components/passwordInput"
|
||||||
import { setSettingsField, useUserStore } from "@root/stores/user";
|
import { setSettingsField, useUserStore } from "@root/stores/user"
|
||||||
|
|
||||||
|
|
||||||
export default function UserFields () {
|
export default function UserFields () {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upSm = useMediaQuery(theme.breakpoints.up("sm"));
|
const upSm = useMediaQuery(theme.breakpoints.up("sm"))
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
|
|
||||||
const fields = useUserStore((state) => state.settingsFields);
|
const fields = useUserStore((state) => state.settingsFields)
|
||||||
|
|
||||||
console.log("fields")
|
console.log("fields")
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ export default function UserFields () {
|
|||||||
gap: upMd ? "16px" : "10px",
|
gap: upMd ? "16px" : "10px",
|
||||||
color: "#F2F3F7",
|
color: "#F2F3F7",
|
||||||
bold: true,
|
bold: true,
|
||||||
};
|
}
|
||||||
|
|
||||||
return(
|
return(
|
||||||
<Box
|
<Box
|
||||||
|
@ -1,68 +1,68 @@
|
|||||||
import { devlog } from "@frontend/kitui";
|
import { devlog } from "@frontend/kitui"
|
||||||
|
|
||||||
import { verification } from "@root/api/verification";
|
import { verification } from "@root/api/verification"
|
||||||
import {
|
import {
|
||||||
setVerificationStatus,
|
setVerificationStatus,
|
||||||
setVerificationType,
|
setVerificationType,
|
||||||
setComment,
|
setComment,
|
||||||
setDocumentUrl,
|
setDocumentUrl,
|
||||||
} from "@root/stores/user";
|
} from "@root/stores/user"
|
||||||
import { VerificationStatus } from "@root/model/account";
|
import { VerificationStatus } from "@root/model/account"
|
||||||
|
|
||||||
import { DOCUMENT_TYPE_MAP } from "@root/constants/documentTypeMap";
|
import { DOCUMENT_TYPE_MAP } from "@root/constants/documentTypeMap"
|
||||||
|
|
||||||
import type { Verification } from "@root/model/auth";
|
import type { Verification } from "@root/model/auth"
|
||||||
|
|
||||||
const updateVerificationStatus = (verification: Verification) => {
|
const updateVerificationStatus = (verification: Verification) => {
|
||||||
if (!verification) {
|
if (!verification) {
|
||||||
setVerificationStatus(VerificationStatus.NOT_VERIFICATED);
|
setVerificationStatus(VerificationStatus.NOT_VERIFICATED)
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verification.accepted) {
|
if (verification.accepted) {
|
||||||
setVerificationStatus(VerificationStatus.VERIFICATED);
|
setVerificationStatus(VerificationStatus.VERIFICATED)
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(!verification.accepted && !verification.files?.length) ||
|
(!verification.accepted && !verification.files?.length) ||
|
||||||
(!verification.accepted && !verification.comment)
|
(!verification.accepted && !verification.comment)
|
||||||
) {
|
) {
|
||||||
setVerificationStatus(VerificationStatus.NOT_VERIFICATED);
|
setVerificationStatus(VerificationStatus.NOT_VERIFICATED)
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verification.files?.length && !verification.comment) {
|
if (verification.files?.length && !verification.comment) {
|
||||||
setVerificationStatus(VerificationStatus.WAITING);
|
setVerificationStatus(VerificationStatus.WAITING)
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setVerificationStatus(VerificationStatus.NOT_VERIFICATED);
|
setVerificationStatus(VerificationStatus.NOT_VERIFICATED)
|
||||||
};
|
}
|
||||||
|
|
||||||
export const verify = async (id: string) => {
|
export const verify = async (id: string) => {
|
||||||
const [verificationResult, verificationError] = await verification(id);
|
const [verificationResult, verificationError] = await verification(id)
|
||||||
|
|
||||||
if (verificationError) {
|
if (verificationError) {
|
||||||
setVerificationStatus(VerificationStatus.NOT_VERIFICATED);
|
setVerificationStatus(VerificationStatus.NOT_VERIFICATED)
|
||||||
|
|
||||||
devlog("Error fetching user", verificationError);
|
devlog("Error fetching user", verificationError)
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verificationResult) {
|
if (verificationResult) {
|
||||||
updateVerificationStatus(verificationResult);
|
updateVerificationStatus(verificationResult)
|
||||||
setVerificationType(verificationResult.status);
|
setVerificationType(verificationResult.status)
|
||||||
setComment(verificationResult.comment);
|
setComment(verificationResult.comment)
|
||||||
verificationResult.files.forEach((file) =>
|
verificationResult.files.forEach((file) =>
|
||||||
setDocumentUrl(DOCUMENT_TYPE_MAP[file.name], file.url)
|
setDocumentUrl(DOCUMENT_TYPE_MAP[file.name], file.url)
|
||||||
);
|
)
|
||||||
|
|
||||||
devlog("User", verificationResult);
|
devlog("User", verificationResult)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
import { Box, IconButton, Typography, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, IconButton, Typography, useMediaQuery, useTheme } from "@mui/material"
|
||||||
import SectionWrapper from "@components/SectionWrapper";
|
import SectionWrapper from "@components/SectionWrapper"
|
||||||
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
import ArrowBackIcon from "@mui/icons-material/ArrowBack"
|
||||||
import TotalPrice from "@components/TotalPrice";
|
import TotalPrice from "@components/TotalPrice"
|
||||||
import CustomWrapper from "./CustomWrapper";
|
import CustomWrapper from "./CustomWrapper"
|
||||||
import { useCart } from "@root/utils/hooks/useCart";
|
import { useCart } from "@root/utils/hooks/useCart"
|
||||||
import { useLocation } from "react-router-dom";
|
import { useLocation } from "react-router-dom"
|
||||||
import { usePrevLocation } from "@root/utils/hooks/handleCustomBackNavigation";
|
import { usePrevLocation } from "@root/utils/hooks/handleCustomBackNavigation"
|
||||||
import { handleComponentError } from "@root/utils/handleComponentError";
|
import { handleComponentError } from "@root/utils/handleComponentError"
|
||||||
import { withErrorBoundary } from "react-error-boundary";
|
import { withErrorBoundary } from "react-error-boundary"
|
||||||
|
|
||||||
function Cart() {
|
function Cart() {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(550));
|
const isMobile = useMediaQuery(theme.breakpoints.down(550))
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
const isTablet = useMediaQuery(theme.breakpoints.down(1000))
|
||||||
const cart = useCart();
|
const cart = useCart()
|
||||||
const location = useLocation();
|
const location = useLocation()
|
||||||
|
|
||||||
const totalPriceBeforeDiscounts = cart.priceBeforeDiscounts;
|
const totalPriceBeforeDiscounts = cart.priceBeforeDiscounts
|
||||||
const totalPriceAfterDiscounts = cart.priceAfterDiscounts;
|
const totalPriceAfterDiscounts = cart.priceAfterDiscounts
|
||||||
|
|
||||||
const handleCustomBackNavigation = usePrevLocation(location);
|
const handleCustomBackNavigation = usePrevLocation(location)
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -71,7 +71,7 @@ function Cart() {
|
|||||||
</Box>
|
</Box>
|
||||||
<TotalPrice priceBeforeDiscounts={totalPriceBeforeDiscounts} priceAfterDiscounts={totalPriceAfterDiscounts} />
|
<TotalPrice priceBeforeDiscounts={totalPriceBeforeDiscounts} priceAfterDiscounts={totalPriceAfterDiscounts} />
|
||||||
</SectionWrapper>
|
</SectionWrapper>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withErrorBoundary(Cart, {
|
export default withErrorBoundary(Cart, {
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react"
|
||||||
import { Box, SvgIcon, Typography, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, SvgIcon, Typography, useMediaQuery, useTheme } from "@mui/material"
|
||||||
import ExpandIcon from "@components/icons/ExpandIcon";
|
import ExpandIcon from "@components/icons/ExpandIcon"
|
||||||
import ClearIcon from "@mui/icons-material/Clear";
|
import ClearIcon from "@mui/icons-material/Clear"
|
||||||
import { currencyFormatter } from "@root/utils/currencyFormatter";
|
import { currencyFormatter } from "@root/utils/currencyFormatter"
|
||||||
import { removeTariffFromCart } from "@root/stores/user";
|
import { removeTariffFromCart } from "@root/stores/user"
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack"
|
||||||
import { CloseButton, ServiceCartData, getMessageFromFetchError } from "@frontend/kitui";
|
import { CloseButton, ServiceCartData, getMessageFromFetchError } from "@frontend/kitui"
|
||||||
|
|
||||||
import type { MouseEvent } from "react";
|
import type { MouseEvent } from "react"
|
||||||
import CustomTariffAccordion from "@root/components/CustomTariffAccordion";
|
import CustomTariffAccordion from "@root/components/CustomTariffAccordion"
|
||||||
|
|
||||||
const name: Record<string, string> = {
|
const name: Record<string, string> = {
|
||||||
templategen: "Шаблонизатор",
|
templategen: "Шаблонизатор",
|
||||||
squiz: "Опросник",
|
squiz: "Опросник",
|
||||||
reducer: "Сокращатель ссылок",
|
reducer: "Сокращатель ссылок",
|
||||||
custom: "Кастомные тарифы",
|
custom: "Кастомные тарифы",
|
||||||
};
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
serviceData: ServiceCartData;
|
serviceData: ServiceCartData;
|
||||||
@ -24,35 +24,35 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function CustomWrapper({ serviceData, last, first }: Props) {
|
export default function CustomWrapper({ serviceData, last, first }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
const upSm = useMediaQuery(theme.breakpoints.up("sm"));
|
const upSm = useMediaQuery(theme.breakpoints.up("sm"))
|
||||||
const [isExpanded, setIsExpanded] = useState<boolean>(false);
|
const [isExpanded, setIsExpanded] = useState<boolean>(false)
|
||||||
|
|
||||||
function handleItemDeleteClick(tariffId: string) {
|
function handleItemDeleteClick(tariffId: string) {
|
||||||
removeTariffFromCart(tariffId)
|
removeTariffFromCart(tariffId)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
enqueueSnackbar("Тариф удален");
|
enqueueSnackbar("Тариф удален")
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
const message = getMessageFromFetchError(error);
|
const message = getMessageFromFetchError(error)
|
||||||
if (message) enqueueSnackbar(message);
|
if (message) enqueueSnackbar(message)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteService = async (event: MouseEvent<HTMLButtonElement>) => {
|
const deleteService = async (event: MouseEvent<HTMLButtonElement>) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation()
|
||||||
|
|
||||||
setIsExpanded(false);
|
setIsExpanded(false)
|
||||||
|
|
||||||
for (const { id } of serviceData.tariffs) {
|
for (const { id } of serviceData.tariffs) {
|
||||||
try {
|
try {
|
||||||
await removeTariffFromCart(id);
|
await removeTariffFromCart(id)
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
enqueueSnackbar("Тарифы удалены");
|
enqueueSnackbar("Тарифы удалены")
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@ -133,7 +133,7 @@ export default function CustomWrapper({ serviceData, last, first }: Props) {
|
|||||||
</Box>
|
</Box>
|
||||||
{isExpanded &&
|
{isExpanded &&
|
||||||
serviceData.tariffs.map((tariff) => {
|
serviceData.tariffs.map((tariff) => {
|
||||||
const privilege = tariff.privileges[0];
|
const privilege = tariff.privileges[0]
|
||||||
|
|
||||||
|
|
||||||
return tariff.privileges.length > 1 ? (
|
return tariff.privileges.length > 1 ? (
|
||||||
@ -192,9 +192,9 @@ export default function CustomWrapper({ serviceData, last, first }: Props) {
|
|||||||
<SvgIcon component={ClearIcon} sx={{ fill: theme.palette.purple.main }} />
|
<SvgIcon component={ClearIcon} sx={{ fill: theme.palette.purple.main }} />
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
})}
|
})}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Box } from "@mui/material";
|
import { Box } from "@mui/material"
|
||||||
|
|
||||||
import CustomAccordion from "@components/CustomAccordion";
|
import CustomAccordion from "@components/CustomAccordion"
|
||||||
import { cardShadow } from "@root/utils/theme";
|
import { cardShadow } from "@root/utils/theme"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
content: [string, string][];
|
content: [string, string][];
|
||||||
@ -20,5 +20,5 @@ export default function AccordionWrapper({ content }: Props) {
|
|||||||
<CustomAccordion key={index} header={accordionItem[0]} text={accordionItem[1]} />
|
<CustomAccordion key={index} header={accordionItem[0]} text={accordionItem[1]} />
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import { IconButton, useMediaQuery, useTheme } from "@mui/material";
|
import { IconButton, useMediaQuery, useTheme } from "@mui/material"
|
||||||
import { Select } from "@root/components/Select";
|
import { Select } from "@root/components/Select"
|
||||||
import { Box } from "@mui/material";
|
import { Box } from "@mui/material"
|
||||||
import { Typography } from "@mui/material";
|
import { Typography } from "@mui/material"
|
||||||
import { useState } from "react";
|
import { useState } from "react"
|
||||||
import SectionWrapper from "../../components/SectionWrapper";
|
import SectionWrapper from "../../components/SectionWrapper"
|
||||||
import AccordionWrapper from "./AccordionWrapper";
|
import AccordionWrapper from "./AccordionWrapper"
|
||||||
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
import ArrowBackIcon from "@mui/icons-material/ArrowBack"
|
||||||
import { Tabs } from "@root/components/Tabs";
|
import { Tabs } from "@root/components/Tabs"
|
||||||
import { useHistoryTracker } from "@root/utils/hooks/useHistoryTracker";
|
import { useHistoryTracker } from "@root/utils/hooks/useHistoryTracker"
|
||||||
|
|
||||||
const subPages = ["Pena hub", "Шаблоны", "Опросы", "Ссылки", "Финансовые", "Юридические", "Юридические лица"];
|
const subPages = ["Pena hub", "Шаблоны", "Опросы", "Ссылки", "Финансовые", "Юридические", "Юридические лица"]
|
||||||
|
|
||||||
export default function Faq() {
|
export default function Faq() {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(550));
|
const isMobile = useMediaQuery(theme.breakpoints.down(550))
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
const isTablet = useMediaQuery(theme.breakpoints.down(1000))
|
||||||
const [tabIndex, setTabIndex] = useState<number>(0);
|
const [tabIndex, setTabIndex] = useState<number>(0)
|
||||||
const [selectedItem, setSelectedItem] = useState<number>(0);
|
const [selectedItem, setSelectedItem] = useState<number>(0)
|
||||||
|
|
||||||
const handleCustomBackNavigation = useHistoryTracker();
|
const handleCustomBackNavigation = useHistoryTracker()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionWrapper
|
<SectionWrapper
|
||||||
@ -164,7 +164,7 @@ export default function Faq() {
|
|||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</SectionWrapper>
|
</SectionWrapper>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TabPanelProps {
|
interface TabPanelProps {
|
||||||
@ -179,5 +179,5 @@ function TabPanel({ index, value, children, mt }: TabPanelProps) {
|
|||||||
<Box hidden={index !== value} sx={{ mt }}>
|
<Box hidden={index !== value} sx={{ mt }}>
|
||||||
{children}
|
{children}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { Box, IconButton, Typography, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, IconButton, Typography, useMediaQuery, useTheme } from "@mui/material"
|
||||||
import CustomAccordion from "@components/CustomAccordion";
|
import CustomAccordion from "@components/CustomAccordion"
|
||||||
import File from "@components/icons/File"
|
import File from "@components/icons/File"
|
||||||
import { getDeclension } from "@utils/declension"
|
import { getDeclension } from "@utils/declension"
|
||||||
import { enqueueSnackbar } from "notistack";
|
import { enqueueSnackbar } from "notistack"
|
||||||
import { addTariffToCart } from "@root/stores/user";
|
import { addTariffToCart } from "@root/stores/user"
|
||||||
import { getMessageFromFetchError } from "@frontend/kitui";
|
import { getMessageFromFetchError } from "@frontend/kitui"
|
||||||
|
|
||||||
export type History = {
|
export type History = {
|
||||||
title: string;
|
title: string;
|
||||||
@ -25,32 +25,32 @@ interface AccordionWrapperProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function AccordionWrapper({ content, last, first, createdAt }: AccordionWrapperProps) {
|
export default function AccordionWrapper({ content, last, first, createdAt }: AccordionWrapperProps) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
const upSm = useMediaQuery(theme.breakpoints.up("sm"));
|
const upSm = useMediaQuery(theme.breakpoints.up("sm"))
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(900));
|
const isTablet = useMediaQuery(theme.breakpoints.down(900))
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(560));
|
const isMobile = useMediaQuery(theme.breakpoints.down(560))
|
||||||
|
|
||||||
const valuesByKey: any = {};
|
const valuesByKey: any = {}
|
||||||
content[0].Value[0].forEach((item) => {
|
content[0].Value[0].forEach((item) => {
|
||||||
valuesByKey[item.Key] = item.Value;
|
valuesByKey[item.Key] = item.Value
|
||||||
});
|
})
|
||||||
console.log(content)
|
console.log(content)
|
||||||
console.log(content[0])
|
console.log(content[0])
|
||||||
console.log(content[0].Value)
|
console.log(content[0].Value)
|
||||||
console.log(valuesByKey)
|
console.log(valuesByKey)
|
||||||
const extractDateFromString = (tariffName: string) => {
|
const extractDateFromString = (tariffName: string) => {
|
||||||
const dateMatch = tariffName.match(/\d{4}-\d{2}-\d{2}/);
|
const dateMatch = tariffName.match(/\d{4}-\d{2}-\d{2}/)
|
||||||
return dateMatch ? dateMatch[0] : null;
|
return dateMatch ? dateMatch[0] : null
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
async function handleTariffItemClick(tariffId: string) {
|
async function handleTariffItemClick(tariffId: string) {
|
||||||
const { patchCartError } = await addTariffToCart(tariffId)
|
const { patchCartError } = await addTariffToCart(tariffId)
|
||||||
if (patchCartError) {
|
if (patchCartError) {
|
||||||
enqueueSnackbar(patchCartError);
|
enqueueSnackbar(patchCartError)
|
||||||
} else {
|
} else {
|
||||||
enqueueSnackbar("Тариф добавлен в корзину");
|
enqueueSnackbar("Тариф добавлен в корзину")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,10 +118,10 @@ export default function AccordionWrapper({ content, last, first, createdAt }: Ac
|
|||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
color: valuesByKey.expired ? theme.palette.text.disabled : theme.palette.gray.dark,
|
color: valuesByKey.expired ? theme.palette.text.disabled : theme.palette.gray.dark,
|
||||||
px: 0,
|
px: 0,
|
||||||
width: '200px',
|
width: "200px",
|
||||||
maxWidth: '200px',
|
maxWidth: "200px",
|
||||||
overflow: 'hidden',
|
overflow: "hidden",
|
||||||
textOverflow: 'ellipsis'
|
textOverflow: "ellipsis"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{valuesByKey.iscustom ? "Кастомный тариф" : valuesByKey.name}
|
{valuesByKey.iscustom ? "Кастомный тариф" : valuesByKey.name}
|
||||||
@ -150,10 +150,10 @@ export default function AccordionWrapper({ content, last, first, createdAt }: Ac
|
|||||||
>
|
>
|
||||||
{valuesByKey.payMethod && <Typography
|
{valuesByKey.payMethod && <Typography
|
||||||
sx={{
|
sx={{
|
||||||
maxWidth: '300px',
|
maxWidth: "300px",
|
||||||
width: '300px',
|
width: "300px",
|
||||||
overflow: 'hidden',
|
overflow: "hidden",
|
||||||
textOverflow: 'ellipsis'
|
textOverflow: "ellipsis"
|
||||||
}}
|
}}
|
||||||
>Способ оплаты: {valuesByKey.payMethod}</Typography>}
|
>Способ оплаты: {valuesByKey.payMethod}</Typography>}
|
||||||
</Typography>
|
</Typography>
|
||||||
@ -236,5 +236,5 @@ export default function AccordionWrapper({ content, last, first, createdAt }: Ac
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { History } from "./AccordionWrapper";
|
import type { History } from "./AccordionWrapper"
|
||||||
|
|
||||||
const PAYMENT_HISTORY: History[] = [
|
const PAYMENT_HISTORY: History[] = [
|
||||||
{
|
{
|
||||||
@ -43,7 +43,7 @@ const PAYMENT_HISTORY: History[] = [
|
|||||||
info: "6 190 руб.",
|
info: "6 190 руб.",
|
||||||
description: "Дата действия приобретенной лицензии (в формате дд.мм.гггг-дд.мм.гггг) Или же объем",
|
description: "Дата действия приобретенной лицензии (в формате дд.мм.гггг-дд.мм.гггг) Или же объем",
|
||||||
},
|
},
|
||||||
];
|
]
|
||||||
|
|
||||||
const PURCHASED_TARIFFS_HISTORY: History[] = [
|
const PURCHASED_TARIFFS_HISTORY: History[] = [
|
||||||
{
|
{
|
||||||
@ -82,7 +82,7 @@ const PURCHASED_TARIFFS_HISTORY: History[] = [
|
|||||||
info: "9 месяцев 1 000 шаблонов",
|
info: "9 месяцев 1 000 шаблонов",
|
||||||
description: "Тариф на время/ объем/ кастомный или другая информация",
|
description: "Тариф на время/ объем/ кастомный или другая информация",
|
||||||
},
|
},
|
||||||
];
|
]
|
||||||
|
|
||||||
const FINISHED_TARIFFS_HISTORY: History[] = [
|
const FINISHED_TARIFFS_HISTORY: History[] = [
|
||||||
{
|
{
|
||||||
@ -124,6 +124,6 @@ const FINISHED_TARIFFS_HISTORY: History[] = [
|
|||||||
expired: true,
|
expired: true,
|
||||||
description: "Тариф на время/ объем/ кастомный или другая информация",
|
description: "Тариф на время/ объем/ кастомный или другая информация",
|
||||||
},
|
},
|
||||||
];
|
]
|
||||||
|
|
||||||
export const HISTORY: History[][] = [PAYMENT_HISTORY, PURCHASED_TARIFFS_HISTORY, FINISHED_TARIFFS_HISTORY];
|
export const HISTORY: History[][] = [PAYMENT_HISTORY, PURCHASED_TARIFFS_HISTORY, FINISHED_TARIFFS_HISTORY]
|
||||||
|
@ -1,36 +1,36 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react"
|
||||||
import { Box, IconButton, Typography, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, IconButton, Typography, useMediaQuery, useTheme } from "@mui/material"
|
||||||
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
import ArrowBackIcon from "@mui/icons-material/ArrowBack"
|
||||||
|
|
||||||
import SectionWrapper from "@root/components/SectionWrapper";
|
import SectionWrapper from "@root/components/SectionWrapper"
|
||||||
import { Select } from "@root/components/Select";
|
import { Select } from "@root/components/Select"
|
||||||
import { Tabs } from "@root/components/Tabs";
|
import { Tabs } from "@root/components/Tabs"
|
||||||
|
|
||||||
import AccordionWrapper from "./AccordionWrapper";
|
import AccordionWrapper from "./AccordionWrapper"
|
||||||
import { HISTORY } from "./historyMocks";
|
import { HISTORY } from "./historyMocks"
|
||||||
import { useHistoryTracker } from "@root/utils/hooks/useHistoryTracker";
|
import { useHistoryTracker } from "@root/utils/hooks/useHistoryTracker"
|
||||||
import { useHistoryData } from "@root/utils/hooks/useHistoryData";
|
import { useHistoryData } from "@root/utils/hooks/useHistoryData"
|
||||||
import { isArray } from "cypress/types/lodash";
|
import { isArray } from "cypress/types/lodash"
|
||||||
import { ErrorBoundary } from "react-error-boundary";
|
import { ErrorBoundary } from "react-error-boundary"
|
||||||
import { handleComponentError } from "@root/utils/handleComponentError";
|
import { handleComponentError } from "@root/utils/handleComponentError"
|
||||||
|
|
||||||
const subPages = ["Платежи", "Покупки тарифов", "Окончания тарифов"];
|
const subPages = ["Платежи", "Покупки тарифов", "Окончания тарифов"]
|
||||||
|
|
||||||
export default function History() {
|
export default function History() {
|
||||||
const [selectedItem, setSelectedItem] = useState<number>(0);
|
const [selectedItem, setSelectedItem] = useState<number>(0)
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
const isMobile = useMediaQuery(theme.breakpoints.down(600))
|
||||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
const isTablet = useMediaQuery(theme.breakpoints.down(1000))
|
||||||
const { historyData, error } = useHistoryData();
|
const { historyData, error } = useHistoryData()
|
||||||
|
|
||||||
const handleCustomBackNavigation = useHistoryTracker();
|
const handleCustomBackNavigation = useHistoryTracker()
|
||||||
|
|
||||||
const extractDateFromString = (tariffName: string) => {
|
const extractDateFromString = (tariffName: string) => {
|
||||||
const dateMatch = tariffName.match(/\d{4}-\d{2}-\d{2}/);
|
const dateMatch = tariffName.match(/\d{4}-\d{2}-\d{2}/)
|
||||||
return dateMatch ? dateMatch[0] : "";
|
return dateMatch ? dateMatch[0] : ""
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionWrapper
|
<SectionWrapper
|
||||||
@ -94,5 +94,5 @@ export default function History() {
|
|||||||
)})}
|
)})}
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</SectionWrapper>
|
</SectionWrapper>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
|
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
bigText: string;
|
bigText: string;
|
||||||
@ -7,8 +7,8 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Infographics({ bigText, text, flex }: Props) {
|
export default function Infographics({ bigText, text, flex }: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme()
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@ -30,5 +30,5 @@ export default function Infographics({ bigText, text, flex }: Props) {
|
|||||||
{text}
|
{text}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import { Box, CssBaseline, ThemeProvider, useTheme, useMediaQuery } from "@mui/material";
|
import { Box, CssBaseline, ThemeProvider, useTheme, useMediaQuery } from "@mui/material"
|
||||||
import Section1 from "./Section1";
|
import Section1 from "./Section1"
|
||||||
import Section2 from "./Section2";
|
import Section2 from "./Section2"
|
||||||
import Section3 from "./Section3";
|
import Section3 from "./Section3"
|
||||||
import Section4 from "./Section4";
|
import Section4 from "./Section4"
|
||||||
import Section5 from "./Section5";
|
import Section5 from "./Section5"
|
||||||
import FloatingSupportChat from "@root/components/FloatingSupportChat/FloatingSupportChat";
|
import FloatingSupportChat from "@root/components/FloatingSupportChat/FloatingSupportChat"
|
||||||
import Footer from "@root/components/Footer";
|
import Footer from "@root/components/Footer"
|
||||||
import Navbar from "@root/components/NavbarLanding/Navbar";
|
import Navbar from "@root/components/NavbarLanding/Navbar"
|
||||||
import { theme } from "@root/utils/theme";
|
import { theme } from "@root/utils/theme"
|
||||||
|
|
||||||
export default function Landing() {
|
export default function Landing() {
|
||||||
const muiTheme = useTheme();
|
const muiTheme = useTheme()
|
||||||
const isTablet = useMediaQuery(muiTheme.breakpoints.down(900));
|
const isTablet = useMediaQuery(muiTheme.breakpoints.down(900))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
@ -33,5 +33,5 @@ export default function Landing() {
|
|||||||
<FloatingSupportChat />
|
<FloatingSupportChat />
|
||||||
</Box>
|
</Box>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user