Compare commits
No commits in common. "v1.0.92" and "master" have entirely different histories.
@ -1,25 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
env: { browser: true, es2020: true },
|
|
||||||
extends: [
|
|
||||||
'eslint:recommended',
|
|
||||||
'plugin:@typescript-eslint/recommended',
|
|
||||||
'plugin:react-hooks/recommended',
|
|
||||||
],
|
|
||||||
ignorePatterns: ['dist', '.eslintrc.cjs', "vite.config.ts"],
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
plugins: ['react-refresh'],
|
|
||||||
rules: {
|
|
||||||
'react-refresh/only-export-components': [
|
|
||||||
'warn',
|
|
||||||
{ allowConstantExport: true },
|
|
||||||
],
|
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
|
||||||
"@typescript-eslint/no-unused-vars": "warn",
|
|
||||||
"no-restricted-exports": ["error", {
|
|
||||||
"restrictDefaultExports": {
|
|
||||||
direct: true,
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
};
|
|
24
.gitignore
vendored
24
.gitignore
vendored
@ -1,24 +0,0 @@
|
|||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
pnpm-debug.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
|
|
||||||
node_modules
|
|
||||||
dist
|
|
||||||
dist-ssr
|
|
||||||
*.local
|
|
||||||
|
|
||||||
# Editor directories and files
|
|
||||||
.vscode/*
|
|
||||||
!.vscode/extensions.json
|
|
||||||
.idea
|
|
||||||
.DS_Store
|
|
||||||
*.suo
|
|
||||||
*.ntvs*
|
|
||||||
*.njsproj
|
|
||||||
*.sln
|
|
||||||
*.sw?
|
|
4
.npmrc
Normal file
4
.npmrc
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
@frontend:kitui=https://penahub.gitlab.yandexcloud.net/api/v4/packages/npm/
|
||||||
|
'//penahub.gitlab.yandexcloud.net/api/v4/packages/npm/:_authToken'="${GITLAB_AUTH_TOKEN}"
|
||||||
|
'//penahub.gitlab.yandexcloud.net/api/v4/projects/21/packages/npm/:_authToken'="${GITLAB_AUTH_TOKEN}"
|
||||||
|
|
1
.yarnrc
1
.yarnrc
@ -1 +0,0 @@
|
|||||||
"@frontend:registry" "http://gitea.pena/api/packages/skeris/npm/"
|
|
19
README.md
19
README.md
@ -1,19 +0,0 @@
|
|||||||
## Перед использованием и публикацией
|
|
||||||
```bash
|
|
||||||
# заменить TOKEN на актуальный токен
|
|
||||||
npm config set //penahub.gitlab.yandexcloud.net/api/v4/packages/npm/:_authToken=TOKEN
|
|
||||||
npm config set //penahub.gitlab.yandexcloud.net/api/v4/projects/21/packages/npm/:_authToken=TOKEN
|
|
||||||
```
|
|
||||||
## Публикация
|
|
||||||
```bash
|
|
||||||
yarn publish
|
|
||||||
```
|
|
||||||
## Использование
|
|
||||||
Добавить в корень проекта файл .yarnrc с содержимым
|
|
||||||
```
|
|
||||||
"@frontend:registry" "https://penahub.gitlab.yandexcloud.net/api/v4/packages/npm/"
|
|
||||||
```
|
|
||||||
Установка
|
|
||||||
```bash
|
|
||||||
yarn add @frontend/kitui
|
|
||||||
```
|
|
17
index.html
17
index.html
@ -1,17 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Vite + React + TS</title>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Rubik:wght@400;500;600&display=swap" rel="stylesheet">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="root"></div>
|
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
@ -1,27 +0,0 @@
|
|||||||
import { UserAccount, UserName } from "../model/account";
|
|
||||||
import { makeRequest } from "./makeRequest";
|
|
||||||
|
|
||||||
|
|
||||||
const apiUrl = process.env.REACT_APP_DOMAIN + "/customer";
|
|
||||||
|
|
||||||
export function patchUserAccount(user: UserName, version:string | undefined) {
|
|
||||||
return makeRequest<UserName, UserAccount>({
|
|
||||||
url: `${apiUrl + (version ? `/${version}` : "")}/account`,
|
|
||||||
contentType: true,
|
|
||||||
method: "PATCH",
|
|
||||||
useToken: true,
|
|
||||||
withCredentials: false,
|
|
||||||
body: user,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createUserAccount(signal: AbortSignal, url: string | undefined, version: string) {
|
|
||||||
return makeRequest<never, UserAccount>({
|
|
||||||
url: url || `${apiUrl + (version.length > 0 ? `/${version}` : "")}/account`,
|
|
||||||
contentType: true,
|
|
||||||
method: "POST",
|
|
||||||
useToken: true,
|
|
||||||
withCredentials: false,
|
|
||||||
signal,
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
export * from "./account";
|
|
||||||
export * from "./makeRequest";
|
|
||||||
export * from "./tariff";
|
|
||||||
export * from "./tickets";
|
|
@ -1,76 +0,0 @@
|
|||||||
import axios, { AxiosResponse, Method, ResponseType } from "axios";
|
|
||||||
import { getAuthToken, setAuthToken } from "../stores/auth";
|
|
||||||
|
|
||||||
|
|
||||||
export async function makeRequest<TRequest = unknown, TResponse = unknown>({
|
|
||||||
method = "post",
|
|
||||||
url,
|
|
||||||
body,
|
|
||||||
useToken = true,
|
|
||||||
contentType = false,
|
|
||||||
responseType = "json",
|
|
||||||
signal,
|
|
||||||
withCredentials,
|
|
||||||
}: {
|
|
||||||
method?: Method;
|
|
||||||
url: string;
|
|
||||||
body?: TRequest;
|
|
||||||
/** Send access token */
|
|
||||||
useToken?: boolean;
|
|
||||||
contentType?: boolean;
|
|
||||||
responseType?: ResponseType;
|
|
||||||
signal?: AbortSignal;
|
|
||||||
/** Send refresh token */
|
|
||||||
withCredentials?: boolean;
|
|
||||||
}): Promise<TResponse> {
|
|
||||||
const headers: Record<string, string> = {};
|
|
||||||
if (useToken) headers["Authorization"] = `Bearer ${getAuthToken()}`;
|
|
||||||
if (contentType) headers["Content-Type"] = "application/json";
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await axios<TRequest, AxiosResponse<TResponse & { accessToken?: string; }>>({
|
|
||||||
url,
|
|
||||||
method,
|
|
||||||
headers,
|
|
||||||
data: body,
|
|
||||||
signal,
|
|
||||||
responseType,
|
|
||||||
withCredentials,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.data?.accessToken) {
|
|
||||||
setAuthToken(response.data.accessToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
if (axios.isAxiosError(error) && error.response?.status === 401 && !withCredentials) {
|
|
||||||
const refreshResponse = await refresh(getAuthToken());
|
|
||||||
if (refreshResponse.data?.accessToken) setAuthToken(refreshResponse.data.accessToken);
|
|
||||||
|
|
||||||
headers["Authorization"] = refreshResponse.data.accessToken;
|
|
||||||
const response = await axios.request<TRequest, AxiosResponse<TResponse>>({
|
|
||||||
url,
|
|
||||||
method,
|
|
||||||
headers,
|
|
||||||
data: body,
|
|
||||||
signal,
|
|
||||||
});
|
|
||||||
|
|
||||||
return response.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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"
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
import { Tariff } from "../model/tariff";
|
|
||||||
import { makeRequest } from "./makeRequest";
|
|
||||||
|
|
||||||
|
|
||||||
export function getTariffById(tariffId:string){
|
|
||||||
return makeRequest<never, Tariff>({
|
|
||||||
url: process.env.REACT_APP_DOMAIN + `/strator/tariff/${tariffId}`,
|
|
||||||
method: "get",
|
|
||||||
useToken: true,
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
import { CreateTicketRequest, CreateTicketResponse } from "../model/ticket";
|
|
||||||
import { makeRequest } from "./makeRequest";
|
|
||||||
|
|
||||||
|
|
||||||
export function createTicket({ url, body, useToken = true }: {
|
|
||||||
url: string;
|
|
||||||
body: CreateTicketRequest;
|
|
||||||
useToken?: boolean;
|
|
||||||
}): Promise<CreateTicketResponse> {
|
|
||||||
return makeRequest({
|
|
||||||
url,
|
|
||||||
method: "POST",
|
|
||||||
useToken,
|
|
||||||
body,
|
|
||||||
withCredentials: true,
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
import { Avatar, IconButton, IconButtonProps, Typography, useTheme } from "@mui/material";
|
|
||||||
import { deepmerge } from "@mui/utils";
|
|
||||||
import { ForwardRefExoticComponent, RefAttributes } from "react";
|
|
||||||
import { LinkProps } from "react-router-dom";
|
|
||||||
|
|
||||||
|
|
||||||
type Props = IconButtonProps & {
|
|
||||||
component?: ForwardRefExoticComponent<LinkProps & RefAttributes<HTMLAnchorElement>>;
|
|
||||||
to?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function AvatarButton(props: Props) {
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<IconButton
|
|
||||||
{...deepmerge({
|
|
||||||
sx: {
|
|
||||||
height: 36,
|
|
||||||
width: 36,
|
|
||||||
p: 0,
|
|
||||||
"&:hover .MuiAvatar-root": {
|
|
||||||
border: `2px solid ${theme.palette.gray.main}`,
|
|
||||||
},
|
|
||||||
"&:active .MuiAvatar-root": {
|
|
||||||
backgroundColor: theme.palette.purple.main,
|
|
||||||
color: theme.palette.purple.main,
|
|
||||||
border: "1px solid black",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}, props)}
|
|
||||||
>
|
|
||||||
<Avatar sx={{
|
|
||||||
height: "100%",
|
|
||||||
width: "100%",
|
|
||||||
backgroundColor: theme.palette.orange.main,
|
|
||||||
color: theme.palette.orange.light,
|
|
||||||
transition: "all 100ms",
|
|
||||||
}}>
|
|
||||||
<Typography sx={{
|
|
||||||
fontWeight: 500,
|
|
||||||
fontSize: "14px",
|
|
||||||
lineHeight: "20px",
|
|
||||||
zIndex: 1,
|
|
||||||
textTransform: "uppercase",
|
|
||||||
position: "absolute",
|
|
||||||
color: "white",
|
|
||||||
}}>
|
|
||||||
{props.children ?? "AA"}
|
|
||||||
</Typography>
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 36 37" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path fillRule="evenodd" clipRule="evenodd" d="M15.348 16.146c.1-5.981 6.823-10.034 12.62-11.479 6.055-1.508 13.264-.719 17.31 4.023 3.673 4.303.83 10.565-.085 16.16-.678 4.152-1.209 8.41-4.383 11.171-3.418 2.973-8.742 6.062-12.43 3.452-3.663-2.593 1.412-8.88-.78-12.8-2.764-4.95-12.347-4.85-12.252-10.527Z" fill="currentColor" />
|
|
||||||
<circle cx="28.052" cy="-3.333" r="5.519" transform="rotate(-32.339 28.052 -3.333)" fill="currentColor" />
|
|
||||||
<circle cx="24.363" cy="29.03" r="1.27" transform="rotate(-32.339 24.363 29.03)" fill="currentColor" />
|
|
||||||
</svg>
|
|
||||||
</Avatar>
|
|
||||||
</IconButton>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
import { IconButton, IconButtonProps, useTheme } from "@mui/material";
|
|
||||||
import { deepmerge } from "@mui/utils";
|
|
||||||
|
|
||||||
|
|
||||||
export function BurgerButton(props: IconButtonProps) {
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<IconButton
|
|
||||||
{...deepmerge({
|
|
||||||
sx: {
|
|
||||||
height: 30,
|
|
||||||
width: 30,
|
|
||||||
p: 0,
|
|
||||||
color: "black",
|
|
||||||
"&:hover": {
|
|
||||||
color: theme.palette.purple.main,
|
|
||||||
backgroundColor: "rgb(0 0 0 / 0)",
|
|
||||||
},
|
|
||||||
"&:active": {
|
|
||||||
color: theme.palette.purple.main,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, props)}
|
|
||||||
>
|
|
||||||
<svg width="30" height="31" viewBox="0 0 30 31" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M28 8.005H3M28 16.005H3M28 24.005H3" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
|
|
||||||
</svg>
|
|
||||||
</IconButton>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
import { IconButton, IconButtonProps, useTheme } from "@mui/material";
|
|
||||||
import { deepmerge } from "@mui/utils";
|
|
||||||
|
|
||||||
|
|
||||||
export function CloseButton(props: IconButtonProps) {
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<IconButton
|
|
||||||
{...deepmerge({
|
|
||||||
sx: {
|
|
||||||
height: 30,
|
|
||||||
width: 30,
|
|
||||||
p: 0,
|
|
||||||
color: "black",
|
|
||||||
"&:hover": {
|
|
||||||
color: theme.palette.orange.main,
|
|
||||||
backgroundColor: "rgb(0 0 0 / 0)",
|
|
||||||
},
|
|
||||||
"&:active": {
|
|
||||||
color: theme.palette.orange.main,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, props)}
|
|
||||||
>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 30 31" fill="none">
|
|
||||||
<path d="m3 3.605 24 24m-24 0 24-24" stroke="currentColor" />
|
|
||||||
</svg>
|
|
||||||
</IconButton>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
import { IconButton, IconButtonProps, useTheme } from "@mui/material";
|
|
||||||
import { deepmerge } from "@mui/utils";
|
|
||||||
|
|
||||||
|
|
||||||
export function CloseButtonSmall(props: IconButtonProps) {
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<IconButton
|
|
||||||
{...deepmerge({
|
|
||||||
sx: {
|
|
||||||
height: 12,
|
|
||||||
width: 12,
|
|
||||||
p: 0,
|
|
||||||
color: theme.palette.purple.main,
|
|
||||||
"&:hover": {
|
|
||||||
color: theme.palette.orange.main,
|
|
||||||
backgroundColor: "rgb(0 0 0 / 0)",
|
|
||||||
},
|
|
||||||
"&:active": {
|
|
||||||
color: theme.palette.orange.main,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, props)}
|
|
||||||
>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 14 14" fill="none">
|
|
||||||
<path d="m13 1.176-12 12M13 13.176l-12-12" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
|
||||||
</svg>
|
|
||||||
</IconButton>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
import { IconButton, IconButtonProps, useTheme } from "@mui/material";
|
|
||||||
import { deepmerge } from "@mui/utils";
|
|
||||||
|
|
||||||
|
|
||||||
export function LogoutButton(props: IconButtonProps) {
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<IconButton
|
|
||||||
{...deepmerge({
|
|
||||||
sx: {
|
|
||||||
height: 36,
|
|
||||||
width: 36,
|
|
||||||
p: 0,
|
|
||||||
borderRadius: "6px",
|
|
||||||
backgroundColor: theme.palette.background.default,
|
|
||||||
color: theme.palette.gray.main,
|
|
||||||
"&:hover": {
|
|
||||||
color: theme.palette.background.default,
|
|
||||||
backgroundColor: theme.palette.gray.main,
|
|
||||||
},
|
|
||||||
"&:active": {
|
|
||||||
backgroundColor: theme.palette.purple.main,
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}, props)}
|
|
||||||
>
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 36 37" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path
|
|
||||||
d="M22.559 22.052v2.95a2 2 0 0 1-2 2h-6.865a2 2 0 0 1-2-2v-12.5a2 2 0 0 1 2-2h6.865a2 2 0 0 1 2 2v2.95M25.067 21.227l1.786-1.763a1 1 0 0 0 0-1.423l-1.786-1.764M26.737 18.752H16.71"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</IconButton>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
import { Link, LinkProps, LinkTypeMap, Typography, useTheme } from "@mui/material";
|
|
||||||
import ArrowForwardIcon from '@mui/icons-material/ArrowForward';
|
|
||||||
import { deepmerge } from "@mui/utils";
|
|
||||||
import { OverridableComponent } from "@mui/material/OverridableComponent";
|
|
||||||
|
|
||||||
|
|
||||||
export const PenaLink: OverridableComponent<LinkTypeMap<object, "a">> = (props: LinkProps) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
{...deepmerge({
|
|
||||||
sx: {
|
|
||||||
display: "flex",
|
|
||||||
gap: "3px",
|
|
||||||
textUnderlinePosition: "under",
|
|
||||||
color: theme.palette.purple.light,
|
|
||||||
textDecorationColor: theme.palette.purple.main,
|
|
||||||
textUnderlineOffset: "2px",
|
|
||||||
"&:hover": {
|
|
||||||
textDecorationColor: theme.palette.purple.light,
|
|
||||||
},
|
|
||||||
"&:active": {
|
|
||||||
color: "white",
|
|
||||||
textDecorationColor: "white",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}, props)}
|
|
||||||
>
|
|
||||||
<Typography variant="body2">{props.children}</Typography>
|
|
||||||
<ArrowForwardIcon sx={{ height: "20px", width: "20px" }} />
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,120 +0,0 @@
|
|||||||
import { FormControl, InputLabel, SxProps, TextField, TextFieldProps, Theme, useMediaQuery, useTheme } from "@mui/material";
|
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
id?: string;
|
|
||||||
label?: string;
|
|
||||||
labelSx?: SxProps<Theme>;
|
|
||||||
bold?: boolean;
|
|
||||||
gap?: string;
|
|
||||||
backgroundColor?: string;
|
|
||||||
FormControlSx?: SxProps<Theme>;
|
|
||||||
TextFieldSx?: SxProps<Theme>;
|
|
||||||
placeholder?: TextFieldProps["placeholder"];
|
|
||||||
value?: TextFieldProps["value"];
|
|
||||||
helperText?: TextFieldProps["helperText"];
|
|
||||||
error?: TextFieldProps["error"];
|
|
||||||
type?: TextFieldProps["type"];
|
|
||||||
onBlur?: TextFieldProps["onBlur"];
|
|
||||||
onChange?: TextFieldProps["onChange"];
|
|
||||||
fullWidth?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function PenaTextField({
|
|
||||||
id = "pena-textfield",
|
|
||||||
label,
|
|
||||||
labelSx,
|
|
||||||
bold = false,
|
|
||||||
gap = "10px",
|
|
||||||
onChange,
|
|
||||||
error,
|
|
||||||
helperText,
|
|
||||||
onBlur,
|
|
||||||
placeholder,
|
|
||||||
type,
|
|
||||||
value,
|
|
||||||
backgroundColor,
|
|
||||||
FormControlSx,
|
|
||||||
TextFieldSx,
|
|
||||||
fullWidth = true,
|
|
||||||
}: Props) {
|
|
||||||
const theme = useTheme();
|
|
||||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
|
||||||
|
|
||||||
const labelFont = upMd
|
|
||||||
? bold
|
|
||||||
? theme.typography.p1
|
|
||||||
: { ...theme.typography.body1, fontWeight: 500 }
|
|
||||||
: theme.typography.body2;
|
|
||||||
|
|
||||||
const placeholderFont = upMd ? undefined : { fontWeight: 400, fontSize: "16px", lineHeight: "19px" };
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FormControl
|
|
||||||
fullWidth={fullWidth}
|
|
||||||
variant="standard"
|
|
||||||
sx={{
|
|
||||||
gap,
|
|
||||||
...FormControlSx,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{label && (
|
|
||||||
<InputLabel
|
|
||||||
shrink
|
|
||||||
htmlFor={id}
|
|
||||||
sx={{
|
|
||||||
position: "inherit",
|
|
||||||
color: "black",
|
|
||||||
transform: "none",
|
|
||||||
...labelFont,
|
|
||||||
...labelSx,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{label}
|
|
||||||
</InputLabel>
|
|
||||||
)}
|
|
||||||
<TextField
|
|
||||||
fullWidth
|
|
||||||
id={id}
|
|
||||||
error={error}
|
|
||||||
helperText={helperText}
|
|
||||||
onBlur={onBlur}
|
|
||||||
placeholder={placeholder}
|
|
||||||
type={type}
|
|
||||||
value={value}
|
|
||||||
sx={{
|
|
||||||
"& .MuiInputBase-root": {
|
|
||||||
height: "48px",
|
|
||||||
borderRadius: "8px",
|
|
||||||
"& fieldset": {
|
|
||||||
border: `1px solid ${theme.palette.gray.main}`,
|
|
||||||
},
|
|
||||||
"&:hover fieldset": {
|
|
||||||
border: `1px solid ${theme.palette.gray.dark}`,
|
|
||||||
},
|
|
||||||
"&.Mui-focused fieldset": {
|
|
||||||
border: `2px solid ${theme.palette.purple.main}`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"& .MuiFormHelperText-root.MuiFormHelperText-contained.MuiFormHelperText-filled.Mui-error": {
|
|
||||||
position: "absolute",
|
|
||||||
top: "45px",
|
|
||||||
},
|
|
||||||
...TextFieldSx,
|
|
||||||
}}
|
|
||||||
inputProps={{
|
|
||||||
sx: {
|
|
||||||
boxSizing: "border-box",
|
|
||||||
backgroundColor: backgroundColor ?? theme.palette.background.default,
|
|
||||||
borderRadius: "8px",
|
|
||||||
height: "48px",
|
|
||||||
py: 0,
|
|
||||||
color: theme.palette.gray.dark,
|
|
||||||
...placeholderFont,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
import { IconButton, IconButtonProps, useTheme } from "@mui/material";
|
|
||||||
import { ForwardRefExoticComponent, RefAttributes } from "react";
|
|
||||||
import { LinkProps } from "react-router-dom";
|
|
||||||
import { deepmerge } from "@mui/utils";
|
|
||||||
|
|
||||||
|
|
||||||
type Props = IconButtonProps & {
|
|
||||||
component?: ForwardRefExoticComponent<LinkProps & RefAttributes<HTMLAnchorElement>>;
|
|
||||||
to?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function WalletButton(props: Props) {
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<IconButton
|
|
||||||
{...deepmerge({
|
|
||||||
sx: {
|
|
||||||
height: 36,
|
|
||||||
width: 36,
|
|
||||||
p: 0,
|
|
||||||
borderRadius: "6px",
|
|
||||||
backgroundColor: theme.palette.background.default,
|
|
||||||
color: theme.palette.gray.main,
|
|
||||||
"&:hover": {
|
|
||||||
color: theme.palette.background.default,
|
|
||||||
backgroundColor: theme.palette.gray.main,
|
|
||||||
},
|
|
||||||
"&:active": {
|
|
||||||
backgroundColor: theme.palette.purple.main,
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, props)}
|
|
||||||
>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 36 37" fill="none">
|
|
||||||
<path d="M26.571 16.051v-5c0-.789-.64-1.428-1.428-1.428H9.429c-.79 0-1.429.64-1.429 1.428v14.286c0 .789.64 1.428 1.429 1.428h15.714c.789 0 1.428-.64 1.428-1.428v-5m1.33-5h-7.044a2.857 2.857 0 0 0 0 5.714h7.044a.099.099 0 0 0 .099-.099v-5.516a.1.1 0 0 0-.099-.1Z" stroke="currentColor" strokeWidth="1.5" />
|
|
||||||
</svg>
|
|
||||||
</IconButton>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
export * from "./AvatarButton";
|
|
||||||
export * from "./BurgerButton";
|
|
||||||
export * from "./CloseButton";
|
|
||||||
export * from "./CloseButtonSmall";
|
|
||||||
export * from "./LogoutButton";
|
|
||||||
export * from "./PenaLink";
|
|
||||||
export * from "./PenaTextField";
|
|
||||||
export * from "./theme";
|
|
||||||
export * from "./WalletButton";
|
|
@ -1,612 +0,0 @@
|
|||||||
import { createTheme } from "@mui/material";
|
|
||||||
|
|
||||||
|
|
||||||
export const penaMuiTheme = createTheme({
|
|
||||||
breakpoints: {
|
|
||||||
values: {
|
|
||||||
xs: 300,
|
|
||||||
sm: 560,
|
|
||||||
md: 900,
|
|
||||||
lg: 1200,
|
|
||||||
xl: 1536,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
palette: {
|
|
||||||
mode: "light",
|
|
||||||
primary: {
|
|
||||||
main: "#000000",
|
|
||||||
},
|
|
||||||
secondary: {
|
|
||||||
main: "#252734",
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
primary: "#000000",
|
|
||||||
secondary: "#7E2AEA",
|
|
||||||
},
|
|
||||||
background: {
|
|
||||||
default: "#F2F3F7",
|
|
||||||
},
|
|
||||||
purple: {
|
|
||||||
main: "#7E2AEA",
|
|
||||||
dark: "#581CA7",
|
|
||||||
light: "#944FEE",
|
|
||||||
},
|
|
||||||
bg: {
|
|
||||||
main: "#333647",
|
|
||||||
dark: "#252734",
|
|
||||||
},
|
|
||||||
gray: {
|
|
||||||
main: "#9A9AAF",
|
|
||||||
dark: "#4D4D4D",
|
|
||||||
},
|
|
||||||
orange: {
|
|
||||||
main: "#FB5607",
|
|
||||||
light: "#FC712F",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
MuiButton: {
|
|
||||||
variants: [
|
|
||||||
{
|
|
||||||
props: { variant: "pena-contained-dark" },
|
|
||||||
style: ({ theme }) => theme.unstable_sx({
|
|
||||||
minWidth: "180px",
|
|
||||||
py: "9px",
|
|
||||||
px: "43px",
|
|
||||||
borderRadius: "8px",
|
|
||||||
boxShadow: "none",
|
|
||||||
fontSize: "18px",
|
|
||||||
lineHeight: "24px",
|
|
||||||
fontWeight: 400,
|
|
||||||
textTransform: "none",
|
|
||||||
color: "white",
|
|
||||||
backgroundColor: theme.palette.purple.main,
|
|
||||||
border: `1px solid ${theme.palette.purple.main}`,
|
|
||||||
"&:hover": {
|
|
||||||
color: "white",
|
|
||||||
backgroundColor: theme.palette.purple.light,
|
|
||||||
border: `1px solid ${theme.palette.purple.light}`,
|
|
||||||
},
|
|
||||||
"&:active": {
|
|
||||||
color: theme.palette.purple.main,
|
|
||||||
backgroundColor: "white",
|
|
||||||
border: `1px solid ${theme.palette.purple.main}`,
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
props: { variant: "pena-outlined-dark" },
|
|
||||||
style: ({ theme }) => theme.unstable_sx({
|
|
||||||
minWidth: "180px",
|
|
||||||
py: "9px",
|
|
||||||
px: "43px",
|
|
||||||
borderRadius: "8px",
|
|
||||||
boxShadow: "none",
|
|
||||||
fontSize: "18px",
|
|
||||||
lineHeight: "24px",
|
|
||||||
fontWeight: 400,
|
|
||||||
textTransform: "none",
|
|
||||||
color: "white",
|
|
||||||
backgroundColor: "rgb(0 0 0 / 0)",
|
|
||||||
border: `1px solid white`,
|
|
||||||
"&:hover": {
|
|
||||||
color: "white",
|
|
||||||
backgroundColor: theme.palette.bg.dark,
|
|
||||||
border: `1px solid white`,
|
|
||||||
},
|
|
||||||
"&:active": {
|
|
||||||
color: "white",
|
|
||||||
backgroundColor: theme.palette.purple.main,
|
|
||||||
border: `1px solid ${theme.palette.purple.main}`,
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
props: { variant: "pena-contained-light" },
|
|
||||||
style: ({ theme }) => theme.unstable_sx({
|
|
||||||
minWidth: "180px",
|
|
||||||
py: "9px",
|
|
||||||
px: "43px",
|
|
||||||
borderRadius: "8px",
|
|
||||||
boxShadow: "none",
|
|
||||||
fontSize: "18px",
|
|
||||||
lineHeight: "24px",
|
|
||||||
fontWeight: 400,
|
|
||||||
textTransform: "none",
|
|
||||||
color: theme.palette.bg.dark,
|
|
||||||
backgroundColor: "white",
|
|
||||||
border: `1px solid white`,
|
|
||||||
"&:hover": {
|
|
||||||
color: theme.palette.bg.dark,
|
|
||||||
backgroundColor: theme.palette.background.default,
|
|
||||||
border: `1px solid ${theme.palette.background.default}`,
|
|
||||||
},
|
|
||||||
"&:active": {
|
|
||||||
color: "white",
|
|
||||||
backgroundColor: "black",
|
|
||||||
border: `1px solid black`,
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
props: { variant: "pena-outlined-light" },
|
|
||||||
style: ({ theme }) => theme.unstable_sx({
|
|
||||||
minWidth: "180px",
|
|
||||||
py: "9px",
|
|
||||||
px: "43px",
|
|
||||||
borderRadius: "8px",
|
|
||||||
boxShadow: "none",
|
|
||||||
fontSize: "18px",
|
|
||||||
lineHeight: "24px",
|
|
||||||
fontWeight: 400,
|
|
||||||
textTransform: "none",
|
|
||||||
color: "white",
|
|
||||||
backgroundColor: "rgb(0 0 0 / 0)",
|
|
||||||
border: `1px solid white`,
|
|
||||||
"&:hover": {
|
|
||||||
color: "white",
|
|
||||||
backgroundColor: "#581CA7",
|
|
||||||
border: `1px solid white`,
|
|
||||||
},
|
|
||||||
"&:active": {
|
|
||||||
color: "white",
|
|
||||||
backgroundColor: "black",
|
|
||||||
border: `1px solid black`,
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
props: { variant: "pena-outlined-purple" },
|
|
||||||
style: ({ theme }) => theme.unstable_sx({
|
|
||||||
minWidth: "180px",
|
|
||||||
py: "9px",
|
|
||||||
px: "43px",
|
|
||||||
borderRadius: "8px",
|
|
||||||
boxShadow: "none",
|
|
||||||
fontSize: "18px",
|
|
||||||
lineHeight: "24px",
|
|
||||||
fontWeight: 400,
|
|
||||||
textTransform: "none",
|
|
||||||
color: theme.palette.purple.main,
|
|
||||||
backgroundColor: "rgb(0 0 0 / 0)",
|
|
||||||
border: `1px solid ${theme.palette.purple.main}`,
|
|
||||||
"&:hover": {
|
|
||||||
color: theme.palette.purple.main,
|
|
||||||
backgroundColor: theme.palette.background.default,
|
|
||||||
border: `1px solid ${theme.palette.purple.main}`,
|
|
||||||
},
|
|
||||||
"&:active": {
|
|
||||||
color: "white",
|
|
||||||
backgroundColor: theme.palette.purple.dark,
|
|
||||||
border: `1px solid ${theme.palette.purple.dark}`,
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
props: { variant: "pena-navitem-dark" },
|
|
||||||
style: ({ theme }) => theme.unstable_sx({
|
|
||||||
p: 0,
|
|
||||||
boxShadow: "none",
|
|
||||||
fontSize: "16px",
|
|
||||||
lineHeight: "20px",
|
|
||||||
fontWeight: 500,
|
|
||||||
textTransform: "none",
|
|
||||||
whiteSpace: "nowrap",
|
|
||||||
color: "white",
|
|
||||||
backgroundColor: "rgb(0 0 0 / 0)",
|
|
||||||
"&:hover": {
|
|
||||||
color: theme.palette.purple.light,
|
|
||||||
backgroundColor: "rgb(0 0 0 / 0)",
|
|
||||||
},
|
|
||||||
"&:active": {
|
|
||||||
color: theme.palette.purple.light,
|
|
||||||
backgroundColor: "rgb(0 0 0 / 0)",
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
props: { variant: "pena-navitem-light" },
|
|
||||||
style: ({ theme }) => theme.unstable_sx({
|
|
||||||
p: 0,
|
|
||||||
boxShadow: "none",
|
|
||||||
fontSize: "16px",
|
|
||||||
lineHeight: "20px",
|
|
||||||
fontWeight: 500,
|
|
||||||
textTransform: "none",
|
|
||||||
whiteSpace: "nowrap",
|
|
||||||
color: "black",
|
|
||||||
backgroundColor: "rgb(0 0 0 / 0)",
|
|
||||||
"&:hover": {
|
|
||||||
color: theme.palette.purple.main,
|
|
||||||
backgroundColor: "rgb(0 0 0 / 0)",
|
|
||||||
},
|
|
||||||
"&:active": {
|
|
||||||
color: theme.palette.purple.main,
|
|
||||||
backgroundColor: "rgb(0 0 0 / 0)",
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
props: { variant: "pena-contained-white1" },
|
|
||||||
style: ({ theme }) => theme.unstable_sx({
|
|
||||||
minWidth: "180px",
|
|
||||||
py: "9px",
|
|
||||||
px: "43px",
|
|
||||||
borderRadius: "8px",
|
|
||||||
boxShadow: "none",
|
|
||||||
fontSize: "18px",
|
|
||||||
lineHeight: "24px",
|
|
||||||
fontWeight: 400,
|
|
||||||
textTransform: "none",
|
|
||||||
color: theme.palette.purple.main,
|
|
||||||
backgroundColor: theme.palette.background.default,
|
|
||||||
border: `1px solid ${theme.palette.gray.main}`,
|
|
||||||
"&:hover": {
|
|
||||||
color: "white",
|
|
||||||
backgroundColor: theme.palette.purple.main,
|
|
||||||
border: `1px solid ${theme.palette.purple.main}`,
|
|
||||||
},
|
|
||||||
"&:active": {
|
|
||||||
color: "white",
|
|
||||||
backgroundColor: "black",
|
|
||||||
border: `1px solid black`,
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
props: { variant: "pena-contained-white2" },
|
|
||||||
style: ({ theme }) => theme.unstable_sx({
|
|
||||||
minWidth: "180px",
|
|
||||||
py: "9px",
|
|
||||||
px: "43px",
|
|
||||||
borderRadius: "8px",
|
|
||||||
boxShadow: "none",
|
|
||||||
fontSize: "18px",
|
|
||||||
lineHeight: "24px",
|
|
||||||
fontWeight: 400,
|
|
||||||
textTransform: "none",
|
|
||||||
color: "black",
|
|
||||||
backgroundColor: theme.palette.background.default,
|
|
||||||
border: `1px solid ${theme.palette.background.default}`,
|
|
||||||
"&:hover": {
|
|
||||||
color: "white",
|
|
||||||
backgroundColor: theme.palette.purple.light,
|
|
||||||
border: `1px solid white`,
|
|
||||||
},
|
|
||||||
"&:active": {
|
|
||||||
color: theme.palette.purple.main,
|
|
||||||
backgroundColor: "white",
|
|
||||||
border: `1px solid ${theme.palette.purple.light}`,
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
props: {
|
|
||||||
variant: "pena-text",
|
|
||||||
},
|
|
||||||
style: ({ theme }) => ({
|
|
||||||
color: theme.palette.purple.main,
|
|
||||||
padding: 0,
|
|
||||||
textTransform: "none",
|
|
||||||
textDecoration: "underline",
|
|
||||||
textUnderlineOffset: "7px",
|
|
||||||
fontSize: "16px",
|
|
||||||
fontWeight: 500,
|
|
||||||
lineHeight: "20px",
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
defaultProps: {
|
|
||||||
disableTouchRipple: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
MuiIconButton: {
|
|
||||||
defaultProps: {
|
|
||||||
disableTouchRipple: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
MuiTypography: {
|
|
||||||
defaultProps: {
|
|
||||||
variantMapping: {
|
|
||||||
p1: "p",
|
|
||||||
t1: "p",
|
|
||||||
"pena-h1": "h1",
|
|
||||||
"pena-card-header1": "h5",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
MuiAlert: {
|
|
||||||
styleOverrides: {
|
|
||||||
filledError: {
|
|
||||||
backgroundColor: "#FB5607",
|
|
||||||
},
|
|
||||||
root: {
|
|
||||||
borderRadius: "8px",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
MuiPagination: {
|
|
||||||
variants: [
|
|
||||||
{
|
|
||||||
props: { variant: "pena-pagination" },
|
|
||||||
style: {
|
|
||||||
marginRight: "-15px",
|
|
||||||
marginLeft: "-15px",
|
|
||||||
"& .MuiPaginationItem-root": {
|
|
||||||
height: "30px",
|
|
||||||
width: "30px",
|
|
||||||
minWidth: "30px",
|
|
||||||
marginLeft: "5px",
|
|
||||||
marginRight: "5px",
|
|
||||||
backgroundColor: "white",
|
|
||||||
color: "black",
|
|
||||||
fontSize: "16px",
|
|
||||||
lineHeight: "20px",
|
|
||||||
fontWeight: 400,
|
|
||||||
borderRadius: "5px",
|
|
||||||
"&.Mui-selected": {
|
|
||||||
backgroundColor: "white",
|
|
||||||
color: "#7E2AEA",
|
|
||||||
fontWeight: 500,
|
|
||||||
},
|
|
||||||
"&:hover": {
|
|
||||||
backgroundColor: "#ffffff55",
|
|
||||||
},
|
|
||||||
"&:active": {
|
|
||||||
backgroundColor: "#7F2CEA",
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
boxShadow: `
|
|
||||||
0px 77.2727px 238.773px rgba(210, 208, 225, 0.24),
|
|
||||||
0px 32.2827px 99.7535px rgba(210, 208, 225, 0.172525),
|
|
||||||
0px 17.2599px 53.333px rgba(210, 208, 225, 0.143066),
|
|
||||||
0px 9.67574px 29.8981px rgba(210, 208, 225, 0.12),
|
|
||||||
0px 5.13872px 15.8786px rgba(210, 208, 225, 0.0969343),
|
|
||||||
0px 2.13833px 6.60745px rgba(210, 208, 225, 0.0674749)
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
"& .MuiPaginationItem-previousNext": {
|
|
||||||
backgroundColor: "#7E2AEA",
|
|
||||||
color: "white",
|
|
||||||
marginLeft: "15px",
|
|
||||||
marginRight: "15px",
|
|
||||||
"&:hover": {
|
|
||||||
backgroundColor: "#995DED",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
},
|
|
||||||
MuiSwitch: {
|
|
||||||
styleOverrides: {
|
|
||||||
root: {
|
|
||||||
color: "#7E2AEA",
|
|
||||||
height: "50px",
|
|
||||||
width: "69px",
|
|
||||||
"& .MuiSwitch-switchBase.Mui-checked+.MuiSwitch-track": {
|
|
||||||
backgroundColor: "#7E2AEA",
|
|
||||||
opacity: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
track: {
|
|
||||||
height: "12px",
|
|
||||||
alignSelf: "center",
|
|
||||||
backgroundColor: "#00000000",
|
|
||||||
opacity: 1,
|
|
||||||
border: "1px solid #9A9AAF",
|
|
||||||
},
|
|
||||||
thumb: {
|
|
||||||
height: "32px",
|
|
||||||
width: "32px",
|
|
||||||
border: `6px solid #7E2AEA`,
|
|
||||||
backgroundColor: "white",
|
|
||||||
boxShadow: `0px 0px 0px 3px white,
|
|
||||||
0px 4px 4px 3px #C3C8DD
|
|
||||||
`,
|
|
||||||
"&:focus, &:hover, &.Mui-active, &.Mui-focusVisible": {
|
|
||||||
boxShadow: `0px 0px 0px 3px white,
|
|
||||||
0px 4px 4px 3px #C3C8DD
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
typography: palette => ({
|
|
||||||
h5: {
|
|
||||||
fontSize: "24px",
|
|
||||||
lineHeight: "28.44px",
|
|
||||||
fontWeight: 500,
|
|
||||||
},
|
|
||||||
button: {
|
|
||||||
fontSize: "18px",
|
|
||||||
lineHeight: "24px",
|
|
||||||
fontWeight: 400,
|
|
||||||
textTransform: "none",
|
|
||||||
},
|
|
||||||
body1: {
|
|
||||||
fontSize: "18px",
|
|
||||||
lineHeight: "21.33px",
|
|
||||||
fontWeight: 400,
|
|
||||||
},
|
|
||||||
body2: {
|
|
||||||
fontSize: "16px",
|
|
||||||
lineHeight: "20px",
|
|
||||||
fontWeight: 500,
|
|
||||||
},
|
|
||||||
p1: {
|
|
||||||
fontSize: "20px",
|
|
||||||
lineHeight: "24px",
|
|
||||||
fontWeight: 500,
|
|
||||||
},
|
|
||||||
price: {
|
|
||||||
fontWeight: 500,
|
|
||||||
fontSize: "20px",
|
|
||||||
lineHeight: "24px",
|
|
||||||
color: palette.gray.dark,
|
|
||||||
},
|
|
||||||
oldPrice: {
|
|
||||||
fontWeight: 400,
|
|
||||||
fontSize: "18px",
|
|
||||||
lineHeight: "21px",
|
|
||||||
textDecorationLine: "line-through",
|
|
||||||
color: palette.orange.main,
|
|
||||||
},
|
|
||||||
t1: {
|
|
||||||
display: "block",
|
|
||||||
fontWeight: 400,
|
|
||||||
fontSize: "18px",
|
|
||||||
lineHeight: "21.33px",
|
|
||||||
},
|
|
||||||
fontFamily: [
|
|
||||||
"Rubik",
|
|
||||||
"-apple-system",
|
|
||||||
"BlinkMacSystemFont",
|
|
||||||
'"Segoe UI"',
|
|
||||||
'"Helvetica Neue"',
|
|
||||||
"Arial",
|
|
||||||
"sans-serif",
|
|
||||||
'"Apple Color Emoji"',
|
|
||||||
'"Segoe UI Emoji"',
|
|
||||||
'"Segoe UI Symbol"',
|
|
||||||
].join(","),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
penaMuiTheme.typography["pena-h1"] = {
|
|
||||||
fontSize: "70px",
|
|
||||||
fontWeight: 500,
|
|
||||||
lineHeight: "100%",
|
|
||||||
[penaMuiTheme.breakpoints.down("md")]: {
|
|
||||||
fontSize: "36px",
|
|
||||||
lineHeight: "100%",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
penaMuiTheme.typography["pena-h3"] = {
|
|
||||||
color: "#000000",
|
|
||||||
fontWeight: 500,
|
|
||||||
fontSize: "36px",
|
|
||||||
lineHeight: "100%",
|
|
||||||
[penaMuiTheme.breakpoints.down("md")]: {
|
|
||||||
fontSize: "30px",
|
|
||||||
lineHeight: "100%",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
penaMuiTheme.typography["pena-card-header1"] = {
|
|
||||||
fontWeight: 500,
|
|
||||||
fontSize: "24px",
|
|
||||||
lineHeight: "100%",
|
|
||||||
[penaMuiTheme.breakpoints.down("md")]: {
|
|
||||||
fontSize: "21px",
|
|
||||||
lineHeight: "100%",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
penaMuiTheme.typography.h2 = {
|
|
||||||
fontSize: "70px",
|
|
||||||
lineHeight: "70px",
|
|
||||||
fontWeight: 500,
|
|
||||||
[penaMuiTheme.breakpoints.down("md")]: {
|
|
||||||
fontSize: "42px",
|
|
||||||
lineHeight: "50px",
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
penaMuiTheme.typography.h4 = {
|
|
||||||
fontSize: "36px",
|
|
||||||
lineHeight: "42.66px",
|
|
||||||
fontWeight: 500,
|
|
||||||
[penaMuiTheme.breakpoints.down("md")]: {
|
|
||||||
fontSize: "24px",
|
|
||||||
lineHeight: "28.44px",
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
penaMuiTheme.typography.infographic = {
|
|
||||||
fontSize: "80px",
|
|
||||||
lineHeight: "94.8px",
|
|
||||||
fontWeight: 400,
|
|
||||||
[penaMuiTheme.breakpoints.down("md")]: {
|
|
||||||
fontSize: "50px",
|
|
||||||
lineHeight: "59px",
|
|
||||||
fontWeight: 400,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
declare module '@mui/material/Button' {
|
|
||||||
interface ButtonPropsVariantOverrides {
|
|
||||||
"pena-contained-light": true;
|
|
||||||
"pena-outlined-light": true;
|
|
||||||
"pena-contained-dark": true;
|
|
||||||
"pena-outlined-dark": true;
|
|
||||||
"pena-outlined-purple": true;
|
|
||||||
"pena-navitem-light": true;
|
|
||||||
"pena-navitem-dark": true;
|
|
||||||
"pena-contained-white1": true;
|
|
||||||
"pena-contained-white2": true;
|
|
||||||
"pena-text": true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module '@mui/material/Pagination' {
|
|
||||||
interface PaginationPropsVariantOverrides {
|
|
||||||
"pena-pagination": true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module "@mui/material/styles" {
|
|
||||||
interface Palette {
|
|
||||||
purple: Palette["primary"],
|
|
||||||
bg: Palette["primary"],
|
|
||||||
gray: Palette["primary"],
|
|
||||||
orange: Palette["primary"],
|
|
||||||
}
|
|
||||||
interface PaletteOptions {
|
|
||||||
purple?: PaletteOptions["primary"],
|
|
||||||
bg?: PaletteOptions["primary"],
|
|
||||||
gray?: PaletteOptions["primary"],
|
|
||||||
orange?: PaletteOptions["primary"],
|
|
||||||
}
|
|
||||||
interface TypographyVariants {
|
|
||||||
infographic: React.CSSProperties;
|
|
||||||
p1: React.CSSProperties;
|
|
||||||
price: React.CSSProperties;
|
|
||||||
oldPrice: React.CSSProperties;
|
|
||||||
t1: React.CSSProperties;
|
|
||||||
"pena-card-header1": React.CSSProperties;
|
|
||||||
"pena-h1": React.CSSProperties;
|
|
||||||
"pena-h3": React.CSSProperties;
|
|
||||||
}
|
|
||||||
interface TypographyVariantsOptions {
|
|
||||||
infographic?: React.CSSProperties;
|
|
||||||
p1?: React.CSSProperties;
|
|
||||||
price?: React.CSSProperties;
|
|
||||||
oldPrice?: React.CSSProperties;
|
|
||||||
t1?: React.CSSProperties;
|
|
||||||
"pena-card-header1"?: React.CSSProperties;
|
|
||||||
"pena-h1"?: React.CSSProperties;
|
|
||||||
"pena-h3"?: React.CSSProperties;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module "@mui/material/Typography" {
|
|
||||||
interface TypographyPropsVariantOverrides {
|
|
||||||
infographic: true;
|
|
||||||
p1: true;
|
|
||||||
price: true;
|
|
||||||
oldPrice: true;
|
|
||||||
t1: true;
|
|
||||||
"pena-card-header1": true;
|
|
||||||
"pena-h1": true;
|
|
||||||
"pena-h3": true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module "@mui/material/Switch" {
|
|
||||||
interface SwitchPropsVariantOverrides {
|
|
||||||
"pena-switch": true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
export * from "./throttle";
|
|
@ -1,29 +0,0 @@
|
|||||||
export type ThrottledFunction<T extends (...args: any) => any> = (...args: Parameters<T>) => void;
|
|
||||||
|
|
||||||
export function throttle<T extends (...args: any) => any>(func: T, ms: number): ThrottledFunction<T> {
|
|
||||||
let isThrottled = false;
|
|
||||||
let savedArgs: Parameters<T> | null;
|
|
||||||
let savedThis: any;
|
|
||||||
|
|
||||||
function wrapper(this: any, ...args: Parameters<T>) {
|
|
||||||
if (isThrottled) {
|
|
||||||
savedArgs = args;
|
|
||||||
savedThis = this;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
func.apply(this, args);
|
|
||||||
|
|
||||||
isThrottled = true;
|
|
||||||
|
|
||||||
setTimeout(function () {
|
|
||||||
isThrottled = false;
|
|
||||||
if (savedArgs) {
|
|
||||||
wrapper.apply(savedThis, savedArgs);
|
|
||||||
savedArgs = savedThis = null;
|
|
||||||
}
|
|
||||||
}, ms);
|
|
||||||
}
|
|
||||||
|
|
||||||
return wrapper;
|
|
||||||
}
|
|
7
lib/env.d.ts
vendored
7
lib/env.d.ts
vendored
@ -1,7 +0,0 @@
|
|||||||
export declare global {
|
|
||||||
namespace NodeJS {
|
|
||||||
interface ProcessEnv {
|
|
||||||
readonly NODE_ENV: 'development' | 'production' | 'test';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
export * from "./useAllTariffsFetcher";
|
|
||||||
export * from "./useDebounce";
|
|
||||||
export * from "./useEventListener";
|
|
||||||
export * from "./usePrivilegeFetcher";
|
|
||||||
export * from "./useSSESubscription";
|
|
||||||
export * from "./usePaginatedTariffsFetcher";
|
|
||||||
export * from "./useThrottle";
|
|
||||||
export * from "./useTicketMessages";
|
|
||||||
export * from "./useTickets";
|
|
||||||
export * from "./useToken";
|
|
||||||
export * from "./useUserAccountFetcher";
|
|
||||||
export * from "./useUserFetcher";
|
|
@ -1,60 +0,0 @@
|
|||||||
import { useRef, useLayoutEffect, useEffect } from "react";
|
|
||||||
import { GetTariffsResponse, Tariff } from "../model/tariff";
|
|
||||||
import { makeRequest } from "../api/makeRequest";
|
|
||||||
|
|
||||||
|
|
||||||
export function useAllTariffsFetcher({
|
|
||||||
enabled = true,
|
|
||||||
baseUrl = process.env.REACT_APP_DOMAIN + "/strator/tariff",
|
|
||||||
onSuccess,
|
|
||||||
onError,
|
|
||||||
}: {
|
|
||||||
enabled?: boolean;
|
|
||||||
baseUrl?: string;
|
|
||||||
onSuccess: (response: Tariff[]) => void;
|
|
||||||
onError?: (error: Error) => void;
|
|
||||||
}) {
|
|
||||||
const onNewTariffsRef = useRef(onSuccess);
|
|
||||||
const onErrorRef = useRef(onError);
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
onNewTariffsRef.current = onSuccess;
|
|
||||||
onErrorRef.current = onError;
|
|
||||||
}, [onError, onSuccess]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!enabled) return;
|
|
||||||
|
|
||||||
const controller = new AbortController();
|
|
||||||
|
|
||||||
async function getPaginatedTariffs() {
|
|
||||||
let apiPage = 1;
|
|
||||||
const tariffsPerPage = 100;
|
|
||||||
let isDone = false;
|
|
||||||
|
|
||||||
while (!isDone) {
|
|
||||||
try {
|
|
||||||
const result = await makeRequest<never, GetTariffsResponse>({
|
|
||||||
url: baseUrl + `?page=${apiPage}&limit=${tariffsPerPage}`,
|
|
||||||
method: "get",
|
|
||||||
useToken: true,
|
|
||||||
signal: controller.signal,
|
|
||||||
});
|
|
||||||
if (result) onNewTariffsRef.current(result.tariffs);
|
|
||||||
if (result.totalPages < apiPage) {
|
|
||||||
apiPage++;
|
|
||||||
} else {
|
|
||||||
isDone = true;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
onErrorRef.current?.(error as Error);
|
|
||||||
isDone = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getPaginatedTariffs();
|
|
||||||
|
|
||||||
return () => controller.abort();
|
|
||||||
}, [baseUrl, enabled]);
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
import { useState, useEffect } from "react";
|
|
||||||
|
|
||||||
|
|
||||||
export function useDebounce<T>(value: T, delay: number) {
|
|
||||||
const [debouncedValue, setDebouncedValue] = useState<T>(value);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handler = setTimeout(() => {
|
|
||||||
setDebouncedValue(value);
|
|
||||||
}, delay);
|
|
||||||
return () => clearTimeout(handler);
|
|
||||||
}, [value, delay]);
|
|
||||||
|
|
||||||
return debouncedValue;
|
|
||||||
}
|
|
@ -1,80 +0,0 @@
|
|||||||
import { useEffect, useRef, RefObject, useLayoutEffect } from "react";
|
|
||||||
|
|
||||||
// https://usehooks-ts.com/react-hook/use-event-listener
|
|
||||||
|
|
||||||
// MediaQueryList Event based useEventListener interface
|
|
||||||
export function useEventListener<K extends keyof MediaQueryListEventMap>(
|
|
||||||
eventName: K,
|
|
||||||
handler: (event: MediaQueryListEventMap[K]) => void,
|
|
||||||
element: RefObject<MediaQueryList>,
|
|
||||||
options?: boolean | AddEventListenerOptions,
|
|
||||||
): void;
|
|
||||||
|
|
||||||
// Window Event based useEventListener interface
|
|
||||||
export function useEventListener<K extends keyof WindowEventMap>(
|
|
||||||
eventName: K,
|
|
||||||
handler: (event: WindowEventMap[K]) => void,
|
|
||||||
element?: undefined,
|
|
||||||
options?: boolean | AddEventListenerOptions,
|
|
||||||
): void;
|
|
||||||
|
|
||||||
// Element Event based useEventListener interface
|
|
||||||
export function useEventListener<
|
|
||||||
K extends keyof HTMLElementEventMap,
|
|
||||||
T extends HTMLElement = HTMLDivElement,
|
|
||||||
>(
|
|
||||||
eventName: K,
|
|
||||||
handler: (event: HTMLElementEventMap[K]) => void,
|
|
||||||
element: RefObject<T>,
|
|
||||||
options?: boolean | AddEventListenerOptions,
|
|
||||||
): void;
|
|
||||||
|
|
||||||
// Document Event based useEventListener interface
|
|
||||||
export function useEventListener<K extends keyof DocumentEventMap>(
|
|
||||||
eventName: K,
|
|
||||||
handler: (event: DocumentEventMap[K]) => void,
|
|
||||||
element: RefObject<Document>,
|
|
||||||
options?: boolean | AddEventListenerOptions,
|
|
||||||
): void;
|
|
||||||
|
|
||||||
export function useEventListener<
|
|
||||||
KW extends keyof WindowEventMap,
|
|
||||||
KH extends keyof HTMLElementEventMap,
|
|
||||||
KM extends keyof MediaQueryListEventMap,
|
|
||||||
T extends HTMLElement | MediaQueryList | void = void,
|
|
||||||
>(
|
|
||||||
eventName: KW | KH | KM,
|
|
||||||
handler: (
|
|
||||||
event:
|
|
||||||
| WindowEventMap[KW]
|
|
||||||
| HTMLElementEventMap[KH]
|
|
||||||
| MediaQueryListEventMap[KM]
|
|
||||||
| Event,
|
|
||||||
) => void,
|
|
||||||
element?: RefObject<T>,
|
|
||||||
options?: boolean | AddEventListenerOptions,
|
|
||||||
) {
|
|
||||||
// Create a ref that stores handler
|
|
||||||
const savedHandler = useRef(handler);
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
savedHandler.current = handler;
|
|
||||||
}, [handler]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// Define the listening target
|
|
||||||
const targetElement: T | Window = element?.current ?? window;
|
|
||||||
|
|
||||||
if (!(targetElement && targetElement.addEventListener)) return;
|
|
||||||
|
|
||||||
// Create event listener that calls handler export function stored in ref
|
|
||||||
const listener: typeof handler = event => savedHandler.current(event);
|
|
||||||
|
|
||||||
targetElement.addEventListener(eventName, listener, options);
|
|
||||||
|
|
||||||
// Remove event listener on cleanup
|
|
||||||
return () => {
|
|
||||||
targetElement.removeEventListener(eventName, listener, options);
|
|
||||||
};
|
|
||||||
}, [eventName, element, options]);
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
import { useEffect, useLayoutEffect, useRef } from "react";
|
|
||||||
import { makeRequest } from "../api";
|
|
||||||
import { Tariff, GetTariffsResponse } from "../model/tariff";
|
|
||||||
import { devlog } from "../utils";
|
|
||||||
import { FetchState } from "../model/fetchState";
|
|
||||||
|
|
||||||
|
|
||||||
export function usePaginatedTariffsFetcher({ enabled = true, url, tariffsPerPage, apiPage, onSuccess, onError, onFetchStateChange }: {
|
|
||||||
enabled?: boolean;
|
|
||||||
url: string;
|
|
||||||
tariffsPerPage: number;
|
|
||||||
apiPage: number;
|
|
||||||
onSuccess: (response: Tariff[]) => void;
|
|
||||||
onError?: (error: Error) => void;
|
|
||||||
onFetchStateChange?: (state: FetchState) => void;
|
|
||||||
}) {
|
|
||||||
const onNewTariffsRef = useRef(onSuccess);
|
|
||||||
const onErrorRef = useRef(onError);
|
|
||||||
const onFetchStateChangeRef = useRef(onFetchStateChange);
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
onNewTariffsRef.current = onSuccess;
|
|
||||||
onErrorRef.current = onError;
|
|
||||||
onFetchStateChangeRef.current = onFetchStateChange;
|
|
||||||
}, [onSuccess, onError, onFetchStateChange]);
|
|
||||||
|
|
||||||
useEffect(function fetchTickets() {
|
|
||||||
if (!enabled) return;
|
|
||||||
|
|
||||||
const controller = new AbortController();
|
|
||||||
|
|
||||||
onFetchStateChangeRef.current?.("fetching");
|
|
||||||
makeRequest<never, GetTariffsResponse>({
|
|
||||||
url,
|
|
||||||
method: "get",
|
|
||||||
useToken: true,
|
|
||||||
signal: controller.signal,
|
|
||||||
}).then((result) => {
|
|
||||||
devlog("GetTariffsResponse", result);
|
|
||||||
if (result.tariffs.length > 0) {
|
|
||||||
onNewTariffsRef.current(result.tariffs);
|
|
||||||
onFetchStateChangeRef.current?.("idle");
|
|
||||||
} else onFetchStateChangeRef.current?.("all fetched");
|
|
||||||
}).catch(error => {
|
|
||||||
devlog("Error fetching tariffs", error);
|
|
||||||
onErrorRef.current?.(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => controller.abort();
|
|
||||||
}, [apiPage, enabled, tariffsPerPage, url]);
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
import { useEffect, useLayoutEffect, useRef } from "react";
|
|
||||||
import { makeRequest } from "../api";
|
|
||||||
import { Privilege } from "../model";
|
|
||||||
|
|
||||||
|
|
||||||
export function usePrivilegeFetcher({
|
|
||||||
onSuccess,
|
|
||||||
url = process.env.REACT_APP_DOMAIN + "/strator/privilege",
|
|
||||||
onError,
|
|
||||||
}: {
|
|
||||||
onSuccess: (response: Privilege[]) => void;
|
|
||||||
url?: string;
|
|
||||||
onError?: (error: Error) => void;
|
|
||||||
}) {
|
|
||||||
const onSuccessRef = useRef(onSuccess);
|
|
||||||
const onErrorRef = useRef(onError);
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
onSuccessRef.current = onSuccess;
|
|
||||||
onErrorRef.current = onError;
|
|
||||||
}, [onSuccess, onError]);
|
|
||||||
|
|
||||||
useEffect(function fetchTickets() {
|
|
||||||
const controller = new AbortController();
|
|
||||||
|
|
||||||
makeRequest<never, Privilege[]>({
|
|
||||||
url,
|
|
||||||
method: "get",
|
|
||||||
useToken: true,
|
|
||||||
signal: controller.signal,
|
|
||||||
}).then((result) => {
|
|
||||||
onSuccessRef.current(result);
|
|
||||||
}).catch(error => {
|
|
||||||
onErrorRef.current?.(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => controller.abort();
|
|
||||||
}, [url]);
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
import { useEffect, useLayoutEffect, useRef } from "react";
|
|
||||||
import ReconnectingEventSource from "reconnecting-eventsource";
|
|
||||||
import { devlog } from "../utils";
|
|
||||||
|
|
||||||
|
|
||||||
export function useSSESubscription<T>({ enabled = true, url, onNewData, onDisconnect, marker = "" }: {
|
|
||||||
enabled?: boolean;
|
|
||||||
url: string;
|
|
||||||
onNewData: (data: T[]) => void;
|
|
||||||
onDisconnect?: () => void;
|
|
||||||
marker?: string;
|
|
||||||
}) {
|
|
||||||
const onNewDataRef = useRef(onNewData);
|
|
||||||
const onDisconnectRef = useRef(onDisconnect);
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
onNewDataRef.current = onNewData;
|
|
||||||
onDisconnectRef.current = onDisconnect;
|
|
||||||
}, [onNewData, onDisconnect]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!enabled) return;
|
|
||||||
|
|
||||||
const eventSource = new ReconnectingEventSource(url);
|
|
||||||
|
|
||||||
eventSource.addEventListener("open", () => devlog(`EventSource connected with ${url}`));
|
|
||||||
eventSource.addEventListener("close", () => devlog(`EventSource closed with ${url}`));
|
|
||||||
eventSource.addEventListener("message", event => {
|
|
||||||
try {
|
|
||||||
const newData = JSON.parse(event.data) as T;
|
|
||||||
devlog(`new SSE: ${marker}`, newData);
|
|
||||||
onNewDataRef.current([newData]);
|
|
||||||
} catch (error) {
|
|
||||||
devlog(`SSE parsing error: ${marker}`, event.data, error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
eventSource.addEventListener("error", event => {
|
|
||||||
devlog("SSE Error:", event);
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
eventSource.close();
|
|
||||||
onDisconnectRef.current?.();
|
|
||||||
};
|
|
||||||
}, [enabled, marker, url]);
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
import { useState, useEffect, useRef } from "react";
|
|
||||||
|
|
||||||
|
|
||||||
export function useThrottle<T>(value: T, delay: number) {
|
|
||||||
const [throttledValue, setThrottledValue] = useState<T>(value);
|
|
||||||
const time = useRef<number>(0);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const now = Date.now();
|
|
||||||
if (now > time.current + delay) {
|
|
||||||
time.current = now;
|
|
||||||
setThrottledValue(value);
|
|
||||||
} else {
|
|
||||||
const handler = setTimeout(() => {
|
|
||||||
setThrottledValue(value);
|
|
||||||
}, delay);
|
|
||||||
return () => clearTimeout(handler);
|
|
||||||
}
|
|
||||||
}, [value, delay]);
|
|
||||||
|
|
||||||
return throttledValue;
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
import { useEffect, useRef, useLayoutEffect } from "react";
|
|
||||||
import { TicketMessage, GetMessagesRequest, GetMessagesResponse } from "../model";
|
|
||||||
import { devlog } from "../utils";
|
|
||||||
import { makeRequest } from "../api";
|
|
||||||
import { FetchState } from "../model/fetchState";
|
|
||||||
|
|
||||||
|
|
||||||
export function useTicketMessages({ url, messageApiPage, messagesPerPage, ticketId, isUnauth = false, onSuccess, onError, onFetchStateChange }: {
|
|
||||||
url: string;
|
|
||||||
ticketId: string | undefined;
|
|
||||||
messagesPerPage: number;
|
|
||||||
messageApiPage: number;
|
|
||||||
isUnauth?: boolean;
|
|
||||||
onSuccess: (messages: TicketMessage[]) => void;
|
|
||||||
onError?: (error: Error) => void;
|
|
||||||
onFetchStateChange?: (state: FetchState) => void;
|
|
||||||
}) {
|
|
||||||
const onNewMessagesRef = useRef(onSuccess);
|
|
||||||
const onErrorRef = useRef(onError);
|
|
||||||
const onFetchStateChangeRef = useRef(onFetchStateChange);
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
onNewMessagesRef.current = onSuccess;
|
|
||||||
onErrorRef.current = onError;
|
|
||||||
onFetchStateChangeRef.current = onFetchStateChange;
|
|
||||||
}, [onSuccess, onError, onFetchStateChange]);
|
|
||||||
|
|
||||||
useEffect(function fetchTicketMessages() {
|
|
||||||
if (!ticketId) return;
|
|
||||||
|
|
||||||
const controller = new AbortController();
|
|
||||||
|
|
||||||
onFetchStateChangeRef.current?.("fetching");
|
|
||||||
makeRequest<GetMessagesRequest, GetMessagesResponse>({
|
|
||||||
url,
|
|
||||||
method: "POST",
|
|
||||||
useToken: !isUnauth,
|
|
||||||
body: {
|
|
||||||
amt: messagesPerPage,
|
|
||||||
page: messageApiPage,
|
|
||||||
ticket: ticketId,
|
|
||||||
},
|
|
||||||
signal: controller.signal,
|
|
||||||
withCredentials: isUnauth,
|
|
||||||
}).then(result => {
|
|
||||||
devlog("GetMessagesResponse", result);
|
|
||||||
if (result?.length > 0) {
|
|
||||||
onNewMessagesRef.current(result);
|
|
||||||
onFetchStateChangeRef.current?.("idle");
|
|
||||||
} else onFetchStateChangeRef.current?.("all fetched");
|
|
||||||
}).catch(error => {
|
|
||||||
devlog("Error fetching messages", error);
|
|
||||||
onErrorRef.current?.(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => controller.abort();
|
|
||||||
}, [isUnauth, messageApiPage, messagesPerPage, ticketId, url]);
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
import { useEffect, useRef, useLayoutEffect } from "react";
|
|
||||||
import { GetTicketsResponse, GetTicketsRequest } from "../model";
|
|
||||||
import { devlog } from "../utils";
|
|
||||||
import { makeRequest } from "../api";
|
|
||||||
import { FetchState } from "../model/fetchState";
|
|
||||||
|
|
||||||
|
|
||||||
export function useTicketsFetcher({ enabled = true, url, ticketsPerPage, ticketApiPage, onSuccess, onError, onFetchStateChange }: {
|
|
||||||
enabled?: boolean;
|
|
||||||
url: string;
|
|
||||||
ticketsPerPage: number;
|
|
||||||
ticketApiPage: number;
|
|
||||||
onSuccess: (response: GetTicketsResponse) => void;
|
|
||||||
onError?: (error: Error) => void;
|
|
||||||
onFetchStateChange?: (state: FetchState) => void;
|
|
||||||
}) {
|
|
||||||
const onNewTicketsRef = useRef(onSuccess);
|
|
||||||
const onErrorRef = useRef(onError);
|
|
||||||
const onFetchStateChangeRef = useRef(onFetchStateChange);
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
onNewTicketsRef.current = onSuccess;
|
|
||||||
onErrorRef.current = onError;
|
|
||||||
onFetchStateChangeRef.current = onFetchStateChange;
|
|
||||||
}, [onSuccess, onError, onFetchStateChange]);
|
|
||||||
|
|
||||||
useEffect(function fetchTickets() {
|
|
||||||
if (!enabled) return;
|
|
||||||
|
|
||||||
const controller = new AbortController();
|
|
||||||
|
|
||||||
onFetchStateChangeRef.current?.("fetching");
|
|
||||||
makeRequest<GetTicketsRequest, GetTicketsResponse>({
|
|
||||||
url,
|
|
||||||
method: "POST",
|
|
||||||
useToken: true,
|
|
||||||
body: {
|
|
||||||
amt: ticketsPerPage,
|
|
||||||
page: ticketApiPage,
|
|
||||||
status: "open",
|
|
||||||
},
|
|
||||||
signal: controller.signal,
|
|
||||||
}).then((result) => {
|
|
||||||
devlog("GetTicketsResponse", result);
|
|
||||||
if (result.data) {
|
|
||||||
onNewTicketsRef.current(result);
|
|
||||||
onFetchStateChangeRef.current?.("idle");
|
|
||||||
} else onFetchStateChangeRef.current?.("all fetched");
|
|
||||||
}).catch(error => {
|
|
||||||
devlog("Error fetching tickets", error);
|
|
||||||
onErrorRef.current?.(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => controller.abort();
|
|
||||||
}, [enabled, ticketApiPage, ticketsPerPage, url]);
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
import { useAuthStore } from "../stores/auth";
|
|
||||||
|
|
||||||
|
|
||||||
export function useToken() {
|
|
||||||
return useAuthStore(state => state.token);
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
import { isAxiosError } from "axios";
|
|
||||||
import { useEffect, useLayoutEffect, useRef } from "react";
|
|
||||||
import { UserAccount } from "../model/account";
|
|
||||||
import { makeRequest } from "../api/makeRequest";
|
|
||||||
import { devlog } from "../utils/devlog";
|
|
||||||
import { createUserAccount } from "../api/account";
|
|
||||||
|
|
||||||
|
|
||||||
export function useUserAccountFetcher<T = UserAccount>({ onError, onNewUserAccount, versionOfCustomer="v1.0.0", url, userId }: {
|
|
||||||
url: string;
|
|
||||||
userId: string | null;
|
|
||||||
versionOfCustomer?: string;
|
|
||||||
onNewUserAccount: (response: T) => void;
|
|
||||||
onError?: (error: any) => void;
|
|
||||||
}) {
|
|
||||||
const onNewUserAccountRef = useRef(onNewUserAccount);
|
|
||||||
const onErrorRef = useRef(onError);
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
onNewUserAccountRef.current = onNewUserAccount;
|
|
||||||
onErrorRef.current = onError;
|
|
||||||
}, [onError, onNewUserAccount]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!userId) return;
|
|
||||||
|
|
||||||
const controller = new AbortController();
|
|
||||||
makeRequest<never, T>({
|
|
||||||
url,
|
|
||||||
contentType: true,
|
|
||||||
method: "GET",
|
|
||||||
useToken: true,
|
|
||||||
withCredentials: false,
|
|
||||||
signal: controller.signal,
|
|
||||||
}).then(result => {
|
|
||||||
devlog("User account", result);
|
|
||||||
onNewUserAccountRef.current(result);
|
|
||||||
}).catch(error => {
|
|
||||||
devlog("Error fetching user account", error);
|
|
||||||
if (isAxiosError(error) && error.response?.status === 404) {
|
|
||||||
createUserAccount(controller.signal, url, versionOfCustomer).then(result => {
|
|
||||||
devlog("Created user account", result);
|
|
||||||
onNewUserAccountRef.current(result as T);
|
|
||||||
}).catch(error => {
|
|
||||||
devlog("Error creating user account", error);
|
|
||||||
onErrorRef.current?.(error);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
onErrorRef.current?.(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => controller.abort();
|
|
||||||
}, [url, userId]);
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
import { useEffect, useLayoutEffect, useRef } from "react";
|
|
||||||
import { User } from "../model/user";
|
|
||||||
import { devlog } from "../utils/devlog";
|
|
||||||
import { makeRequest } from "../api/makeRequest";
|
|
||||||
|
|
||||||
|
|
||||||
export function useUserFetcher({ onError, onNewUser, url, userId }: {
|
|
||||||
url: string;
|
|
||||||
userId: string | null;
|
|
||||||
onNewUser: (response: User) => void;
|
|
||||||
onError?: (error: any) => void;
|
|
||||||
}) {
|
|
||||||
const onNewUserRef = useRef(onNewUser);
|
|
||||||
const onErrorRef = useRef(onError);
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
onNewUserRef.current = onNewUser;
|
|
||||||
onErrorRef.current = onError;
|
|
||||||
}, [onError, onNewUser]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!userId) return;
|
|
||||||
|
|
||||||
const controller = new AbortController();
|
|
||||||
|
|
||||||
makeRequest<never, User>({
|
|
||||||
url,
|
|
||||||
contentType: true,
|
|
||||||
method: "GET",
|
|
||||||
useToken: true,
|
|
||||||
withCredentials: false,
|
|
||||||
signal: controller.signal,
|
|
||||||
}).then(result => {
|
|
||||||
devlog("User", result);
|
|
||||||
onNewUserRef.current(result);
|
|
||||||
}).catch(error => {
|
|
||||||
devlog("Error fetching user", error);
|
|
||||||
onErrorRef.current?.(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => controller.abort();
|
|
||||||
}, [url, userId]);
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
export * from "./api";
|
|
||||||
export * from "./components";
|
|
||||||
export * from "./decorators";
|
|
||||||
export * from "./hooks";
|
|
||||||
export * from "./model";
|
|
||||||
export * from "./stores";
|
|
||||||
export * from "./utils";
|
|
||||||
export type * from "./model";
|
|
@ -1,25 +0,0 @@
|
|||||||
export interface UserAccount {
|
|
||||||
_id: string;
|
|
||||||
userId: string;
|
|
||||||
name: UserName;
|
|
||||||
cart: string[];
|
|
||||||
wallet: {
|
|
||||||
currency: string;
|
|
||||||
cash: number;
|
|
||||||
purchasesAmount: number;
|
|
||||||
spent: number;
|
|
||||||
money: number;
|
|
||||||
};
|
|
||||||
status: "no" | "nko" | "org";
|
|
||||||
isDeleted: false;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
deletedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UserName {
|
|
||||||
firstname?: string;
|
|
||||||
secondname?: string;
|
|
||||||
middlename?: string;
|
|
||||||
orgname?: string;
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
export interface RegisterRequest {
|
|
||||||
login: string;
|
|
||||||
password: string;
|
|
||||||
phoneNumber: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RegisterResponse {
|
|
||||||
accessToken: string;
|
|
||||||
login: string;
|
|
||||||
email: string;
|
|
||||||
phoneNumber: string;
|
|
||||||
refreshToken: string;
|
|
||||||
_id: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LoginRequest {
|
|
||||||
login: string;
|
|
||||||
password: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type LoginResponse = RegisterResponse;
|
|
@ -1,32 +0,0 @@
|
|||||||
import { Discount } from "./discount";
|
|
||||||
|
|
||||||
|
|
||||||
export type PrivilegeCartData = {
|
|
||||||
serviceKey: string;
|
|
||||||
privilegeId: string;
|
|
||||||
description: string;
|
|
||||||
price: number;
|
|
||||||
amount: number;
|
|
||||||
appliedDiscounts: Set<Discount>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TariffCartData = {
|
|
||||||
name: string;
|
|
||||||
id: string;
|
|
||||||
price: number;
|
|
||||||
isCustom: boolean;
|
|
||||||
privileges: PrivilegeCartData[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ServiceCartData = {
|
|
||||||
serviceKey: string;
|
|
||||||
tariffs: TariffCartData[];
|
|
||||||
price: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type CartData = {
|
|
||||||
services: ServiceCartData[];
|
|
||||||
priceBeforeDiscounts: number;
|
|
||||||
priceAfterDiscounts: number;
|
|
||||||
allAppliedDiscounts: Set<Discount>;
|
|
||||||
};
|
|
@ -1,12 +0,0 @@
|
|||||||
import { CustomPrivilegeWithAmount } from "./privilege";
|
|
||||||
|
|
||||||
|
|
||||||
type ServiceKey = string;
|
|
||||||
|
|
||||||
export type PrivilegeWithoutPrice = Omit<CustomPrivilegeWithAmount, "price">;
|
|
||||||
|
|
||||||
export type CustomTariffUserValues = Record<string, number>;
|
|
||||||
|
|
||||||
export type CustomTariffUserValuesMap = Record<ServiceKey, CustomTariffUserValues>;
|
|
||||||
|
|
||||||
export type ServiceKeyToPriceMap = Record<ServiceKey, number>;
|
|
@ -1,40 +0,0 @@
|
|||||||
export interface Discount {
|
|
||||||
ID: string;
|
|
||||||
Name: string;
|
|
||||||
Layer: number;
|
|
||||||
Description: string;
|
|
||||||
Condition: {
|
|
||||||
Period?: {
|
|
||||||
From: string;
|
|
||||||
To: string;
|
|
||||||
};
|
|
||||||
User: string;
|
|
||||||
UserType?: string;
|
|
||||||
Coupon: string;
|
|
||||||
PurchasesAmount?: string;
|
|
||||||
CartPurchasesAmount?: string;
|
|
||||||
Product?: string;
|
|
||||||
Term?: string;
|
|
||||||
Usage?: string;
|
|
||||||
PriceFrom?: string;
|
|
||||||
Group?: string;
|
|
||||||
};
|
|
||||||
Target: {
|
|
||||||
Products: {
|
|
||||||
ID: string;
|
|
||||||
Factor: number;
|
|
||||||
Overhelm: boolean;
|
|
||||||
}[];
|
|
||||||
Factor: number;
|
|
||||||
TargetScope: string;
|
|
||||||
TargetGroup: string;
|
|
||||||
Overhelm: boolean;
|
|
||||||
};
|
|
||||||
Audit: {
|
|
||||||
UpdatedAt: string;
|
|
||||||
CreatedAt: string;
|
|
||||||
DeletedAt?: string;
|
|
||||||
Deleted: boolean;
|
|
||||||
};
|
|
||||||
Deprecated: boolean;
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
export type FetchState = "fetching" | "idle" | "all fetched";
|
|
@ -1,10 +0,0 @@
|
|||||||
export type * from "./account";
|
|
||||||
export type * from "./auth";
|
|
||||||
export type * from "./cart";
|
|
||||||
export type * from "./customTariffs";
|
|
||||||
export type * from "./discount";
|
|
||||||
export type * from "./fetchState";
|
|
||||||
export type * from "./privilege";
|
|
||||||
export type * from "./tariff";
|
|
||||||
export type * from "./ticket";
|
|
||||||
export type * from "./user";
|
|
@ -1,30 +0,0 @@
|
|||||||
export interface Privilege {
|
|
||||||
name: string;
|
|
||||||
privilegeId: string;
|
|
||||||
serviceKey: string;
|
|
||||||
description: string;
|
|
||||||
type: "day" | "count";
|
|
||||||
value: PrivilegeValueType;
|
|
||||||
price: number;
|
|
||||||
amount: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CustomPrivilege {
|
|
||||||
_id: string;
|
|
||||||
name: string;
|
|
||||||
privilegeId: string;
|
|
||||||
serviceKey: string;
|
|
||||||
description: string;
|
|
||||||
type: "day" | "count";
|
|
||||||
value: PrivilegeValueType;
|
|
||||||
price: number;
|
|
||||||
updatedAt?: string;
|
|
||||||
isDeleted?: boolean;
|
|
||||||
createdAt?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PrivilegeMap = Record<string, CustomPrivilege[]>;
|
|
||||||
|
|
||||||
export type PrivilegeValueType = "шаблон" | "день" | "МБ" | "заявка";
|
|
||||||
|
|
||||||
export type CustomPrivilegeWithAmount = CustomPrivilege & { amount: number; };
|
|
@ -1,21 +0,0 @@
|
|||||||
import { Privilege } from "./privilege";
|
|
||||||
|
|
||||||
|
|
||||||
export interface GetTariffsResponse {
|
|
||||||
totalPages: number;
|
|
||||||
tariffs: Tariff[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Tariff {
|
|
||||||
_id: string;
|
|
||||||
name: string;
|
|
||||||
description?: string;
|
|
||||||
order?: number;
|
|
||||||
price?: number;
|
|
||||||
isCustom: boolean;
|
|
||||||
privileges: Privilege[];
|
|
||||||
isDeleted: boolean;
|
|
||||||
createdAt?: string;
|
|
||||||
updatedAt?: string;
|
|
||||||
deletedAt?: string;
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
export interface CreateTicketRequest {
|
|
||||||
Title: string;
|
|
||||||
Message: string;
|
|
||||||
System?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CreateTicketResponse {
|
|
||||||
Ticket: string;
|
|
||||||
sess: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SendTicketMessageRequest {
|
|
||||||
message: string;
|
|
||||||
ticket: string;
|
|
||||||
lang: string;
|
|
||||||
files: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TicketStatus = "open";
|
|
||||||
|
|
||||||
export interface GetTicketsRequest {
|
|
||||||
amt: number;
|
|
||||||
/** Пагинация начинается с индекса 0 */
|
|
||||||
page: number;
|
|
||||||
srch?: string;
|
|
||||||
status?: TicketStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GetTicketsResponse {
|
|
||||||
count: number;
|
|
||||||
data: Ticket[] | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Ticket {
|
|
||||||
id: string;
|
|
||||||
user: string;
|
|
||||||
sess: string;
|
|
||||||
ans: string;
|
|
||||||
state: string;
|
|
||||||
top_message: TicketMessage;
|
|
||||||
title: string;
|
|
||||||
created_at: string;
|
|
||||||
updated_at: string;
|
|
||||||
rate: number;
|
|
||||||
origin: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TicketMessage {
|
|
||||||
id: string;
|
|
||||||
ticket_id: string;
|
|
||||||
user_id: string,
|
|
||||||
session_id: string;
|
|
||||||
message: string;
|
|
||||||
files: string[],
|
|
||||||
shown: { [key: string]: number; },
|
|
||||||
request_screenshot: string,
|
|
||||||
created_at: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GetMessagesRequest {
|
|
||||||
amt: number;
|
|
||||||
page: number;
|
|
||||||
srch?: string;
|
|
||||||
ticket: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type GetMessagesResponse = TicketMessage[];
|
|
@ -1,10 +0,0 @@
|
|||||||
export interface User {
|
|
||||||
_id: string;
|
|
||||||
login: string;
|
|
||||||
email: string;
|
|
||||||
phoneNumber: string;
|
|
||||||
isDeleted: boolean;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
deletedAt?: string;
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
import { create } from "zustand";
|
|
||||||
import { persist } from "zustand/middleware";
|
|
||||||
|
|
||||||
|
|
||||||
interface AuthStore {
|
|
||||||
token: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useAuthStore = create<AuthStore>()(
|
|
||||||
persist(
|
|
||||||
(set, get) => ({
|
|
||||||
token: "",
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
name: "token",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
export const getAuthToken = () => useAuthStore.getState().token;
|
|
||||||
|
|
||||||
export const setAuthToken = (token: string) => useAuthStore.setState({ token });
|
|
||||||
|
|
||||||
export const clearAuthToken = () => useAuthStore.setState({ token: "" });
|
|
@ -1 +0,0 @@
|
|||||||
export * from "./auth";
|
|
@ -1,30 +0,0 @@
|
|||||||
import { isAxiosError } from "axios";
|
|
||||||
|
|
||||||
|
|
||||||
const translateMessage: Record<string, string> = {
|
|
||||||
"user not found": "Пользователь не найден",
|
|
||||||
"invalid password": "Неправильный пароль",
|
|
||||||
"field <password> is empty": "Поле \"Пароль\" не заполнено",
|
|
||||||
"field <login> is empty": "Поле \"Логин\" не заполнено",
|
|
||||||
"field <email> is empty": "Поле \"E-mail\" не заполнено",
|
|
||||||
"field <phoneNumber> is empty": "Поле \"Номер телефона\" не заполнено",
|
|
||||||
"user with this email or login is exist": "Пользователь уже существует",
|
|
||||||
};
|
|
||||||
|
|
||||||
export function getMessageFromFetchError(error: any, defaultMessage = "Что-то пошло не так. Повторите попытку позже"): string | null {
|
|
||||||
const rawMessage = error.response?.data?.message;
|
|
||||||
|
|
||||||
const translatedMessage = translateMessage[rawMessage];
|
|
||||||
if (translatedMessage) return translatedMessage;
|
|
||||||
|
|
||||||
if (isAxiosError(error)) {
|
|
||||||
switch (error.code) {
|
|
||||||
case "ERR_NETWORK": return "Ошибка сети";
|
|
||||||
case "ERR_CANCELED": return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV === "development") return rawMessage ?? error.message ?? defaultMessage;
|
|
||||||
|
|
||||||
return defaultMessage;
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
import { expect, test, describe } from "vitest";
|
|
||||||
import { calcCart } from "./calcCart";
|
|
||||||
import { testDiscounts } from "./mockData/discounts";
|
|
||||||
import { cartTestResults } from "./mockData/results";
|
|
||||||
import { testTariffs } from "./mockData/tariffs";
|
|
||||||
|
|
||||||
|
|
||||||
describe("Cart calculation", () => {
|
|
||||||
for (let i = 0; i < cartTestResults.length; i++) {
|
|
||||||
test(`Cart calculation №${i}`, () => {
|
|
||||||
const usedTariffsMask = cartTestResults[i][1];
|
|
||||||
const isNkoApplied = Boolean(usedTariffsMask.pop());
|
|
||||||
|
|
||||||
const tariffs = testTariffs.filter((_, index) => (usedTariffsMask[index] === 1));
|
|
||||||
|
|
||||||
const cart = calcCart(tariffs, testDiscounts, 0, "someuserid", isNkoApplied);
|
|
||||||
|
|
||||||
expect(cart.priceAfterDiscounts).toBeCloseTo(cartTestResults[i][0]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,192 +0,0 @@
|
|||||||
import { CartData, PrivilegeCartData, TariffCartData } from "../../model/cart";
|
|
||||||
import { Discount } from "../../model/discount";
|
|
||||||
import { Tariff } from "../../model/tariff";
|
|
||||||
import { findCartDiscount, findDiscountFactor, findLoyaltyDiscount, findNkoDiscount, findPrivilegeDiscount, findServiceDiscount } from "./utils";
|
|
||||||
|
|
||||||
|
|
||||||
export function calcCart(tariffs: Tariff[], discounts: Discount[], purchasesAmount: number, userId: string, isUserNko?: boolean): CartData {
|
|
||||||
const cartData: CartData = {
|
|
||||||
services: [],
|
|
||||||
priceBeforeDiscounts: 0,
|
|
||||||
priceAfterDiscounts: 0,
|
|
||||||
allAppliedDiscounts: new Set(),
|
|
||||||
};
|
|
||||||
const privilegeAmountById = new Map<string, number>();
|
|
||||||
const servicePriceByKey = new Map<string, number>();
|
|
||||||
|
|
||||||
tariffs.forEach(tariff => {
|
|
||||||
if (tariff.privileges === undefined) return;
|
|
||||||
if (
|
|
||||||
(tariff.price || 0) > 0
|
|
||||||
&& tariff.privileges.length !== 1
|
|
||||||
) throw new Error("Price is defined for tariff with several privileges");
|
|
||||||
|
|
||||||
let serviceData = cartData.services.find(service => (service.serviceKey === "custom" && tariff.isCustom));
|
|
||||||
if (!serviceData && !tariff.isCustom) serviceData = cartData.services.find(service => service.serviceKey === tariff.privileges[0]?.serviceKey);
|
|
||||||
|
|
||||||
if (!serviceData) {
|
|
||||||
serviceData = {
|
|
||||||
serviceKey: tariff.isCustom ? "custom" : tariff.privileges[0]?.serviceKey,
|
|
||||||
tariffs: [],
|
|
||||||
price: 0,
|
|
||||||
};
|
|
||||||
cartData.services.push(serviceData);
|
|
||||||
}
|
|
||||||
|
|
||||||
const tariffCartData: TariffCartData = {
|
|
||||||
price: tariff.price ?? 0,
|
|
||||||
isCustom: tariff.isCustom,
|
|
||||||
privileges: [],
|
|
||||||
id: tariff._id,
|
|
||||||
name: tariff.name,
|
|
||||||
};
|
|
||||||
serviceData.tariffs.push(tariffCartData);
|
|
||||||
|
|
||||||
tariff.privileges.forEach(privilege => {
|
|
||||||
let privilegePrice = privilege.amount * privilege.price;
|
|
||||||
if (!tariff.price) tariffCartData.price += privilegePrice;
|
|
||||||
else privilegePrice = tariff.price;
|
|
||||||
|
|
||||||
const privilegeCartData: PrivilegeCartData = {
|
|
||||||
serviceKey: privilege.serviceKey,
|
|
||||||
privilegeId: privilege.privilegeId,
|
|
||||||
description: privilege.description,
|
|
||||||
price: privilegePrice,
|
|
||||||
amount: privilege.amount,
|
|
||||||
appliedDiscounts: new Set(),
|
|
||||||
};
|
|
||||||
|
|
||||||
privilegeAmountById.set(
|
|
||||||
privilege.privilegeId,
|
|
||||||
privilege.amount + (privilegeAmountById.get(privilege.privilegeId) ?? 0)
|
|
||||||
);
|
|
||||||
servicePriceByKey.set(
|
|
||||||
privilege.serviceKey,
|
|
||||||
privilegePrice + (servicePriceByKey.get(privilege.serviceKey) ?? 0)
|
|
||||||
);
|
|
||||||
|
|
||||||
tariffCartData.privileges.push(privilegeCartData);
|
|
||||||
});
|
|
||||||
|
|
||||||
serviceData.price += tariffCartData.price;
|
|
||||||
});
|
|
||||||
|
|
||||||
cartData.priceBeforeDiscounts = Array.from(servicePriceByKey.values()).reduce((a, b) => a + b, 0);
|
|
||||||
cartData.priceAfterDiscounts = cartData.priceBeforeDiscounts;
|
|
||||||
|
|
||||||
const nkoDiscount = findNkoDiscount(discounts);
|
|
||||||
if (isUserNko && nkoDiscount) {
|
|
||||||
cartData.allAppliedDiscounts.add(nkoDiscount);
|
|
||||||
|
|
||||||
cartData.services.forEach(service => {
|
|
||||||
service.tariffs.forEach(tariff => {
|
|
||||||
tariff.privileges.forEach(privilege => {
|
|
||||||
privilege.appliedDiscounts.add(nkoDiscount);
|
|
||||||
|
|
||||||
const discountAmount = privilege.price * (1 - findDiscountFactor(nkoDiscount));
|
|
||||||
|
|
||||||
privilege.price -= discountAmount;
|
|
||||||
tariff.price -= discountAmount;
|
|
||||||
service.price -= discountAmount;
|
|
||||||
cartData.priceAfterDiscounts -= discountAmount;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return cartData;
|
|
||||||
}
|
|
||||||
|
|
||||||
cartData.services.forEach(service => {
|
|
||||||
service.tariffs.forEach(tariff => {
|
|
||||||
tariff.privileges.forEach(privilege => {
|
|
||||||
const privilegeTotalAmount = privilegeAmountById.get(privilege.privilegeId) ?? 0;
|
|
||||||
|
|
||||||
const discount = findPrivilegeDiscount(privilege.privilegeId, privilegeTotalAmount, discounts, userId);
|
|
||||||
if (!discount) return;
|
|
||||||
|
|
||||||
cartData.allAppliedDiscounts.add(discount);
|
|
||||||
privilege.appliedDiscounts.add(discount);
|
|
||||||
|
|
||||||
const discountAmount = privilege.price * (1 - findDiscountFactor(discount));
|
|
||||||
|
|
||||||
privilege.price -= discountAmount;
|
|
||||||
tariff.price -= discountAmount;
|
|
||||||
service.price -= discountAmount;
|
|
||||||
cartData.priceAfterDiscounts -= discountAmount;
|
|
||||||
|
|
||||||
const serviceTotalPrice = servicePriceByKey.get(privilege.serviceKey);
|
|
||||||
if (!serviceTotalPrice) throw new Error(`Service key ${privilege.serviceKey} not found in servicePriceByKey`);
|
|
||||||
|
|
||||||
servicePriceByKey.set(privilege.serviceKey, serviceTotalPrice - discountAmount);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
cartData.services.forEach(service => {
|
|
||||||
service.tariffs.map(tariff => {
|
|
||||||
tariff.privileges.forEach(privilege => {
|
|
||||||
const serviceTotalPrice = servicePriceByKey.get(privilege.serviceKey);
|
|
||||||
if (!serviceTotalPrice) throw new Error(`Service key ${privilege.serviceKey} not found in servicePriceByKey`);
|
|
||||||
|
|
||||||
const discount = findServiceDiscount(privilege.serviceKey, serviceTotalPrice, discounts, userId);
|
|
||||||
if (!discount) return;
|
|
||||||
|
|
||||||
cartData.allAppliedDiscounts.add(discount);
|
|
||||||
privilege.appliedDiscounts.add(discount);
|
|
||||||
|
|
||||||
const discountAmount = privilege.price * (1 - findDiscountFactor(discount));
|
|
||||||
|
|
||||||
privilege.price -= discountAmount;
|
|
||||||
tariff.price -= discountAmount;
|
|
||||||
service.price -= discountAmount;
|
|
||||||
cartData.priceAfterDiscounts -= discountAmount;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const userDiscount = discounts.find(discount => discount.Condition.User === userId);
|
|
||||||
|
|
||||||
const cartDiscount = findCartDiscount(cartData.priceAfterDiscounts, discounts);
|
|
||||||
if (cartDiscount) {
|
|
||||||
cartData.services.forEach(service => {
|
|
||||||
if (service.serviceKey === userDiscount?.Condition.Group) return;
|
|
||||||
|
|
||||||
service.tariffs.forEach(tariff => {
|
|
||||||
tariff.privileges.forEach(privilege => {
|
|
||||||
cartData.allAppliedDiscounts.add(cartDiscount);
|
|
||||||
privilege.appliedDiscounts.add(cartDiscount);
|
|
||||||
|
|
||||||
const discountAmount = privilege.price * (1 - findDiscountFactor(cartDiscount));
|
|
||||||
|
|
||||||
privilege.price -= discountAmount;
|
|
||||||
tariff.price -= discountAmount;
|
|
||||||
service.price -= discountAmount;
|
|
||||||
cartData.priceAfterDiscounts -= discountAmount;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const loyalDiscount = findLoyaltyDiscount(purchasesAmount, discounts);
|
|
||||||
if (loyalDiscount) {
|
|
||||||
cartData.services.forEach(service => {
|
|
||||||
if (service.serviceKey === userDiscount?.Condition.Group) return;
|
|
||||||
|
|
||||||
service.tariffs.forEach(tariff => {
|
|
||||||
tariff.privileges.forEach(privilege => {
|
|
||||||
cartData.allAppliedDiscounts.add(loyalDiscount);
|
|
||||||
privilege.appliedDiscounts.add(loyalDiscount);
|
|
||||||
|
|
||||||
const discountAmount = privilege.price * (1 - findDiscountFactor(loyalDiscount));
|
|
||||||
|
|
||||||
privilege.price -= discountAmount;
|
|
||||||
tariff.price -= discountAmount;
|
|
||||||
service.price -= discountAmount;
|
|
||||||
cartData.priceAfterDiscounts -= discountAmount;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return cartData;
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
import { CustomTariffUserValues } from "../../model/customTariffs";
|
|
||||||
import { Discount } from "../../model/discount";
|
|
||||||
import { CustomPrivilegeWithAmount } from "../../model/privilege";
|
|
||||||
import { Tariff } from "../../model/tariff";
|
|
||||||
import { calcCart } from "./calcCart";
|
|
||||||
|
|
||||||
|
|
||||||
export function calcCustomTariffPrice(
|
|
||||||
customTariffUserValues: CustomTariffUserValues,
|
|
||||||
servicePrivileges: CustomPrivilegeWithAmount[],
|
|
||||||
cartTariffs: Tariff[],
|
|
||||||
discounts: Discount[],
|
|
||||||
purchasesAmount: number,
|
|
||||||
isUserNko: boolean,
|
|
||||||
userId: string,
|
|
||||||
) {
|
|
||||||
const privileges = new Array<CustomPrivilegeWithAmount>();
|
|
||||||
|
|
||||||
const priceBeforeDiscounts = servicePrivileges.reduce((price, privilege) => {
|
|
||||||
const amount = customTariffUserValues?.[privilege._id] ?? 0;
|
|
||||||
return price + privilege.price * amount;
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
Object.keys(customTariffUserValues).forEach(privilegeId => {
|
|
||||||
const pwa = servicePrivileges.find(p => p._id === privilegeId);
|
|
||||||
if (!pwa) return;
|
|
||||||
if (customTariffUserValues[privilegeId] > 0) privileges.push({
|
|
||||||
...pwa,
|
|
||||||
amount: customTariffUserValues[privilegeId]
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const customTariff: Tariff = {
|
|
||||||
_id: crypto.randomUUID(),
|
|
||||||
name: "",
|
|
||||||
price: 0,
|
|
||||||
description: "",
|
|
||||||
isCustom: true,
|
|
||||||
isDeleted: false,
|
|
||||||
privileges: privileges,
|
|
||||||
};
|
|
||||||
|
|
||||||
const cart = calcCart([...cartTariffs, customTariff], discounts, purchasesAmount, userId, isUserNko);
|
|
||||||
|
|
||||||
const customService = cart.services.flatMap(service => service.tariffs).find(tariff => tariff.id === customTariff._id);
|
|
||||||
if (!customService) throw new Error("Custom service not found in cart");
|
|
||||||
|
|
||||||
return {
|
|
||||||
priceBeforeDiscounts,
|
|
||||||
priceAfterDiscounts: customService.price,
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
import { Discount } from "../../model/discount";
|
|
||||||
import { Tariff } from "../../model/tariff";
|
|
||||||
import { calcCart } from "./calcCart";
|
|
||||||
|
|
||||||
|
|
||||||
export function calcTariffPrice(
|
|
||||||
targetTariff: Tariff,
|
|
||||||
discounts: Discount[],
|
|
||||||
purchasesAmount: number,
|
|
||||||
currentTariffs: Tariff[],
|
|
||||||
isUserNko: boolean,
|
|
||||||
userId: string,
|
|
||||||
): {
|
|
||||||
priceBeforeDiscounts: number;
|
|
||||||
priceAfterDiscounts: number;
|
|
||||||
} {
|
|
||||||
const priceBeforeDiscounts = targetTariff.price || targetTariff.privileges.reduce(
|
|
||||||
(sum, privilege) => sum + privilege.amount * privilege.price,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
|
|
||||||
const cart = calcCart([...currentTariffs, targetTariff], discounts, purchasesAmount, userId, isUserNko);
|
|
||||||
|
|
||||||
const tariffCartData = cart.services.flatMap(service => service.tariffs).find(tariff => tariff.id === targetTariff._id);
|
|
||||||
if (!tariffCartData) throw new Error(`Target tariff ${targetTariff._id} not found in cart`);
|
|
||||||
|
|
||||||
return {
|
|
||||||
priceBeforeDiscounts,
|
|
||||||
priceAfterDiscounts: tariffCartData.price,
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
export * from "./calcCart";
|
|
||||||
export * from "./calcCustomTariffPrice";
|
|
||||||
export * from "./calcTariffPrice";
|
|
||||||
export * from "./utils";
|
|
@ -1,901 +0,0 @@
|
|||||||
import { Discount } from "../../../model/discount";
|
|
||||||
|
|
||||||
|
|
||||||
export const testDiscounts: Discount[] = [
|
|
||||||
{
|
|
||||||
"ID": "6521d98b166f36879928ebbf",
|
|
||||||
"Name": "NKO",
|
|
||||||
"Layer": 4,
|
|
||||||
"Description": "скидка ветеранам НКО",
|
|
||||||
"Condition": {
|
|
||||||
"Period": {
|
|
||||||
"From": "2023-10-07T21:00:45.829Z",
|
|
||||||
"To": "2023-11-06T21:00:45.829Z"
|
|
||||||
},
|
|
||||||
"User": "",
|
|
||||||
"UserType": "nko",
|
|
||||||
"Coupon": "",
|
|
||||||
"PurchasesAmount": "0",
|
|
||||||
"CartPurchasesAmount": "0",
|
|
||||||
"Product": "",
|
|
||||||
"Term": "0",
|
|
||||||
"Usage": "0",
|
|
||||||
"PriceFrom": "0",
|
|
||||||
"Group": ""
|
|
||||||
},
|
|
||||||
"Target": {
|
|
||||||
"Products": [],
|
|
||||||
"Factor": 0.4,
|
|
||||||
"TargetScope": "Sum",
|
|
||||||
"TargetGroup": "",
|
|
||||||
"Overhelm": true
|
|
||||||
},
|
|
||||||
"Audit": {
|
|
||||||
"UpdatedAt": "2023-11-10T23:20:32.619Z",
|
|
||||||
"CreatedAt": "2023-09-16T20:10:26.048Z",
|
|
||||||
"Deleted": false
|
|
||||||
},
|
|
||||||
"Deprecated": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ID": "657b9b73153787e41052c25b",
|
|
||||||
"Name": "1000 шаблонов",
|
|
||||||
"Layer": 1,
|
|
||||||
"Description": "Тариф на 1000 шаблонов",
|
|
||||||
"Condition": {
|
|
||||||
"Period": {
|
|
||||||
"From": "2023-12-15T00:18:57.999Z",
|
|
||||||
"To": "2024-01-14T00:18:58Z"
|
|
||||||
},
|
|
||||||
"User": "",
|
|
||||||
"UserType": "",
|
|
||||||
"Coupon": "",
|
|
||||||
"PurchasesAmount": "0",
|
|
||||||
"CartPurchasesAmount": "0",
|
|
||||||
"Product": "templateCnt",
|
|
||||||
"Term": "1000",
|
|
||||||
"Usage": "0",
|
|
||||||
"PriceFrom": "0",
|
|
||||||
"Group": ""
|
|
||||||
},
|
|
||||||
"Target": {
|
|
||||||
"Products": [
|
|
||||||
{
|
|
||||||
"ID": "templateCnt",
|
|
||||||
"Factor": 0.7,
|
|
||||||
"Overhelm": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Factor": 0.7,
|
|
||||||
"TargetScope": "Sum",
|
|
||||||
"TargetGroup": "",
|
|
||||||
"Overhelm": false
|
|
||||||
},
|
|
||||||
"Audit": {
|
|
||||||
"UpdatedAt": "2023-12-15T00:18:59.138Z",
|
|
||||||
"CreatedAt": "2023-12-15T00:18:59.138Z",
|
|
||||||
"Deleted": false
|
|
||||||
},
|
|
||||||
"Deprecated": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ID": "657b9cb0153787e41052c25c",
|
|
||||||
"Name": "десять тысяч шаблонов",
|
|
||||||
"Layer": 1,
|
|
||||||
"Description": "Тариф 10 000",
|
|
||||||
"Condition": {
|
|
||||||
"Period": {
|
|
||||||
"From": "2023-12-15T00:24:15.555Z",
|
|
||||||
"To": "2024-01-14T00:24:15.555Z"
|
|
||||||
},
|
|
||||||
"User": "",
|
|
||||||
"UserType": "",
|
|
||||||
"Coupon": "",
|
|
||||||
"PurchasesAmount": "0",
|
|
||||||
"CartPurchasesAmount": "0",
|
|
||||||
"Product": "templateCnt",
|
|
||||||
"Term": "10000",
|
|
||||||
"Usage": "0",
|
|
||||||
"PriceFrom": "0",
|
|
||||||
"Group": ""
|
|
||||||
},
|
|
||||||
"Target": {
|
|
||||||
"Products": [
|
|
||||||
{
|
|
||||||
"ID": "templateCnt",
|
|
||||||
"Factor": 0.5,
|
|
||||||
"Overhelm": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Factor": 0.5,
|
|
||||||
"TargetScope": "Sum",
|
|
||||||
"TargetGroup": "",
|
|
||||||
"Overhelm": false
|
|
||||||
},
|
|
||||||
"Audit": {
|
|
||||||
"UpdatedAt": "2023-12-15T00:24:16.562Z",
|
|
||||||
"CreatedAt": "2023-12-15T00:24:16.562Z",
|
|
||||||
"Deleted": false
|
|
||||||
},
|
|
||||||
"Deprecated": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ID": "657b9e67153787e41052c25d",
|
|
||||||
"Name": "3 месяца",
|
|
||||||
"Layer": 1,
|
|
||||||
"Description": "Тариф 3 месяца",
|
|
||||||
"Condition": {
|
|
||||||
"Period": {
|
|
||||||
"From": "2023-12-15T00:31:34.807Z",
|
|
||||||
"To": "2024-01-14T00:31:34.807Z"
|
|
||||||
},
|
|
||||||
"User": "",
|
|
||||||
"UserType": "",
|
|
||||||
"Coupon": "",
|
|
||||||
"PurchasesAmount": "0",
|
|
||||||
"CartPurchasesAmount": "0",
|
|
||||||
"Product": "templateUnlimTime",
|
|
||||||
"Term": "90",
|
|
||||||
"Usage": "0",
|
|
||||||
"PriceFrom": "0",
|
|
||||||
"Group": ""
|
|
||||||
},
|
|
||||||
"Target": {
|
|
||||||
"Products": [
|
|
||||||
{
|
|
||||||
"ID": "templateUnlimTime",
|
|
||||||
"Factor": 0.8,
|
|
||||||
"Overhelm": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Factor": 0.8,
|
|
||||||
"TargetScope": "Sum",
|
|
||||||
"TargetGroup": "",
|
|
||||||
"Overhelm": false
|
|
||||||
},
|
|
||||||
"Audit": {
|
|
||||||
"UpdatedAt": "2023-12-15T00:31:35.601Z",
|
|
||||||
"CreatedAt": "2023-12-15T00:31:35.601Z",
|
|
||||||
"Deleted": false
|
|
||||||
},
|
|
||||||
"Deprecated": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ID": "657b9e8a153787e41052c25e",
|
|
||||||
"Name": "год",
|
|
||||||
"Layer": 1,
|
|
||||||
"Description": "Тариф год",
|
|
||||||
"Condition": {
|
|
||||||
"Period": {
|
|
||||||
"From": "2023-12-15T00:32:09.329Z",
|
|
||||||
"To": "2024-01-14T00:32:09.329Z"
|
|
||||||
},
|
|
||||||
"User": "",
|
|
||||||
"UserType": "",
|
|
||||||
"Coupon": "",
|
|
||||||
"PurchasesAmount": "0",
|
|
||||||
"CartPurchasesAmount": "0",
|
|
||||||
"Product": "templateUnlimTime",
|
|
||||||
"Term": "365",
|
|
||||||
"Usage": "0",
|
|
||||||
"PriceFrom": "0",
|
|
||||||
"Group": ""
|
|
||||||
},
|
|
||||||
"Target": {
|
|
||||||
"Products": [
|
|
||||||
{
|
|
||||||
"ID": "templateUnlimTime",
|
|
||||||
"Factor": 0.65,
|
|
||||||
"Overhelm": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Factor": 0.65,
|
|
||||||
"TargetScope": "Sum",
|
|
||||||
"TargetGroup": "",
|
|
||||||
"Overhelm": false
|
|
||||||
},
|
|
||||||
"Audit": {
|
|
||||||
"UpdatedAt": "2023-12-15T00:32:10.123Z",
|
|
||||||
"CreatedAt": "2023-12-15T00:32:10.123Z",
|
|
||||||
"Deleted": false
|
|
||||||
},
|
|
||||||
"Deprecated": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ID": "657b9eb4153787e41052c25f",
|
|
||||||
"Name": "3 года",
|
|
||||||
"Layer": 1,
|
|
||||||
"Description": "Тариф 3 года",
|
|
||||||
"Condition": {
|
|
||||||
"Period": {
|
|
||||||
"From": "2023-12-15T00:32:51.379Z",
|
|
||||||
"To": "2024-01-14T00:32:51.379Z"
|
|
||||||
},
|
|
||||||
"User": "",
|
|
||||||
"UserType": "",
|
|
||||||
"Coupon": "",
|
|
||||||
"PurchasesAmount": "0",
|
|
||||||
"CartPurchasesAmount": "0",
|
|
||||||
"Product": "templateUnlimTime",
|
|
||||||
"Term": "1095",
|
|
||||||
"Usage": "0",
|
|
||||||
"PriceFrom": "0",
|
|
||||||
"Group": ""
|
|
||||||
},
|
|
||||||
"Target": {
|
|
||||||
"Products": [
|
|
||||||
{
|
|
||||||
"ID": "templateUnlimTime",
|
|
||||||
"Factor": 0.5,
|
|
||||||
"Overhelm": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Factor": 0.5,
|
|
||||||
"TargetScope": "Sum",
|
|
||||||
"TargetGroup": "",
|
|
||||||
"Overhelm": false
|
|
||||||
},
|
|
||||||
"Audit": {
|
|
||||||
"UpdatedAt": "2023-12-15T00:32:52.174Z",
|
|
||||||
"CreatedAt": "2023-12-15T00:32:52.174Z",
|
|
||||||
"Deleted": false
|
|
||||||
},
|
|
||||||
"Deprecated": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ID": "657f5028153787e41052c266",
|
|
||||||
"Name": "10т.р",
|
|
||||||
"Layer": 4,
|
|
||||||
"Description": "купил больше чем на 10 тыров",
|
|
||||||
"Condition": {
|
|
||||||
"Period": {
|
|
||||||
"From": "2023-12-17T19:48:47.466Z",
|
|
||||||
"To": "2024-01-16T19:48:47.466Z"
|
|
||||||
},
|
|
||||||
"User": "",
|
|
||||||
"UserType": "",
|
|
||||||
"Coupon": "",
|
|
||||||
"PurchasesAmount": "10000",
|
|
||||||
"CartPurchasesAmount": "0",
|
|
||||||
"Product": "",
|
|
||||||
"Term": "0",
|
|
||||||
"Usage": "0",
|
|
||||||
"PriceFrom": "0",
|
|
||||||
"Group": ""
|
|
||||||
},
|
|
||||||
"Target": {
|
|
||||||
"Products": [
|
|
||||||
{
|
|
||||||
"ID": "",
|
|
||||||
"Factor": 0,
|
|
||||||
"Overhelm": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Factor": 0.98,
|
|
||||||
"TargetScope": "Sum",
|
|
||||||
"TargetGroup": "",
|
|
||||||
"Overhelm": false
|
|
||||||
},
|
|
||||||
"Audit": {
|
|
||||||
"UpdatedAt": "2023-12-17T19:48:46.072Z",
|
|
||||||
"CreatedAt": "2023-12-17T19:46:48.854Z",
|
|
||||||
"Deleted": false
|
|
||||||
},
|
|
||||||
"Deprecated": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ID": "657f50c3153787e41052c268",
|
|
||||||
"Name": "1т.р",
|
|
||||||
"Layer": 4,
|
|
||||||
"Description": "купил больше чем на 1 тыр",
|
|
||||||
"Condition": {
|
|
||||||
"Period": {
|
|
||||||
"From": "2023-12-17T19:49:24.782Z",
|
|
||||||
"To": "2024-01-16T19:49:24.782Z"
|
|
||||||
},
|
|
||||||
"User": "",
|
|
||||||
"UserType": "",
|
|
||||||
"Coupon": "",
|
|
||||||
"PurchasesAmount": "100000",
|
|
||||||
"CartPurchasesAmount": "0",
|
|
||||||
"Product": "",
|
|
||||||
"Term": "0",
|
|
||||||
"Usage": "0",
|
|
||||||
"PriceFrom": "0",
|
|
||||||
"Group": ""
|
|
||||||
},
|
|
||||||
"Target": {
|
|
||||||
"Products": [
|
|
||||||
{
|
|
||||||
"ID": "",
|
|
||||||
"Factor": 0,
|
|
||||||
"Overhelm": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Factor": 0.99,
|
|
||||||
"TargetScope": "Sum",
|
|
||||||
"TargetGroup": "",
|
|
||||||
"Overhelm": false
|
|
||||||
},
|
|
||||||
"Audit": {
|
|
||||||
"UpdatedAt": "2023-12-17T19:49:23.384Z",
|
|
||||||
"CreatedAt": "2023-12-17T19:49:23.384Z",
|
|
||||||
"Deleted": false
|
|
||||||
},
|
|
||||||
"Deprecated": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ID": "657f50e4153787e41052c269",
|
|
||||||
"Name": "100т.р",
|
|
||||||
"Layer": 4,
|
|
||||||
"Description": "купил больше чем на 100 тыров",
|
|
||||||
"Condition": {
|
|
||||||
"Period": {
|
|
||||||
"From": "2023-12-17T19:49:57.462Z",
|
|
||||||
"To": "2024-01-16T19:49:57.462Z"
|
|
||||||
},
|
|
||||||
"User": "",
|
|
||||||
"UserType": "",
|
|
||||||
"Coupon": "",
|
|
||||||
"PurchasesAmount": "10000000",
|
|
||||||
"CartPurchasesAmount": "0",
|
|
||||||
"Product": "",
|
|
||||||
"Term": "0",
|
|
||||||
"Usage": "0",
|
|
||||||
"PriceFrom": "0",
|
|
||||||
"Group": ""
|
|
||||||
},
|
|
||||||
"Target": {
|
|
||||||
"Products": [
|
|
||||||
{
|
|
||||||
"ID": "",
|
|
||||||
"Factor": 0,
|
|
||||||
"Overhelm": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Factor": 0.95,
|
|
||||||
"TargetScope": "Sum",
|
|
||||||
"TargetGroup": "",
|
|
||||||
"Overhelm": false
|
|
||||||
},
|
|
||||||
"Audit": {
|
|
||||||
"UpdatedAt": "2023-12-17T19:49:56.066Z",
|
|
||||||
"CreatedAt": "2023-12-17T19:49:56.066Z",
|
|
||||||
"Deleted": false
|
|
||||||
},
|
|
||||||
"Deprecated": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ID": "657f511b153787e41052c26a",
|
|
||||||
"Name": "1 т.р",
|
|
||||||
"Layer": 3,
|
|
||||||
"Description": "Больще 1т.р",
|
|
||||||
"Condition": {
|
|
||||||
"Period": {
|
|
||||||
"From": "2023-12-17T19:50:52.764Z",
|
|
||||||
"To": "2024-01-16T19:50:52.764Z"
|
|
||||||
},
|
|
||||||
"User": "",
|
|
||||||
"UserType": "",
|
|
||||||
"Coupon": "",
|
|
||||||
"PurchasesAmount": "0",
|
|
||||||
"CartPurchasesAmount": "100000",
|
|
||||||
"Product": "",
|
|
||||||
"Term": "0",
|
|
||||||
"Usage": "0",
|
|
||||||
"PriceFrom": "0",
|
|
||||||
"Group": ""
|
|
||||||
},
|
|
||||||
"Target": {
|
|
||||||
"Products": [
|
|
||||||
{
|
|
||||||
"ID": "",
|
|
||||||
"Factor": 0,
|
|
||||||
"Overhelm": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Factor": 0.95,
|
|
||||||
"TargetScope": "Sum",
|
|
||||||
"TargetGroup": "",
|
|
||||||
"Overhelm": false
|
|
||||||
},
|
|
||||||
"Audit": {
|
|
||||||
"UpdatedAt": "2023-12-17T19:50:51.408Z",
|
|
||||||
"CreatedAt": "2023-12-17T19:50:51.408Z",
|
|
||||||
"Deleted": false
|
|
||||||
},
|
|
||||||
"Deprecated": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ID": "657f512d153787e41052c26b",
|
|
||||||
"Name": "5 т.р",
|
|
||||||
"Layer": 3,
|
|
||||||
"Description": "Больще 5т.р",
|
|
||||||
"Condition": {
|
|
||||||
"Period": {
|
|
||||||
"From": "2023-12-17T19:51:11.104Z",
|
|
||||||
"To": "2024-01-16T19:51:11.104Z"
|
|
||||||
},
|
|
||||||
"User": "",
|
|
||||||
"UserType": "",
|
|
||||||
"Coupon": "",
|
|
||||||
"PurchasesAmount": "0",
|
|
||||||
"CartPurchasesAmount": "500000",
|
|
||||||
"Product": "",
|
|
||||||
"Term": "0",
|
|
||||||
"Usage": "0",
|
|
||||||
"PriceFrom": "0",
|
|
||||||
"Group": ""
|
|
||||||
},
|
|
||||||
"Target": {
|
|
||||||
"Products": [
|
|
||||||
{
|
|
||||||
"ID": "",
|
|
||||||
"Factor": 0,
|
|
||||||
"Overhelm": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Factor": 0.93,
|
|
||||||
"TargetScope": "Sum",
|
|
||||||
"TargetGroup": "",
|
|
||||||
"Overhelm": false
|
|
||||||
},
|
|
||||||
"Audit": {
|
|
||||||
"UpdatedAt": "2023-12-17T19:51:09.707Z",
|
|
||||||
"CreatedAt": "2023-12-17T19:51:09.707Z",
|
|
||||||
"Deleted": false
|
|
||||||
},
|
|
||||||
"Deprecated": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ID": "657f5144153787e41052c26c",
|
|
||||||
"Name": "10 т.р",
|
|
||||||
"Layer": 3,
|
|
||||||
"Description": "Больше 10т.р",
|
|
||||||
"Condition": {
|
|
||||||
"Period": {
|
|
||||||
"From": "2023-12-17T19:51:33.502Z",
|
|
||||||
"To": "2024-01-16T19:51:33.502Z"
|
|
||||||
},
|
|
||||||
"User": "",
|
|
||||||
"UserType": "",
|
|
||||||
"Coupon": "",
|
|
||||||
"PurchasesAmount": "0",
|
|
||||||
"CartPurchasesAmount": "1000000",
|
|
||||||
"Product": "",
|
|
||||||
"Term": "0",
|
|
||||||
"Usage": "0",
|
|
||||||
"PriceFrom": "0",
|
|
||||||
"Group": ""
|
|
||||||
},
|
|
||||||
"Target": {
|
|
||||||
"Products": [
|
|
||||||
{
|
|
||||||
"ID": "",
|
|
||||||
"Factor": 0,
|
|
||||||
"Overhelm": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Factor": 0.91,
|
|
||||||
"TargetScope": "Sum",
|
|
||||||
"TargetGroup": "",
|
|
||||||
"Overhelm": false
|
|
||||||
},
|
|
||||||
"Audit": {
|
|
||||||
"UpdatedAt": "2023-12-17T19:51:32.105Z",
|
|
||||||
"CreatedAt": "2023-12-17T19:51:32.105Z",
|
|
||||||
"Deleted": false
|
|
||||||
},
|
|
||||||
"Deprecated": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ID": "657f515a153787e41052c26d",
|
|
||||||
"Name": "50 т.р",
|
|
||||||
"Layer": 3,
|
|
||||||
"Description": "Больше 50т.р",
|
|
||||||
"Condition": {
|
|
||||||
"Period": {
|
|
||||||
"From": "2023-12-17T19:51:56.316Z",
|
|
||||||
"To": "2024-01-16T19:51:56.316Z"
|
|
||||||
},
|
|
||||||
"User": "",
|
|
||||||
"UserType": "",
|
|
||||||
"Coupon": "",
|
|
||||||
"PurchasesAmount": "0",
|
|
||||||
"CartPurchasesAmount": "5000000",
|
|
||||||
"Product": "",
|
|
||||||
"Term": "0",
|
|
||||||
"Usage": "0",
|
|
||||||
"PriceFrom": "0",
|
|
||||||
"Group": ""
|
|
||||||
},
|
|
||||||
"Target": {
|
|
||||||
"Products": [
|
|
||||||
{
|
|
||||||
"ID": "",
|
|
||||||
"Factor": 0,
|
|
||||||
"Overhelm": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Factor": 0.89,
|
|
||||||
"TargetScope": "Sum",
|
|
||||||
"TargetGroup": "",
|
|
||||||
"Overhelm": false
|
|
||||||
},
|
|
||||||
"Audit": {
|
|
||||||
"UpdatedAt": "2023-12-17T19:51:54.919Z",
|
|
||||||
"CreatedAt": "2023-12-17T19:51:54.919Z",
|
|
||||||
"Deleted": false
|
|
||||||
},
|
|
||||||
"Deprecated": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ID": "65872fb4153787e41052c26e",
|
|
||||||
"Name": "Лямчик",
|
|
||||||
"Layer": 4,
|
|
||||||
"Description": "Купил больше чем на миллиона",
|
|
||||||
"Condition": {
|
|
||||||
"Period": {
|
|
||||||
"From": "2023-12-23T19:06:27.521Z",
|
|
||||||
"To": "2024-01-22T19:06:27.521Z"
|
|
||||||
},
|
|
||||||
"User": "",
|
|
||||||
"UserType": "",
|
|
||||||
"Coupon": "",
|
|
||||||
"PurchasesAmount": "100000000",
|
|
||||||
"CartPurchasesAmount": "0",
|
|
||||||
"Product": "",
|
|
||||||
"Term": "0",
|
|
||||||
"Usage": "0",
|
|
||||||
"PriceFrom": "0",
|
|
||||||
"Group": ""
|
|
||||||
},
|
|
||||||
"Target": {
|
|
||||||
"Products": [
|
|
||||||
{
|
|
||||||
"ID": "",
|
|
||||||
"Factor": 0,
|
|
||||||
"Overhelm": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Factor": 0.9,
|
|
||||||
"TargetScope": "Sum",
|
|
||||||
"TargetGroup": "",
|
|
||||||
"Overhelm": false
|
|
||||||
},
|
|
||||||
"Audit": {
|
|
||||||
"UpdatedAt": "2023-12-23T19:06:28.253Z",
|
|
||||||
"CreatedAt": "2023-12-23T19:06:28.253Z",
|
|
||||||
"Deleted": false
|
|
||||||
},
|
|
||||||
"Deprecated": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ID": "6588c6e9153787e41052c26f",
|
|
||||||
"Name": "больше 5т.р",
|
|
||||||
"Layer": 2,
|
|
||||||
"Description": "Шаблонизатор:Больше 5т.р",
|
|
||||||
"Condition": {
|
|
||||||
"Period": {
|
|
||||||
"From": "2023-12-25T00:03:53.024Z",
|
|
||||||
"To": "2024-01-24T00:03:53.024Z"
|
|
||||||
},
|
|
||||||
"User": "",
|
|
||||||
"UserType": "",
|
|
||||||
"Coupon": "",
|
|
||||||
"PurchasesAmount": "0",
|
|
||||||
"CartPurchasesAmount": "0",
|
|
||||||
"Product": "",
|
|
||||||
"Term": "0",
|
|
||||||
"Usage": "0",
|
|
||||||
"PriceFrom": "500000",
|
|
||||||
"Group": "templategen"
|
|
||||||
},
|
|
||||||
"Target": {
|
|
||||||
"Products": [
|
|
||||||
{
|
|
||||||
"ID": "",
|
|
||||||
"Factor": 0,
|
|
||||||
"Overhelm": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Factor": 0.98,
|
|
||||||
"TargetScope": "Sum",
|
|
||||||
"TargetGroup": "templategen",
|
|
||||||
"Overhelm": false
|
|
||||||
},
|
|
||||||
"Audit": {
|
|
||||||
"UpdatedAt": "2023-12-25T00:03:53.269Z",
|
|
||||||
"CreatedAt": "2023-12-25T00:03:53.269Z",
|
|
||||||
"Deleted": false
|
|
||||||
},
|
|
||||||
"Deprecated": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ID": "6588c70c153787e41052c270",
|
|
||||||
"Name": "больше 10 т.р",
|
|
||||||
"Layer": 2,
|
|
||||||
"Description": "Шаблонизатор:Больше 10 т.р",
|
|
||||||
"Condition": {
|
|
||||||
"Period": {
|
|
||||||
"From": "2023-12-25T00:04:28.279Z",
|
|
||||||
"To": "2024-01-24T00:04:28.279Z"
|
|
||||||
},
|
|
||||||
"User": "",
|
|
||||||
"UserType": "",
|
|
||||||
"Coupon": "",
|
|
||||||
"PurchasesAmount": "0",
|
|
||||||
"CartPurchasesAmount": "0",
|
|
||||||
"Product": "",
|
|
||||||
"Term": "0",
|
|
||||||
"Usage": "0",
|
|
||||||
"PriceFrom": "1000000",
|
|
||||||
"Group": "templategen"
|
|
||||||
},
|
|
||||||
"Target": {
|
|
||||||
"Products": [
|
|
||||||
{
|
|
||||||
"ID": "",
|
|
||||||
"Factor": 0,
|
|
||||||
"Overhelm": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Factor": 0.97,
|
|
||||||
"TargetScope": "Sum",
|
|
||||||
"TargetGroup": "templategen",
|
|
||||||
"Overhelm": false
|
|
||||||
},
|
|
||||||
"Audit": {
|
|
||||||
"UpdatedAt": "2023-12-25T00:04:28.442Z",
|
|
||||||
"CreatedAt": "2023-12-25T00:04:28.442Z",
|
|
||||||
"Deleted": false
|
|
||||||
},
|
|
||||||
"Deprecated": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ID": "6588c724153787e41052c271",
|
|
||||||
"Name": "больше 100 т.р",
|
|
||||||
"Layer": 2,
|
|
||||||
"Description": "Шаблонизатор:Больше 100 т.р",
|
|
||||||
"Condition": {
|
|
||||||
"Period": {
|
|
||||||
"From": "2023-12-25T00:04:52.461Z",
|
|
||||||
"To": "2024-01-24T00:04:52.461Z"
|
|
||||||
},
|
|
||||||
"User": "",
|
|
||||||
"UserType": "",
|
|
||||||
"Coupon": "",
|
|
||||||
"PurchasesAmount": "0",
|
|
||||||
"CartPurchasesAmount": "0",
|
|
||||||
"Product": "",
|
|
||||||
"Term": "0",
|
|
||||||
"Usage": "0",
|
|
||||||
"PriceFrom": "10000000",
|
|
||||||
"Group": "templategen"
|
|
||||||
},
|
|
||||||
"Target": {
|
|
||||||
"Products": [
|
|
||||||
{
|
|
||||||
"ID": "",
|
|
||||||
"Factor": 0,
|
|
||||||
"Overhelm": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Factor": 0.95,
|
|
||||||
"TargetScope": "Sum",
|
|
||||||
"TargetGroup": "templategen",
|
|
||||||
"Overhelm": false
|
|
||||||
},
|
|
||||||
"Audit": {
|
|
||||||
"UpdatedAt": "2023-12-25T00:04:52.625Z",
|
|
||||||
"CreatedAt": "2023-12-25T00:04:52.625Z",
|
|
||||||
"Deleted": false
|
|
||||||
},
|
|
||||||
"Deprecated": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ID": "65a49c215389294d1c348511",
|
|
||||||
"Name": "1000 заявок",
|
|
||||||
"Layer": 1,
|
|
||||||
"Description": "Полное прохождение 1000 опросов респондентом",
|
|
||||||
"Condition": {
|
|
||||||
"Period": {
|
|
||||||
"From": "2024-01-15T02:44:49.156Z",
|
|
||||||
"To": "2024-02-14T02:44:49.156Z"
|
|
||||||
},
|
|
||||||
"User": "",
|
|
||||||
"UserType": "",
|
|
||||||
"Coupon": "",
|
|
||||||
"PurchasesAmount": "0",
|
|
||||||
"CartPurchasesAmount": "0",
|
|
||||||
"Product": "quizCnt",
|
|
||||||
"Term": "1000",
|
|
||||||
"Usage": "0",
|
|
||||||
"PriceFrom": "0",
|
|
||||||
"Group": ""
|
|
||||||
},
|
|
||||||
"Target": {
|
|
||||||
"Products": [
|
|
||||||
{
|
|
||||||
"ID": "quizCnt",
|
|
||||||
"Factor": 0.7,
|
|
||||||
"Overhelm": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Factor": 0.7,
|
|
||||||
"TargetScope": "Sum",
|
|
||||||
"TargetGroup": "",
|
|
||||||
"Overhelm": false
|
|
||||||
},
|
|
||||||
"Audit": {
|
|
||||||
"UpdatedAt": "2024-01-15T02:44:48.995Z",
|
|
||||||
"CreatedAt": "2024-01-15T02:44:48.995Z",
|
|
||||||
"Deleted": false
|
|
||||||
},
|
|
||||||
"Deprecated": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ID": "65a49c4f5389294d1c348512",
|
|
||||||
"Name": "10000 заявок",
|
|
||||||
"Layer": 1,
|
|
||||||
"Description": "Полное прохождение 10000 опросов респондентом",
|
|
||||||
"Condition": {
|
|
||||||
"Period": {
|
|
||||||
"From": "2024-01-15T02:45:37.236Z",
|
|
||||||
"To": "2024-02-14T02:45:37.236Z"
|
|
||||||
},
|
|
||||||
"User": "",
|
|
||||||
"UserType": "",
|
|
||||||
"Coupon": "",
|
|
||||||
"PurchasesAmount": "0",
|
|
||||||
"CartPurchasesAmount": "0",
|
|
||||||
"Product": "quizCnt",
|
|
||||||
"Term": "10000",
|
|
||||||
"Usage": "0",
|
|
||||||
"PriceFrom": "0",
|
|
||||||
"Group": ""
|
|
||||||
},
|
|
||||||
"Target": {
|
|
||||||
"Products": [
|
|
||||||
{
|
|
||||||
"ID": "quizCnt",
|
|
||||||
"Factor": 0.5,
|
|
||||||
"Overhelm": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Factor": 0.5,
|
|
||||||
"TargetScope": "Sum",
|
|
||||||
"TargetGroup": "",
|
|
||||||
"Overhelm": false
|
|
||||||
},
|
|
||||||
"Audit": {
|
|
||||||
"UpdatedAt": "2024-01-15T02:45:35.984Z",
|
|
||||||
"CreatedAt": "2024-01-15T02:45:35.984Z",
|
|
||||||
"Deleted": false
|
|
||||||
},
|
|
||||||
"Deprecated": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ID": "65a49d1a5389294d1c348513",
|
|
||||||
"Name": "3 месяца",
|
|
||||||
"Layer": 1,
|
|
||||||
"Description": "3 Месяца безлимита",
|
|
||||||
"Condition": {
|
|
||||||
"Period": {
|
|
||||||
"From": "2024-01-15T02:48:59.617Z",
|
|
||||||
"To": "2024-02-14T02:48:59.617Z"
|
|
||||||
},
|
|
||||||
"User": "",
|
|
||||||
"UserType": "",
|
|
||||||
"Coupon": "",
|
|
||||||
"PurchasesAmount": "0",
|
|
||||||
"CartPurchasesAmount": "0",
|
|
||||||
"Product": "quizUnlimTime",
|
|
||||||
"Term": "90",
|
|
||||||
"Usage": "0",
|
|
||||||
"PriceFrom": "0",
|
|
||||||
"Group": ""
|
|
||||||
},
|
|
||||||
"Target": {
|
|
||||||
"Products": [
|
|
||||||
{
|
|
||||||
"ID": "quizUnlimTime",
|
|
||||||
"Factor": 0.8,
|
|
||||||
"Overhelm": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Factor": 0.8,
|
|
||||||
"TargetScope": "Sum",
|
|
||||||
"TargetGroup": "",
|
|
||||||
"Overhelm": false
|
|
||||||
},
|
|
||||||
"Audit": {
|
|
||||||
"UpdatedAt": "2024-01-15T02:48:58.560Z",
|
|
||||||
"CreatedAt": "2024-01-15T02:48:58.560Z",
|
|
||||||
"Deleted": false
|
|
||||||
},
|
|
||||||
"Deprecated": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ID": "65a49d335389294d1c348514",
|
|
||||||
"Name": "Год",
|
|
||||||
"Layer": 1,
|
|
||||||
"Description": "Год безлимита",
|
|
||||||
"Condition": {
|
|
||||||
"Period": {
|
|
||||||
"From": "2024-01-15T02:49:24.266Z",
|
|
||||||
"To": "2024-02-14T02:49:24.266Z"
|
|
||||||
},
|
|
||||||
"User": "",
|
|
||||||
"UserType": "",
|
|
||||||
"Coupon": "",
|
|
||||||
"PurchasesAmount": "0",
|
|
||||||
"CartPurchasesAmount": "0",
|
|
||||||
"Product": "quizUnlimTime",
|
|
||||||
"Term": "365",
|
|
||||||
"Usage": "0",
|
|
||||||
"PriceFrom": "0",
|
|
||||||
"Group": ""
|
|
||||||
},
|
|
||||||
"Target": {
|
|
||||||
"Products": [
|
|
||||||
{
|
|
||||||
"ID": "quizUnlimTime",
|
|
||||||
"Factor": 0.65,
|
|
||||||
"Overhelm": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Factor": 0.65,
|
|
||||||
"TargetScope": "Sum",
|
|
||||||
"TargetGroup": "",
|
|
||||||
"Overhelm": false
|
|
||||||
},
|
|
||||||
"Audit": {
|
|
||||||
"UpdatedAt": "2024-01-15T02:49:23.024Z",
|
|
||||||
"CreatedAt": "2024-01-15T02:49:23.024Z",
|
|
||||||
"Deleted": false
|
|
||||||
},
|
|
||||||
"Deprecated": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ID": "65a49d4f5389294d1c348515",
|
|
||||||
"Name": "3 Года",
|
|
||||||
"Layer": 1,
|
|
||||||
"Description": "3 Года безлимита",
|
|
||||||
"Condition": {
|
|
||||||
"Period": {
|
|
||||||
"From": "2024-01-15T02:49:52.264Z",
|
|
||||||
"To": "2024-02-14T02:49:52.264Z"
|
|
||||||
},
|
|
||||||
"User": "",
|
|
||||||
"UserType": "",
|
|
||||||
"Coupon": "",
|
|
||||||
"PurchasesAmount": "0",
|
|
||||||
"CartPurchasesAmount": "0",
|
|
||||||
"Product": "quizUnlimTime",
|
|
||||||
"Term": "1095",
|
|
||||||
"Usage": "0",
|
|
||||||
"PriceFrom": "0",
|
|
||||||
"Group": ""
|
|
||||||
},
|
|
||||||
"Target": {
|
|
||||||
"Products": [
|
|
||||||
{
|
|
||||||
"ID": "quizUnlimTime",
|
|
||||||
"Factor": 0.5,
|
|
||||||
"Overhelm": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Factor": 0.5,
|
|
||||||
"TargetScope": "Sum",
|
|
||||||
"TargetGroup": "",
|
|
||||||
"Overhelm": false
|
|
||||||
},
|
|
||||||
"Audit": {
|
|
||||||
"UpdatedAt": "2024-01-15T02:49:51.024Z",
|
|
||||||
"CreatedAt": "2024-01-15T02:49:51.024Z",
|
|
||||||
"Deleted": false
|
|
||||||
},
|
|
||||||
"Deprecated": false
|
|
||||||
}
|
|
||||||
];
|
|
@ -1,84 +0,0 @@
|
|||||||
type CartTestResult = [
|
|
||||||
/** Cart price */
|
|
||||||
number,
|
|
||||||
/** Used tariff mask, last number shows if nko applied */
|
|
||||||
number[],
|
|
||||||
];
|
|
||||||
|
|
||||||
export const cartTestResults: CartTestResult[] = [
|
|
||||||
[750184.5, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]],
|
|
||||||
[680, [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]],
|
|
||||||
[232560, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[910227.5, [0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0]],
|
|
||||||
[1274000, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]],
|
|
||||||
[95000, [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[465000, [0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[1700, [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[465093, [0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[910273, [0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[910000, [0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[848285.55, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[911207.5700000001, [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[383158.75, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[1101077.25, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]],
|
|
||||||
[602756.25, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[465069.75, [0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[910887.25, [0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[910250.25, [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[116280, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[864016.5, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]],
|
|
||||||
[348840, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[910591.5, [0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0]],
|
|
||||||
[465186, [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[465000, [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[912912, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[910000, [0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[465569.82499999995, [0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[683432.355, [0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[465068.35500000004, [0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[910039.5850000001, [0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[910089.635, [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[637980, [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[865644, [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[910078.26, [1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[910076.4400000001, [0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[973904.9775, [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[1196672.9775, [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[749535.36, [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[956184.3200000001, [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[910247.52, [0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[903498.255, [0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[911006.915, [1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[1180018.385, [0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]],
|
|
||||||
[797926.05, [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[910668.85, [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[1035015.8, [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]],
|
|
||||||
[536665.8, [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[910054.6, [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[910338.0650000001, [0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[877021.155, [0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[1037969.66, [0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[1147125.98, [0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[760745.5800000001, [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[967153.4600000001, [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[910182, [0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[1092804.6675, [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[1315572.6675, [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[1504998.2675, [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[872300.9400000001, [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[1076309.78, [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[910538.7200000001, [0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[1265735.3800000001, [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[902551.05, [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[1105909.35, [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[910080.0800000001, [0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[1295334.95, [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[1354679.69, [0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[910176.54, [1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[902190.2100000001, [0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[1105556.27, [0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[910063.7000000001, [0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[902280.42, [1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
|
||||||
[1105644.54, [1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
[1295070.1400000001, [1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]],
|
|
||||||
];
|
|
@ -1,812 +0,0 @@
|
|||||||
import { Tariff } from "../../../model/tariff";
|
|
||||||
|
|
||||||
|
|
||||||
export const testTariffs: Tariff[] = [
|
|
||||||
{
|
|
||||||
"_id": "64f06be63fae7d590bf6426c",
|
|
||||||
"name": "Безлимит, Количество Шаблонов, 2023-08-31T10:31:02.472Z",
|
|
||||||
"isCustom": true,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Безлимит",
|
|
||||||
"privilegeId": "64e88d30c4c82e949d5c443d",
|
|
||||||
"serviceKey": "templategen",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 10,
|
|
||||||
"amount": 130
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Количество Шаблонов",
|
|
||||||
"privilegeId": "64e88d30c4c82e949d5c443e",
|
|
||||||
"serviceKey": "templategen",
|
|
||||||
"description": "Количество шаблонов, которые может сделать пользователь сервиса",
|
|
||||||
"type": "count",
|
|
||||||
"value": "шаблон",
|
|
||||||
"price": 5,
|
|
||||||
"amount": 2100
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2023-08-31T10:31:02.570Z",
|
|
||||||
"updatedAt": "2023-08-31T10:31:02.570Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "64f06be93fae7d590bf64271",
|
|
||||||
"name": "Безлимит, Количество Шаблонов, 2023-08-31T10:31:05.703Z",
|
|
||||||
"isCustom": true,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Безлимит",
|
|
||||||
"privilegeId": "64e88d30c4c82e949d5c443d",
|
|
||||||
"serviceKey": "templategen",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 10,
|
|
||||||
"amount": 130
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Количество Шаблонов",
|
|
||||||
"privilegeId": "64e88d30c4c82e949d5c443e",
|
|
||||||
"serviceKey": "templategen",
|
|
||||||
"description": "Количество шаблонов, которые может сделать пользователь сервиса",
|
|
||||||
"type": "count",
|
|
||||||
"value": "шаблон",
|
|
||||||
"price": 5,
|
|
||||||
"amount": 2100
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2023-08-31T10:31:05.732Z",
|
|
||||||
"updatedAt": "2023-08-31T10:31:05.732Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "64f06be93fae7d590bf64276",
|
|
||||||
"name": "Безлимит, Количество Шаблонов, 2023-08-31T10:31:05.874Z",
|
|
||||||
"isCustom": true,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Безлимит",
|
|
||||||
"privilegeId": "64e88d30c4c82e949d5c443d",
|
|
||||||
"serviceKey": "templategen",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 10,
|
|
||||||
"amount": 130
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Количество Шаблонов",
|
|
||||||
"privilegeId": "64e88d30c4c82e949d5c443e",
|
|
||||||
"serviceKey": "templategen",
|
|
||||||
"description": "Количество шаблонов, которые может сделать пользователь сервиса",
|
|
||||||
"type": "count",
|
|
||||||
"value": "шаблон",
|
|
||||||
"price": 5,
|
|
||||||
"amount": 2100
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2023-08-31T10:31:05.884Z",
|
|
||||||
"updatedAt": "2023-08-31T10:31:05.884Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "64f06c103fae7d590bf6427b",
|
|
||||||
"name": "Безлимит, Количество Шаблонов, 2023-08-31T10:31:44.015Z",
|
|
||||||
"isCustom": true,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Безлимит",
|
|
||||||
"privilegeId": "64e88d30c4c82e949d5c443d",
|
|
||||||
"serviceKey": "templategen",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 10,
|
|
||||||
"amount": 130
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Количество Шаблонов",
|
|
||||||
"privilegeId": "64e88d30c4c82e949d5c443e",
|
|
||||||
"serviceKey": "templategen",
|
|
||||||
"description": "Количество шаблонов, которые может сделать пользователь сервиса",
|
|
||||||
"type": "count",
|
|
||||||
"value": "шаблон",
|
|
||||||
"price": 5,
|
|
||||||
"amount": 2100
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2023-08-31T10:31:44.198Z",
|
|
||||||
"updatedAt": "2023-08-31T10:31:44.198Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "64f06c123fae7d590bf64280",
|
|
||||||
"name": "Безлимит, Количество Шаблонов, 2023-08-31T10:31:16.087Z",
|
|
||||||
"isCustom": true,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Безлимит",
|
|
||||||
"privilegeId": "64e88d30c4c82e949d5c443d",
|
|
||||||
"serviceKey": "templategen",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 10,
|
|
||||||
"amount": 130
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Количество Шаблонов",
|
|
||||||
"privilegeId": "64e88d30c4c82e949d5c443e",
|
|
||||||
"serviceKey": "templategen",
|
|
||||||
"description": "Количество шаблонов, которые может сделать пользователь сервиса",
|
|
||||||
"type": "count",
|
|
||||||
"value": "шаблон",
|
|
||||||
"price": 5,
|
|
||||||
"amount": 2100
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2023-08-31T10:31:46.954Z",
|
|
||||||
"updatedAt": "2023-08-31T10:31:46.954Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "64f61a713fae7d590bf6494c",
|
|
||||||
"name": "Размер Диска, Безлимит, 2023-09-04T17:57:05.862Z",
|
|
||||||
"isCustom": true,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Размер Диска",
|
|
||||||
"privilegeId": "64e88d30c4c82e949d5c443c",
|
|
||||||
"serviceKey": "templategen",
|
|
||||||
"description": "Обьём ПенаДиска для хранения шаблонов и результатов шаблонизации",
|
|
||||||
"type": "count",
|
|
||||||
"value": "МБ",
|
|
||||||
"price": 555,
|
|
||||||
"amount": 1500
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Безлимит",
|
|
||||||
"privilegeId": "64e88d30c4c82e949d5c443d",
|
|
||||||
"serviceKey": "templategen",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 10,
|
|
||||||
"amount": 220
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2023-09-04T17:57:05.987Z",
|
|
||||||
"updatedAt": "2023-09-04T17:57:05.987Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "64ff6eb75913fc89c5667d85",
|
|
||||||
"name": "Безлимит, 2023-09-11T19:47:03.383Z",
|
|
||||||
"isCustom": true,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Безлимит",
|
|
||||||
"privilegeId": "64e88d30c4c82e949d5c443d",
|
|
||||||
"serviceKey": "templategen",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 10,
|
|
||||||
"amount": 170
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2023-09-11T19:47:03.546Z",
|
|
||||||
"updatedAt": "2023-09-11T19:47:03.546Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "657b9b11215b615d2e35741f",
|
|
||||||
"name": "100 шаблонов",
|
|
||||||
"price": 0,
|
|
||||||
"isCustom": false,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Количество Шаблонов",
|
|
||||||
"privilegeId": "templateCnt",
|
|
||||||
"serviceKey": "templategen",
|
|
||||||
"description": "Количество шаблонов, которые может сделать пользователь сервиса",
|
|
||||||
"type": "count",
|
|
||||||
"value": "шаблон",
|
|
||||||
"price": 1000,
|
|
||||||
"amount": 100
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2023-12-15T00:17:21.104Z",
|
|
||||||
"updatedAt": "2023-12-15T00:17:21.104Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "657b9b1a215b615d2e357424",
|
|
||||||
"name": "1000 шаблонов",
|
|
||||||
"price": 0,
|
|
||||||
"isCustom": false,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Количество Шаблонов",
|
|
||||||
"privilegeId": "templateCnt",
|
|
||||||
"serviceKey": "templategen",
|
|
||||||
"description": "Количество шаблонов, которые может сделать пользователь сервиса",
|
|
||||||
"type": "count",
|
|
||||||
"value": "шаблон",
|
|
||||||
"price": 1000,
|
|
||||||
"amount": 1000
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2023-12-15T00:17:30.813Z",
|
|
||||||
"updatedAt": "2023-12-15T00:17:30.813Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "657b9b98215b615d2e35743a",
|
|
||||||
"name": "10000 шаблонов",
|
|
||||||
"price": 0,
|
|
||||||
"isCustom": false,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Количество Шаблонов",
|
|
||||||
"privilegeId": "templateCnt",
|
|
||||||
"serviceKey": "templategen",
|
|
||||||
"description": "Количество шаблонов, которые может сделать пользователь сервиса",
|
|
||||||
"type": "count",
|
|
||||||
"value": "шаблон",
|
|
||||||
"price": 1000,
|
|
||||||
"amount": 10000
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2023-12-15T00:19:36.848Z",
|
|
||||||
"updatedAt": "2023-12-15T00:19:36.848Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "657b9bd0215b615d2e357445",
|
|
||||||
"name": "1 день",
|
|
||||||
"price": 10000,
|
|
||||||
"isCustom": false,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Безлимит",
|
|
||||||
"privilegeId": "templateUnlimTime",
|
|
||||||
"serviceKey": "templategen",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 1700,
|
|
||||||
"amount": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2023-12-15T00:20:32.586Z",
|
|
||||||
"updatedAt": "2023-12-15T00:20:32.586Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "657b9c15215b615d2e35745f",
|
|
||||||
"name": "Месяц",
|
|
||||||
"price": 0,
|
|
||||||
"isCustom": false,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Безлимит",
|
|
||||||
"privilegeId": "templateUnlimTime",
|
|
||||||
"serviceKey": "templategen",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 1700,
|
|
||||||
"amount": 30
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2023-12-15T00:21:41.878Z",
|
|
||||||
"updatedAt": "2023-12-15T00:21:41.878Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "657b9c25215b615d2e357464",
|
|
||||||
"name": "3 месяца",
|
|
||||||
"price": 0,
|
|
||||||
"isCustom": false,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Безлимит",
|
|
||||||
"privilegeId": "templateUnlimTime",
|
|
||||||
"serviceKey": "templategen",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 1700,
|
|
||||||
"amount": 90
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2023-12-15T00:21:57.114Z",
|
|
||||||
"updatedAt": "2023-12-15T00:21:57.114Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "657b9c4d215b615d2e357469",
|
|
||||||
"name": "Год",
|
|
||||||
"price": 0,
|
|
||||||
"isCustom": false,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Безлимит",
|
|
||||||
"privilegeId": "templateUnlimTime",
|
|
||||||
"serviceKey": "templategen",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 1700,
|
|
||||||
"amount": 365
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2023-12-15T00:22:37.456Z",
|
|
||||||
"updatedAt": "2023-12-15T00:22:37.456Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "657b9c59215b615d2e35746e",
|
|
||||||
"name": "3 года",
|
|
||||||
"price": 0,
|
|
||||||
"isCustom": false,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Безлимит",
|
|
||||||
"privilegeId": "templateUnlimTime",
|
|
||||||
"serviceKey": "templategen",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 1700,
|
|
||||||
"amount": 1095
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2023-12-15T00:22:49.492Z",
|
|
||||||
"updatedAt": "2023-12-15T00:22:49.492Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "65af14358507c326f5a2db91",
|
|
||||||
"name": "Безлимит Опросов, Количество Заявок, 2024-01-23T01:19:49.676Z",
|
|
||||||
"isCustom": true,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Безлимит Опросов",
|
|
||||||
"privilegeId": "quizUnlimTime",
|
|
||||||
"serviceKey": "squiz",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 3400,
|
|
||||||
"amount": 30
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Количество Заявок",
|
|
||||||
"privilegeId": "quizCnt",
|
|
||||||
"serviceKey": "squiz",
|
|
||||||
"description": "Количество полных прохождений опросов",
|
|
||||||
"type": "count",
|
|
||||||
"value": "заявка",
|
|
||||||
"price": 2000,
|
|
||||||
"amount": 100
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2024-01-23T01:19:49.897Z",
|
|
||||||
"updatedAt": "2024-01-23T01:19:49.897Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "65af1b978507c326f5a2dbaa",
|
|
||||||
"name": "Безлимит Опросов, Количество Заявок, 2024-01-23T01:51:19.622Z",
|
|
||||||
"isCustom": true,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Безлимит Опросов",
|
|
||||||
"privilegeId": "quizUnlimTime",
|
|
||||||
"serviceKey": "squiz",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 3400,
|
|
||||||
"amount": 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Количество Заявок",
|
|
||||||
"privilegeId": "quizCnt",
|
|
||||||
"serviceKey": "squiz",
|
|
||||||
"description": "Количество полных прохождений опросов",
|
|
||||||
"type": "count",
|
|
||||||
"value": "заявка",
|
|
||||||
"price": 2000,
|
|
||||||
"amount": 100
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2024-01-23T01:51:19.814Z",
|
|
||||||
"updatedAt": "2024-01-23T01:51:19.814Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "65afd0518507c326f5a2e59d",
|
|
||||||
"name": "Безлимит Опросов, Количество Заявок, 2024-01-23T14:42:25.093Z",
|
|
||||||
"isCustom": true,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Безлимит Опросов",
|
|
||||||
"privilegeId": "quizUnlimTime",
|
|
||||||
"serviceKey": "squiz",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 3400,
|
|
||||||
"amount": 70
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Количество Заявок",
|
|
||||||
"privilegeId": "quizCnt",
|
|
||||||
"serviceKey": "squiz",
|
|
||||||
"description": "Количество полных прохождений опросов",
|
|
||||||
"type": "count",
|
|
||||||
"value": "заявка",
|
|
||||||
"price": 2000,
|
|
||||||
"amount": 850
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2024-01-23T14:42:25.154Z",
|
|
||||||
"updatedAt": "2024-01-23T14:42:25.154Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "65afd05e8507c326f5a2e5a2",
|
|
||||||
"name": "Безлимит Опросов, Количество Заявок, 2024-01-23T14:42:38.254Z",
|
|
||||||
"isCustom": true,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Безлимит Опросов",
|
|
||||||
"privilegeId": "quizUnlimTime",
|
|
||||||
"serviceKey": "squiz",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 3400,
|
|
||||||
"amount": 70
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Количество Заявок",
|
|
||||||
"privilegeId": "quizCnt",
|
|
||||||
"serviceKey": "squiz",
|
|
||||||
"description": "Количество полных прохождений опросов",
|
|
||||||
"type": "count",
|
|
||||||
"value": "заявка",
|
|
||||||
"price": 2000,
|
|
||||||
"amount": 850
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2024-01-23T14:42:38.338Z",
|
|
||||||
"updatedAt": "2024-01-23T14:42:38.339Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "65afd0738507c326f5a2e5a7",
|
|
||||||
"name": "Безлимит Опросов, Количество Заявок, 2024-01-23T14:42:58.966Z",
|
|
||||||
"isCustom": true,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Безлимит Опросов",
|
|
||||||
"privilegeId": "quizUnlimTime",
|
|
||||||
"serviceKey": "squiz",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 3400,
|
|
||||||
"amount": 70
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Количество Заявок",
|
|
||||||
"privilegeId": "quizCnt",
|
|
||||||
"serviceKey": "squiz",
|
|
||||||
"description": "Количество полных прохождений опросов",
|
|
||||||
"type": "count",
|
|
||||||
"value": "заявка",
|
|
||||||
"price": 2000,
|
|
||||||
"amount": 850
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2024-01-23T14:42:59.050Z",
|
|
||||||
"updatedAt": "2024-01-23T14:42:59.050Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "65afd08a8507c326f5a2e5ac",
|
|
||||||
"name": "Безлимит Опросов, Количество Заявок, 2024-01-23T14:43:22.214Z",
|
|
||||||
"isCustom": true,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Безлимит Опросов",
|
|
||||||
"privilegeId": "quizUnlimTime",
|
|
||||||
"serviceKey": "squiz",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 3400,
|
|
||||||
"amount": 30
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Количество Заявок",
|
|
||||||
"privilegeId": "quizCnt",
|
|
||||||
"serviceKey": "squiz",
|
|
||||||
"description": "Количество полных прохождений опросов",
|
|
||||||
"type": "count",
|
|
||||||
"value": "заявка",
|
|
||||||
"price": 2000,
|
|
||||||
"amount": 100
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2024-01-23T14:43:22.284Z",
|
|
||||||
"updatedAt": "2024-01-23T14:43:22.284Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "65b2d740c644401f2ff3ad26",
|
|
||||||
"name": "Безлимит Опросов, Количество Заявок, 2024-01-25T21:48:47.933Z",
|
|
||||||
"isCustom": true,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Безлимит Опросов",
|
|
||||||
"privilegeId": "quizUnlimTime",
|
|
||||||
"serviceKey": "squiz",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 3400,
|
|
||||||
"amount": 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Количество Заявок",
|
|
||||||
"privilegeId": "quizCnt",
|
|
||||||
"serviceKey": "squiz",
|
|
||||||
"description": "Количество полных прохождений опросов",
|
|
||||||
"type": "count",
|
|
||||||
"value": "заявка",
|
|
||||||
"price": 2000,
|
|
||||||
"amount": 3480
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2024-01-25T21:48:48.015Z",
|
|
||||||
"updatedAt": "2024-01-25T21:48:48.015Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "657b9b06215b615d2e35741a",
|
|
||||||
"name": "10 шаблонов",
|
|
||||||
"price": 20000,
|
|
||||||
"order": 1,
|
|
||||||
"isCustom": false,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Количество Шаблонов",
|
|
||||||
"privilegeId": "templateCnt",
|
|
||||||
"serviceKey": "templategen",
|
|
||||||
"description": "Количество шаблонов, которые может сделать пользователь сервиса",
|
|
||||||
"type": "count",
|
|
||||||
"value": "шаблон",
|
|
||||||
"price": 1000,
|
|
||||||
"amount": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2024-01-14T19:22:07.206Z",
|
|
||||||
"updatedAt": "2024-01-14T19:22:07.206Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "65a493550089bcd87ba53d4b",
|
|
||||||
"name": "10 заявок",
|
|
||||||
"description": "Полное прохождение 10 опросов респондентом",
|
|
||||||
"price": 0,
|
|
||||||
"order": 1,
|
|
||||||
"isCustom": false,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Количество Заявок",
|
|
||||||
"privilegeId": "quizCnt",
|
|
||||||
"serviceKey": "squiz",
|
|
||||||
"description": "Количество полных прохождений опросов",
|
|
||||||
"type": "count",
|
|
||||||
"value": "заявка",
|
|
||||||
"price": 2000,
|
|
||||||
"amount": 10
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2024-01-15T02:07:17.403Z",
|
|
||||||
"updatedAt": "2024-01-15T02:07:17.403Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "65a493b60089bcd87ba53d5f",
|
|
||||||
"name": "1 день",
|
|
||||||
"description": "день безлимитного пользования сервисом",
|
|
||||||
"price": 10000,
|
|
||||||
"order": 1,
|
|
||||||
"isCustom": false,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Безлимит Опросов",
|
|
||||||
"privilegeId": "quizUnlimTime",
|
|
||||||
"serviceKey": "squiz",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 3400,
|
|
||||||
"amount": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2024-01-15T02:08:54.275Z",
|
|
||||||
"updatedAt": "2024-01-15T02:08:54.275Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "65a493640089bcd87ba53d50",
|
|
||||||
"name": "100 заявок",
|
|
||||||
"description": "Полное прохождение 100 опросов респондентом",
|
|
||||||
"price": 0,
|
|
||||||
"order": 2,
|
|
||||||
"isCustom": false,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Количество Заявок",
|
|
||||||
"privilegeId": "quizCnt",
|
|
||||||
"serviceKey": "squiz",
|
|
||||||
"description": "Количество полных прохождений опросов",
|
|
||||||
"type": "count",
|
|
||||||
"value": "заявка",
|
|
||||||
"price": 2000,
|
|
||||||
"amount": 100
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2024-01-15T02:07:32.444Z",
|
|
||||||
"updatedAt": "2024-01-15T02:07:32.444Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "65a497890089bcd87ba53d64",
|
|
||||||
"name": "Месяц",
|
|
||||||
"description": "Месяц безлимитного пользования сервисом",
|
|
||||||
"price": 0,
|
|
||||||
"order": 2,
|
|
||||||
"isCustom": false,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Безлимит Опросов",
|
|
||||||
"privilegeId": "quizUnlimTime",
|
|
||||||
"serviceKey": "squiz",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 3400,
|
|
||||||
"amount": 30
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2024-01-15T02:25:13.366Z",
|
|
||||||
"updatedAt": "2024-01-15T02:25:13.366Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "65a493740089bcd87ba53d55",
|
|
||||||
"name": "1000 заявок",
|
|
||||||
"description": "Полное прохождение 1000 опросов респондентом",
|
|
||||||
"price": 0,
|
|
||||||
"order": 3,
|
|
||||||
"isCustom": false,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Количество Заявок",
|
|
||||||
"privilegeId": "quizCnt",
|
|
||||||
"serviceKey": "squiz",
|
|
||||||
"description": "Количество полных прохождений опросов",
|
|
||||||
"type": "count",
|
|
||||||
"value": "заявка",
|
|
||||||
"price": 2000,
|
|
||||||
"amount": 1000
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2024-01-15T02:07:48.095Z",
|
|
||||||
"updatedAt": "2024-01-15T02:07:48.095Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "65a4987e0089bcd87ba53d75",
|
|
||||||
"name": "3 Месяца",
|
|
||||||
"description": "3 Месяца безлимитного пользования сервисом",
|
|
||||||
"price": 0,
|
|
||||||
"order": 3,
|
|
||||||
"isCustom": false,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Безлимит Опросов",
|
|
||||||
"privilegeId": "quizUnlimTime",
|
|
||||||
"serviceKey": "squiz",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 3400,
|
|
||||||
"amount": 90
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2024-01-15T02:29:18.577Z",
|
|
||||||
"updatedAt": "2024-01-15T02:29:18.577Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "65a498cc0089bcd87ba53d92",
|
|
||||||
"name": "Год",
|
|
||||||
"description": "Год безлимитного пользования сервисом",
|
|
||||||
"price": 0,
|
|
||||||
"order": 3,
|
|
||||||
"isCustom": false,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Безлимит Опросов",
|
|
||||||
"privilegeId": "quizUnlimTime",
|
|
||||||
"serviceKey": "squiz",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 3400,
|
|
||||||
"amount": 365
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2024-01-15T02:30:36.131Z",
|
|
||||||
"updatedAt": "2024-01-15T02:30:36.131Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "65a493830089bcd87ba53d5a",
|
|
||||||
"name": "10000 заявок",
|
|
||||||
"description": "Полное прохождение 10000 опросов респондентом",
|
|
||||||
"price": 0,
|
|
||||||
"order": 4,
|
|
||||||
"isCustom": false,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Количество Заявок",
|
|
||||||
"privilegeId": "quizCnt",
|
|
||||||
"serviceKey": "squiz",
|
|
||||||
"description": "Количество полных прохождений опросов",
|
|
||||||
"type": "count",
|
|
||||||
"value": "заявка",
|
|
||||||
"price": 2000,
|
|
||||||
"amount": 10000
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2024-01-15T02:08:03.341Z",
|
|
||||||
"updatedAt": "2024-01-15T02:08:03.341Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_id": "65a498f80089bcd87ba53d97",
|
|
||||||
"name": "3 Года",
|
|
||||||
"description": "3 Года безлимитного пользования сервисом",
|
|
||||||
"price": 0,
|
|
||||||
"order": 4,
|
|
||||||
"isCustom": false,
|
|
||||||
"privileges": [
|
|
||||||
{
|
|
||||||
"name": "Безлимит Опросов",
|
|
||||||
"privilegeId": "quizUnlimTime",
|
|
||||||
"serviceKey": "squiz",
|
|
||||||
"description": "Количество дней, в течении которых пользование сервисом безлимитно",
|
|
||||||
"type": "day",
|
|
||||||
"value": "день",
|
|
||||||
"price": 3400,
|
|
||||||
"amount": 1095
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isDeleted": false,
|
|
||||||
"createdAt": "2024-01-15T02:31:20.448Z",
|
|
||||||
"updatedAt": "2024-01-15T02:31:20.448Z"
|
|
||||||
}
|
|
||||||
];
|
|
@ -1,127 +0,0 @@
|
|||||||
import { Discount } from "../../model/discount";
|
|
||||||
|
|
||||||
|
|
||||||
export function findDiscountFactor(discount: Discount | null | undefined): number {
|
|
||||||
if (!discount) return 1;
|
|
||||||
|
|
||||||
if (discount.Layer === 1) return discount.Target.Products[0].Factor;
|
|
||||||
|
|
||||||
return discount.Target.Factor;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function findNkoDiscount(discounts: Discount[]): Discount | null {
|
|
||||||
const applicableDiscounts = discounts.filter(discount => discount.Condition.UserType === "nko");
|
|
||||||
|
|
||||||
if (!applicableDiscounts.length) return null;
|
|
||||||
|
|
||||||
const maxValueDiscount = applicableDiscounts.reduce((prev, current) => {
|
|
||||||
return Number(current.Condition.CartPurchasesAmount) > Number(prev.Condition.CartPurchasesAmount) ? current : prev;
|
|
||||||
});
|
|
||||||
|
|
||||||
return maxValueDiscount;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function findPrivilegeDiscount(
|
|
||||||
privilegeId: string,
|
|
||||||
privilegeAmount: number,
|
|
||||||
discounts: Discount[],
|
|
||||||
userId: string,
|
|
||||||
): Discount | null {
|
|
||||||
const applicableDiscounts = discounts.filter(discount => {
|
|
||||||
return (
|
|
||||||
discount.Layer === 1
|
|
||||||
&& privilegeId === discount.Condition.Product
|
|
||||||
&& privilegeAmount >= Number(discount.Condition.Term)
|
|
||||||
&& (discount.Condition.User === "" || discount.Condition.User === userId)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!applicableDiscounts.length) return null;
|
|
||||||
|
|
||||||
let maxValueDiscount: Discount = applicableDiscounts[0];
|
|
||||||
for (const discount of applicableDiscounts) {
|
|
||||||
if (discount.Condition.User !== "" && discount.Condition.User === userId) {
|
|
||||||
maxValueDiscount = discount;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Number(discount.Condition.Term) > Number(maxValueDiscount.Condition.Term)) {
|
|
||||||
maxValueDiscount = discount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return maxValueDiscount;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function findServiceDiscount(
|
|
||||||
serviceKey: string,
|
|
||||||
currentPrice: number,
|
|
||||||
discounts: Discount[],
|
|
||||||
userId: string,
|
|
||||||
): Discount | null {
|
|
||||||
const applicableDiscounts = discounts.filter(discount => {
|
|
||||||
return (
|
|
||||||
discount.Layer === 2
|
|
||||||
&& serviceKey === discount.Condition.Group
|
|
||||||
&& currentPrice >= Number(discount.Condition.PriceFrom)
|
|
||||||
&& (discount.Condition.User === "" || discount.Condition.User === userId)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!applicableDiscounts.length) return null;
|
|
||||||
|
|
||||||
let maxValueDiscount: Discount = applicableDiscounts[0];
|
|
||||||
for (const discount of applicableDiscounts) {
|
|
||||||
if (discount.Condition.User !== "" && discount.Condition.User === userId) {
|
|
||||||
maxValueDiscount = discount;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Number(discount.Condition.PriceFrom) > Number(maxValueDiscount.Condition.PriceFrom)) {
|
|
||||||
maxValueDiscount = discount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return maxValueDiscount;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function findCartDiscount(
|
|
||||||
cartPurchasesAmount: number,
|
|
||||||
discounts: Discount[],
|
|
||||||
): Discount | null {
|
|
||||||
const applicableDiscounts = discounts.filter(discount => {
|
|
||||||
return (
|
|
||||||
discount.Layer === 3
|
|
||||||
&& cartPurchasesAmount >= Number(discount.Condition.CartPurchasesAmount)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!applicableDiscounts.length) return null;
|
|
||||||
|
|
||||||
const maxValueDiscount = applicableDiscounts.reduce((prev, current) => {
|
|
||||||
return Number(current.Condition.CartPurchasesAmount) > Number(prev.Condition.CartPurchasesAmount) ? current : prev;
|
|
||||||
});
|
|
||||||
|
|
||||||
return maxValueDiscount;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function findLoyaltyDiscount(
|
|
||||||
purchasesAmount: number,
|
|
||||||
discounts: Discount[],
|
|
||||||
): Discount | null {
|
|
||||||
const applicableDiscounts = discounts.filter(discount => {
|
|
||||||
return (
|
|
||||||
discount.Layer === 4
|
|
||||||
&& discount.Condition.UserType !== "nko"
|
|
||||||
&& purchasesAmount >= Number(discount.Condition.PurchasesAmount)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!applicableDiscounts.length) return null;
|
|
||||||
|
|
||||||
const maxValueDiscount = applicableDiscounts.reduce((prev, current) => {
|
|
||||||
return Number(current.Condition.PurchasesAmount) > Number(prev.Condition.PurchasesAmount) ? current : prev;
|
|
||||||
});
|
|
||||||
|
|
||||||
return maxValueDiscount;
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
export const devlog: typeof console.log = (...args) => {
|
|
||||||
if (process.env.NODE_ENV === "development") console.log(...args);
|
|
||||||
};
|
|
@ -1,5 +0,0 @@
|
|||||||
export function getInitials(firstname: string, secondname: string) {
|
|
||||||
return firstname[0] && secondname[0]
|
|
||||||
? firstname[0] + secondname[0]
|
|
||||||
: "АА";
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
export * from "./backendMessageHandler";
|
|
||||||
export * from "./cart";
|
|
||||||
export * from "./devlog";
|
|
||||||
export * from "./getInitials";
|
|
6488
package-lock.json
generated
6488
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
67
package.json
67
package.json
@ -1,71 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@frontend/kitui",
|
"name": "@frontend/kitui",
|
||||||
"version": "1.0.92",
|
"version": "1.0.0",
|
||||||
"description": "test",
|
"description": "test",
|
||||||
"main": "./dist/index.js",
|
"main": "index.js",
|
||||||
"module": "./dist/index.js",
|
"repository": "git@penahub.gitlab.yandexcloud.net:frontend/kitui.git",
|
||||||
"types": "./dist/index.d.ts",
|
|
||||||
"repository": "git@gitea.pena:PenaSide/UIKit.git",
|
|
||||||
"author": "skeris <kotilion.95@gmail.com>",
|
"author": "skeris <kotilion.95@gmail.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
|
||||||
"files": [
|
|
||||||
"dist"
|
|
||||||
],
|
|
||||||
"exports": {
|
|
||||||
".": {
|
|
||||||
"import": "./dist/index.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"dev": "vite",
|
|
||||||
"build": "tsc && vite build",
|
|
||||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
|
||||||
"preview": "vite preview",
|
|
||||||
"test:cart": "vitest ./lib/utils/cart",
|
|
||||||
"prepublishOnly": "npm run build"
|
|
||||||
},
|
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "http://gitea.pena/api/packages/skeris/npm/"
|
"registry": "https://penahub.gitlab.yandexcloud.net/api/v4/projects/21/packages/npm/"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"immer": "^10.0.2",
|
|
||||||
"reconnecting-eventsource": "^1.6.2"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@emotion/react": "^11.11.1",
|
|
||||||
"@emotion/styled": "^11.11.0",
|
|
||||||
"@mui/icons-material": "^5.14.3",
|
|
||||||
"@mui/material": "^5.14.4",
|
|
||||||
"@types/node": "^20.5.0",
|
|
||||||
"@types/react": "^18.2.15",
|
|
||||||
"@types/react-dom": "^18.2.7",
|
|
||||||
"@types/react-syntax-highlighter": "^15.5.7",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
||||||
"@typescript-eslint/parser": "^6.0.0",
|
|
||||||
"@vitejs/plugin-react": "^4.0.3",
|
|
||||||
"axios": "^1.4.0",
|
|
||||||
"eslint": "^8.45.0",
|
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
|
||||||
"eslint-plugin-react-refresh": "^0.4.3",
|
|
||||||
"react": "^18.2.0",
|
|
||||||
"react-dom": "^18.2.0",
|
|
||||||
"react-router-dom": "^6.15.0",
|
|
||||||
"react-syntax-highlighter": "^15.5.0",
|
|
||||||
"typescript": "^5.0.2",
|
|
||||||
"vite": "^4.4.5",
|
|
||||||
"vite-plugin-dts": "^3.5.2",
|
|
||||||
"vitest": "^1.4.0",
|
|
||||||
"zustand": "^4.3.8"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@emotion/react": "^11.11.1",
|
|
||||||
"@emotion/styled": "^11.11.0",
|
|
||||||
"@mui/icons-material": "^5.14.3",
|
|
||||||
"@mui/material": "^5.14.4",
|
|
||||||
"axios": "^1.4.0",
|
|
||||||
"react": "^18.2.0",
|
|
||||||
"react-router-dom": "^6.15.0",
|
|
||||||
"zustand": "^4.3.8"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
274
src/App.tsx
274
src/App.tsx
@ -1,274 +0,0 @@
|
|||||||
import { Box, Button, Container, Pagination, SxProps, Theme, Typography, useTheme } from "@mui/material";
|
|
||||||
import { PenaLink } from "../lib/components/PenaLink";
|
|
||||||
import { PrismLight as SyntaxHighlighter } from "react-syntax-highlighter";
|
|
||||||
import { oneDark } from "react-syntax-highlighter/dist/esm/styles/prism";
|
|
||||||
import jsx from "react-syntax-highlighter/dist/esm/languages/prism/tsx";
|
|
||||||
import { ReactNode } from "react";
|
|
||||||
import { BurgerButton, CloseButton, CloseButtonSmall, PenaTextField, WalletButton } from "../lib";
|
|
||||||
import { AvatarButton } from "../lib/components/AvatarButton";
|
|
||||||
import { LogoutButton } from "../lib/components/LogoutButton";
|
|
||||||
|
|
||||||
|
|
||||||
SyntaxHighlighter.registerLanguage("jsx", jsx);
|
|
||||||
|
|
||||||
export function App() {
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box sx={{
|
|
||||||
backgroundColor: theme.palette.bg.main,
|
|
||||||
minHeight: "100dvh",
|
|
||||||
width: "100%",
|
|
||||||
}}>
|
|
||||||
<Container sx={{
|
|
||||||
py: 4,
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
gap: 3,
|
|
||||||
alignItems: "start",
|
|
||||||
color: "white",
|
|
||||||
}}>
|
|
||||||
<Typography variant="h4">Components</Typography>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<Button variant="pena-contained-dark">Подробнее</Button>`}
|
|
||||||
element={<Button variant="pena-contained-dark">Подробнее</Button>}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<Button variant="pena-contained-light">Подробнее</Button>`}
|
|
||||||
element={<Button variant="pena-contained-light">Подробнее</Button>}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<Button variant="pena-outlined-dark">Подробнее</Button>`}
|
|
||||||
element={<Button variant="pena-outlined-dark">Подробнее</Button>}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<Button variant="pena-outlined-light">Подробнее</Button>`}
|
|
||||||
element={<Button variant="pena-outlined-light">Подробнее</Button>}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<Button variant="pena-outlined-purple">Перейти</Button>`}
|
|
||||||
element={<Button variant="pena-outlined-purple">Перейти</Button>}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<Button variant="pena-contained-white1">Купить</Button>`}
|
|
||||||
element={<Button variant="pena-contained-white1">Купить</Button>}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<Button variant="pena-contained-white2">Купить</Button>`}
|
|
||||||
element={<Button variant="pena-contained-white2">Выбрать</Button>}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<Button variant="pena-navitem-dark">Подробнее</Button>`}
|
|
||||||
element={<Button variant="pena-navitem-dark">Подробнее</Button>}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<Button variant="pena-navitem-light">Подробнее</Button>`}
|
|
||||||
element={<Button variant="pena-navitem-light">Подробнее</Button>}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<Button variant="pena-text">Подробнее</Button>`}
|
|
||||||
element={<Button variant="pena-text">Подробнее</Button>}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<PenaLink>Подробнее</PenaLink>`}
|
|
||||||
element={<PenaLink href="/">Подробнее</PenaLink>}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<PenaTextField />`}
|
|
||||||
element={<PenaTextField />}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<AvatarButton>AB</AvatarButton>`}
|
|
||||||
element={<AvatarButton>AB</AvatarButton>}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<LogoutButton />`}
|
|
||||||
element={<LogoutButton />}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<WalletButton />`}
|
|
||||||
element={<WalletButton />}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<CloseButton />`}
|
|
||||||
element={<CloseButton />}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<CloseButtonSmall />`}
|
|
||||||
element={<CloseButtonSmall />}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<BurgerButton />`}
|
|
||||||
element={<BurgerButton />}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
sx={{
|
|
||||||
p: 1,
|
|
||||||
backgroundColor: theme.palette.background.default,
|
|
||||||
}}
|
|
||||||
code={`<Pagination variant="pena-pagination" />`}
|
|
||||||
element={<Pagination
|
|
||||||
variant="pena-pagination"
|
|
||||||
count={10}
|
|
||||||
page={1}
|
|
||||||
/>}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<Typography variant="infographic">Some text</Typography>`}
|
|
||||||
element={<Typography variant="infographic">Lorem ipsum dolor sit amet</Typography>}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<Typography variant="p1">Some text</Typography>`}
|
|
||||||
element={<Typography variant="p1">Lorem ipsum dolor sit amet</Typography>}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<Typography variant="price">Some text</Typography>`}
|
|
||||||
element={<Typography variant="price">Lorem ipsum dolor sit amet</Typography>}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<Typography variant="oldPrice">Some text</Typography>`}
|
|
||||||
element={<Typography variant="oldPrice">Lorem ipsum dolor sit amet</Typography>}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<Typography variant="t1">Some text</Typography>`}
|
|
||||||
element={<Typography variant="t1">Lorem ipsum dolor sit amet</Typography>}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<Typography variant="pena-card-header1">Some text</Typography>`}
|
|
||||||
element={<Typography variant="pena-card-header1">Lorem ipsum dolor sit amet</Typography>}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<Typography variant="pena-h1">Some text</Typography>`}
|
|
||||||
element={<Typography variant="pena-h1">Lorem ipsum dolor sit amet</Typography>}
|
|
||||||
/>
|
|
||||||
<ComponentWithCode
|
|
||||||
code={`<Typography variant="pena-h3">Some text</Typography>`}
|
|
||||||
element={<Typography variant="pena-h3">Lorem ipsum dolor sit amet</Typography>}
|
|
||||||
/>
|
|
||||||
<Typography variant="h4">Colors</Typography>
|
|
||||||
<Typography variant="body2">Click text to copy</Typography>
|
|
||||||
<Box sx={{
|
|
||||||
width: "100%",
|
|
||||||
display: "flex",
|
|
||||||
flexWrap: "wrap",
|
|
||||||
gap: 1,
|
|
||||||
}}>
|
|
||||||
<ColorShowcase
|
|
||||||
color={theme.palette.purple.light}
|
|
||||||
text1="theme.palette.purple.light"
|
|
||||||
text2="#944FEE"
|
|
||||||
/>
|
|
||||||
<ColorShowcase
|
|
||||||
color={theme.palette.purple.main}
|
|
||||||
text1="theme.palette.purple.main"
|
|
||||||
text2="#7E2AEA"
|
|
||||||
/>
|
|
||||||
<ColorShowcase
|
|
||||||
color={theme.palette.purple.dark}
|
|
||||||
text1="theme.palette.purple.dark"
|
|
||||||
text2="#581CA7"
|
|
||||||
/>
|
|
||||||
<ColorShowcase
|
|
||||||
color={theme.palette.background.default}
|
|
||||||
text1="theme.palette.background.default"
|
|
||||||
text2="#F2F3F7"
|
|
||||||
/>
|
|
||||||
<ColorShowcase
|
|
||||||
color={theme.palette.bg.main}
|
|
||||||
text1="theme.palette.bg.main"
|
|
||||||
text2="#333647"
|
|
||||||
/>
|
|
||||||
<ColorShowcase
|
|
||||||
color={theme.palette.bg.dark}
|
|
||||||
text1="theme.palette.bg.dark"
|
|
||||||
text2="#252734"
|
|
||||||
/>
|
|
||||||
<ColorShowcase
|
|
||||||
color={theme.palette.gray.main}
|
|
||||||
text1="theme.palette.gray.main"
|
|
||||||
text2="#9A9AAF"
|
|
||||||
/>
|
|
||||||
<ColorShowcase
|
|
||||||
color={theme.palette.gray.dark}
|
|
||||||
text1="theme.palette.gray.dark"
|
|
||||||
text2="#4D4D4D"
|
|
||||||
/>
|
|
||||||
<ColorShowcase
|
|
||||||
color={theme.palette.orange.main}
|
|
||||||
text1="theme.palette.orange.main"
|
|
||||||
text2="#FB5607"
|
|
||||||
/>
|
|
||||||
<ColorShowcase
|
|
||||||
color={theme.palette.orange.light}
|
|
||||||
text1="theme.palette.orange.light"
|
|
||||||
text2="#FC712F"
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Container>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ComponentWithCode({ sx, code, element }: {
|
|
||||||
sx?: SxProps<Theme>;
|
|
||||||
code: string;
|
|
||||||
element: ReactNode;
|
|
||||||
}) {
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
gap: 1,
|
|
||||||
alignItems: "start",
|
|
||||||
...sx,
|
|
||||||
}}>
|
|
||||||
<SyntaxHighlighter
|
|
||||||
language="jsx"
|
|
||||||
style={oneDark}
|
|
||||||
customStyle={{
|
|
||||||
padding: "4px",
|
|
||||||
fontSize: "16px",
|
|
||||||
margin: 0,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{code}
|
|
||||||
</SyntaxHighlighter>
|
|
||||||
{element}
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ColorShowcase({ color, text1, text2 }: {
|
|
||||||
text1: string;
|
|
||||||
text2: string;
|
|
||||||
color: string;
|
|
||||||
}) {
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box sx={{
|
|
||||||
backgroundColor: color,
|
|
||||||
width: "100%",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
maxWidth: "350px",
|
|
||||||
p: 2,
|
|
||||||
gap: 1,
|
|
||||||
border: "1px solid white",
|
|
||||||
overflow: "hidden",
|
|
||||||
}}>
|
|
||||||
<Typography
|
|
||||||
onClick={() => navigator.clipboard.writeText(text1)}
|
|
||||||
sx={{
|
|
||||||
textShadow: "0 0 6px black",
|
|
||||||
}}
|
|
||||||
>{text1}</Typography>
|
|
||||||
<Typography
|
|
||||||
onClick={() => navigator.clipboard.writeText(text2)}
|
|
||||||
sx={{
|
|
||||||
textShadow: "0 0 6px black",
|
|
||||||
}}
|
|
||||||
>{text2}</Typography>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
15
src/main.tsx
15
src/main.tsx
@ -1,15 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import ReactDOM from "react-dom/client";
|
|
||||||
import { App } from "./App.tsx";
|
|
||||||
import { CssBaseline, ThemeProvider } from "@mui/material";
|
|
||||||
import { penaMuiTheme } from "../lib/index.ts";
|
|
||||||
|
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
||||||
<React.StrictMode>
|
|
||||||
<ThemeProvider theme={penaMuiTheme}>
|
|
||||||
<CssBaseline />
|
|
||||||
<App />
|
|
||||||
</ThemeProvider>
|
|
||||||
</React.StrictMode>,
|
|
||||||
);
|
|
1
src/vite-env.d.ts
vendored
1
src/vite-env.d.ts
vendored
@ -1 +0,0 @@
|
|||||||
/// <reference types="vite/client" />
|
|
0
themes/theme.tsx
Normal file
0
themes/theme.tsx
Normal file
@ -1,32 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ES2020",
|
|
||||||
"useDefineForClassFields": true,
|
|
||||||
"lib": [
|
|
||||||
"ES2020",
|
|
||||||
"DOM",
|
|
||||||
"DOM.Iterable"
|
|
||||||
],
|
|
||||||
"module": "ESNext",
|
|
||||||
"skipLibCheck": true,
|
|
||||||
/* Bundler mode */
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"allowImportingTsExtensions": true,
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"jsx": "react-jsx",
|
|
||||||
/* Linting */
|
|
||||||
"strict": true,
|
|
||||||
"noFallthroughCasesInSwitch": true
|
|
||||||
},
|
|
||||||
"include": [
|
|
||||||
"src",
|
|
||||||
"lib"
|
|
||||||
],
|
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.node.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"composite": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"module": "ESNext",
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"allowSyntheticDefaultImports": true
|
|
||||||
},
|
|
||||||
"include": ["vite.config.ts"]
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
import react from '@vitejs/plugin-react';
|
|
||||||
import { resolve } from "path";
|
|
||||||
import { defineConfig } from 'vite';
|
|
||||||
import dts from 'vite-plugin-dts';
|
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [react(), dts({ include: ["lib"] })],
|
|
||||||
build: {
|
|
||||||
lib: {
|
|
||||||
entry: resolve(__dirname, "lib/index.ts"),
|
|
||||||
formats: ["es"],
|
|
||||||
fileName: "index"
|
|
||||||
},
|
|
||||||
copyPublicDir: false,
|
|
||||||
rollupOptions: {
|
|
||||||
external: [
|
|
||||||
"@emotion/react",
|
|
||||||
"@emotion/styled",
|
|
||||||
"@mui/icons-material",
|
|
||||||
"@mui/material",
|
|
||||||
"axios",
|
|
||||||
"react-router-dom",
|
|
||||||
"react",
|
|
||||||
"zustand",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
});
|
|
Loading…
Reference in New Issue
Block a user