refactor: makeRequests decomposed
This commit is contained in:
parent
8e79f16640
commit
9261f3e797
@ -9,7 +9,7 @@ import type {
|
||||
RegisterResponse,
|
||||
} from "@frontend/kitui";
|
||||
|
||||
const apiUrl = process.env.REACT_APP_DOMAIN + "/auth";
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/auth`;
|
||||
|
||||
export async function register(
|
||||
login: string,
|
||||
@ -21,7 +21,7 @@ export async function register(
|
||||
RegisterRequest,
|
||||
RegisterResponse
|
||||
>({
|
||||
url: apiUrl + "/register",
|
||||
url: `${API_URL}/register`,
|
||||
body: { login, password, phoneNumber },
|
||||
useToken: false,
|
||||
withCredentials: true,
|
||||
@ -41,7 +41,7 @@ export async function login(
|
||||
): Promise<[LoginResponse | null, string?]> {
|
||||
try {
|
||||
const loginResponse = await makeRequest<LoginRequest, LoginResponse>({
|
||||
url: apiUrl + "/login",
|
||||
url: `${API_URL}/login`,
|
||||
body: { login, password },
|
||||
useToken: false,
|
||||
withCredentials: true,
|
||||
@ -60,14 +60,20 @@ export async function recover(
|
||||
): Promise<[unknown | null, string?]> {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append("email", email);
|
||||
formData.append("RedirectionURL", process.env.REACT_APP_DOMAIN + "/changepwd")
|
||||
formData.append(
|
||||
"RedirectionURL",
|
||||
`${process.env.REACT_APP_DOMAIN}/changepwd`
|
||||
);
|
||||
|
||||
const recoverResponse = await makeRequest<unknown, unknown>({
|
||||
url: process.env.REACT_APP_DOMAIN + "/codeword/recover",
|
||||
url: `${process.env.REACT_APP_DOMAIN}/codeword/recover`,
|
||||
body: formData,
|
||||
useToken: false,
|
||||
withCredentials: true,
|
||||
});
|
||||
|
||||
return [recoverResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
@ -79,7 +85,7 @@ export async function recover(
|
||||
export async function logout(): Promise<[unknown, string?]> {
|
||||
try {
|
||||
const logoutResponse = await makeRequest<never, void>({
|
||||
url: apiUrl + "/logout",
|
||||
url: `${API_URL}/logout`,
|
||||
method: "POST",
|
||||
useToken: true,
|
||||
withCredentials: true,
|
||||
|
113
src/api/cart.ts
113
src/api/cart.ts
@ -1,83 +1,84 @@
|
||||
import { UserAccount } from "@frontend/kitui"
|
||||
import makeRequest from "@api/makeRequest"
|
||||
import { UserAccount } from "@frontend/kitui";
|
||||
import makeRequest from "@api/makeRequest";
|
||||
|
||||
import { parseAxiosError } from "@root/utils/parse-error"
|
||||
import { parseAxiosError } from "@root/utils/parse-error";
|
||||
|
||||
const apiUrl = process.env.REACT_APP_DOMAIN + "/customer"
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/customer`;
|
||||
|
||||
export async function patchCart(
|
||||
tariffId: string
|
||||
tariffId: string
|
||||
): Promise<[string[], string?]> {
|
||||
try {
|
||||
const patchCartResponse = await makeRequest<never, UserAccount>({
|
||||
url: apiUrl + `/cart?id=${tariffId}`,
|
||||
method: "PATCH",
|
||||
useToken: true,
|
||||
})
|
||||
try {
|
||||
const patchCartResponse = await makeRequest<never, UserAccount>({
|
||||
url: `${API_URL}/cart?id=${tariffId}`,
|
||||
method: "PATCH",
|
||||
useToken: true,
|
||||
});
|
||||
|
||||
return [patchCartResponse.cart]
|
||||
} catch (nativeError) {
|
||||
let [error, status] = parseAxiosError(nativeError)
|
||||
if (status === 400 && error.indexOf("invalid id") !== -1) error = "Данный тариф более недоступен"
|
||||
return [patchCartResponse.cart];
|
||||
} catch (nativeError) {
|
||||
let [error, status] = parseAxiosError(nativeError);
|
||||
if (status === 400 && error.indexOf("invalid id") !== -1)
|
||||
error = "Данный тариф более недоступен";
|
||||
|
||||
return [[], `Не удалось добавить товар в корзину. ${error}`]
|
||||
}
|
||||
return [[], `Не удалось добавить товар в корзину. ${error}`];
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteCart(
|
||||
tariffId: string
|
||||
tariffId: string
|
||||
): Promise<[string[], string?]> {
|
||||
try {
|
||||
const deleteCartResponse = await makeRequest<never, UserAccount>({
|
||||
url: apiUrl + `/cart?id=${tariffId}`,
|
||||
method: "DELETE",
|
||||
useToken: true,
|
||||
})
|
||||
try {
|
||||
const deleteCartResponse = await makeRequest<never, UserAccount>({
|
||||
url: `${API_URL}/cart?id=${tariffId}`,
|
||||
method: "DELETE",
|
||||
useToken: true,
|
||||
});
|
||||
|
||||
return [deleteCartResponse.cart]
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError)
|
||||
return [deleteCartResponse.cart];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [[], `Не удалось удалить товар из корзины. ${error}`]
|
||||
}
|
||||
return [[], `Не удалось удалить товар из корзины. ${error}`];
|
||||
}
|
||||
}
|
||||
|
||||
export async function payCart(): Promise<[UserAccount | null, string?]> {
|
||||
try {
|
||||
const payCartResponse = await makeRequest<never, UserAccount>({
|
||||
url: apiUrl + "/cart/pay",
|
||||
method: "POST",
|
||||
useToken: true,
|
||||
})
|
||||
try {
|
||||
const payCartResponse = await makeRequest<never, UserAccount>({
|
||||
url: `${API_URL}/cart/pay`,
|
||||
method: "POST",
|
||||
useToken: true,
|
||||
});
|
||||
|
||||
return [payCartResponse]
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError)
|
||||
return [payCartResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось оплатить товар из корзины. ${error}`]
|
||||
}
|
||||
return [null, `Не удалось оплатить товар из корзины. ${error}`];
|
||||
}
|
||||
}
|
||||
|
||||
export async function patchCurrency(
|
||||
currency: string
|
||||
currency: string
|
||||
): Promise<[UserAccount | null, string?]> {
|
||||
try {
|
||||
const patchCurrencyResponse = await makeRequest<
|
||||
try {
|
||||
const patchCurrencyResponse = await makeRequest<
|
||||
{ currency: string },
|
||||
UserAccount
|
||||
>({
|
||||
url: apiUrl + "/wallet",
|
||||
method: "PATCH",
|
||||
useToken: true,
|
||||
body: {
|
||||
currency,
|
||||
},
|
||||
})
|
||||
url: `${API_URL}/wallet`,
|
||||
method: "PATCH",
|
||||
useToken: true,
|
||||
body: {
|
||||
currency,
|
||||
},
|
||||
});
|
||||
|
||||
return [patchCurrencyResponse]
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError)
|
||||
return [patchCurrencyResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось изменить валюту. ${error}`]
|
||||
}
|
||||
}
|
||||
return [null, `Не удалось изменить валюту. ${error}`];
|
||||
}
|
||||
}
|
||||
|
@ -1,84 +1,128 @@
|
||||
import {Tariff} from "@frontend/kitui"
|
||||
import {parseAxiosError} from "@root/utils/parse-error"
|
||||
import makeRequest from "@api/makeRequest"
|
||||
import { Tariff } from "@frontend/kitui";
|
||||
import { parseAxiosError } from "@root/utils/parse-error";
|
||||
import makeRequest from "@api/makeRequest";
|
||||
|
||||
export interface GetHistoryResponse {
|
||||
totalPages: number;
|
||||
records: HistoryRecord[];
|
||||
totalPages: number;
|
||||
records: HistoryRecord[];
|
||||
}
|
||||
|
||||
export type HistoryRecord = {
|
||||
comment: string;
|
||||
createdAt: string;
|
||||
id: string;
|
||||
isDeleted: boolean;
|
||||
key: string;
|
||||
rawDetails: [RawDetails, KeyValue];
|
||||
updatedAt: string;
|
||||
userId: string;
|
||||
comment: string;
|
||||
createdAt: string;
|
||||
id: string;
|
||||
isDeleted: boolean;
|
||||
key: string;
|
||||
rawDetails: [RawDetails, KeyValue];
|
||||
updatedAt: string;
|
||||
userId: string;
|
||||
};
|
||||
|
||||
export interface GetHistoryResponse2 {
|
||||
totalPages: number;
|
||||
records: HistoryRecord2[];
|
||||
totalPages: number;
|
||||
records: HistoryRecord2[];
|
||||
}
|
||||
|
||||
export type HistoryRecord2 = {
|
||||
comment: string;
|
||||
createdAt: string;
|
||||
id: string;
|
||||
isDeleted: boolean;
|
||||
key: string;
|
||||
rawDetails: {
|
||||
price: number;
|
||||
tariffs: Tariff[];
|
||||
};
|
||||
updatedAt: string;
|
||||
userId: string;
|
||||
comment: string;
|
||||
createdAt: string;
|
||||
id: string;
|
||||
isDeleted: boolean;
|
||||
key: string;
|
||||
rawDetails: {
|
||||
price: number;
|
||||
tariffs: Tariff[];
|
||||
};
|
||||
updatedAt: string;
|
||||
userId: string;
|
||||
};
|
||||
|
||||
|
||||
export type KeyValue = { Key: string; Value: string | number };
|
||||
|
||||
export type RawDetails = {
|
||||
Key: "tariffs" | "price";
|
||||
Value: string | number | KeyValue[][];
|
||||
}
|
||||
Key: "tariffs" | "price";
|
||||
Value: string | number | KeyValue[][];
|
||||
};
|
||||
|
||||
export async function getHistory(): Promise<[GetHistoryResponse | GetHistoryResponse2 | null, string?]> {
|
||||
try {
|
||||
const historyResponse = await makeRequest<never, GetHistoryResponse|GetHistoryResponse2>({
|
||||
url: process.env.REACT_APP_DOMAIN + "/customer/history?page=1&limit=100&type=payCart",
|
||||
method: "get",
|
||||
useToken: true,
|
||||
})
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/customer`;
|
||||
|
||||
if (!Array.isArray(historyResponse.records[0]?.rawDetails)) {
|
||||
return [historyResponse] as [GetHistoryResponse2]
|
||||
}
|
||||
export async function getHistory(): Promise<
|
||||
[GetHistoryResponse | GetHistoryResponse2 | null, string?]
|
||||
> {
|
||||
try {
|
||||
const historyResponse = await makeRequest<
|
||||
never,
|
||||
GetHistoryResponse | GetHistoryResponse2
|
||||
>({
|
||||
url: `${API_URL}/history?page=1&limit=100&type=payCart`,
|
||||
method: "GET",
|
||||
useToken: true,
|
||||
});
|
||||
|
||||
const checked = historyResponse.records.map((data) => {
|
||||
//const buffer:RawDetails[] = [];
|
||||
/*(data.rawDetails as HistoryRecord["rawDetails"]).forEach((slot) => {
|
||||
if (!Array.isArray(historyResponse.records[0]?.rawDetails)) {
|
||||
return [historyResponse] as [GetHistoryResponse2];
|
||||
}
|
||||
|
||||
const checked = historyResponse.records.map((data) => {
|
||||
//const buffer:RawDetails[] = [];
|
||||
/*(data.rawDetails as HistoryRecord["rawDetails"]).forEach((slot) => {
|
||||
const index = regList[slot.Key]
|
||||
buffer[index] = { ...slot }
|
||||
})*/
|
||||
//Чистим дыры с помощью .filter(() => true)
|
||||
//@ts-ignore
|
||||
//data.rawDetails = buffer
|
||||
const checkedRowDetails = [
|
||||
(data.rawDetails as HistoryRecord["rawDetails"]).find((details) => details.Key === "tariffs") as RawDetails,
|
||||
(data.rawDetails as HistoryRecord["rawDetails"]).find((details) => details.Key === "price") as KeyValue
|
||||
]
|
||||
return {...data, rawDetails: checkedRowDetails} as HistoryRecord
|
||||
})
|
||||
//Чистим дыры с помощью .filter(() => true)
|
||||
//@ts-ignore
|
||||
//data.rawDetails = buffer
|
||||
const checkedRowDetails = [
|
||||
(data.rawDetails as HistoryRecord["rawDetails"]).find(
|
||||
(details) => details.Key === "tariffs"
|
||||
) as RawDetails,
|
||||
(data.rawDetails as HistoryRecord["rawDetails"]).find(
|
||||
(details) => details.Key === "price"
|
||||
) as KeyValue,
|
||||
];
|
||||
return { ...data, rawDetails: checkedRowDetails } as HistoryRecord;
|
||||
});
|
||||
|
||||
historyResponse.records = checked || [];
|
||||
return [historyResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
historyResponse.records = checked || []
|
||||
return [historyResponse]
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError)
|
||||
|
||||
return [null, `Не удалось получить историю. ${error}`]
|
||||
}
|
||||
return [null, `Не удалось получить историю. ${error}`];
|
||||
}
|
||||
}
|
||||
|
||||
export const sendReport = async (
|
||||
id: string
|
||||
): Promise<[unknown | null, string?]> => {
|
||||
try {
|
||||
const sendReportResponse = await makeRequest<{ id: string }, unknown>({
|
||||
url: `${API_URL}/sendReport`,
|
||||
method: "POST",
|
||||
body: { id },
|
||||
});
|
||||
|
||||
return [sendReportResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [[], `Не удалось отправить отчёт. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
export const sendReportById = async (
|
||||
tariffId: string
|
||||
): Promise<[unknown | null, string?]> => {
|
||||
try {
|
||||
const sendReportResponse = await makeRequest<never, unknown>({
|
||||
url: `${API_URL}/sendReport/${tariffId}`,
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
return [sendReportResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [[], `Не удалось отправить отчёт. ${error}`];
|
||||
}
|
||||
};
|
||||
|
@ -5,30 +5,44 @@ import { clearUserData } from "@root/stores/user";
|
||||
import { clearCustomTariffs } from "@root/stores/customTariffs";
|
||||
import { clearTickets } from "@root/stores/tickets";
|
||||
import { redirect } from "react-router-dom";
|
||||
import {setNotEnoughMoneyAmount} from "@stores/cart"
|
||||
import { setNotEnoughMoneyAmount } from "@stores/cart";
|
||||
|
||||
interface MakeRequest { method?: Method | undefined; url: string; body?: unknown; useToken?: boolean | undefined; contentType?: boolean | undefined; responseType?: ResponseType | undefined; signal?: AbortSignal | undefined; withCredentials?: boolean | undefined; }
|
||||
interface MakeRequest {
|
||||
method?: Method | undefined;
|
||||
url: string;
|
||||
body?: unknown;
|
||||
useToken?: boolean | undefined;
|
||||
contentType?: boolean | undefined;
|
||||
responseType?: ResponseType | undefined;
|
||||
signal?: AbortSignal | undefined;
|
||||
withCredentials?: boolean | undefined;
|
||||
}
|
||||
interface ErrorResponseData {
|
||||
message?: string;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
async function makeRequest<TRequest = unknown, TResponse = unknown>(data: MakeRequest): Promise<TResponse> {
|
||||
try {
|
||||
const response = await KIT.makeRequest<unknown>(data)
|
||||
async function makeRequest<TRequest = unknown, TResponse = unknown>(
|
||||
data: MakeRequest
|
||||
): Promise<TResponse> {
|
||||
try {
|
||||
const response = await KIT.makeRequest<unknown>(data);
|
||||
|
||||
return response as TResponse
|
||||
} catch (e) {
|
||||
const error = e as AxiosError;
|
||||
if (error.response?.status === 400 && (error.response?.data as ErrorResponseData)?.message === "refreshToken is empty") {
|
||||
|
||||
clearAuthToken();
|
||||
clearUserData();
|
||||
clearCustomTariffs();
|
||||
clearTickets();
|
||||
setNotEnoughMoneyAmount(0)
|
||||
redirect("/");
|
||||
}
|
||||
throw e
|
||||
};
|
||||
};
|
||||
return response as TResponse;
|
||||
} catch (e) {
|
||||
const error = e as AxiosError;
|
||||
if (
|
||||
error.response?.status === 400 &&
|
||||
(error.response?.data as ErrorResponseData)?.message ===
|
||||
"refreshToken is empty"
|
||||
) {
|
||||
clearAuthToken();
|
||||
clearUserData();
|
||||
clearCustomTariffs();
|
||||
clearTickets();
|
||||
setNotEnoughMoneyAmount(0);
|
||||
redirect("/");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
export default makeRequest;
|
||||
|
@ -5,7 +5,7 @@ import { parseAxiosError } from "@root/utils/parse-error";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import useSWR from "swr";
|
||||
|
||||
const apiUrl = process.env.REACT_APP_DOMAIN + "/price";
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/price`;
|
||||
|
||||
export async function getDiscounts(userId: string | null) {
|
||||
if (userId === null) {
|
||||
@ -14,7 +14,7 @@ export async function getDiscounts(userId: string | null) {
|
||||
|
||||
try {
|
||||
const discountsResponse = await makeRequest<never, GetDiscountsResponse>({
|
||||
url: `${apiUrl}/discount/user/${userId}`,
|
||||
url: `${API_URL}/discount/user/${userId}`,
|
||||
method: "get",
|
||||
useToken: true,
|
||||
});
|
||||
|
@ -1,28 +1,19 @@
|
||||
import makeRequest from "@api/makeRequest"
|
||||
import makeRequest from "@api/makeRequest";
|
||||
|
||||
import { parseAxiosError } from "@utils/parse-error";
|
||||
|
||||
const apiUrl = process.env.REACT_APP_DOMAIN + "/codeword/promocode";
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/codeword/promocode`;
|
||||
|
||||
export async function activatePromocode(promocode: string) {
|
||||
try {
|
||||
const response = await makeRequest<
|
||||
| {
|
||||
codeword: string;
|
||||
}
|
||||
| {
|
||||
fastLink: string;
|
||||
},
|
||||
{
|
||||
greetings: string;
|
||||
}
|
||||
{ codeword: string } | { fastLink: string },
|
||||
{ greetings: string }
|
||||
>({
|
||||
url: apiUrl + "/activate",
|
||||
url: `${API_URL}/activate`,
|
||||
method: "POST",
|
||||
contentType: true,
|
||||
body: {
|
||||
codeword: promocode,
|
||||
},
|
||||
body: { codeword: promocode },
|
||||
});
|
||||
|
||||
return response.greetings;
|
||||
|
@ -1,17 +1,22 @@
|
||||
import makeRequest from "@api/makeRequest"
|
||||
import { parseAxiosError } from "@root/utils/parse-error"
|
||||
import makeRequest from "@api/makeRequest";
|
||||
import { parseAxiosError } from "@root/utils/parse-error";
|
||||
|
||||
export async function getRecentlyPurchasedTariffs(): Promise<[any | null, string?]> {
|
||||
try {
|
||||
const recentlyPurchased = await makeRequest<never, any>({
|
||||
url: process.env.REACT_APP_DOMAIN + "/customer/recent",
|
||||
method: "get",
|
||||
useToken: true,
|
||||
})
|
||||
return [recentlyPurchased]
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError)
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/customer`;
|
||||
|
||||
return [null, `Не удалось получить историю. ${error}`]
|
||||
}
|
||||
}
|
||||
export async function getRecentlyPurchasedTariffs(): Promise<
|
||||
[any | null, string?]
|
||||
> {
|
||||
try {
|
||||
const recentlyPurchased = await makeRequest<never, any>({
|
||||
url: `${API_URL}/recent`,
|
||||
method: "GET",
|
||||
useToken: true,
|
||||
});
|
||||
|
||||
return [recentlyPurchased];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось получить историю. ${error}`];
|
||||
}
|
||||
}
|
||||
|
@ -1,124 +1,136 @@
|
||||
import makeRequest from "@api/makeRequest"
|
||||
import makeRequest from "@api/makeRequest";
|
||||
import { Tariff } from "@frontend/kitui";
|
||||
import { parseAxiosError } from "@root/utils/parse-error";
|
||||
|
||||
import type { PrivilegeWithoutPrice, ServiceKeyToPrivilegesMap } from "@root/model/privilege";
|
||||
import type {
|
||||
PrivilegeWithoutPrice,
|
||||
ServiceKeyToPrivilegesMap,
|
||||
} from "@root/model/privilege";
|
||||
import type { GetTariffsResponse } from "@root/model/tariff";
|
||||
import { removeTariffFromCart } from "@root/stores/user";
|
||||
|
||||
const apiUrl = process.env.REACT_APP_DOMAIN + "/strator"
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/strator`;
|
||||
|
||||
export async function getTariffs(
|
||||
apiPage: number,
|
||||
tariffsPerPage: number,
|
||||
signal: AbortSignal | undefined
|
||||
apiPage: number,
|
||||
tariffsPerPage: number,
|
||||
signal: AbortSignal | undefined
|
||||
): Promise<[GetTariffsResponse | null, string?]> {
|
||||
try {
|
||||
const tariffsResponse = await makeRequest<never, GetTariffsResponse>({
|
||||
url: apiUrl + `/tariff?page=${apiPage}&limit=${tariffsPerPage}`,
|
||||
method: "get",
|
||||
useToken: true,
|
||||
signal,
|
||||
});
|
||||
try {
|
||||
const tariffsResponse = await makeRequest<never, GetTariffsResponse>({
|
||||
url: `${API_URL}/tariff?page=${apiPage}&limit=${tariffsPerPage}`,
|
||||
method: "get",
|
||||
useToken: true,
|
||||
signal,
|
||||
});
|
||||
|
||||
return [tariffsResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
return [tariffsResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось получить список тарифов. ${error}`];
|
||||
}
|
||||
return [null, `Не удалось получить список тарифов. ${error}`];
|
||||
}
|
||||
}
|
||||
|
||||
interface CreateTariffBody {
|
||||
name: string;
|
||||
price?: number;
|
||||
isCustom: boolean;
|
||||
privileges: PrivilegeWithoutPrice[];
|
||||
name: string;
|
||||
price?: number;
|
||||
isCustom: boolean;
|
||||
privileges: PrivilegeWithoutPrice[];
|
||||
}
|
||||
|
||||
export async function createTariff(tariff: CreateTariffBody): Promise<[Tariff | null, string?]> {
|
||||
try {
|
||||
const createTariffResponse = await makeRequest<CreateTariffBody, Tariff>({
|
||||
url: `${apiUrl}/tariff`,
|
||||
method: "post",
|
||||
useToken: true,
|
||||
body: tariff,
|
||||
});
|
||||
export async function createTariff(
|
||||
tariff: CreateTariffBody
|
||||
): Promise<[Tariff | null, string?]> {
|
||||
try {
|
||||
const createTariffResponse = await makeRequest<CreateTariffBody, Tariff>({
|
||||
url: `${API_URL}/tariff`,
|
||||
method: "post",
|
||||
useToken: true,
|
||||
body: tariff,
|
||||
});
|
||||
|
||||
return [createTariffResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
return [createTariffResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось создать тариф. ${error}`];
|
||||
}
|
||||
return [null, `Не удалось создать тариф. ${error}`];
|
||||
}
|
||||
}
|
||||
|
||||
export async function getTariffById(tariffId: string): Promise<[Tariff | null, string?, number?]> {
|
||||
try {
|
||||
const getTariffByIdResponse = await makeRequest<never, Tariff>({
|
||||
url: `${apiUrl}/tariff/${tariffId}`,
|
||||
method: "get",
|
||||
useToken: true,
|
||||
});
|
||||
export async function getTariffById(
|
||||
tariffId: string
|
||||
): Promise<[Tariff | null, string?, number?]> {
|
||||
try {
|
||||
const getTariffByIdResponse = await makeRequest<never, Tariff>({
|
||||
url: `${API_URL}/tariff/${tariffId}`,
|
||||
method: "get",
|
||||
useToken: true,
|
||||
});
|
||||
|
||||
return [getTariffByIdResponse];
|
||||
} catch (nativeError) {
|
||||
const [error, status] = parseAxiosError(nativeError);
|
||||
return [getTariffByIdResponse];
|
||||
} catch (nativeError) {
|
||||
const [error, status] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось получить тарифы. ${error}`, status];
|
||||
}
|
||||
return [null, `Не удалось получить тарифы. ${error}`, status];
|
||||
}
|
||||
}
|
||||
|
||||
export async function getCustomTariffs(
|
||||
signal: AbortSignal | undefined
|
||||
signal: AbortSignal | undefined
|
||||
): Promise<[ServiceKeyToPrivilegesMap | null, string?]> {
|
||||
try {
|
||||
const customTariffsResponse = await makeRequest<null, ServiceKeyToPrivilegesMap>({
|
||||
url: apiUrl + "/privilege/service",
|
||||
signal,
|
||||
method: "get",
|
||||
useToken: true,
|
||||
});
|
||||
try {
|
||||
const customTariffsResponse = await makeRequest<
|
||||
null,
|
||||
ServiceKeyToPrivilegesMap
|
||||
>({
|
||||
url: `${API_URL}/privilege/service`,
|
||||
signal,
|
||||
method: "get",
|
||||
useToken: true,
|
||||
});
|
||||
|
||||
const tempCustomTariffsResponse = {
|
||||
...customTariffsResponse,
|
||||
squiz: customTariffsResponse.squiz
|
||||
};
|
||||
const tempCustomTariffsResponse = {
|
||||
...customTariffsResponse,
|
||||
squiz: customTariffsResponse.squiz,
|
||||
};
|
||||
|
||||
return [tempCustomTariffsResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
return [tempCustomTariffsResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось получить мои тарифы. ${error}`];
|
||||
}
|
||||
return [null, `Не удалось получить мои тарифы. ${error}`];
|
||||
}
|
||||
}
|
||||
|
||||
export async function getTariffArray(tariffIds: string[] | undefined) {
|
||||
if (!tariffIds) return null;
|
||||
if (!tariffIds) return null;
|
||||
|
||||
const responses = await Promise.allSettled(tariffIds.map(tariffId =>
|
||||
makeRequest<never, Tariff>({
|
||||
url: `${apiUrl}/tariff/${tariffId}`,
|
||||
method: "get",
|
||||
useToken: true,
|
||||
})
|
||||
));
|
||||
const responses = await Promise.allSettled(
|
||||
tariffIds.map((tariffId) =>
|
||||
makeRequest<never, Tariff>({
|
||||
url: `${API_URL}/tariff/${tariffId}`,
|
||||
method: "get",
|
||||
useToken: true,
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
const tariffs: Tariff[] = [];
|
||||
const tariffs: Tariff[] = [];
|
||||
|
||||
responses.forEach((response, index) => {
|
||||
switch (response.status) {
|
||||
case "fulfilled": {
|
||||
tariffs.push(response.value);
|
||||
break;
|
||||
}
|
||||
case "rejected": {
|
||||
const [, status] = parseAxiosError(response.reason);
|
||||
if (status === 404) removeTariffFromCart(tariffIds[index]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
responses.forEach((response, index) => {
|
||||
switch (response.status) {
|
||||
case "fulfilled": {
|
||||
tariffs.push(response.value);
|
||||
break;
|
||||
}
|
||||
case "rejected": {
|
||||
const [, status] = parseAxiosError(response.reason);
|
||||
if (status === 404) removeTariffFromCart(tariffIds[index]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return tariffs;
|
||||
return tariffs;
|
||||
}
|
||||
|
@ -1,9 +1,13 @@
|
||||
import makeRequest from "@api/makeRequest"
|
||||
import makeRequest from "@api/makeRequest";
|
||||
import { parseAxiosError } from "@root/utils/parse-error";
|
||||
|
||||
import { SendTicketMessageRequest } from "@frontend/kitui";
|
||||
|
||||
const apiUrl = process.env.REACT_APP_DOMAIN + "/heruvym";
|
||||
type SendFileResponse = {
|
||||
message: string;
|
||||
};
|
||||
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/heruvym`;
|
||||
|
||||
export async function sendTicketMessage(
|
||||
ticketId: string,
|
||||
@ -14,7 +18,7 @@ export async function sendTicketMessage(
|
||||
SendTicketMessageRequest,
|
||||
null
|
||||
>({
|
||||
url: `${apiUrl}/send`,
|
||||
url: `${API_URL}/send`,
|
||||
method: "POST",
|
||||
useToken: true,
|
||||
body: { ticket: ticketId, message: message, lang: "ru", files: [] },
|
||||
@ -31,7 +35,7 @@ export async function sendTicketMessage(
|
||||
export async function shownMessage(id: string): Promise<[null, string?]> {
|
||||
try {
|
||||
const shownMessageResponse = await makeRequest<{ id: string }, null>({
|
||||
url: apiUrl + "/shown",
|
||||
url: `${API_URL}/shown`,
|
||||
method: "POST",
|
||||
useToken: true,
|
||||
body: { id },
|
||||
@ -44,3 +48,27 @@ export async function shownMessage(id: string): Promise<[null, string?]> {
|
||||
return [null, `Не удалось прочесть сообщение. ${error}`];
|
||||
}
|
||||
}
|
||||
|
||||
export const sendFile = async (
|
||||
ticketId: string,
|
||||
file: File
|
||||
): Promise<[SendFileResponse | null, string?]> => {
|
||||
try {
|
||||
const body = new FormData();
|
||||
|
||||
body.append(file.name, file);
|
||||
body.append("ticket", ticketId);
|
||||
|
||||
const sendResponse = await makeRequest<FormData, SendFileResponse>({
|
||||
method: "POST",
|
||||
url: `${process.env.REACT_APP_DOMAIN}/sendFiles`,
|
||||
body,
|
||||
});
|
||||
|
||||
return [sendResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось отправить файл. ${error}`];
|
||||
}
|
||||
};
|
||||
|
@ -1,27 +1,27 @@
|
||||
import { User } from "@frontend/kitui"
|
||||
import makeRequest from "@api/makeRequest"
|
||||
import { PatchUserRequest } from "@root/model/user"
|
||||
import { parseAxiosError } from "@root/utils/parse-error"
|
||||
import { User } from "@frontend/kitui";
|
||||
import makeRequest from "@api/makeRequest";
|
||||
import { PatchUserRequest } from "@root/model/user";
|
||||
import { parseAxiosError } from "@root/utils/parse-error";
|
||||
|
||||
const apiUrl = process.env.REACT_APP_DOMAIN + "/user"
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/user`;
|
||||
|
||||
export async function patchUser(
|
||||
user: PatchUserRequest
|
||||
user: PatchUserRequest
|
||||
): Promise<[User | null, string?]> {
|
||||
try {
|
||||
const patchUserResponse = await makeRequest<PatchUserRequest, User>({
|
||||
url: apiUrl+"/",
|
||||
contentType: true,
|
||||
method: "PATCH",
|
||||
useToken: true,
|
||||
withCredentials: false,
|
||||
body: user,
|
||||
})
|
||||
try {
|
||||
const patchUserResponse = await makeRequest<PatchUserRequest, User>({
|
||||
url: `${API_URL}/`,
|
||||
contentType: true,
|
||||
method: "PATCH",
|
||||
useToken: true,
|
||||
withCredentials: false,
|
||||
body: user,
|
||||
});
|
||||
|
||||
return [patchUserResponse]
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError)
|
||||
return [patchUserResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось изменить пользователя. ${error}`]
|
||||
}
|
||||
return [null, `Не удалось изменить пользователя. ${error}`];
|
||||
}
|
||||
}
|
||||
|
@ -1,83 +1,110 @@
|
||||
import makeRequest from "@api/makeRequest"
|
||||
import makeRequest from "@api/makeRequest";
|
||||
|
||||
import { jsonToFormdata } from "@root/utils/jsonToFormdata"
|
||||
import { parseAxiosError } from "@root/utils/parse-error"
|
||||
import { jsonToFormdata } from "@root/utils/jsonToFormdata";
|
||||
import { parseAxiosError } from "@root/utils/parse-error";
|
||||
|
||||
import type {
|
||||
Verification,
|
||||
SendDocumentsArgs,
|
||||
UpdateDocumentsArgs,
|
||||
} from "@root/model/auth"
|
||||
import { AxiosError } from "axios"
|
||||
Verification,
|
||||
SendDocumentsArgs,
|
||||
UpdateDocumentsArgs,
|
||||
} from "@root/model/auth";
|
||||
|
||||
const apiUrl = process.env.REACT_APP_DOMAIN + "/verification/v1.0.0"
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/verification/v1.0.0/verification`;
|
||||
|
||||
export async function verification(
|
||||
userId: string
|
||||
userId: string
|
||||
): Promise<[Verification | null, string?]> {
|
||||
try {
|
||||
const verificationResponse = await makeRequest<never, Verification>({
|
||||
url: apiUrl + "/verification/" + userId,
|
||||
method: "GET",
|
||||
useToken: true,
|
||||
withCredentials: true,
|
||||
})
|
||||
try {
|
||||
const verificationResponse = await makeRequest<never, Verification>({
|
||||
url: `${API_URL}/${userId}`,
|
||||
method: "GET",
|
||||
useToken: true,
|
||||
withCredentials: true,
|
||||
});
|
||||
|
||||
verificationResponse.files = verificationResponse.files.map((obj) => {
|
||||
obj.url = obj.url.replace("https://hub.pena.digital", process.env.REACT_APP_DOMAIN?.toString() || "").replace("https://shub.pena.digital", process.env.REACT_APP_DOMAIN?.toString() || "")
|
||||
return obj
|
||||
})
|
||||
verificationResponse.files = verificationResponse.files.map((obj) => {
|
||||
obj.url = obj.url
|
||||
.replace(
|
||||
"https://hub.pena.digital",
|
||||
process.env.REACT_APP_DOMAIN?.toString() || ""
|
||||
)
|
||||
.replace(
|
||||
"https://shub.pena.digital",
|
||||
process.env.REACT_APP_DOMAIN?.toString() || ""
|
||||
);
|
||||
return obj;
|
||||
});
|
||||
|
||||
return [verificationResponse]
|
||||
} catch (nativeError) {
|
||||
const err = nativeError as AxiosError
|
||||
if (err.response?.status === 404) {
|
||||
return [null, `нет данных`]
|
||||
}
|
||||
const [error] = parseAxiosError(nativeError)
|
||||
return [verificationResponse];
|
||||
} catch (nativeError) {
|
||||
const [error, status] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Ошибка запроса верификации. ${error}`]
|
||||
}
|
||||
if (status === 404) {
|
||||
return [null, "нет данных"];
|
||||
}
|
||||
|
||||
return [null, `Ошибка запроса верификации. ${error}`];
|
||||
}
|
||||
}
|
||||
|
||||
export async function sendDocuments(
|
||||
documents: SendDocumentsArgs
|
||||
documents: SendDocumentsArgs
|
||||
): Promise<[Verification | "OK" | null, string?]> {
|
||||
try {
|
||||
const sendDocumentsResponse = await makeRequest<FormData, Verification>({
|
||||
url: apiUrl + "/verification",
|
||||
method: "POST",
|
||||
useToken: true,
|
||||
withCredentials: true,
|
||||
body: jsonToFormdata({ ...documents, egrule: documents.inn }),
|
||||
})
|
||||
try {
|
||||
const sendDocumentsResponse = await makeRequest<FormData, Verification>({
|
||||
url: API_URL,
|
||||
method: "POST",
|
||||
useToken: true,
|
||||
withCredentials: true,
|
||||
body: jsonToFormdata({ ...documents, egrule: documents.inn }),
|
||||
});
|
||||
|
||||
return [sendDocumentsResponse]
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError)
|
||||
return [sendDocumentsResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Ошибка отправки документов. ${error}`]
|
||||
}
|
||||
return [null, `Ошибка отправки документов. ${error}`];
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateDocuments(
|
||||
documents: UpdateDocumentsArgs
|
||||
): Promise<[Verification | "OK" | null, string? ]> {
|
||||
try {
|
||||
const updateDocumentsResponse = await makeRequest<FormData, Verification>({
|
||||
url: apiUrl + "/verification/file",
|
||||
method: "PATCH",
|
||||
useToken: true,
|
||||
withCredentials: true,
|
||||
body: jsonToFormdata(
|
||||
documents.inn ? { ...documents, egrule: documents.inn } : documents
|
||||
),
|
||||
})
|
||||
documents: UpdateDocumentsArgs
|
||||
): Promise<[Verification | "OK" | null, string?]> {
|
||||
try {
|
||||
const updateDocumentsResponse = await makeRequest<FormData, Verification>({
|
||||
url: `${API_URL}/file`,
|
||||
method: "PATCH",
|
||||
useToken: true,
|
||||
withCredentials: true,
|
||||
body: jsonToFormdata(
|
||||
documents.inn ? { ...documents, egrule: documents.inn } : documents
|
||||
),
|
||||
});
|
||||
|
||||
return [updateDocumentsResponse]
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError)
|
||||
return [updateDocumentsResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Ошибка обновления документов. ${error}`]
|
||||
}
|
||||
return [null, `Ошибка обновления документов. ${error}`];
|
||||
}
|
||||
}
|
||||
|
||||
export const updateDocument = async (
|
||||
body: FormData
|
||||
): Promise<[Verification | "OK" | null, string?]> => {
|
||||
try {
|
||||
const updateDocumentResponse = await makeRequest<FormData, Verification>({
|
||||
url: API_URL,
|
||||
method: "PATCH",
|
||||
body,
|
||||
useToken: true,
|
||||
withCredentials: true,
|
||||
});
|
||||
|
||||
return [updateDocumentResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Ошибка обновления документа. ${error}`];
|
||||
}
|
||||
};
|
||||
|
@ -10,7 +10,6 @@ import {
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import makeRequest from "@api/makeRequest"
|
||||
import {
|
||||
createTicket,
|
||||
getMessageFromFetchError,
|
||||
@ -28,13 +27,17 @@ import {
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
WheelEvent
|
||||
WheelEvent,
|
||||
} from "react";
|
||||
import ChatMessage from "../ChatMessage";
|
||||
import SendIcon from "../icons/SendIcon";
|
||||
import ArrowLeft from "@root/assets/Icons/arrowLeft";
|
||||
import UserCircleIcon from "./UserCircleIcon";
|
||||
import { sendTicketMessage, shownMessage } from "@root/api/ticket";
|
||||
import {
|
||||
sendTicketMessage,
|
||||
shownMessage,
|
||||
sendFile as sendFileRequest,
|
||||
} from "@root/api/ticket";
|
||||
import { useSSETab } from "@root/utils/hooks/useSSETab";
|
||||
import {
|
||||
ACCEPT_SEND_MEDIA_TYPES_MAP,
|
||||
@ -115,7 +118,7 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) {
|
||||
? offHoursMessage
|
||||
: workingHoursMessage;
|
||||
|
||||
return ({
|
||||
return {
|
||||
created_at: new Date().toISOString(),
|
||||
files: [],
|
||||
id: "111",
|
||||
@ -125,8 +128,7 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) {
|
||||
shown: { me: 1 },
|
||||
ticket_id: "111",
|
||||
user_id: "greetingMessage",
|
||||
});
|
||||
|
||||
};
|
||||
}, [open]);
|
||||
|
||||
useTicketMessages({
|
||||
@ -182,7 +184,8 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) {
|
||||
onSuccess: (result) => {
|
||||
if (result.data?.length) {
|
||||
const currentTicket = result.data.find(
|
||||
({ origin, state }) => !origin.includes("/support") && state !== "close"
|
||||
({ origin, state }) =>
|
||||
!origin.includes("/support") && state !== "close"
|
||||
);
|
||||
|
||||
if (!currentTicket) {
|
||||
@ -199,7 +202,7 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) {
|
||||
const message = getMessageFromFetchError(error);
|
||||
if (message) enqueueSnackbar(message);
|
||||
},
|
||||
onFetchStateChange: () => { },
|
||||
onFetchStateChange: () => {},
|
||||
enabled: Boolean(user),
|
||||
});
|
||||
|
||||
@ -228,7 +231,6 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) {
|
||||
scrollToBottom();
|
||||
}, [open]);
|
||||
|
||||
|
||||
useEffect(
|
||||
function scrollOnNewMessage() {
|
||||
if (!chatBoxRef.current) return;
|
||||
@ -353,19 +355,15 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) {
|
||||
const ticketId = ticket.sessionData?.ticketId || data?.Ticket;
|
||||
if (ticketId !== undefined) {
|
||||
if (file.size > MAX_FILE_SIZE) return setModalWarningType("errorSize");
|
||||
try {
|
||||
const body = new FormData();
|
||||
|
||||
body.append(file.name, file);
|
||||
body.append("ticket", ticketId);
|
||||
await makeRequest({
|
||||
url: process.env.REACT_APP_DOMAIN + "/heruvym/sendFiles",
|
||||
body: body,
|
||||
method: "POST",
|
||||
});
|
||||
} catch (error: any) {
|
||||
const errorMessage = getMessageFromFetchError(error);
|
||||
if (errorMessage) enqueueSnackbar(errorMessage);
|
||||
const [, sendFileError] = await sendFileRequest(
|
||||
ticketId,
|
||||
file
|
||||
);
|
||||
|
||||
if(sendFileError) {
|
||||
enqueueSnackbar(sendFileError)
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -547,8 +545,13 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) {
|
||||
);
|
||||
})}
|
||||
{!ticket.sessionData?.ticketId && (
|
||||
<ChatMessage unAuthenticated text={getGreetingMessage.message} createdAt={getGreetingMessage.created_at} isSelf={false} />)
|
||||
}
|
||||
<ChatMessage
|
||||
unAuthenticated
|
||||
text={getGreetingMessage.message}
|
||||
createdAt={getGreetingMessage.created_at}
|
||||
isSelf={false}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
<FormControl fullWidth sx={{ borderTop: "1px solid black" }}>
|
||||
<InputBase
|
||||
|
@ -1,135 +1,134 @@
|
||||
import axios from "axios"
|
||||
import { Box, IconButton, SxProps, Theme, Typography, useTheme } from "@mui/material"
|
||||
import { Document, Page } from "react-pdf"
|
||||
import { Buffer } from "buffer"
|
||||
import { downloadFileToDevice } from "@root/utils/downloadFileToDevice"
|
||||
import EditIcon from "@mui/icons-material/Edit"
|
||||
import { ChangeEvent, useRef } from "react"
|
||||
import { SendDocumentsArgs, Verification } from "@root/model/auth"
|
||||
import makeRequest from "@api/makeRequest"
|
||||
import { jsonToFormdata } from "@utils/jsonToFormdata"
|
||||
import { parseAxiosError } from "@utils/parse-error"
|
||||
import { readFile } from "@root/utils/readFile"
|
||||
import { enqueueSnackbar } from "notistack"
|
||||
import axios from "axios";
|
||||
import {
|
||||
Box,
|
||||
IconButton,
|
||||
SxProps,
|
||||
Theme,
|
||||
Typography,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import { Document, Page } from "react-pdf";
|
||||
import { Buffer } from "buffer";
|
||||
import { downloadFileToDevice } from "@root/utils/downloadFileToDevice";
|
||||
import EditIcon from "@mui/icons-material/Edit";
|
||||
import { ChangeEvent, useRef } from "react";
|
||||
import { SendDocumentsArgs, Verification } from "@root/model/auth";
|
||||
import { updateDocument } from "@api/verification";
|
||||
import { jsonToFormdata } from "@utils/jsonToFormdata";
|
||||
import { parseAxiosError } from "@utils/parse-error";
|
||||
import { readFile } from "@root/utils/readFile";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
|
||||
type KeyNames =
|
||||
"inn" |
|
||||
"rule" |
|
||||
"certificate"
|
||||
type KeyNames = "inn" | "rule" | "certificate";
|
||||
interface Props {
|
||||
text: string;
|
||||
documentUrl: string;
|
||||
sx?: SxProps<Theme>;
|
||||
keyName: KeyNames
|
||||
text: string;
|
||||
documentUrl: string;
|
||||
sx?: SxProps<Theme>;
|
||||
keyName: KeyNames;
|
||||
}
|
||||
|
||||
export default function DocumentItem({
|
||||
text,
|
||||
documentUrl = "",
|
||||
sx,
|
||||
keyName,
|
||||
}: Props) {
|
||||
const theme = useTheme();
|
||||
|
||||
export default function DocumentItem({ text, documentUrl = "", sx, keyName }: Props) {
|
||||
const theme = useTheme()
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||
function handleChooseFileClick() {
|
||||
fileInputRef.current?.click();
|
||||
}
|
||||
|
||||
function handleChooseFileClick() {
|
||||
fileInputRef.current?.click()
|
||||
}
|
||||
const downloadFile = async () => {
|
||||
const { data } = await axios.get<ArrayBuffer>(documentUrl, {
|
||||
responseType: "arraybuffer",
|
||||
});
|
||||
|
||||
const downloadFile = async () => {
|
||||
const { data } = await axios.get<ArrayBuffer>(documentUrl, {
|
||||
responseType: "arraybuffer",
|
||||
})
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
return
|
||||
}
|
||||
downloadFileToDevice(
|
||||
`${documentUrl.split("/").pop()?.split(".")?.[0] || "document"}.pdf`,
|
||||
Buffer.from(data)
|
||||
);
|
||||
|
||||
downloadFileToDevice(
|
||||
`${documentUrl.split("/").pop()?.split(".")?.[0] || "document"}.pdf`,
|
||||
Buffer.from(data)
|
||||
)
|
||||
return;
|
||||
};
|
||||
|
||||
return
|
||||
}
|
||||
async function sendDocument(e: ChangeEvent<HTMLInputElement>) {
|
||||
const target = e.target as HTMLInputElement;
|
||||
const file = target?.files?.[0] || null;
|
||||
if (file !== null) {
|
||||
const readedFile = await readFile(file, "binary");
|
||||
const [, updateDocumentError] = await updateDocument(
|
||||
jsonToFormdata({ [keyName]: readedFile })
|
||||
);
|
||||
|
||||
async function sendDocument(
|
||||
e: ChangeEvent<HTMLInputElement>
|
||||
) {
|
||||
const target = e.target as HTMLInputElement;
|
||||
const file = target?.files?.[0] || null;
|
||||
if (file !== null) {
|
||||
const readedFile = await readFile(file, "binary")
|
||||
try {
|
||||
await makeRequest<FormData, Verification>({
|
||||
url: `${process.env.REACT_APP_DOMAIN}/verification/v1.0.0/verification`,
|
||||
method: "PATCH",
|
||||
useToken: true,
|
||||
withCredentials: true,
|
||||
body: jsonToFormdata({ [keyName]: readedFile }),
|
||||
})
|
||||
if (updateDocumentError) {
|
||||
return enqueueSnackbar(
|
||||
`Ошибка отправки документов. ${updateDocumentError}`
|
||||
);
|
||||
}
|
||||
|
||||
enqueueSnackbar("Данные обновлены")
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError)
|
||||
enqueueSnackbar("Данные обновлены");
|
||||
}
|
||||
}
|
||||
|
||||
enqueueSnackbar(`Ошибка отправки документов. ${error}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "start",
|
||||
gap: "10px",
|
||||
...sx,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
color: "#4D4D4D",
|
||||
fontWeight: 500,
|
||||
fontVariantNumeric: "tabular-nums",
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</Typography>
|
||||
{documentUrl && (
|
||||
<>
|
||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||||
<Typography
|
||||
sx={{ color: theme.palette.purple.main, cursor: "pointer" }}
|
||||
onClick={downloadFile}
|
||||
>
|
||||
{documentUrl.split("/").pop()?.split(".")?.[0]}
|
||||
</Typography>
|
||||
<IconButton onClick={handleChooseFileClick}>
|
||||
<EditIcon sx={{ color: theme.palette.purple.main }} />
|
||||
</IconButton>
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
style={{ display: "none" }}
|
||||
onChange={sendDocument}
|
||||
type="file"
|
||||
id="image-file"
|
||||
multiple
|
||||
accept={"application/pdf"}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "start",
|
||||
gap: "10px",
|
||||
...sx,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
color: "#4D4D4D",
|
||||
fontWeight: 500,
|
||||
fontVariantNumeric: "tabular-nums",
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</Typography>
|
||||
{documentUrl && (
|
||||
<>
|
||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||||
<Typography
|
||||
sx={{ color: theme.palette.purple.main, cursor: "pointer" }}
|
||||
onClick={downloadFile}
|
||||
>
|
||||
{documentUrl.split("/").pop()?.split(".")?.[0]}
|
||||
</Typography>
|
||||
<IconButton
|
||||
onClick={handleChooseFileClick}
|
||||
>
|
||||
<EditIcon sx={{ color: theme.palette.purple.main }} />
|
||||
</IconButton>
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
style={{ display: "none" }}
|
||||
onChange={sendDocument}
|
||||
type="file"
|
||||
id="image-file"
|
||||
multiple
|
||||
accept={"application/pdf"}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Document file={documentUrl}>
|
||||
|
||||
<Page
|
||||
pageNumber={1}
|
||||
width={200}
|
||||
renderTextLayer={false}
|
||||
renderAnnotationLayer={false}
|
||||
/>
|
||||
</Document>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
<Document file={documentUrl}>
|
||||
<Page
|
||||
pageNumber={1}
|
||||
width={200}
|
||||
renderTextLayer={false}
|
||||
renderAnnotationLayer={false}
|
||||
/>
|
||||
</Document>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
@ -1,21 +1,19 @@
|
||||
import {
|
||||
Box,
|
||||
IconButton,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme
|
||||
} from "@mui/material"
|
||||
import CustomAccordion from "@components/CustomAccordion"
|
||||
import File from "@components/icons/File"
|
||||
import {getDeclension} from "@utils/declension"
|
||||
import {enqueueSnackbar} from "notistack"
|
||||
import {addTariffToCart, useUserStore} from "@root/stores/user"
|
||||
import ForwardToInboxOutlinedIcon
|
||||
from "@mui/icons-material/ForwardToInboxOutlined";
|
||||
import {makeRequest} from "@frontend/kitui";
|
||||
import {KeyValue, RawDetails} from "@api/history";
|
||||
import {useNavigate} from "react-router-dom"
|
||||
import {VerificationStatus} from "@root/model/account"
|
||||
Box,
|
||||
IconButton,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import CustomAccordion from "@components/CustomAccordion";
|
||||
import File from "@components/icons/File";
|
||||
import { getDeclension } from "@utils/declension";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { addTariffToCart, useUserStore } from "@root/stores/user";
|
||||
import ForwardToInboxOutlinedIcon from "@mui/icons-material/ForwardToInboxOutlined";
|
||||
import { KeyValue, RawDetails, sendReport } from "@api/history";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { VerificationStatus } from "@root/model/account";
|
||||
|
||||
export type History = {
|
||||
title: string;
|
||||
@ -31,296 +29,322 @@ interface AccordionWrapperProps {
|
||||
last?: boolean;
|
||||
first?: boolean;
|
||||
createdAt: string;
|
||||
onClickMail?: any
|
||||
mainId: string
|
||||
onClickMail?: any;
|
||||
mainId: string;
|
||||
}
|
||||
|
||||
export default function AccordionWrapper({ content, last, first, createdAt, onClickMail, mainId }: AccordionWrapperProps) {
|
||||
const theme = useTheme()
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||
const upSm = useMediaQuery(theme.breakpoints.up("sm"))
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(900))
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(560))
|
||||
const navigate = useNavigate();
|
||||
const verificationStatus = useUserStore((state) => state.verificationStatus)
|
||||
const OrgName = useUserStore((state) => state.userAccount?.name.orgname)
|
||||
export default function AccordionWrapper({
|
||||
content,
|
||||
last,
|
||||
first,
|
||||
createdAt,
|
||||
onClickMail,
|
||||
mainId,
|
||||
}: AccordionWrapperProps) {
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const upSm = useMediaQuery(theme.breakpoints.up("sm"));
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(900));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(560));
|
||||
const navigate = useNavigate();
|
||||
const verificationStatus = useUserStore((state) => state.verificationStatus);
|
||||
const OrgName = useUserStore((state) => state.userAccount?.name.orgname);
|
||||
|
||||
const valuesByKey: any = {}
|
||||
if (Array.isArray(content[0].Value) && Array.isArray(content[0].Value[0])) {
|
||||
(content[0].Value[0] as KeyValue[]).forEach((item: KeyValue) => {
|
||||
valuesByKey[item.Key] = item.Value;
|
||||
});
|
||||
}
|
||||
const extractDateFromString = (tariffName: string) => {
|
||||
const dateMatch = tariffName.match(/\d{4}-\d{2}-\d{2}/)
|
||||
return dateMatch ? dateMatch[0] : null
|
||||
}
|
||||
const valuesByKey: any = {};
|
||||
if (Array.isArray(content[0].Value) && Array.isArray(content[0].Value[0])) {
|
||||
(content[0].Value[0] as KeyValue[]).forEach((item: KeyValue) => {
|
||||
valuesByKey[item.Key] = item.Value;
|
||||
});
|
||||
}
|
||||
const extractDateFromString = (tariffName: string) => {
|
||||
const dateMatch = tariffName.match(/\d{4}-\d{2}-\d{2}/);
|
||||
return dateMatch ? dateMatch[0] : null;
|
||||
};
|
||||
|
||||
async function handleTariffItemClick(tariffId: string) {
|
||||
const { patchCartError } = await addTariffToCart(tariffId);
|
||||
if (patchCartError) {
|
||||
enqueueSnackbar(patchCartError);
|
||||
} else {
|
||||
enqueueSnackbar("Тариф добавлен в корзину");
|
||||
}
|
||||
}
|
||||
|
||||
async function handleTariffItemClick(tariffId: string) {
|
||||
const { patchCartError } = await addTariffToCart(tariffId)
|
||||
if (patchCartError) {
|
||||
enqueueSnackbar(patchCartError)
|
||||
} else {
|
||||
enqueueSnackbar("Тариф добавлен в корзину")
|
||||
}
|
||||
}
|
||||
async function sendBillByEmail(logId: string) {
|
||||
if (verificationStatus === VerificationStatus.VERIFICATED && OrgName) {
|
||||
const [, sendReportError] = await sendReport(logId);
|
||||
|
||||
async function sendBillByEmail(logId: string) {
|
||||
if (!sendReportError) {
|
||||
return enqueueSnackbar(
|
||||
"Акт будет отправлен на почту, указанную при регистрации"
|
||||
);
|
||||
}
|
||||
|
||||
if(verificationStatus === VerificationStatus.VERIFICATED && OrgName){
|
||||
try {
|
||||
await makeRequest({
|
||||
url: process.env.REACT_APP_DOMAIN + `/customer/sendReport`,
|
||||
body: {id: logId},
|
||||
method: "POST",
|
||||
});
|
||||
return enqueueSnackbar("Акт будет отправлен на почту, указанную при регистрации");
|
||||
} catch (e) {
|
||||
enqueueSnackbar("Извините, произошла ошибка");
|
||||
}
|
||||
}
|
||||
navigate("/settings")
|
||||
if(verificationStatus !== VerificationStatus.VERIFICATED && !OrgName){
|
||||
enqueueSnackbar("Пройдите верификацию и заполните название организации");
|
||||
} else if(!OrgName){
|
||||
enqueueSnackbar("Заполните поле название организации");
|
||||
}else if(verificationStatus !== VerificationStatus.VERIFICATED) {
|
||||
enqueueSnackbar("Пройдите верификацию");
|
||||
}
|
||||
}
|
||||
enqueueSnackbar("Извините, произошла ошибка");
|
||||
}
|
||||
navigate("/settings");
|
||||
if (verificationStatus !== VerificationStatus.VERIFICATED && !OrgName) {
|
||||
enqueueSnackbar("Пройдите верификацию и заполните название организации");
|
||||
} else if (!OrgName) {
|
||||
enqueueSnackbar("Заполните поле название организации");
|
||||
} else if (verificationStatus !== VerificationStatus.VERIFICATED) {
|
||||
enqueueSnackbar("Пройдите верификацию");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
borderRadius: "12px",
|
||||
}}
|
||||
>
|
||||
<CustomAccordion
|
||||
last={last}
|
||||
first={first}
|
||||
divide
|
||||
text={valuesByKey.privileges.map((e:KeyValue[]) => (
|
||||
<Typography
|
||||
key={valuesByKey.id}
|
||||
>
|
||||
{e[1].Value} - {e[5].Value} {getDeclension(Number(e[5].Value), e[7].Value.toString())}
|
||||
</Typography>)
|
||||
)}
|
||||
header={
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: upMd ? "72px" : undefined,
|
||||
padding: "20px 20px 20px 0",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
cursor: "pointer",
|
||||
userSelect: "none",
|
||||
gap: "20px",
|
||||
alignItems: upSm ? "center" : undefined,
|
||||
flexDirection: upSm ? undefined : "column",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: upSm ? "center" : undefined,
|
||||
justifyContent: "space-between",
|
||||
flexDirection: upSm ? undefined : "column",
|
||||
gap: upMd ? "51px" : "10px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
width: "110px",
|
||||
fontSize: upMd ? "20px" : "18px",
|
||||
lineHeight: upMd ? undefined : "19px",
|
||||
fontWeight: 500,
|
||||
color: valuesByKey.expired ? theme.palette.text.disabled : theme.palette.text.secondary,
|
||||
px: 0,
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
{createdAt}
|
||||
</Typography>
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
borderRadius: "12px",
|
||||
}}
|
||||
>
|
||||
<CustomAccordion
|
||||
last={last}
|
||||
first={first}
|
||||
divide
|
||||
text={valuesByKey.privileges.map((e: KeyValue[]) => (
|
||||
<Typography key={valuesByKey.id}>
|
||||
{e[1].Value} - {e[5].Value}{" "}
|
||||
{getDeclension(Number(e[5].Value), e[7].Value.toString())}
|
||||
</Typography>
|
||||
))}
|
||||
header={
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: upMd ? "72px" : undefined,
|
||||
padding: "20px 20px 20px 0",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
cursor: "pointer",
|
||||
userSelect: "none",
|
||||
gap: "20px",
|
||||
alignItems: upSm ? "center" : undefined,
|
||||
flexDirection: upSm ? undefined : "column",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: upSm ? "center" : undefined,
|
||||
justifyContent: "space-between",
|
||||
flexDirection: upSm ? undefined : "column",
|
||||
gap: upMd ? "51px" : "10px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
width: "110px",
|
||||
fontSize: upMd ? "20px" : "18px",
|
||||
lineHeight: upMd ? undefined : "19px",
|
||||
fontWeight: 500,
|
||||
color: valuesByKey.expired
|
||||
? theme.palette.text.disabled
|
||||
: theme.palette.text.secondary,
|
||||
px: 0,
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
{createdAt}
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
title={valuesByKey.iscustom ? "Мой тариф" : valuesByKey.name}
|
||||
sx={{
|
||||
fontSize: upMd ? "18px" : "16px",
|
||||
lineHeight: upMd ? undefined : "19px",
|
||||
fontWeight: 500,
|
||||
color: valuesByKey.expired ? theme.palette.text.disabled : theme.palette.gray.dark,
|
||||
px: 0,
|
||||
width: "200px",
|
||||
maxWidth: "200px",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis"
|
||||
}}
|
||||
>
|
||||
{valuesByKey.iscustom ? "Мой тариф" : valuesByKey.name}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
flexFlow: "1",
|
||||
flexBasis: "60%",
|
||||
}}
|
||||
>
|
||||
<Box display="flex" width="100%" justifyContent="space-between">
|
||||
<Typography
|
||||
sx={{
|
||||
display: upMd ? undefined : "none",
|
||||
fontSize: upMd ? "18px" : "16px",
|
||||
lineHeight: upMd ? undefined : "19px",
|
||||
fontWeight: 400,
|
||||
color: valuesByKey.expired ? theme.palette.text.disabled : theme.palette.gray.dark,
|
||||
px: 0,
|
||||
}}
|
||||
title={`>Способ оплаты: ${valuesByKey.payMethod}</Typography>}`}
|
||||
>
|
||||
{valuesByKey.payMethod && <Typography
|
||||
sx={{
|
||||
maxWidth: "300px",
|
||||
width: "300px",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis"
|
||||
}}
|
||||
>Способ оплаты: {valuesByKey.payMethod}</Typography>}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
height: "100%",
|
||||
alignItems: "center",
|
||||
gap: upSm ? "111px" : "17px",
|
||||
width: "100%",
|
||||
maxWidth: isTablet ? null : "160px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
marginLeft: isTablet ? (isMobile ? null : "auto") : null,
|
||||
color: valuesByKey.expired ? theme.palette.text.disabled : theme.palette.gray.dark,
|
||||
fontSize: upSm ? "20px" : "16px",
|
||||
fontWeight: 500,
|
||||
textAlign: "left",
|
||||
}}
|
||||
>
|
||||
{Number(content[1].Value) / 100 ? Number(content[1].Value) / 100 : "nodata"} руб.
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
{!isMobile &&
|
||||
<>
|
||||
<IconButton
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
sendBillByEmail(mainId);
|
||||
}}
|
||||
sx={{
|
||||
ml: "20px",
|
||||
bgcolor: "#EEE4FC",
|
||||
color: "#7E2AEA",
|
||||
borderRadius: 2,
|
||||
"&:hover": {
|
||||
bgcolor: "#7E2AEA",
|
||||
color: "white",
|
||||
},
|
||||
"&:active": {
|
||||
bgcolor: "black",
|
||||
color: "white",
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ForwardToInboxOutlinedIcon fontSize={"medium"} sx={{ opacity: 0.9 }}/>
|
||||
</IconButton>
|
||||
<IconButton
|
||||
title="Добавить в корзину тариф"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
handleTariffItemClick(valuesByKey.id)
|
||||
}}
|
||||
sx={{
|
||||
ml: "20px",
|
||||
bgcolor:"#EEE4FC",
|
||||
stroke: "#7E2AEA",
|
||||
borderRadius: 2,
|
||||
"&:hover": {
|
||||
bgcolor:"#7E2AEA",
|
||||
stroke: "white",
|
||||
},
|
||||
"&:active": {
|
||||
bgcolor:"black",
|
||||
stroke: "white",
|
||||
}
|
||||
}}
|
||||
>
|
||||
<File></File>
|
||||
</IconButton>
|
||||
</>
|
||||
|
||||
}
|
||||
</Box>
|
||||
</Box>
|
||||
{isMobile &&
|
||||
<>
|
||||
<IconButton
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
sendBillByEmail(mainId);
|
||||
}}
|
||||
sx={{
|
||||
m: "0 10px",
|
||||
bgcolor: "#EEE4FC",
|
||||
color: "#7E2AEA",
|
||||
borderRadius: 2,
|
||||
"&:hover": {
|
||||
bgcolor: "#7E2AEA",
|
||||
color: "white",
|
||||
},
|
||||
"&:active": {
|
||||
bgcolor: "black",
|
||||
color: "white",
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ForwardToInboxOutlinedIcon fontSize={"medium"} sx={{ opacity: 0.9 }}/>
|
||||
</IconButton>
|
||||
<IconButton
|
||||
title="Добавить в корзину тариф"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
handleTariffItemClick(valuesByKey.id)
|
||||
}}
|
||||
sx={{
|
||||
mr: "10px",
|
||||
bgcolor:"#EEE4FC",
|
||||
stroke: "#7E2AEA",
|
||||
borderRadius: 2,
|
||||
"&:hover": {
|
||||
bgcolor:"#7E2AEA",
|
||||
stroke: "white",
|
||||
},
|
||||
"&:active": {
|
||||
bgcolor:"black",
|
||||
stroke: "white",
|
||||
}
|
||||
}}
|
||||
>
|
||||
<File></File>
|
||||
</IconButton>
|
||||
</>
|
||||
|
||||
}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
<Typography
|
||||
title={valuesByKey.iscustom ? "Мой тариф" : valuesByKey.name}
|
||||
sx={{
|
||||
fontSize: upMd ? "18px" : "16px",
|
||||
lineHeight: upMd ? undefined : "19px",
|
||||
fontWeight: 500,
|
||||
color: valuesByKey.expired
|
||||
? theme.palette.text.disabled
|
||||
: theme.palette.gray.dark,
|
||||
px: 0,
|
||||
width: "200px",
|
||||
maxWidth: "200px",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
>
|
||||
{valuesByKey.iscustom ? "Мой тариф" : valuesByKey.name}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
flexFlow: "1",
|
||||
flexBasis: "60%",
|
||||
}}
|
||||
>
|
||||
<Box display="flex" width="100%" justifyContent="space-between">
|
||||
<Typography
|
||||
sx={{
|
||||
display: upMd ? undefined : "none",
|
||||
fontSize: upMd ? "18px" : "16px",
|
||||
lineHeight: upMd ? undefined : "19px",
|
||||
fontWeight: 400,
|
||||
color: valuesByKey.expired
|
||||
? theme.palette.text.disabled
|
||||
: theme.palette.gray.dark,
|
||||
px: 0,
|
||||
}}
|
||||
title={`>Способ оплаты: ${valuesByKey.payMethod}</Typography>}`}
|
||||
>
|
||||
{valuesByKey.payMethod && (
|
||||
<Typography
|
||||
sx={{
|
||||
maxWidth: "300px",
|
||||
width: "300px",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
>
|
||||
Способ оплаты: {valuesByKey.payMethod}
|
||||
</Typography>
|
||||
)}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
height: "100%",
|
||||
alignItems: "center",
|
||||
gap: upSm ? "111px" : "17px",
|
||||
width: "100%",
|
||||
maxWidth: isTablet ? null : "160px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
marginLeft: isTablet
|
||||
? isMobile
|
||||
? null
|
||||
: "auto"
|
||||
: null,
|
||||
color: valuesByKey.expired
|
||||
? theme.palette.text.disabled
|
||||
: theme.palette.gray.dark,
|
||||
fontSize: upSm ? "20px" : "16px",
|
||||
fontWeight: 500,
|
||||
textAlign: "left",
|
||||
}}
|
||||
>
|
||||
{Number(content[1].Value) / 100
|
||||
? Number(content[1].Value) / 100
|
||||
: "nodata"}{" "}
|
||||
руб.
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
{!isMobile && (
|
||||
<>
|
||||
<IconButton
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
sendBillByEmail(mainId);
|
||||
}}
|
||||
sx={{
|
||||
ml: "20px",
|
||||
bgcolor: "#EEE4FC",
|
||||
color: "#7E2AEA",
|
||||
borderRadius: 2,
|
||||
"&:hover": {
|
||||
bgcolor: "#7E2AEA",
|
||||
color: "white",
|
||||
},
|
||||
"&:active": {
|
||||
bgcolor: "black",
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ForwardToInboxOutlinedIcon
|
||||
fontSize={"medium"}
|
||||
sx={{ opacity: 0.9 }}
|
||||
/>
|
||||
</IconButton>
|
||||
<IconButton
|
||||
title="Добавить в корзину тариф"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleTariffItemClick(valuesByKey.id);
|
||||
}}
|
||||
sx={{
|
||||
ml: "20px",
|
||||
bgcolor: "#EEE4FC",
|
||||
stroke: "#7E2AEA",
|
||||
borderRadius: 2,
|
||||
"&:hover": {
|
||||
bgcolor: "#7E2AEA",
|
||||
stroke: "white",
|
||||
},
|
||||
"&:active": {
|
||||
bgcolor: "black",
|
||||
stroke: "white",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<File></File>
|
||||
</IconButton>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
{isMobile && (
|
||||
<>
|
||||
<IconButton
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
sendBillByEmail(mainId);
|
||||
}}
|
||||
sx={{
|
||||
m: "0 10px",
|
||||
bgcolor: "#EEE4FC",
|
||||
color: "#7E2AEA",
|
||||
borderRadius: 2,
|
||||
"&:hover": {
|
||||
bgcolor: "#7E2AEA",
|
||||
color: "white",
|
||||
},
|
||||
"&:active": {
|
||||
bgcolor: "black",
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ForwardToInboxOutlinedIcon
|
||||
fontSize={"medium"}
|
||||
sx={{ opacity: 0.9 }}
|
||||
/>
|
||||
</IconButton>
|
||||
<IconButton
|
||||
title="Добавить в корзину тариф"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleTariffItemClick(valuesByKey.id);
|
||||
}}
|
||||
sx={{
|
||||
mr: "10px",
|
||||
bgcolor: "#EEE4FC",
|
||||
stroke: "#7E2AEA",
|
||||
borderRadius: 2,
|
||||
"&:hover": {
|
||||
bgcolor: "#7E2AEA",
|
||||
stroke: "white",
|
||||
},
|
||||
"&:active": {
|
||||
bgcolor: "black",
|
||||
stroke: "white",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<File></File>
|
||||
</IconButton>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
@ -1,160 +1,175 @@
|
||||
import {
|
||||
Box,
|
||||
IconButton,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme
|
||||
Box,
|
||||
IconButton,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import CustomAccordion from "@components/CustomAccordion";
|
||||
import File from "@components/icons/File";
|
||||
import {getDeclension} from "@utils/declension";
|
||||
import {enqueueSnackbar} from "notistack";
|
||||
import {addTariffToCart, useUserStore} from "@root/stores/user"
|
||||
import {makeRequest, Tariff} from "@frontend/kitui";
|
||||
import {currencyFormatter} from "@root/utils/currencyFormatter";
|
||||
import ForwardToInboxIcon from '@mui/icons-material/ForwardToInbox';
|
||||
import {VerificationStatus} from "@root/model/account"
|
||||
import {useNavigate} from "react-router-dom"
|
||||
import { getDeclension } from "@utils/declension";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { addTariffToCart, useUserStore } from "@root/stores/user";
|
||||
import { makeRequest, Tariff } from "@frontend/kitui";
|
||||
import { currencyFormatter } from "@root/utils/currencyFormatter";
|
||||
import ForwardToInboxIcon from "@mui/icons-material/ForwardToInbox";
|
||||
import { VerificationStatus } from "@root/model/account";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { sendReport } from "@api/history";
|
||||
|
||||
export type History = {
|
||||
title: string;
|
||||
date: string;
|
||||
info: string;
|
||||
description: string;
|
||||
payMethod?: string;
|
||||
expired?: boolean;
|
||||
title: string;
|
||||
date: string;
|
||||
info: string;
|
||||
description: string;
|
||||
payMethod?: string;
|
||||
expired?: boolean;
|
||||
};
|
||||
|
||||
interface AccordionWrapperProps {
|
||||
tariff: Tariff;
|
||||
price: number;
|
||||
last?: boolean;
|
||||
first?: boolean;
|
||||
createdAt: string;
|
||||
mainId: string
|
||||
tariff: Tariff;
|
||||
price: number;
|
||||
last?: boolean;
|
||||
first?: boolean;
|
||||
createdAt: string;
|
||||
mainId: string;
|
||||
}
|
||||
|
||||
export default function AccordionWrapper2({ tariff, price, last, first, createdAt, mainId }: AccordionWrapperProps) {
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const upSm = useMediaQuery(theme.breakpoints.up("sm"));
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(900));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(560));
|
||||
const navigate = useNavigate();
|
||||
const verificationStatus = useUserStore((state) => state.verificationStatus)
|
||||
const OrgName = useUserStore((state) => state.userAccount?.name.orgname)
|
||||
async function handleTariffItemClick(tariffId: string) {
|
||||
const { patchCartError } = await addTariffToCart(tariffId);
|
||||
if (patchCartError) {
|
||||
enqueueSnackbar(patchCartError);
|
||||
} else {
|
||||
enqueueSnackbar("Тариф добавлен в корзину");
|
||||
}
|
||||
export default function AccordionWrapper2({
|
||||
tariff,
|
||||
price,
|
||||
last,
|
||||
first,
|
||||
createdAt,
|
||||
mainId,
|
||||
}: AccordionWrapperProps) {
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const upSm = useMediaQuery(theme.breakpoints.up("sm"));
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(900));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(560));
|
||||
const navigate = useNavigate();
|
||||
const verificationStatus = useUserStore((state) => state.verificationStatus);
|
||||
const OrgName = useUserStore((state) => state.userAccount?.name.orgname);
|
||||
async function handleTariffItemClick(tariffId: string) {
|
||||
const { patchCartError } = await addTariffToCart(tariffId);
|
||||
if (patchCartError) {
|
||||
enqueueSnackbar(patchCartError);
|
||||
} else {
|
||||
enqueueSnackbar("Тариф добавлен в корзину");
|
||||
}
|
||||
}
|
||||
|
||||
async function sendBillByEmail(logId: string) {
|
||||
if(verificationStatus === VerificationStatus.VERIFICATED && OrgName){
|
||||
try {
|
||||
await makeRequest({
|
||||
url: process.env.REACT_APP_DOMAIN + `/customer/sendReport`,
|
||||
body: {id: logId},
|
||||
method: "POST",
|
||||
});
|
||||
return enqueueSnackbar("Акт будет отправлен на почту, указанную при регистрации");
|
||||
} catch (e) {
|
||||
enqueueSnackbar("Извините, произошла ошибка");
|
||||
}
|
||||
}
|
||||
navigate("/settings")
|
||||
if(verificationStatus !== VerificationStatus.VERIFICATED && !OrgName){
|
||||
enqueueSnackbar("Пройдите верификацию и заполните название организации");
|
||||
} else if(!OrgName){
|
||||
enqueueSnackbar("Заполните поле название организации");
|
||||
}else if(verificationStatus !== VerificationStatus.VERIFICATED) {
|
||||
enqueueSnackbar("Пройдите верификацию");
|
||||
}
|
||||
async function sendBillByEmail(logId: string) {
|
||||
if (verificationStatus === VerificationStatus.VERIFICATED && OrgName) {
|
||||
const [, sendReportError] = await sendReport(logId);
|
||||
|
||||
if (!sendReportError) {
|
||||
return enqueueSnackbar(
|
||||
"Акт будет отправлен на почту, указанную при регистрации"
|
||||
);
|
||||
}
|
||||
|
||||
enqueueSnackbar("Извините, произошла ошибка");
|
||||
}
|
||||
navigate("/settings");
|
||||
if (verificationStatus !== VerificationStatus.VERIFICATED && !OrgName) {
|
||||
enqueueSnackbar("Пройдите верификацию и заполните название организации");
|
||||
} else if (!OrgName) {
|
||||
enqueueSnackbar("Заполните поле название организации");
|
||||
} else if (verificationStatus !== VerificationStatus.VERIFICATED) {
|
||||
enqueueSnackbar("Пройдите верификацию");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
borderRadius: "12px",
|
||||
}}
|
||||
>
|
||||
<CustomAccordion
|
||||
last={last}
|
||||
first={first}
|
||||
divide
|
||||
text={tariff.privileges.map(privilege => (
|
||||
`${privilege.description} - ${privilege.amount} ${getDeclension(Number(privilege.serviceKey), privilege.value)} `
|
||||
))}
|
||||
header={
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: upMd ? "72px" : undefined,
|
||||
padding: "20px 20px 20px 0",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
cursor: "pointer",
|
||||
userSelect: "none",
|
||||
gap: "20px",
|
||||
alignItems: upSm ? "center" : undefined,
|
||||
flexDirection: upSm ? undefined : "column",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: upSm ? "center" : undefined,
|
||||
justifyContent: "space-between",
|
||||
flexDirection: upSm ? undefined : "column",
|
||||
gap: upMd ? "51px" : "10px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
width: "110px",
|
||||
fontSize: upMd ? "20px" : "18px",
|
||||
lineHeight: upMd ? undefined : "19px",
|
||||
fontWeight: 500,
|
||||
color: /* valuesByKey.expired */ false ? theme.palette.text.disabled : theme.palette.text.secondary,
|
||||
px: 0,
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
{createdAt}
|
||||
</Typography>
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
borderRadius: "12px",
|
||||
}}
|
||||
>
|
||||
<CustomAccordion
|
||||
last={last}
|
||||
first={first}
|
||||
divide
|
||||
text={tariff.privileges.map(
|
||||
(privilege) =>
|
||||
`${privilege.description} - ${privilege.amount} ${getDeclension(
|
||||
Number(privilege.serviceKey),
|
||||
privilege.value
|
||||
)} `
|
||||
)}
|
||||
header={
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: upMd ? "72px" : undefined,
|
||||
padding: "20px 20px 20px 0",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
cursor: "pointer",
|
||||
userSelect: "none",
|
||||
gap: "20px",
|
||||
alignItems: upSm ? "center" : undefined,
|
||||
flexDirection: upSm ? undefined : "column",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: upSm ? "center" : undefined,
|
||||
justifyContent: "space-between",
|
||||
flexDirection: upSm ? undefined : "column",
|
||||
gap: upMd ? "51px" : "10px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
width: "110px",
|
||||
fontSize: upMd ? "20px" : "18px",
|
||||
lineHeight: upMd ? undefined : "19px",
|
||||
fontWeight: 500,
|
||||
color: /* valuesByKey.expired */ false
|
||||
? theme.palette.text.disabled
|
||||
: theme.palette.text.secondary,
|
||||
px: 0,
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
{createdAt}
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
title={tariff.isCustom ? "Мой тариф" : tariff.name}
|
||||
sx={{
|
||||
fontSize: upMd ? "18px" : "16px",
|
||||
lineHeight: upMd ? undefined : "19px",
|
||||
fontWeight: 500,
|
||||
color: /* valuesByKey.expired */ false ? theme.palette.text.disabled : theme.palette.gray.dark,
|
||||
px: 0,
|
||||
width: upMd? "200px" : "auto",
|
||||
maxWidth: "200px",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
>
|
||||
{tariff.isCustom ? "Мой тариф" : tariff.name}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
flexFlow: "1",
|
||||
flexBasis: "60%",
|
||||
}}
|
||||
>
|
||||
<Box display="flex" width="100%" justifyContent="space-between">
|
||||
{/* <Typography
|
||||
<Typography
|
||||
title={tariff.isCustom ? "Мой тариф" : tariff.name}
|
||||
sx={{
|
||||
fontSize: upMd ? "18px" : "16px",
|
||||
lineHeight: upMd ? undefined : "19px",
|
||||
fontWeight: 500,
|
||||
color: /* valuesByKey.expired */ false
|
||||
? theme.palette.text.disabled
|
||||
: theme.palette.gray.dark,
|
||||
px: 0,
|
||||
width: upMd ? "200px" : "auto",
|
||||
maxWidth: "200px",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
>
|
||||
{tariff.isCustom ? "Мой тариф" : tariff.name}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
flexFlow: "1",
|
||||
flexBasis: "60%",
|
||||
}}
|
||||
>
|
||||
<Box display="flex" width="100%" justifyContent="space-between">
|
||||
{/* <Typography
|
||||
sx={{
|
||||
display: upMd ? undefined : "none",
|
||||
fontSize: upMd ? "18px" : "16px",
|
||||
@ -173,134 +188,144 @@ export default function AccordionWrapper2({ tariff, price, last, first, createdA
|
||||
}}
|
||||
>Способ оплаты: {valuesByKey.payMethod}</Typography>}
|
||||
</Typography> */}
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
height: "100%",
|
||||
alignItems: "center",
|
||||
gap: upSm ? "111px" : "17px",
|
||||
width: "100%",
|
||||
maxWidth: isTablet ? null : "160px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
marginLeft: isTablet ? (isMobile ? null : "auto") : null,
|
||||
color: /* valuesByKey.expired */ false ? theme.palette.text.disabled : theme.palette.gray.dark,
|
||||
fontSize: upSm ? "20px" : "16px",
|
||||
fontWeight: 500,
|
||||
textAlign: "left",
|
||||
}}
|
||||
>
|
||||
{currencyFormatter.format(price)}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
{!isMobile &&
|
||||
<>
|
||||
<IconButton
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
sendBillByEmail(mainId);
|
||||
}}
|
||||
sx={{
|
||||
ml: "20px",
|
||||
bgcolor: "#EEE4FC",
|
||||
color: "#7E2AEA",
|
||||
borderRadius: 2,
|
||||
"&:hover": {
|
||||
bgcolor: "#7E2AEA",
|
||||
color: "white",
|
||||
},
|
||||
"&:active": {
|
||||
bgcolor: "black",
|
||||
color: "white",
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ForwardToInboxIcon fontSize={"medium"} sx={{ opacity: 0.9 }}/>
|
||||
</IconButton>
|
||||
<IconButton
|
||||
title="Добавить в корзину тариф"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleTariffItemClick(tariff._id);
|
||||
}}
|
||||
sx={{
|
||||
ml: "20px",
|
||||
bgcolor: "#EEE4FC",
|
||||
stroke: "#7E2AEA",
|
||||
borderRadius: 2,
|
||||
"&:hover": {
|
||||
bgcolor: "#7E2AEA",
|
||||
stroke: "white",
|
||||
},
|
||||
"&:active": {
|
||||
bgcolor: "black",
|
||||
stroke: "white",
|
||||
}
|
||||
}}
|
||||
>
|
||||
<File></File>
|
||||
</IconButton>
|
||||
</>
|
||||
|
||||
}
|
||||
</Box>
|
||||
</Box>
|
||||
{isMobile &&
|
||||
<>
|
||||
<IconButton
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
sendBillByEmail(mainId);
|
||||
}}
|
||||
sx={{
|
||||
m: "0 10px",
|
||||
bgcolor: "#EEE4FC",
|
||||
color: "#7E2AEA",
|
||||
borderRadius: 2,
|
||||
"&:hover": {
|
||||
bgcolor: "#7E2AEA",
|
||||
color: "white",
|
||||
},
|
||||
"&:active": {
|
||||
bgcolor: "black",
|
||||
color: "white",
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ForwardToInboxIcon fontSize={"medium"} sx={{ opacity: 0.9 }}/>
|
||||
</IconButton>
|
||||
<IconButton
|
||||
title="Добавить в корзину тариф"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleTariffItemClick(tariff._id);
|
||||
}}
|
||||
sx={{
|
||||
mr: "10px",
|
||||
bgcolor: "#EEE4FC",
|
||||
stroke: "#7E2AEA",
|
||||
borderRadius: 2,
|
||||
"&:hover": {
|
||||
bgcolor: "#7E2AEA",
|
||||
stroke: "white",
|
||||
},
|
||||
"&:active": {
|
||||
bgcolor: "black",
|
||||
stroke: "white",
|
||||
}
|
||||
}}
|
||||
>
|
||||
<File></File>
|
||||
</IconButton>
|
||||
</>
|
||||
|
||||
}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
height: "100%",
|
||||
alignItems: "center",
|
||||
gap: upSm ? "111px" : "17px",
|
||||
width: "100%",
|
||||
maxWidth: isTablet ? null : "160px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
marginLeft: isTablet
|
||||
? isMobile
|
||||
? null
|
||||
: "auto"
|
||||
: null,
|
||||
color: /* valuesByKey.expired */ false
|
||||
? theme.palette.text.disabled
|
||||
: theme.palette.gray.dark,
|
||||
fontSize: upSm ? "20px" : "16px",
|
||||
fontWeight: 500,
|
||||
textAlign: "left",
|
||||
}}
|
||||
>
|
||||
{currencyFormatter.format(price)}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
{!isMobile && (
|
||||
<>
|
||||
<IconButton
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
sendBillByEmail(mainId);
|
||||
}}
|
||||
sx={{
|
||||
ml: "20px",
|
||||
bgcolor: "#EEE4FC",
|
||||
color: "#7E2AEA",
|
||||
borderRadius: 2,
|
||||
"&:hover": {
|
||||
bgcolor: "#7E2AEA",
|
||||
color: "white",
|
||||
},
|
||||
"&:active": {
|
||||
bgcolor: "black",
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ForwardToInboxIcon
|
||||
fontSize={"medium"}
|
||||
sx={{ opacity: 0.9 }}
|
||||
/>
|
||||
</IconButton>
|
||||
<IconButton
|
||||
title="Добавить в корзину тариф"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleTariffItemClick(tariff._id);
|
||||
}}
|
||||
sx={{
|
||||
ml: "20px",
|
||||
bgcolor: "#EEE4FC",
|
||||
stroke: "#7E2AEA",
|
||||
borderRadius: 2,
|
||||
"&:hover": {
|
||||
bgcolor: "#7E2AEA",
|
||||
stroke: "white",
|
||||
},
|
||||
"&:active": {
|
||||
bgcolor: "black",
|
||||
stroke: "white",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<File></File>
|
||||
</IconButton>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
{isMobile && (
|
||||
<>
|
||||
<IconButton
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
sendBillByEmail(mainId);
|
||||
}}
|
||||
sx={{
|
||||
m: "0 10px",
|
||||
bgcolor: "#EEE4FC",
|
||||
color: "#7E2AEA",
|
||||
borderRadius: 2,
|
||||
"&:hover": {
|
||||
bgcolor: "#7E2AEA",
|
||||
color: "white",
|
||||
},
|
||||
"&:active": {
|
||||
bgcolor: "black",
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ForwardToInboxIcon
|
||||
fontSize={"medium"}
|
||||
sx={{ opacity: 0.9 }}
|
||||
/>
|
||||
</IconButton>
|
||||
<IconButton
|
||||
title="Добавить в корзину тариф"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleTariffItemClick(tariff._id);
|
||||
}}
|
||||
sx={{
|
||||
mr: "10px",
|
||||
bgcolor: "#EEE4FC",
|
||||
stroke: "#7E2AEA",
|
||||
borderRadius: 2,
|
||||
"&:hover": {
|
||||
bgcolor: "#7E2AEA",
|
||||
stroke: "white",
|
||||
},
|
||||
"&:active": {
|
||||
bgcolor: "black",
|
||||
stroke: "white",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<File></File>
|
||||
</IconButton>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
@ -1,154 +1,168 @@
|
||||
import {useState} from "react";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Box,
|
||||
IconButton,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme
|
||||
Box,
|
||||
IconButton,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
||||
|
||||
import SectionWrapper from "@root/components/SectionWrapper";
|
||||
import {Select} from "@root/components/Select";
|
||||
import {Tabs} from "@root/components/Tabs";
|
||||
import { Select } from "@root/components/Select";
|
||||
import { Tabs } from "@root/components/Tabs";
|
||||
|
||||
import AccordionWrapper from "./AccordionWrapper";
|
||||
import {useHistoryTracker} from "@root/utils/hooks/useHistoryTracker";
|
||||
import {ErrorBoundary} from "react-error-boundary";
|
||||
import {handleComponentError} from "@root/utils/handleComponentError";
|
||||
import {useHistoryStore} from "@root/stores/history";
|
||||
import {enqueueSnackbar} from "notistack";
|
||||
import {makeRequest} from "@frontend/kitui";
|
||||
import {HistoryRecord, HistoryRecord2} from "@root/api/history";
|
||||
import { useHistoryTracker } from "@root/utils/hooks/useHistoryTracker";
|
||||
import { ErrorBoundary } from "react-error-boundary";
|
||||
import { handleComponentError } from "@root/utils/handleComponentError";
|
||||
import { useHistoryStore } from "@root/stores/history";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import {
|
||||
HistoryRecord,
|
||||
HistoryRecord2,
|
||||
sendReportById,
|
||||
} from "@root/api/history";
|
||||
import AccordionWrapper2 from "./AccordionWrapper2";
|
||||
|
||||
const subPages = ["Платежи"];
|
||||
// const subPages = ["Платежи", "Покупки тарифов", "Окончания тарифов"]
|
||||
|
||||
export default function History() {
|
||||
const [selectedItem, setSelectedItem] = useState<number>(0);
|
||||
const [selectedItem, setSelectedItem] = useState<number>(0);
|
||||
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||
const historyData = useHistoryStore(state => state.history);
|
||||
const handleCustomBackNavigation = useHistoryTracker();
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||
const historyData = useHistoryStore((state) => state.history);
|
||||
const handleCustomBackNavigation = useHistoryTracker();
|
||||
|
||||
const extractDateFromString = (tariffName: string) => {
|
||||
const dateMatch = tariffName.match(/\d{4}-\d{2}-\d{2}/);
|
||||
return dateMatch ? dateMatch[0] : "";
|
||||
};
|
||||
const extractDateFromString = (tariffName: string) => {
|
||||
const dateMatch = tariffName.match(/\d{4}-\d{2}-\d{2}/);
|
||||
return dateMatch ? dateMatch[0] : "";
|
||||
};
|
||||
|
||||
async function handleHistoryResponse(tariffId: string) {
|
||||
try {
|
||||
await makeRequest(
|
||||
{
|
||||
url: process.env.REACT_APP_DOMAIN + `/customer/sendReport/${tariffId}`,
|
||||
method: "POST",
|
||||
}
|
||||
);
|
||||
enqueueSnackbar("Запрос отправлен");
|
||||
} catch (e) {
|
||||
enqueueSnackbar("извините, произошла ошибка");
|
||||
}
|
||||
async function handleHistoryResponse(tariffId: string) {
|
||||
const [, sendReportError] = await sendReportById(tariffId);
|
||||
|
||||
if (sendReportError) {
|
||||
return enqueueSnackbar(sendReportError);
|
||||
}
|
||||
return (
|
||||
<SectionWrapper
|
||||
maxWidth="lg"
|
||||
sx={{
|
||||
mt: upMd ? "25px" : "20px",
|
||||
mb: upMd ? "70px" : "37px",
|
||||
px: isTablet ? (isTablet ? "18px" : "40px") : "20px",
|
||||
}}
|
||||
|
||||
enqueueSnackbar("Запрос отправлен");
|
||||
}
|
||||
return (
|
||||
<SectionWrapper
|
||||
maxWidth="lg"
|
||||
sx={{
|
||||
mt: upMd ? "25px" : "20px",
|
||||
mb: upMd ? "70px" : "37px",
|
||||
px: isTablet ? (isTablet ? "18px" : "40px") : "20px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
mt: "20px",
|
||||
mb: isTablet ? "38px" : "20px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
{isMobile && (
|
||||
<IconButton
|
||||
onClick={handleCustomBackNavigation}
|
||||
sx={{ p: 0, height: "28px", width: "28px", color: "black" }}
|
||||
>
|
||||
<ArrowBackIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: isMobile ? "24px" : "36px",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
mt: "20px",
|
||||
mb: isTablet ? "38px" : "20px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
{isMobile && (
|
||||
<IconButton onClick={handleCustomBackNavigation} sx={{ p: 0, height: "28px", width: "28px", color: "black" }}>
|
||||
<ArrowBackIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: isMobile ? "24px" : "36px",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
История
|
||||
</Typography>
|
||||
</Box>
|
||||
{isMobile ? (
|
||||
<Select items={subPages} selectedItem={selectedItem} setSelectedItem={setSelectedItem} />
|
||||
) : (
|
||||
<Tabs items={subPages} selectedItem={selectedItem} setSelectedItem={setSelectedItem} />
|
||||
)}
|
||||
<ErrorBoundary
|
||||
fallback={
|
||||
<Typography mt="8px">Ошибка загрузки истории</Typography>
|
||||
}
|
||||
onError={handleComponentError}
|
||||
>
|
||||
{historyData?.length === 0 && <Typography textAlign="center">Нет данных</Typography>}
|
||||
{/* Для ненормального rawDetails */}
|
||||
{historyData?.filter((e): e is HistoryRecord => {
|
||||
e.createdAt = extractDateFromString(e.createdAt);
|
||||
return (
|
||||
!e.isDeleted
|
||||
&& e.key === "payCart"
|
||||
&& Array.isArray(e.rawDetails)
|
||||
&& Array.isArray(e.rawDetails[0].Value)
|
||||
);
|
||||
}).map((e, index) => {
|
||||
return (
|
||||
<Box key={index} sx={{ mt: index === 0 ? "27px" : "0px" }}>
|
||||
<AccordionWrapper
|
||||
first={index === 0}
|
||||
last={index === historyData?.length - 1}
|
||||
content={(e as HistoryRecord).rawDetails}
|
||||
mainId={(e as HistoryRecord).id}
|
||||
key={e.id}
|
||||
createdAt={e.createdAt}
|
||||
onClickMail={(event: any) => {
|
||||
event.stopPropagation();
|
||||
handleHistoryResponse(e.id);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
{/* Для нормального rawDetails */}
|
||||
{historyData?.filter((e): e is HistoryRecord2 => {
|
||||
e.createdAt = extractDateFromString(e.createdAt);
|
||||
return (
|
||||
!e.isDeleted
|
||||
&& e.key === "payCart"
|
||||
&& !Array.isArray(e.rawDetails)
|
||||
&& !!e.rawDetails.tariffs[0]
|
||||
);
|
||||
}).map((e, index) => {
|
||||
return (
|
||||
<Box key={index} sx={{ mt: index === 0 ? "27px" : "0px" }}>
|
||||
<AccordionWrapper2
|
||||
key={e.id}
|
||||
first={index === 0}
|
||||
last={index === historyData?.length - 1}
|
||||
mainId={(e as HistoryRecord2).id}
|
||||
createdAt={e.createdAt}
|
||||
tariff={e.rawDetails.tariffs[0]}
|
||||
price={e.rawDetails.price/100}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</ErrorBoundary>
|
||||
</SectionWrapper>
|
||||
);
|
||||
История
|
||||
</Typography>
|
||||
</Box>
|
||||
{isMobile ? (
|
||||
<Select
|
||||
items={subPages}
|
||||
selectedItem={selectedItem}
|
||||
setSelectedItem={setSelectedItem}
|
||||
/>
|
||||
) : (
|
||||
<Tabs
|
||||
items={subPages}
|
||||
selectedItem={selectedItem}
|
||||
setSelectedItem={setSelectedItem}
|
||||
/>
|
||||
)}
|
||||
<ErrorBoundary
|
||||
fallback={<Typography mt="8px">Ошибка загрузки истории</Typography>}
|
||||
onError={handleComponentError}
|
||||
>
|
||||
{historyData?.length === 0 && (
|
||||
<Typography textAlign="center">Нет данных</Typography>
|
||||
)}
|
||||
{/* Для ненормального rawDetails */}
|
||||
{historyData
|
||||
?.filter((e): e is HistoryRecord => {
|
||||
e.createdAt = extractDateFromString(e.createdAt);
|
||||
return (
|
||||
!e.isDeleted &&
|
||||
e.key === "payCart" &&
|
||||
Array.isArray(e.rawDetails) &&
|
||||
Array.isArray(e.rawDetails[0].Value)
|
||||
);
|
||||
})
|
||||
.map((e, index) => {
|
||||
return (
|
||||
<Box key={index} sx={{ mt: index === 0 ? "27px" : "0px" }}>
|
||||
<AccordionWrapper
|
||||
first={index === 0}
|
||||
last={index === historyData?.length - 1}
|
||||
content={(e as HistoryRecord).rawDetails}
|
||||
mainId={(e as HistoryRecord).id}
|
||||
key={e.id}
|
||||
createdAt={e.createdAt}
|
||||
onClickMail={(event: any) => {
|
||||
event.stopPropagation();
|
||||
handleHistoryResponse(e.id);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
{/* Для нормального rawDetails */}
|
||||
{historyData
|
||||
?.filter((e): e is HistoryRecord2 => {
|
||||
e.createdAt = extractDateFromString(e.createdAt);
|
||||
return (
|
||||
!e.isDeleted &&
|
||||
e.key === "payCart" &&
|
||||
!Array.isArray(e.rawDetails) &&
|
||||
!!e.rawDetails.tariffs[0]
|
||||
);
|
||||
})
|
||||
.map((e, index) => {
|
||||
return (
|
||||
<Box key={index} sx={{ mt: index === 0 ? "27px" : "0px" }}>
|
||||
<AccordionWrapper2
|
||||
key={e.id}
|
||||
first={index === 0}
|
||||
last={index === historyData?.length - 1}
|
||||
mainId={(e as HistoryRecord2).id}
|
||||
createdAt={e.createdAt}
|
||||
tariff={e.rawDetails.tariffs[0]}
|
||||
price={e.rawDetails.price / 100}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</ErrorBoundary>
|
||||
</SectionWrapper>
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import axios, { AxiosResponse } from "axios";
|
||||
import { ApologyPage } from "../ApologyPage";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import {
|
||||
@ -22,19 +21,6 @@ import { clearCustomTariffs } from "@root/stores/customTariffs";
|
||||
import { clearTickets } from "@root/stores/tickets";
|
||||
import {setNotEnoughMoneyAmount} from "@stores/cart"
|
||||
|
||||
function refresh(token: string) {
|
||||
return axios<never, AxiosResponse<{ accessToken: string }>>(
|
||||
process.env.REACT_APP_DOMAIN + "/auth/refresh",
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer " + token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
method: "POST",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const action = params.get("action");
|
||||
const dif = params.get("dif");
|
||||
|
@ -14,7 +14,6 @@ import ArrowDownwardIcon from "@mui/icons-material/ArrowDownward";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import SendIcon from "@components/icons/SendIcon";
|
||||
import makeRequest from "@api/makeRequest"
|
||||
import { throttle, useToken } from "@frontend/kitui";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { useTicketStore } from "@root/stores/tickets";
|
||||
@ -35,7 +34,11 @@ import {
|
||||
useSSESubscription,
|
||||
useTicketMessages,
|
||||
} from "@frontend/kitui";
|
||||
import { shownMessage, sendTicketMessage } from "@root/api/ticket";
|
||||
import {
|
||||
shownMessage,
|
||||
sendTicketMessage,
|
||||
sendFile as sendFileRequest,
|
||||
} from "@root/api/ticket";
|
||||
import { withErrorBoundary } from "react-error-boundary";
|
||||
import { handleComponentError } from "@root/utils/handleComponentError";
|
||||
import { useSSETab } from "@root/utils/hooks/useSSETab";
|
||||
@ -196,20 +199,12 @@ function SupportChat() {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const body = new FormData();
|
||||
const [, sendFileError] = await sendFileRequest(ticketId, file);
|
||||
|
||||
body.append(file.name, file);
|
||||
body.append("ticket", ticketId);
|
||||
await makeRequest({
|
||||
url: process.env.REACT_APP_DOMAIN + "/heruvym/sendFiles",
|
||||
body: body,
|
||||
method: "POST",
|
||||
});
|
||||
} catch (error: any) {
|
||||
const errorMessage = getMessageFromFetchError(error);
|
||||
if (errorMessage) enqueueSnackbar(errorMessage);
|
||||
if (sendFileError) {
|
||||
enqueueSnackbar(sendFileError);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
@ -21,7 +21,6 @@ import { setUserId, useUserStore } from "@root/stores/user";
|
||||
import { cardShadow } from "@root/utils/theme";
|
||||
import AmoButton from "./AmoButton";
|
||||
import { recover } from "@root/api/auth";
|
||||
import {AxiosError} from "axios"
|
||||
|
||||
interface Values {
|
||||
email: string;
|
||||
|
@ -1,12 +1,12 @@
|
||||
import {
|
||||
Box,
|
||||
Dialog,
|
||||
IconButton,
|
||||
Link,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
Button,
|
||||
Box,
|
||||
Dialog,
|
||||
IconButton,
|
||||
Link,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
Button,
|
||||
} from "@mui/material";
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
@ -19,175 +19,180 @@ import { useEffect, useState } from "react";
|
||||
import { useUserStore } from "@root/stores/user";
|
||||
import { cardShadow } from "@root/utils/theme";
|
||||
|
||||
import makeRequest from "@api/makeRequest"
|
||||
import { setAuthToken } from "@frontend/kitui"
|
||||
import { patchUser } from "@api/user";
|
||||
import { setAuthToken } from "@frontend/kitui";
|
||||
interface Values {
|
||||
password: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
const initialValues: Values = {
|
||||
password: "",
|
||||
password: "",
|
||||
};
|
||||
|
||||
const validationSchema = object({
|
||||
password: string()
|
||||
.min(8, "Минимум 8 символов")
|
||||
.matches(/^[.,:;\-_+!&()*<>\[\]\{\}`@"#$\%\^\=?\d\w]+$/, "Некорректные символы")
|
||||
.required("Поле обязательно"),
|
||||
password: string()
|
||||
.min(8, "Минимум 8 символов")
|
||||
.matches(
|
||||
/^[.,:;\-_+!&()*<>\[\]\{\}`@"#$\%\^\=?\d\w]+$/,
|
||||
"Некорректные символы"
|
||||
)
|
||||
.required("Поле обязательно"),
|
||||
});
|
||||
|
||||
export default function RecoverPassword() {
|
||||
const [isDialogOpen, setIsDialogOpen] = useState<boolean>(true);
|
||||
const [tokenUser, setTokenUser] = useState<string | null>("");
|
||||
const user = useUserStore((state) => state.user);
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const formik = useFormik<Values>({
|
||||
initialValues,
|
||||
validationSchema,
|
||||
onSubmit: async (values, formikHelpers) => {
|
||||
if (tokenUser) {
|
||||
setAuthToken(tokenUser || "")
|
||||
try {
|
||||
const response = await makeRequest<unknown, unknown>({
|
||||
url: process.env.REACT_APP_DOMAIN + "/user/",
|
||||
method: "PATCH",
|
||||
body: {password: values.password},
|
||||
});
|
||||
setIsDialogOpen(false)
|
||||
navigate("/")
|
||||
enqueueSnackbar("Пароль успешно сменён")
|
||||
} catch (error) {
|
||||
setAuthToken("")
|
||||
enqueueSnackbar("Извините, произошла ошибка, попробуйте повторить позже")}
|
||||
const [isDialogOpen, setIsDialogOpen] = useState<boolean>(true);
|
||||
const [tokenUser, setTokenUser] = useState<string | null>("");
|
||||
const user = useUserStore((state) => state.user);
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const formik = useFormik<Values>({
|
||||
initialValues,
|
||||
validationSchema,
|
||||
onSubmit: async (values, formikHelpers) => {
|
||||
if (tokenUser) {
|
||||
setAuthToken(tokenUser || "");
|
||||
const [, patchUserError] = await patchUser({
|
||||
password: values.password,
|
||||
});
|
||||
|
||||
} else {
|
||||
enqueueSnackbar("Неверный url-адрес")
|
||||
}
|
||||
},
|
||||
});
|
||||
useEffect(() => {
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
const authToken = params.get("auth")
|
||||
setTokenUser(authToken)
|
||||
if (!patchUserError) {
|
||||
setIsDialogOpen(false);
|
||||
navigate("/");
|
||||
enqueueSnackbar("Пароль успешно сменён");
|
||||
} else {
|
||||
setAuthToken("");
|
||||
enqueueSnackbar(
|
||||
"Извините, произошла ошибка, попробуйте повторить позже"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
enqueueSnackbar("Неверный url-адрес");
|
||||
}
|
||||
},
|
||||
});
|
||||
useEffect(() => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const authToken = params.get("auth");
|
||||
setTokenUser(authToken);
|
||||
|
||||
history.pushState(null, document.title, "/changepwd");
|
||||
return () => {setAuthToken("")}
|
||||
}, []);
|
||||
history.pushState(null, document.title, "/changepwd");
|
||||
return () => {
|
||||
setAuthToken("");
|
||||
};
|
||||
}, []);
|
||||
|
||||
function handleClose() {
|
||||
setIsDialogOpen(false);
|
||||
setTimeout(() => navigate("/"), theme.transitions.duration.leavingScreen);
|
||||
}
|
||||
function handleClose() {
|
||||
setIsDialogOpen(false);
|
||||
setTimeout(() => navigate("/"), theme.transitions.duration.leavingScreen);
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={isDialogOpen}
|
||||
onClose={handleClose}
|
||||
PaperProps={{
|
||||
sx: {
|
||||
width: "600px",
|
||||
maxWidth: "600px",
|
||||
},
|
||||
}}
|
||||
slotProps={{
|
||||
backdrop: {
|
||||
style: {
|
||||
backgroundColor: "rgb(0 0 0 / 0.7)",
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
component="form"
|
||||
onSubmit={formik.handleSubmit}
|
||||
noValidate
|
||||
sx={{
|
||||
position: "relative",
|
||||
backgroundColor: "white",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
p: upMd ? "50px" : "18px",
|
||||
pb: upMd ? "40px" : "30px",
|
||||
gap: "15px",
|
||||
borderRadius: "12px",
|
||||
boxShadow: cardShadow,
|
||||
"& .MuiFormHelperText-root.Mui-error, & .MuiFormHelperText-root.Mui-error.MuiFormHelperText-filled":
|
||||
{
|
||||
position: "absolute",
|
||||
top: "46px",
|
||||
margin: "0",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
onClick={handleClose}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
right: "7px",
|
||||
top: "7px",
|
||||
}}
|
||||
>
|
||||
<CloseIcon sx={{ transform: "scale(1.5)" }} />
|
||||
</IconButton>
|
||||
<Box>
|
||||
<PenaLogo width={upMd ? 233 : 196} color="black" />
|
||||
</Box>
|
||||
<Typography
|
||||
sx={{
|
||||
color: theme.palette.gray.dark,
|
||||
mt: "5px",
|
||||
mb: upMd ? "10px" : "33px",
|
||||
}}
|
||||
>
|
||||
Введите новый пароль
|
||||
</Typography>
|
||||
<InputTextfield
|
||||
TextfieldProps={{
|
||||
value: formik.values.password,
|
||||
placeholder: "введите пароль",
|
||||
onBlur: formik.handleBlur,
|
||||
error: formik.touched.password && Boolean(formik.errors.password),
|
||||
helperText: formik.touched.password && formik.errors.password,
|
||||
}}
|
||||
onChange={formik.handleChange}
|
||||
color="#F2F3F7"
|
||||
id="password"
|
||||
label="Новый пароль"
|
||||
gap={upMd ? "10px" : "10px"}
|
||||
/>
|
||||
<Button
|
||||
variant="pena-contained-dark"
|
||||
fullWidth
|
||||
type="submit"
|
||||
disabled={formik.isSubmitting}
|
||||
sx={{
|
||||
py: "12px",
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.purple.dark,
|
||||
},
|
||||
"&:active": {
|
||||
color: "white",
|
||||
backgroundColor: "black",
|
||||
},
|
||||
}}
|
||||
>
|
||||
Восстановить
|
||||
</Button>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
mt: "auto",
|
||||
}}
|
||||
>
|
||||
</Box>
|
||||
</Box>
|
||||
</Dialog>
|
||||
);
|
||||
return (
|
||||
<Dialog
|
||||
open={isDialogOpen}
|
||||
onClose={handleClose}
|
||||
PaperProps={{
|
||||
sx: {
|
||||
width: "600px",
|
||||
maxWidth: "600px",
|
||||
},
|
||||
}}
|
||||
slotProps={{
|
||||
backdrop: {
|
||||
style: {
|
||||
backgroundColor: "rgb(0 0 0 / 0.7)",
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
component="form"
|
||||
onSubmit={formik.handleSubmit}
|
||||
noValidate
|
||||
sx={{
|
||||
position: "relative",
|
||||
backgroundColor: "white",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
p: upMd ? "50px" : "18px",
|
||||
pb: upMd ? "40px" : "30px",
|
||||
gap: "15px",
|
||||
borderRadius: "12px",
|
||||
boxShadow: cardShadow,
|
||||
"& .MuiFormHelperText-root.Mui-error, & .MuiFormHelperText-root.Mui-error.MuiFormHelperText-filled":
|
||||
{
|
||||
position: "absolute",
|
||||
top: "46px",
|
||||
margin: "0",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
onClick={handleClose}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
right: "7px",
|
||||
top: "7px",
|
||||
}}
|
||||
>
|
||||
<CloseIcon sx={{ transform: "scale(1.5)" }} />
|
||||
</IconButton>
|
||||
<Box>
|
||||
<PenaLogo width={upMd ? 233 : 196} color="black" />
|
||||
</Box>
|
||||
<Typography
|
||||
sx={{
|
||||
color: theme.palette.gray.dark,
|
||||
mt: "5px",
|
||||
mb: upMd ? "10px" : "33px",
|
||||
}}
|
||||
>
|
||||
Введите новый пароль
|
||||
</Typography>
|
||||
<InputTextfield
|
||||
TextfieldProps={{
|
||||
value: formik.values.password,
|
||||
placeholder: "введите пароль",
|
||||
onBlur: formik.handleBlur,
|
||||
error: formik.touched.password && Boolean(formik.errors.password),
|
||||
helperText: formik.touched.password && formik.errors.password,
|
||||
}}
|
||||
onChange={formik.handleChange}
|
||||
color="#F2F3F7"
|
||||
id="password"
|
||||
label="Новый пароль"
|
||||
gap={upMd ? "10px" : "10px"}
|
||||
/>
|
||||
<Button
|
||||
variant="pena-contained-dark"
|
||||
fullWidth
|
||||
type="submit"
|
||||
disabled={formik.isSubmitting}
|
||||
sx={{
|
||||
py: "12px",
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.purple.dark,
|
||||
},
|
||||
"&:active": {
|
||||
color: "white",
|
||||
backgroundColor: "black",
|
||||
},
|
||||
}}
|
||||
>
|
||||
Восстановить
|
||||
</Button>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
mt: "auto",
|
||||
}}
|
||||
></Box>
|
||||
</Box>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
@ -1,42 +1,35 @@
|
||||
import { ErrorInfo } from "react"
|
||||
|
||||
import { ErrorInfo } from "react";
|
||||
|
||||
interface ComponentError {
|
||||
timestamp: number;
|
||||
message: string;
|
||||
callStack: string | undefined;
|
||||
componentStack: string | null | undefined;
|
||||
timestamp: number;
|
||||
message: string;
|
||||
callStack: string | undefined;
|
||||
componentStack: string | null | undefined;
|
||||
}
|
||||
|
||||
export function handleComponentError(error: Error, info: ErrorInfo) {
|
||||
const componentError: ComponentError = {
|
||||
timestamp: Math.floor(Date.now() / 1000),
|
||||
message: error.message,
|
||||
callStack: error.stack,
|
||||
componentStack: info.componentStack,
|
||||
}
|
||||
const componentError: ComponentError = {
|
||||
timestamp: Math.floor(Date.now() / 1000),
|
||||
message: error.message,
|
||||
callStack: error.stack,
|
||||
componentStack: info.componentStack,
|
||||
};
|
||||
|
||||
queueErrorRequest(componentError)
|
||||
queueErrorRequest(componentError);
|
||||
}
|
||||
|
||||
let errorsQueue: ComponentError[] = []
|
||||
let timeoutId: ReturnType<typeof setTimeout>
|
||||
let errorsQueue: ComponentError[] = [];
|
||||
let timeoutId: ReturnType<typeof setTimeout>;
|
||||
|
||||
function queueErrorRequest(error: ComponentError) {
|
||||
errorsQueue.push(error)
|
||||
errorsQueue.push(error);
|
||||
|
||||
clearTimeout(timeoutId)
|
||||
timeoutId = setTimeout(() => {
|
||||
sendErrorsToServer()
|
||||
}, 1000)
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(() => {
|
||||
sendErrorsToServer();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
async function sendErrorsToServer() {
|
||||
// makeRequest({
|
||||
// url: "",
|
||||
// method: "POST",
|
||||
// body: errorsQueue,
|
||||
// useToken: true,
|
||||
// });
|
||||
errorsQueue = []
|
||||
errorsQueue = [];
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user