Merge branch 'dev' into 'main'
CI\CD setup and some refactoring See merge request frontend/admin!1
This commit is contained in:
commit
73aed5ea2e
31
.gitlab-ci.yml
Normal file
31
.gitlab-ci.yml
Normal file
@ -0,0 +1,31 @@
|
||||
include:
|
||||
- project: "devops/pena-continuous-integration"
|
||||
file: "/templates/docker/build-template.gitlab-ci.yml"
|
||||
- project: "devops/pena-continuous-integration"
|
||||
file: "/templates/docker/clean-template.gitlab-ci.yml"
|
||||
- project: "devops/pena-continuous-integration"
|
||||
file: "/templates/docker/deploy-template.gitlab-ci.yml"
|
||||
stages:
|
||||
- clean
|
||||
- build
|
||||
- deploy
|
||||
|
||||
clear-old-images:
|
||||
extends: .clean_template
|
||||
variables:
|
||||
STAGING_BRANCH: "main"
|
||||
PRODUCTION_BRANCH: "main"
|
||||
|
||||
build-app:
|
||||
extends: .build_template
|
||||
variables:
|
||||
DOCKER_BUILD_PATH: "./Dockerfile"
|
||||
STAGING_BRANCH: "main"
|
||||
PRODUCTION_BRANCH: "main"
|
||||
|
||||
deploy-to-staging:
|
||||
extends: .deploy_template
|
||||
variables:
|
||||
DEPLOY_TO: "staging"
|
||||
BRANCH: "main"
|
||||
|
||||
17
Dockerfile
Normal file
17
Dockerfile
Normal file
@ -0,0 +1,17 @@
|
||||
FROM node:19.1-alpine as build
|
||||
|
||||
RUN apk update && rm -rf /var/cache/apk/*
|
||||
WORKDIR /usr/app
|
||||
COPY package.json .
|
||||
COPY tsconfig.json .
|
||||
|
||||
RUN yarn install --ignore-scripts --non-interactive --frozen-lockfile && yarn cache clean
|
||||
COPY . .
|
||||
RUN ls
|
||||
RUN yarn build
|
||||
|
||||
|
||||
FROM nginx:latest as result
|
||||
WORKDIR /usr/share/nginx/html
|
||||
COPY --from=build /usr/app/build/ /usr/share/nginx/html
|
||||
COPY admin.conf /etc/nginx/conf.d/default.conf
|
||||
12
admin.conf
Normal file
12
admin.conf
Normal file
@ -0,0 +1,12 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
0
deployments/docker-compose.yaml
Normal file
0
deployments/docker-compose.yaml
Normal file
11
deployments/staging/docker-compose.yaml
Normal file
11
deployments/staging/docker-compose.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
services:
|
||||
admin:
|
||||
container_name: admin_front
|
||||
restart: unless-stopped
|
||||
image: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID
|
||||
networks:
|
||||
- penahub_frontend
|
||||
hostname: hub
|
||||
networks:
|
||||
penahub_frontend:
|
||||
external: true
|
||||
32012
package-lock.json
generated
32012
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -21,6 +21,7 @@
|
||||
"@types/node": "^16.11.56",
|
||||
"@types/react": "^18.0.18",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"dayjs": "^1.11.5",
|
||||
"moment": "^2.29.4",
|
||||
"numeral": "^2.0.6",
|
||||
|
||||
36
src/model/cart.ts
Normal file
36
src/model/cart.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { ServiceType } from "./tariff";
|
||||
|
||||
|
||||
export interface CartSummary {
|
||||
mbs: number;
|
||||
points: number;
|
||||
days: number;
|
||||
}
|
||||
|
||||
export interface Promocode {
|
||||
id: number;
|
||||
name: string;
|
||||
endless: boolean;
|
||||
from: string;
|
||||
dueTo: string;
|
||||
privileges: Array<Privileges>;
|
||||
}
|
||||
|
||||
export interface Privileges {
|
||||
good: ServiceType,
|
||||
discount: number;
|
||||
}
|
||||
|
||||
export interface Discount {
|
||||
id: number;
|
||||
name: string;
|
||||
endless: boolean;
|
||||
from: string;
|
||||
dueTo: string;
|
||||
privileges: Array<Privileges>;
|
||||
active: boolean;
|
||||
basketMore: number;
|
||||
incomeMore: number;
|
||||
toTime: number;
|
||||
toCapacity: number;
|
||||
}
|
||||
43
src/model/tariff.ts
Normal file
43
src/model/tariff.ts
Normal file
@ -0,0 +1,43 @@
|
||||
export type ServiceType =
|
||||
| "Шаблонизатор документов"
|
||||
| "Опросник"
|
||||
| "Сокращатель ссылок"
|
||||
| "АБ тесты";
|
||||
|
||||
export interface Tariff {
|
||||
id: number;
|
||||
name: string;
|
||||
type: string;
|
||||
service: ServiceType | "";
|
||||
disk: number;
|
||||
time: number;
|
||||
points: number;
|
||||
price: number;
|
||||
}
|
||||
|
||||
// TODO тип пакета тарифов надо как-то реорганизовать
|
||||
export interface ArrayProps {
|
||||
id: number;
|
||||
name: string;
|
||||
type: "package" | "tariff";
|
||||
service: ServiceType | "";
|
||||
disk: number;
|
||||
time: number;
|
||||
points: number;
|
||||
price: number;
|
||||
tariffs?: Array<Tariff>;
|
||||
}
|
||||
|
||||
// Идея для типа пакета тарифов
|
||||
interface TariffPackage {
|
||||
id: number;
|
||||
name: string;
|
||||
service: ServiceType;
|
||||
disk: number;
|
||||
time: number;
|
||||
points: number;
|
||||
price: number;
|
||||
tariffs: Tariff[];
|
||||
}
|
||||
|
||||
type TariffsOrPackages = Array<Tariff | TariffPackage>; // Этот тип должен пойти вместо ArrayProps
|
||||
@ -12,10 +12,10 @@ import Paper from "@mui/material/Paper";
|
||||
import { DataGrid, GridColDef, GridSelectionModel, GridToolbar } from "@mui/x-data-grid";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
||||
import { PrivilegesProps, DiscountProps } from "./types";
|
||||
import useStore, { StoreState } from "../../../../store";
|
||||
import theme from "../../../../theme";
|
||||
import {styled} from "@mui/material/styles";
|
||||
import { styled } from "@mui/material/styles";
|
||||
import { Discount } from "../../../../model/cart";
|
||||
import { useDiscountStore } from "../../../../stores/discounts";
|
||||
|
||||
|
||||
const BoxButton = styled('div')(({ theme }) => ({
|
||||
@ -26,9 +26,9 @@ const BoxButton = styled('div')(({ theme }) => ({
|
||||
|
||||
|
||||
const columns: GridColDef[] = [
|
||||
{
|
||||
field: "id",
|
||||
headerName: "ID",
|
||||
{
|
||||
field: "id",
|
||||
headerName: "ID",
|
||||
width: 30,
|
||||
sortable: false,
|
||||
},
|
||||
@ -90,133 +90,105 @@ const columns: GridColDef[] = [
|
||||
}
|
||||
];
|
||||
|
||||
const rows:Array<DiscountProps> = [
|
||||
{ id: 1, name: "Скидка 1", endless: false, from: "", dueTo: "", privileges: [
|
||||
{
|
||||
good: "Товар 1",
|
||||
discount: 0.3
|
||||
},
|
||||
{
|
||||
good: "Товар 2",
|
||||
discount: 0.2
|
||||
}
|
||||
], active: false,incomeMore:1, basketMore: 10, toTime: 20, toCapacity: 30 },
|
||||
{ id: 2, name: "Скидка 2", endless: false, from: "", dueTo: "", privileges: [
|
||||
{
|
||||
good: "Товар 3",
|
||||
discount: 0.3
|
||||
},
|
||||
{
|
||||
good: "Товар 4",
|
||||
discount: 0.2
|
||||
}
|
||||
], active: true,incomeMore:1, basketMore: 10, toTime: 20, toCapacity: 30 },
|
||||
{ id: 3, name: "Скидка 3", endless: false, from: "", dueTo: "", privileges: [
|
||||
{
|
||||
good: "Товар 5",
|
||||
discount: 0.3
|
||||
},
|
||||
{
|
||||
good: "Товар 6",
|
||||
discount: 0.2
|
||||
}
|
||||
], active: false, incomeMore: 1, basketMore: 10, toTime: 20, toCapacity: 30 },
|
||||
];
|
||||
|
||||
const Discounts: React.FC = () => {
|
||||
const Discounts: React.FC = () => {
|
||||
const [checkboxState, setCheckboxState] = React.useState<boolean>(false);
|
||||
const toggleCheckbox = () => { setCheckboxState( !checkboxState ); }
|
||||
const toggleCheckbox = () => { setCheckboxState(!checkboxState); };
|
||||
|
||||
const [value1, setValue1] = React.useState<Date>( new Date() );
|
||||
const [value2, setValue2] = React.useState<Date>( new Date() );
|
||||
const [value1, setValue1] = React.useState<Date>(new Date());
|
||||
const [value2, setValue2] = React.useState<Date>(new Date());
|
||||
|
||||
const [service, setService] = React.useState("Шаблонизатор");
|
||||
const handleChange = (event: SelectChangeEvent) => {
|
||||
setService(event.target.value as string);
|
||||
};
|
||||
|
||||
const discountsArray = useDiscountStore(state => state.discountsArray);
|
||||
const discountsArraySet = useDiscountStore(state => state.setDiscountsArray);
|
||||
|
||||
const { discountsArray, discountsArraySet } = useStore<StoreState>((state) => state);
|
||||
|
||||
//discountsArraySet( rows );
|
||||
const { discountsActiveArray, discountsActiveArraySet } = useStore<StoreState>((state) => state);
|
||||
let discountsActiveArrayUpdated:Array<number>;
|
||||
const discountsActiveArray = useDiscountStore(state => state.discountsActiveArray);
|
||||
const discountsActiveArraySet = useDiscountStore(state => state.setDiscountsActiveArray);
|
||||
let discountsActiveArrayUpdated: Array<number>;
|
||||
|
||||
const findActiveDiscounts = () => {
|
||||
const actives:Array<number> = [];
|
||||
|
||||
discountsArray.forEach( (item, i) => {
|
||||
if( item.active == true ) { actives.push( i ); }
|
||||
} );
|
||||
const findActiveDiscounts = () => {
|
||||
const actives: Array<number> = [];
|
||||
|
||||
discountsActiveArrayUpdated = [ ...actives ];
|
||||
discountsArray.forEach((item, i) => {
|
||||
if (item.active == true) { actives.push(i); }
|
||||
});
|
||||
|
||||
if( JSON.stringify(discountsActiveArray) != JSON.stringify(discountsActiveArrayUpdated) ) {
|
||||
discountsActiveArraySet( discountsActiveArrayUpdated );
|
||||
discountsActiveArrayUpdated = [...actives];
|
||||
|
||||
if (JSON.stringify(discountsActiveArray) != JSON.stringify(discountsActiveArrayUpdated)) {
|
||||
discountsActiveArraySet(discountsActiveArrayUpdated);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
findActiveDiscounts();
|
||||
|
||||
const discountsArrayConverted = discountsArray.map( (item) => {
|
||||
const discountsArrayConverted = discountsArray.map((item) => {
|
||||
const basketMorePerc = Math.round(Number(item.basketMore) * 100) + "%";
|
||||
const toTimePerc = Math.round(Number(item.toTime) * 100) + "%";
|
||||
const toCapacityPerc = Math.round(Number(item.toCapacity) * 100) + "%";
|
||||
|
||||
const dateFrom = item.from ? new Date( Number(item.from) ) : "";
|
||||
const dateDueTo = item.from ? new Date( Number(item.dueTo) ) : "";
|
||||
const dateFrom = item.from ? new Date(Number(item.from)) : "";
|
||||
const dateDueTo = item.from ? new Date(Number(item.dueTo)) : "";
|
||||
|
||||
const strFrom = dateFrom
|
||||
const strFrom = dateFrom
|
||||
? `${dateFrom.getDate()}.${dateFrom.getMonth()}.${dateFrom.getFullYear()}`
|
||||
: "-"
|
||||
: "-";
|
||||
|
||||
const strDueTo = dateDueTo
|
||||
const strDueTo = dateDueTo
|
||||
? `${dateDueTo.getDate()}.${dateDueTo.getMonth()}.${dateDueTo.getFullYear()}`
|
||||
: "-"
|
||||
: "-";
|
||||
|
||||
if( item.privileges.length ) {
|
||||
const result = item.privileges.reduce( (acc, privilege) => {
|
||||
acc = acc
|
||||
? `${acc}, ${privilege.good} - ${privilege.discount}%`
|
||||
: `${privilege.good} - ${ Math.round(privilege.discount * 100) }%`;
|
||||
if (item.privileges.length) {
|
||||
const result = item.privileges.reduce((acc, privilege) => {
|
||||
acc = acc
|
||||
? `${acc}, ${privilege.good} - ${privilege.discount}%`
|
||||
: `${privilege.good} - ${Math.round(privilege.discount * 100)}%`;
|
||||
|
||||
return acc;
|
||||
}, "" );
|
||||
}, "");
|
||||
|
||||
return { ...item, privileges: result, from: strFrom, dueTo: strDueTo,
|
||||
basketMore: basketMorePerc, toTime: toTimePerc, toCapacity: toCapacityPerc }
|
||||
return {
|
||||
...item, privileges: result, from: strFrom, dueTo: strDueTo,
|
||||
basketMore: basketMorePerc, toTime: toTimePerc, toCapacity: toCapacityPerc
|
||||
};
|
||||
} else {
|
||||
return { ...item, from: strFrom, dueTo: strDueTo,
|
||||
basketMore: basketMorePerc, toTime: toTimePerc, toCapacity: toCapacityPerc }
|
||||
return {
|
||||
...item, from: strFrom, dueTo: strDueTo,
|
||||
basketMore: basketMorePerc, toTime: toTimePerc, toCapacity: toCapacityPerc
|
||||
};
|
||||
}
|
||||
} );
|
||||
});
|
||||
|
||||
const createDiscount = ( name:string,
|
||||
discount: number,
|
||||
addedMore: number,
|
||||
basketMore: number,
|
||||
toTime: number,
|
||||
toCapacity: number, ) => {
|
||||
const newDiscount = {
|
||||
id: new Date().getTime(),
|
||||
name,
|
||||
endless: checkboxState,
|
||||
const createDiscount = (name: string,
|
||||
discount: number,
|
||||
addedMore: number,
|
||||
basketMore: number,
|
||||
toTime: number,
|
||||
toCapacity: number,) => {
|
||||
const newDiscount = {
|
||||
id: new Date().getTime(),
|
||||
name,
|
||||
endless: checkboxState,
|
||||
incomeMore: addedMore,
|
||||
from: checkboxState ? "" : new Date( value1 ).getTime() +"",
|
||||
dueTo: checkboxState ? "" : new Date( value2 ).getTime() +"",
|
||||
privileges: [{
|
||||
good: service,
|
||||
discount: discount / 100
|
||||
}],
|
||||
from: checkboxState ? "" : new Date(value1).getTime() + "",
|
||||
dueTo: checkboxState ? "" : new Date(value2).getTime() + "",
|
||||
privileges: [{
|
||||
good: service,
|
||||
discount: discount / 100
|
||||
}],
|
||||
active: false,
|
||||
basketMore: basketMore / 100,
|
||||
toTime: toTime / 100,
|
||||
toCapacity: toCapacity / 100
|
||||
}
|
||||
} as Discount;
|
||||
|
||||
const discountsArrayUpdated = [ ...discountsArray, newDiscount ];
|
||||
discountsArraySet( discountsArrayUpdated );
|
||||
}
|
||||
const discountsArrayUpdated = [...discountsArray, newDiscount];
|
||||
discountsArraySet(discountsArrayUpdated);
|
||||
};
|
||||
|
||||
const fieldName = React.useRef<HTMLInputElement | null>(null);
|
||||
const fieldDiscount = React.useRef<HTMLInputElement | null>(null);
|
||||
@ -232,61 +204,62 @@ const Discounts: React.FC = () => {
|
||||
// }
|
||||
|
||||
const checkFields = () => {
|
||||
if( fieldName.current != null
|
||||
&& fieldDiscount.current != null
|
||||
if (fieldName.current != null
|
||||
&& fieldDiscount.current != null
|
||||
&& fieldAddedMore.current != null
|
||||
&& basketMore.current != null
|
||||
&& toTime.current != null
|
||||
&& toCapacity.current != null ) {
|
||||
&& toCapacity.current != null) {
|
||||
|
||||
createDiscount( fieldName.current.value,
|
||||
Number(fieldDiscount.current.value),
|
||||
createDiscount(fieldName.current.value,
|
||||
Number(fieldDiscount.current.value),
|
||||
Number(fieldAddedMore.current.value),
|
||||
Number(basketMore.current.value),
|
||||
Number(toTime.current.value),
|
||||
Number(toCapacity.current.value) );
|
||||
|
||||
Number(toCapacity.current.value));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const { discountsSelectedRowsData, discountsSelectedRowsDataSet } = useStore<StoreState>((state) => state);
|
||||
|
||||
const onRowsSelectionHandler = ( ids:GridSelectionModel ) => {
|
||||
const result:Array<DiscountProps> = [];
|
||||
ids.forEach((id) => discountsArray.forEach( (row) => {
|
||||
if(row.id === id) result.push(row);
|
||||
} ) );
|
||||
|
||||
discountsSelectedRowsDataSet( [ ...result ] );
|
||||
};
|
||||
|
||||
const activation = ( value:boolean ) => {
|
||||
discountsArray.forEach( (item, i) => {
|
||||
discountsSelectedRowsData.forEach( (selected) => {
|
||||
if( item.id == selected.id ) {
|
||||
if( value ) {
|
||||
discountsArray[ i ].active = true;
|
||||
const discountsSelectedRowsData = useDiscountStore(state => state.discountsSelectedRowsData);
|
||||
const discountsSelectedRowsDataSet = useDiscountStore(state => state.setDiscountsSelectedRowsData);
|
||||
|
||||
const onRowsSelectionHandler = (ids: GridSelectionModel) => {
|
||||
const result: Array<Discount> = [];
|
||||
ids.forEach((id) => discountsArray.forEach((row) => {
|
||||
if (row.id === id) result.push(row);
|
||||
}));
|
||||
|
||||
discountsSelectedRowsDataSet([...result]);
|
||||
};
|
||||
|
||||
const activation = (value: boolean) => {
|
||||
discountsArray.forEach((item, i) => {
|
||||
discountsSelectedRowsData.forEach((selected) => {
|
||||
if (item.id == selected.id) {
|
||||
if (value) {
|
||||
discountsArray[i].active = true;
|
||||
} else {
|
||||
discountsArray[ i ].active = false;
|
||||
discountsArray[i].active = false;
|
||||
}
|
||||
}
|
||||
} );
|
||||
} );
|
||||
});
|
||||
});
|
||||
|
||||
discountsArraySet( discountsArray );
|
||||
}
|
||||
discountsArraySet(discountsArray);
|
||||
};
|
||||
|
||||
const PositiveInput = (event:any) => {
|
||||
const PositiveInput = (event: any) => {
|
||||
const numberInput = parseInt(event.target.value);
|
||||
if(isNaN(numberInput) || numberInput < 0) {event.target.value = '0'}
|
||||
}
|
||||
if (isNaN(numberInput) || numberInput < 0) { event.target.value = '0'; }
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
sx={{
|
||||
width: "90%",
|
||||
height: "60px",
|
||||
@ -295,24 +268,24 @@ const Discounts: React.FC = () => {
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
color: theme.palette.secondary.main
|
||||
}}>
|
||||
}}>
|
||||
СКИДКИ
|
||||
</Typography>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "left",
|
||||
alignItems: "left",
|
||||
marginTop: "15px",
|
||||
}}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "left",
|
||||
alignItems: "left",
|
||||
marginTop: "15px",
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
id = "standard-basic"
|
||||
label = { "Название" }
|
||||
variant = "filled"
|
||||
color = "secondary"
|
||||
id="standard-basic"
|
||||
label={"Название"}
|
||||
variant="filled"
|
||||
color="secondary"
|
||||
sx={{
|
||||
height: "30px",
|
||||
}}
|
||||
@ -320,16 +293,18 @@ const Discounts: React.FC = () => {
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
} }}
|
||||
}
|
||||
}}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
} }}
|
||||
inputRef={ fieldName }
|
||||
}
|
||||
}}
|
||||
inputRef={fieldName}
|
||||
/>
|
||||
|
||||
<Typography
|
||||
variant="h4"
|
||||
<Typography
|
||||
variant="h4"
|
||||
sx={{
|
||||
width: "90%",
|
||||
height: "40px",
|
||||
@ -337,7 +312,7 @@ const Discounts: React.FC = () => {
|
||||
color: theme.palette.grayDisabled.main,
|
||||
marginTop: "75px",
|
||||
paddingLeft: '10px',
|
||||
}}>
|
||||
}}>
|
||||
Условия:
|
||||
</Typography>
|
||||
|
||||
@ -347,7 +322,7 @@ const Discounts: React.FC = () => {
|
||||
value={service}
|
||||
label="Age"
|
||||
onChange={handleChange}
|
||||
sx={{
|
||||
sx={{
|
||||
color: theme.palette.secondary.main,
|
||||
border: "1px solid",
|
||||
borderColor: theme.palette.secondary.main,
|
||||
@ -366,10 +341,10 @@ const Discounts: React.FC = () => {
|
||||
</Select>
|
||||
|
||||
<TextField
|
||||
id = "standard-basic"
|
||||
label = { "Процент скидки" }
|
||||
variant = "filled"
|
||||
color = "secondary"
|
||||
id="standard-basic"
|
||||
label={"Процент скидки"}
|
||||
variant="filled"
|
||||
color="secondary"
|
||||
sx={{
|
||||
marginTop: "15px"
|
||||
}}
|
||||
@ -377,20 +352,22 @@ const Discounts: React.FC = () => {
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
} }}
|
||||
}
|
||||
}}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
} }}
|
||||
inputRef={ fieldDiscount }
|
||||
}
|
||||
}}
|
||||
inputRef={fieldDiscount}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
id = "standard-basic"
|
||||
label = { "Внесено больше" }
|
||||
variant = "filled"
|
||||
color = "secondary"
|
||||
type = "number"
|
||||
id="standard-basic"
|
||||
label={"Внесено больше"}
|
||||
variant="filled"
|
||||
color="secondary"
|
||||
type="number"
|
||||
sx={{
|
||||
marginTop: "15px"
|
||||
}}
|
||||
@ -398,21 +375,23 @@ const Discounts: React.FC = () => {
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
} }}
|
||||
}
|
||||
}}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
} }}
|
||||
inputRef={ fieldAddedMore }
|
||||
}
|
||||
}}
|
||||
inputRef={fieldAddedMore}
|
||||
onChange={PositiveInput}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
id = "standard-basic"
|
||||
label = { "Объем в корзине" }
|
||||
variant = "filled"
|
||||
color = "secondary"
|
||||
type = "number"
|
||||
id="standard-basic"
|
||||
label={"Объем в корзине"}
|
||||
variant="filled"
|
||||
color="secondary"
|
||||
type="number"
|
||||
sx={{
|
||||
marginTop: "15px"
|
||||
}}
|
||||
@ -420,61 +399,67 @@ const Discounts: React.FC = () => {
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
} }}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
} }}
|
||||
inputRef={ basketMore }
|
||||
onChange={ PositiveInput }
|
||||
/>
|
||||
|
||||
<TextField
|
||||
id = "standard-basic"
|
||||
label = { "На время" }
|
||||
variant = "filled"
|
||||
color = "secondary"
|
||||
type = "number"
|
||||
sx={{
|
||||
marginTop: "15px"
|
||||
}
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
} }}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
} }}
|
||||
inputRef={ toTime }
|
||||
onChange={ PositiveInput }
|
||||
/>
|
||||
|
||||
<TextField
|
||||
id = "standard-basic"
|
||||
label = { "На объем" }
|
||||
variant = "filled"
|
||||
color = "secondary"
|
||||
type = "number"
|
||||
sx={{
|
||||
marginTop: "15px"
|
||||
}
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
} }}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
} }}
|
||||
inputRef={ toCapacity }
|
||||
inputRef={basketMore}
|
||||
onChange={PositiveInput}
|
||||
/>
|
||||
|
||||
<TableContainer component={Paper} sx={{
|
||||
width: "100%",
|
||||
<TextField
|
||||
id="standard-basic"
|
||||
label={"На время"}
|
||||
variant="filled"
|
||||
color="secondary"
|
||||
type="number"
|
||||
sx={{
|
||||
marginTop: "15px"
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
}
|
||||
}}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
}
|
||||
}}
|
||||
inputRef={toTime}
|
||||
onChange={PositiveInput}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
id="standard-basic"
|
||||
label={"На объем"}
|
||||
variant="filled"
|
||||
color="secondary"
|
||||
type="number"
|
||||
sx={{
|
||||
marginTop: "15px"
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
}
|
||||
}}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
}
|
||||
}}
|
||||
inputRef={toCapacity}
|
||||
onChange={PositiveInput}
|
||||
/>
|
||||
|
||||
<TableContainer component={Paper} sx={{
|
||||
width: "100%",
|
||||
marginTop: "35px",
|
||||
backgroundColor: theme.palette.content.main
|
||||
}}>
|
||||
@ -494,24 +479,24 @@ const Discounts: React.FC = () => {
|
||||
</Table>
|
||||
</TableContainer>
|
||||
|
||||
<Typography
|
||||
variant="h4"
|
||||
<Typography
|
||||
variant="h4"
|
||||
sx={{
|
||||
width: "90%",
|
||||
height: "40px",
|
||||
fontWeight: "normal",
|
||||
color: theme.palette.grayDisabled.main,
|
||||
marginTop: "55px"
|
||||
}}>
|
||||
}}>
|
||||
Дата действия:
|
||||
</Typography>
|
||||
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexWrap: 'wrap'
|
||||
}}
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexWrap: 'wrap'
|
||||
}}
|
||||
>
|
||||
<Typography sx={{
|
||||
width: "35px",
|
||||
@ -523,16 +508,18 @@ const Discounts: React.FC = () => {
|
||||
|
||||
<DesktopDatePicker
|
||||
inputFormat="DD/MM/YYYY"
|
||||
value={ value1 }
|
||||
onChange={ (e) => { if(e) { setValue1(e) } } }
|
||||
value={value1}
|
||||
onChange={(e) => { if (e) { setValue1(e); } }}
|
||||
renderInput={(params) => <TextField {...params} />}
|
||||
InputProps={{ sx: {
|
||||
height: "40px",
|
||||
color: theme.palette.secondary.main,
|
||||
border: "1px solid",
|
||||
borderColor: theme.palette.secondary.main,
|
||||
"& .MuiSvgIcon-root": { color: theme.palette.secondary.main }
|
||||
} }}
|
||||
InputProps={{
|
||||
sx: {
|
||||
height: "40px",
|
||||
color: theme.palette.secondary.main,
|
||||
border: "1px solid",
|
||||
borderColor: theme.palette.secondary.main,
|
||||
"& .MuiSvgIcon-root": { color: theme.palette.secondary.main }
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<Typography sx={{
|
||||
@ -545,16 +532,18 @@ const Discounts: React.FC = () => {
|
||||
|
||||
<DesktopDatePicker
|
||||
inputFormat="DD/MM/YYYY"
|
||||
value={ value2 }
|
||||
onChange={ (e) => { if(e) { setValue2(e) } } }
|
||||
value={value2}
|
||||
onChange={(e) => { if (e) { setValue2(e); } }}
|
||||
renderInput={(params) => <TextField {...params} />}
|
||||
InputProps={{ sx: {
|
||||
height: "40px",
|
||||
color: theme.palette.secondary.main,
|
||||
border: "1px solid",
|
||||
borderColor: theme.palette.secondary.main,
|
||||
"& .MuiSvgIcon-root": { color: theme.palette.secondary.main }
|
||||
} }}
|
||||
InputProps={{
|
||||
sx: {
|
||||
height: "40px",
|
||||
color: theme.palette.secondary.main,
|
||||
border: "1px solid",
|
||||
borderColor: theme.palette.secondary.main,
|
||||
"& .MuiSvgIcon-root": { color: theme.palette.secondary.main }
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@ -564,7 +553,7 @@ const Discounts: React.FC = () => {
|
||||
width: "90%",
|
||||
marginTop: theme.spacing(2),
|
||||
}}>
|
||||
<Box sx={{
|
||||
<Box sx={{
|
||||
width: "20px",
|
||||
height: "42px",
|
||||
display: "flex",
|
||||
@ -573,14 +562,14 @@ const Discounts: React.FC = () => {
|
||||
alignItems: "left",
|
||||
marginRight: theme.spacing(1)
|
||||
}}>
|
||||
<Checkbox sx={{
|
||||
<Checkbox sx={{
|
||||
color: theme.palette.secondary.main,
|
||||
"&.Mui-checked": {
|
||||
color: theme.palette.secondary.main,
|
||||
},
|
||||
}} onClick={ () => toggleCheckbox() } />
|
||||
}} onClick={() => toggleCheckbox()} />
|
||||
</Box>
|
||||
<Box sx={{
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
@ -590,7 +579,7 @@ const Discounts: React.FC = () => {
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box sx={{
|
||||
<Box sx={{
|
||||
width: "90%",
|
||||
marginTop: "55px",
|
||||
display: "flex",
|
||||
@ -598,8 +587,8 @@ const Discounts: React.FC = () => {
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}>
|
||||
<Button
|
||||
variant = "contained"
|
||||
<Button
|
||||
variant="contained"
|
||||
sx={{
|
||||
backgroundColor: theme.palette.menu.main,
|
||||
height: "52px",
|
||||
@ -609,19 +598,19 @@ const Discounts: React.FC = () => {
|
||||
backgroundColor: theme.palette.grayMedium.main
|
||||
}
|
||||
}}
|
||||
onClick={ () => checkFields() } >
|
||||
Cоздать
|
||||
onClick={() => checkFields()} >
|
||||
Cоздать
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
|
||||
</Box>
|
||||
|
||||
<Box style={{ width: "80%", marginTop: "55px" }}>
|
||||
<Box style={{ height: 400 }}>
|
||||
<DataGrid
|
||||
checkboxSelection={true}
|
||||
rows={ discountsArrayConverted }
|
||||
columns={ columns }
|
||||
<DataGrid
|
||||
checkboxSelection={true}
|
||||
rows={discountsArrayConverted}
|
||||
columns={columns}
|
||||
sx={{
|
||||
color: theme.palette.secondary.main,
|
||||
"& .MuiDataGrid-iconSeparator": {
|
||||
@ -644,12 +633,12 @@ const Discounts: React.FC = () => {
|
||||
},
|
||||
}}
|
||||
components={{ Toolbar: GridToolbar }}
|
||||
onSelectionModelChange={ (ids) => onRowsSelectionHandler( ids ) }
|
||||
onSelectionModelChange={(ids) => onRowsSelectionHandler(ids)}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box sx={{
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
@ -664,9 +653,9 @@ const Discounts: React.FC = () => {
|
||||
justifyContent: "space-between",
|
||||
flexWrap: 'wrap',
|
||||
}}>
|
||||
<Button
|
||||
variant = "contained"
|
||||
onClick={ () => activation( false ) }
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => activation(false)}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.menu.main,
|
||||
width: "200px",
|
||||
@ -678,12 +667,12 @@ const Discounts: React.FC = () => {
|
||||
backgroundColor: theme.palette.grayMedium.main
|
||||
}
|
||||
}}>
|
||||
Деактивировать
|
||||
Деактивировать
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant = "contained"
|
||||
onClick={ () => activation( true ) }
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => activation(true)}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.menu.main,
|
||||
width: "200px",
|
||||
@ -694,15 +683,15 @@ const Discounts: React.FC = () => {
|
||||
backgroundColor: theme.palette.grayMedium.main
|
||||
}
|
||||
}}>
|
||||
Применить
|
||||
Применить
|
||||
</Button>
|
||||
</BoxButton>
|
||||
</Box>
|
||||
|
||||
|
||||
</LocalizationProvider>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export default Discounts;
|
||||
|
||||
@ -1,18 +0,0 @@
|
||||
export interface PrivilegesProps {
|
||||
good: string,
|
||||
discount: number
|
||||
}
|
||||
|
||||
export interface DiscountProps {
|
||||
id: number
|
||||
name: string
|
||||
endless: boolean
|
||||
from: string
|
||||
dueTo: string
|
||||
privileges: Array<PrivilegesProps>
|
||||
active: boolean
|
||||
basketMore: number
|
||||
incomeMore: number
|
||||
toTime: number
|
||||
toCapacity: number
|
||||
}
|
||||
@ -9,444 +9,421 @@ import TableCell from "@mui/material/TableCell";
|
||||
import TableRow from "@mui/material/TableRow";
|
||||
import TableContainer from "@mui/material/TableContainer";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import { DataGrid, GridColDef, GridSelectionModel, GridToolbar } from "@mui/x-data-grid";
|
||||
import { DataGrid, GridColDef, GridToolbar } from "@mui/x-data-grid";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
||||
import { PrivilegesProps, PromocodeProps } from "./types";
|
||||
import useStore, { StoreState } from "../../../../store";
|
||||
import theme from "../../../../theme";
|
||||
import { Promocode } from "../../../../model/cart";
|
||||
import { ServiceType } from "../../../../model/tariff";
|
||||
import { usePromocodeStore } from "../../../../stores/promocodes";
|
||||
|
||||
|
||||
const columns: GridColDef[] = [
|
||||
{
|
||||
field: "id",
|
||||
headerName: "ID",
|
||||
width: 30,
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
field: "name",
|
||||
headerName: "Название промокода",
|
||||
width: 200,
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
field: "endless",
|
||||
headerName: "Бесконечный",
|
||||
width: 120,
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
field: "from",
|
||||
headerName: "От",
|
||||
width: 120,
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
field: "dueTo",
|
||||
headerName: "До",
|
||||
width: 120,
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
field: "privileges",
|
||||
headerName: "Привилегии",
|
||||
width: 210,
|
||||
sortable: false,
|
||||
}
|
||||
{
|
||||
field: "id",
|
||||
headerName: "ID",
|
||||
width: 30,
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
field: "name",
|
||||
headerName: "Название промокода",
|
||||
width: 200,
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
field: "endless",
|
||||
headerName: "Бесконечный",
|
||||
width: 120,
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
field: "from",
|
||||
headerName: "От",
|
||||
width: 120,
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
field: "dueTo",
|
||||
headerName: "До",
|
||||
width: 120,
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
field: "privileges",
|
||||
headerName: "Привилегии",
|
||||
width: 210,
|
||||
sortable: false,
|
||||
}
|
||||
];
|
||||
|
||||
const rows:Array<PromocodeProps> = [
|
||||
{ id: 1, name: "Промокод 1", endless: false, from: "", dueTo: "", privileges: [
|
||||
{
|
||||
good: "Товар 1",
|
||||
discount: 0.3
|
||||
},
|
||||
{
|
||||
good: "Товар 2",
|
||||
discount: 0.2
|
||||
}
|
||||
] },
|
||||
{ id: 2, name: "Промокод 2", endless: false, from: "", dueTo: "", privileges: [
|
||||
{
|
||||
good: "Товар 3",
|
||||
discount: 0.3
|
||||
},
|
||||
{
|
||||
good: "Товар 4",
|
||||
discount: 0.2
|
||||
}
|
||||
] },
|
||||
{ id: 3, name: "Промокод 3", endless: false, from: "", dueTo: "", privileges: [
|
||||
{
|
||||
good: "Товар 5",
|
||||
discount: 0.3
|
||||
},
|
||||
{
|
||||
good: "Товар 6",
|
||||
discount: 0.2
|
||||
}
|
||||
] },
|
||||
];
|
||||
const Promocodes: React.FC = () => {
|
||||
const [checkboxState, setCheckboxState] = React.useState<boolean>(false);
|
||||
const toggleCheckbox = () => { setCheckboxState(!checkboxState); };
|
||||
|
||||
const Promocode: React.FC = () => {
|
||||
const [checkboxState, setCheckboxState] = React.useState<boolean>(false);
|
||||
const toggleCheckbox = () => { setCheckboxState( !checkboxState ); }
|
||||
const [value1, setValue1] = React.useState<Date>(new Date());
|
||||
const [value2, setValue2] = React.useState<Date>(new Date());
|
||||
|
||||
const [value1, setValue1] = React.useState<Date>( new Date() );
|
||||
const [value2, setValue2] = React.useState<Date>( new Date() );
|
||||
const [service, setService] = React.useState<ServiceType>("Шаблонизатор документов");
|
||||
const handleChange = (event: SelectChangeEvent) => {
|
||||
setService(event.target.value as ServiceType);
|
||||
};
|
||||
|
||||
const [service, setService] = React.useState("Шаблонизатор");
|
||||
const handleChange = (event: SelectChangeEvent) => {
|
||||
setService(event.target.value as string);
|
||||
};
|
||||
const promocodeArray = usePromocodeStore(state => state.promocodeArray);
|
||||
const promocodeArraySet = usePromocodeStore(state => state.setPromocodeArray);
|
||||
|
||||
const { promocodeArray, promocodeArraySet } = useStore<StoreState>((state) => state);
|
||||
|
||||
const promocodeArrayConverted = promocodeArray.map( (item) => {
|
||||
const dateFrom = item.from ? new Date( Number(item.from) ) : "";
|
||||
const dateDueTo = item.from ? new Date( Number(item.dueTo) ) : "";
|
||||
const promocodeArrayConverted = promocodeArray.map((item) => {
|
||||
const dateFrom = item.from ? new Date(Number(item.from)) : "";
|
||||
const dateDueTo = item.from ? new Date(Number(item.dueTo)) : "";
|
||||
|
||||
const strFrom = dateFrom
|
||||
? `${dateFrom.getDate()}.${dateFrom.getMonth()}.${dateFrom.getFullYear()}`
|
||||
: "-"
|
||||
const strFrom = dateFrom
|
||||
? `${dateFrom.getDate()}.${dateFrom.getMonth()}.${dateFrom.getFullYear()}`
|
||||
: "-";
|
||||
|
||||
const strDueTo = dateDueTo
|
||||
? `${dateDueTo.getDate()}.${dateDueTo.getMonth()}.${dateDueTo.getFullYear()}`
|
||||
: "-"
|
||||
const strDueTo = dateDueTo
|
||||
? `${dateDueTo.getDate()}.${dateDueTo.getMonth()}.${dateDueTo.getFullYear()}`
|
||||
: "-";
|
||||
|
||||
if( item.privileges.length ) {
|
||||
const result = item.privileges.reduce( (acc, privilege) => {
|
||||
acc = acc
|
||||
? `${acc}, ${privilege.good} - ${privilege.discount}%`
|
||||
: `${privilege.good} - ${privilege.discount * 100}%`;
|
||||
if (item.privileges.length) {
|
||||
const result = item.privileges.reduce((acc, privilege) => {
|
||||
acc = acc
|
||||
? `${acc}, ${privilege.good} - ${privilege.discount}%`
|
||||
: `${privilege.good} - ${privilege.discount * 100}%`;
|
||||
|
||||
return acc;
|
||||
}, "" );
|
||||
return acc;
|
||||
}, "");
|
||||
|
||||
return { ...item, privileges: result, from: strFrom, dueTo: strDueTo }
|
||||
} else {
|
||||
return { ...item, from: strFrom, dueTo: strDueTo }
|
||||
}
|
||||
} );
|
||||
return { ...item, privileges: result, from: strFrom, dueTo: strDueTo };
|
||||
} else {
|
||||
return { ...item, from: strFrom, dueTo: strDueTo };
|
||||
}
|
||||
});
|
||||
|
||||
const createPromocode = ( name:string, discount: number ) => {
|
||||
const newPromocode = {
|
||||
id: new Date().getTime(),
|
||||
name,
|
||||
endless: checkboxState,
|
||||
from: checkboxState ? "" : new Date( value1 ).getTime() +"",
|
||||
dueTo: checkboxState ? "" : new Date( value2 ).getTime() +"",
|
||||
privileges: [{
|
||||
good: service,
|
||||
discount: discount / 100
|
||||
}]
|
||||
}
|
||||
const createPromocode = (name: string, discount: number) => {
|
||||
const newPromocode = {
|
||||
id: new Date().getTime(),
|
||||
name,
|
||||
endless: checkboxState,
|
||||
from: checkboxState ? "" : new Date(value1).getTime() + "",
|
||||
dueTo: checkboxState ? "" : new Date(value2).getTime() + "",
|
||||
privileges: [{
|
||||
good: service,
|
||||
discount: discount / 100
|
||||
}]
|
||||
};
|
||||
|
||||
const promocodeArrayUpdated = [ ...promocodeArray, newPromocode ];
|
||||
promocodeArraySet( promocodeArrayUpdated );
|
||||
}
|
||||
const promocodeArrayUpdated = [...promocodeArray, newPromocode];
|
||||
promocodeArraySet(promocodeArrayUpdated);
|
||||
};
|
||||
|
||||
const fieldName = React.useRef<HTMLInputElement | null>(null);
|
||||
const fieldDiscount = React.useRef<HTMLInputElement | null>(null);
|
||||
const fieldName = React.useRef<HTMLInputElement | null>(null);
|
||||
const fieldDiscount = React.useRef<HTMLInputElement | null>(null);
|
||||
|
||||
const checkFields = () => {
|
||||
if( fieldName.current != null && fieldDiscount.current != null ) {
|
||||
createPromocode( fieldName.current.value, Number(fieldDiscount.current.value) );
|
||||
}
|
||||
}
|
||||
const checkFields = () => {
|
||||
if (fieldName.current != null && fieldDiscount.current != null) {
|
||||
createPromocode(fieldName.current.value, Number(fieldDiscount.current.value));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
sx={{
|
||||
width: "90%",
|
||||
height: "60px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
color: theme.palette.secondary.main
|
||||
}}>
|
||||
ПРОМОКОД
|
||||
</Typography>
|
||||
return (
|
||||
<React.Fragment>
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
sx={{
|
||||
width: "90%",
|
||||
height: "60px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
color: theme.palette.secondary.main
|
||||
}}>
|
||||
ПРОМОКОД
|
||||
</Typography>
|
||||
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "left",
|
||||
alignItems: "left",
|
||||
marginTop: "15px",
|
||||
}}>
|
||||
{/*<Typography */}
|
||||
{/* variant="h4" */}
|
||||
{/* sx={{*/}
|
||||
{/* width: "90%",*/}
|
||||
{/* height: "40px",*/}
|
||||
{/* fontWeight: "normal",*/}
|
||||
{/* color: theme.palette.grayDisabled.main,*/}
|
||||
{/* marginTop: "35px"*/}
|
||||
{/*}}>*/}
|
||||
{/* Название:*/}
|
||||
{/*</Typography>*/}
|
||||
<TextField
|
||||
id = "standard-basic"
|
||||
label = { "Название" }
|
||||
variant = "filled"
|
||||
color = "secondary"
|
||||
sx={{
|
||||
height: "30px",
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
} }}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
} }}
|
||||
inputRef={ fieldName }
|
||||
/>
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "left",
|
||||
alignItems: "left",
|
||||
marginTop: "15px",
|
||||
}}>
|
||||
{/*<Typography */}
|
||||
{/* variant="h4" */}
|
||||
{/* sx={{*/}
|
||||
{/* width: "90%",*/}
|
||||
{/* height: "40px",*/}
|
||||
{/* fontWeight: "normal",*/}
|
||||
{/* color: theme.palette.grayDisabled.main,*/}
|
||||
{/* marginTop: "35px"*/}
|
||||
{/*}}>*/}
|
||||
{/* Название:*/}
|
||||
{/*</Typography>*/}
|
||||
<TextField
|
||||
id="standard-basic"
|
||||
label={"Название"}
|
||||
variant="filled"
|
||||
color="secondary"
|
||||
sx={{
|
||||
height: "30px",
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
}
|
||||
}}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
}
|
||||
}}
|
||||
inputRef={fieldName}
|
||||
/>
|
||||
|
||||
<Typography
|
||||
variant="h4"
|
||||
sx={{
|
||||
width: "90%",
|
||||
height: "40px",
|
||||
fontWeight: "normal",
|
||||
color: theme.palette.grayDisabled.main,
|
||||
marginTop: "75px"
|
||||
}}>
|
||||
Условия:
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h4"
|
||||
sx={{
|
||||
width: "90%",
|
||||
height: "40px",
|
||||
fontWeight: "normal",
|
||||
color: theme.palette.grayDisabled.main,
|
||||
marginTop: "75px"
|
||||
}}>
|
||||
Условия:
|
||||
</Typography>
|
||||
|
||||
<Select
|
||||
labelId="demo-simple-select-label"
|
||||
id="demo-simple-select"
|
||||
value={service}
|
||||
label="Age"
|
||||
onChange={handleChange}
|
||||
sx={{
|
||||
color: theme.palette.secondary.main,
|
||||
border: "1px solid",
|
||||
borderColor: theme.palette.secondary.main,
|
||||
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
|
||||
borderColor: theme.palette.secondary.main
|
||||
},
|
||||
".MuiSvgIcon-root ": {
|
||||
fill: theme.palette.secondary.main,
|
||||
}
|
||||
}}
|
||||
>
|
||||
<MenuItem value={"Шаблонизатор документов"}>Шаблонизатор</MenuItem>
|
||||
<MenuItem value={"Опросник"}>Опросник</MenuItem>
|
||||
<MenuItem value={"Аналитика сокращателя"}>Аналитика сокращателя</MenuItem>
|
||||
<MenuItem value={"АБ тесты"}>АБ тесты</MenuItem>
|
||||
</Select>
|
||||
<Select
|
||||
labelId="demo-simple-select-label"
|
||||
id="demo-simple-select"
|
||||
value={service}
|
||||
label="Age"
|
||||
onChange={handleChange}
|
||||
sx={{
|
||||
color: theme.palette.secondary.main,
|
||||
border: "1px solid",
|
||||
borderColor: theme.palette.secondary.main,
|
||||
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
|
||||
borderColor: theme.palette.secondary.main
|
||||
},
|
||||
".MuiSvgIcon-root ": {
|
||||
fill: theme.palette.secondary.main,
|
||||
}
|
||||
}}
|
||||
>
|
||||
<MenuItem value={"Шаблонизатор документов"}>Шаблонизатор</MenuItem>
|
||||
<MenuItem value={"Опросник"}>Опросник</MenuItem>
|
||||
<MenuItem value={"Аналитика сокращателя"}>Аналитика сокращателя</MenuItem>
|
||||
<MenuItem value={"АБ тесты"}>АБ тесты</MenuItem>
|
||||
</Select>
|
||||
|
||||
<TextField
|
||||
id = "standard-basic"
|
||||
label = { "Процент скидки" }
|
||||
variant = "filled"
|
||||
color = "secondary"
|
||||
sx={{
|
||||
marginTop: "15px"
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
} }}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
} }}
|
||||
inputRef={ fieldDiscount }
|
||||
/>
|
||||
<TextField
|
||||
id="standard-basic"
|
||||
label={"Процент скидки"}
|
||||
variant="filled"
|
||||
color="secondary"
|
||||
sx={{
|
||||
marginTop: "15px"
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
}
|
||||
}}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
}
|
||||
}}
|
||||
inputRef={fieldDiscount}
|
||||
/>
|
||||
|
||||
<TableContainer component={Paper} sx={{
|
||||
width: "100%",
|
||||
marginTop: "35px",
|
||||
backgroundColor: theme.palette.content.main
|
||||
}}>
|
||||
<Table sx={{ minWidth: 650, }} aria-label="simple table">
|
||||
<TableBody>
|
||||
<TableRow sx={{ border: "1px solid white" }} >
|
||||
<TableCell component="th" scope="row" sx={{ color: theme.palette.secondary.main }}>
|
||||
Работает, если заплатите 100500 денег
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow sx={{ border: "1px solid white" }} >
|
||||
<TableCell component="th" scope="row" sx={{ color: theme.palette.secondary.main }}>
|
||||
Вы должны будете продать душу дьяволу
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<TableContainer component={Paper} sx={{
|
||||
width: "100%",
|
||||
marginTop: "35px",
|
||||
backgroundColor: theme.palette.content.main
|
||||
}}>
|
||||
<Table aria-label="simple table">
|
||||
<TableBody>
|
||||
<TableRow sx={{ border: "1px solid white" }} >
|
||||
<TableCell component="th" scope="row" sx={{ color: theme.palette.secondary.main }}>
|
||||
Работает, если заплатите 100500 денег
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow sx={{ border: "1px solid white" }} >
|
||||
<TableCell component="th" scope="row" sx={{ color: theme.palette.secondary.main }}>
|
||||
Вы должны будете продать душу дьяволу
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
|
||||
<Typography
|
||||
variant="h4"
|
||||
sx={{
|
||||
width: "90%",
|
||||
height: "40px",
|
||||
fontWeight: "normal",
|
||||
color: theme.palette.grayDisabled.main,
|
||||
marginTop: "55px"
|
||||
}}>
|
||||
Дата действия:
|
||||
</Typography>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexWrap: 'wrap'
|
||||
}}
|
||||
>
|
||||
<Typography sx={{
|
||||
width: "35px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "left",
|
||||
}}>С</Typography>
|
||||
<Typography
|
||||
variant="h4"
|
||||
sx={{
|
||||
width: "90%",
|
||||
height: "40px",
|
||||
fontWeight: "normal",
|
||||
color: theme.palette.grayDisabled.main,
|
||||
marginTop: "55px"
|
||||
}}>
|
||||
Дата действия:
|
||||
</Typography>
|
||||
|
||||
<DesktopDatePicker
|
||||
inputFormat="DD/MM/YYYY"
|
||||
value={ value1 }
|
||||
onChange={ (e) => { if(e) { setValue1(e) } } }
|
||||
renderInput={(params) => <TextField {...params} />}
|
||||
InputProps={{ sx: {
|
||||
height: "40px",
|
||||
color: theme.palette.secondary.main,
|
||||
border: "1px solid",
|
||||
borderColor: theme.palette.secondary.main,
|
||||
"& .MuiSvgIcon-root": { color: theme.palette.secondary.main }
|
||||
} }}
|
||||
/>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexWrap: 'wrap'
|
||||
}}
|
||||
>
|
||||
<Typography sx={{
|
||||
width: "35px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "left",
|
||||
}}>С</Typography>
|
||||
|
||||
<Typography sx={{
|
||||
width: "65px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}>по</Typography>
|
||||
<DesktopDatePicker
|
||||
inputFormat="DD/MM/YYYY"
|
||||
value={value1}
|
||||
onChange={(e) => { if (e) { setValue1(e); } }}
|
||||
renderInput={(params) => <TextField {...params} />}
|
||||
InputProps={{
|
||||
sx: {
|
||||
height: "40px",
|
||||
color: theme.palette.secondary.main,
|
||||
border: "1px solid",
|
||||
borderColor: theme.palette.secondary.main,
|
||||
"& .MuiSvgIcon-root": { color: theme.palette.secondary.main }
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<DesktopDatePicker
|
||||
inputFormat="DD/MM/YYYY"
|
||||
value={ value2 }
|
||||
onChange={ (e) => { if(e) { setValue2(e) } } }
|
||||
renderInput={(params) => <TextField {...params} />}
|
||||
InputProps={{ sx: {
|
||||
height: "40px",
|
||||
color: theme.palette.secondary.main,
|
||||
border: "1px solid",
|
||||
borderColor: theme.palette.secondary.main,
|
||||
"& .MuiSvgIcon-root": { color: theme.palette.secondary.main }
|
||||
} }}
|
||||
/>
|
||||
</Box>
|
||||
<Typography sx={{
|
||||
width: "65px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}>по</Typography>
|
||||
|
||||
<DesktopDatePicker
|
||||
inputFormat="DD/MM/YYYY"
|
||||
value={value2}
|
||||
onChange={(e) => { if (e) { setValue2(e); } }}
|
||||
renderInput={(params) => <TextField {...params} />}
|
||||
InputProps={{
|
||||
sx: {
|
||||
height: "40px",
|
||||
color: theme.palette.secondary.main,
|
||||
border: "1px solid",
|
||||
borderColor: theme.palette.secondary.main,
|
||||
"& .MuiSvgIcon-root": { color: theme.palette.secondary.main }
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
width: "90%",
|
||||
marginTop: theme.spacing(2),
|
||||
}}>
|
||||
<Box sx={{
|
||||
width: "20px",
|
||||
height: "42px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "left",
|
||||
alignItems: "left",
|
||||
marginRight: theme.spacing(1)
|
||||
}}>
|
||||
<Checkbox sx={{
|
||||
color: theme.palette.secondary.main,
|
||||
"&.Mui-checked": {
|
||||
color: theme.palette.secondary.main,
|
||||
},
|
||||
}} onClick={ () => toggleCheckbox() } />
|
||||
</Box>
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}>
|
||||
Бессрочно
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
width: "90%",
|
||||
marginTop: theme.spacing(2),
|
||||
}}>
|
||||
<Box sx={{
|
||||
width: "20px",
|
||||
height: "42px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "left",
|
||||
alignItems: "left",
|
||||
marginRight: theme.spacing(1)
|
||||
}}>
|
||||
<Checkbox sx={{
|
||||
color: theme.palette.secondary.main,
|
||||
"&.Mui-checked": {
|
||||
color: theme.palette.secondary.main,
|
||||
},
|
||||
}} onClick={() => toggleCheckbox()} />
|
||||
</Box>
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}>
|
||||
Бессрочно
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box sx={{
|
||||
width: "90%",
|
||||
marginTop: "55px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}>
|
||||
<Button
|
||||
variant = "contained"
|
||||
sx={{
|
||||
backgroundColor: theme.palette.menu.main,
|
||||
height: "52px",
|
||||
fontWeight: "normal",
|
||||
fontSize: "17px",
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.grayMedium.main
|
||||
}
|
||||
}}
|
||||
onClick={ () => checkFields() } >
|
||||
Cоздать
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
</Box>
|
||||
<Box sx={{
|
||||
width: "90%",
|
||||
marginTop: "55px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}>
|
||||
<Button
|
||||
variant="contained"
|
||||
sx={{
|
||||
backgroundColor: theme.palette.menu.main,
|
||||
height: "52px",
|
||||
fontWeight: "normal",
|
||||
fontSize: "17px",
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.grayMedium.main
|
||||
}
|
||||
}}
|
||||
onClick={() => checkFields()} >
|
||||
Cоздать
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<Box style={{ width: "80%", marginTop: "55px" }}>
|
||||
<Box style={{ height: 400 }}>
|
||||
<DataGrid
|
||||
checkboxSelection={true}
|
||||
rows={ promocodeArrayConverted }
|
||||
columns={ columns }
|
||||
sx={{
|
||||
color: theme.palette.secondary.main,
|
||||
"& .MuiDataGrid-iconSeparator": {
|
||||
display: "none"
|
||||
},
|
||||
"& .css-levciy-MuiTablePagination-displayedRows": {
|
||||
color: theme.palette.secondary.main
|
||||
},
|
||||
"& .MuiSvgIcon-root": {
|
||||
color: theme.palette.secondary.main
|
||||
},
|
||||
"& .MuiTablePagination-selectLabel": {
|
||||
color: theme.palette.secondary.main
|
||||
},
|
||||
"& .MuiInputBase-root": {
|
||||
color: theme.palette.secondary.main
|
||||
},
|
||||
"& .MuiButton-text": {
|
||||
color: theme.palette.secondary.main
|
||||
},
|
||||
}}
|
||||
components={{ Toolbar: GridToolbar }}
|
||||
onSelectionModelChange={ (ids) => console.log("datagrid select") }
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
</LocalizationProvider>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
</Box>
|
||||
|
||||
<Box style={{ width: "80%", marginTop: "55px" }}>
|
||||
<Box style={{ height: 400 }}>
|
||||
<DataGrid
|
||||
checkboxSelection={true}
|
||||
rows={promocodeArrayConverted}
|
||||
columns={columns}
|
||||
sx={{
|
||||
color: theme.palette.secondary.main,
|
||||
"& .MuiDataGrid-iconSeparator": {
|
||||
display: "none"
|
||||
},
|
||||
"& .css-levciy-MuiTablePagination-displayedRows": {
|
||||
color: theme.palette.secondary.main
|
||||
},
|
||||
"& .MuiSvgIcon-root": {
|
||||
color: theme.palette.secondary.main
|
||||
},
|
||||
"& .MuiTablePagination-selectLabel": {
|
||||
color: theme.palette.secondary.main
|
||||
},
|
||||
"& .MuiInputBase-root": {
|
||||
color: theme.palette.secondary.main
|
||||
},
|
||||
"& .MuiButton-text": {
|
||||
color: theme.palette.secondary.main
|
||||
},
|
||||
}}
|
||||
components={{ Toolbar: GridToolbar }}
|
||||
onSelectionModelChange={(ids) => console.log("datagrid select")}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
</LocalizationProvider>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export default Promocode;
|
||||
export default Promocodes;
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
export interface PrivilegesProps {
|
||||
good: string,
|
||||
discount: number
|
||||
}
|
||||
|
||||
export interface PromocodeProps {
|
||||
id: number
|
||||
name: string
|
||||
endless: boolean
|
||||
from: string
|
||||
dueTo: string
|
||||
privileges: Array<PrivilegesProps>
|
||||
}
|
||||
235
src/pages/dashboard/Content/Tariffs/Cart.tsx
Normal file
235
src/pages/dashboard/Content/Tariffs/Cart.tsx
Normal file
@ -0,0 +1,235 @@
|
||||
import { Avatar, Box, Checkbox, FormControlLabel, IconButton, List, ListItem, ListItemAvatar, ListItemText, TextField, Typography, useTheme } from "@mui/material";
|
||||
import DeleteIcon from "@mui/icons-material/Delete";
|
||||
import { useMemo, useState } from "react";
|
||||
import { calcFitDiscounts, calcTotalAndRowData, separator } from "./utils";
|
||||
import type { CartSummary, Promocode } from "../../../../model/cart";
|
||||
import ShoppingCartIcon from "@mui/icons-material/ShoppingCart";
|
||||
import { useDiscountStore } from "../../../../stores/discounts";
|
||||
import { useCartStore } from "../../../../stores/cart";
|
||||
|
||||
|
||||
interface Props {
|
||||
promocode?: Promocode;
|
||||
}
|
||||
|
||||
export default function Cart({ promocode }: Props) {
|
||||
const theme = useTheme();
|
||||
const discountsArray = useDiscountStore(state => state.discountsArray);
|
||||
const discountsActiveArray = useDiscountStore(state => state.discountsActiveArray);
|
||||
const cartRowsData = useCartStore(state => state.cartRowsData);
|
||||
const cartRowsDataSet = useCartStore(state => state.setCartRowsData);
|
||||
const [isNonCommercial, setIsNonCommercial] = useState(false);
|
||||
const [addedValueField, setAddedValueField] = useState("");
|
||||
|
||||
const cartSummary = useMemo(() => {
|
||||
const cartSum: { [key: string]: CartSummary; } = {};
|
||||
cartRowsData.forEach((row) => {
|
||||
const prev = cartSum[row.service];
|
||||
cartSum[row.service] = {
|
||||
mbs: row.disk + (prev?.mbs || 0),
|
||||
points: row.points + (prev?.points || 0),
|
||||
days: row.time + (prev?.days || 0),
|
||||
};
|
||||
});
|
||||
return cartSum;
|
||||
}, [cartRowsData]);
|
||||
|
||||
const fitDiscounts = useMemo(() =>
|
||||
calcFitDiscounts(discountsArray, discountsActiveArray, cartSummary, addedValueField),
|
||||
[addedValueField, cartSummary, discountsActiveArray, discountsArray]
|
||||
);
|
||||
|
||||
const { totalPrice, calculatedCartRowData } = useMemo(() => calcTotalAndRowData(
|
||||
cartRowsData,
|
||||
isNonCommercial,
|
||||
discountsArray,
|
||||
discountsActiveArray,
|
||||
fitDiscounts,
|
||||
addedValueField,
|
||||
cartSummary,
|
||||
promocode,
|
||||
), [addedValueField, cartRowsData, cartSummary, discountsActiveArray, discountsArray, fitDiscounts, isNonCommercial, promocode]);
|
||||
|
||||
const { resultPrice, discountText } = useMemo(() => {
|
||||
const discounts: Array<number> = [];
|
||||
|
||||
let resultPrice = totalPrice;
|
||||
|
||||
if (isNonCommercial) {
|
||||
resultPrice *= 0.2;
|
||||
return { resultPrice, discountText: `80%` };
|
||||
}
|
||||
|
||||
// применяем активные скидки за объем корзины
|
||||
if (fitDiscounts.length >= 0 && !isNonCommercial && !promocode) {
|
||||
fitDiscounts.forEach(activeDiscount => {
|
||||
const discount = discountsArray[activeDiscount];
|
||||
if ((discount.basketMore > 0 && totalPrice > discount.basketMore && discount.basketMore === fitDiscounts.reduce((a, e) => Math.max(a, discountsArray[e].basketMore), 0)) ||
|
||||
(discount.incomeMore > 0 && parseInt(addedValueField) > discount.incomeMore)) {
|
||||
resultPrice *= (1 - discount.privileges[0].discount);
|
||||
discounts.push(discount.privileges[0].discount);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const discountText = discounts.map(e => `${(e * 100).toFixed(2)}%`).join(' × ') + ` = ${(100 - discounts.reduce((a: number, cv: number) => a * (1 - cv), 100)).toFixed(2)}%`;
|
||||
|
||||
return {
|
||||
resultPrice,
|
||||
discountText,
|
||||
};
|
||||
}, [addedValueField, discountsArray, fitDiscounts, isNonCommercial, promocode, totalPrice]);
|
||||
|
||||
const handleRemoveBasket = (id: number) => cartRowsDataSet(cartRowsData.filter((row) => row.id !== id));
|
||||
|
||||
return (
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
paddingBottom: "45px"
|
||||
}}>
|
||||
<Typography id="transition-modal-title" variant="caption">
|
||||
Корзина
|
||||
</Typography>
|
||||
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
marginTop: "15px",
|
||||
marginBottom: "15px",
|
||||
maxWidth: "350px",
|
||||
width: '100%',
|
||||
justifyContent: "space-between"
|
||||
}}>
|
||||
<FormControlLabel
|
||||
label="НКО"
|
||||
control={<Checkbox
|
||||
sx={{
|
||||
color: theme.palette.secondary.main,
|
||||
"&.Mui-checked": {
|
||||
color: theme.palette.secondary.main,
|
||||
},
|
||||
}}
|
||||
onClick={() => setIsNonCommercial(prev => !prev)}
|
||||
/>}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
id="standard-basic"
|
||||
label={"Внесено"}
|
||||
variant="filled"
|
||||
size="small"
|
||||
color="secondary"
|
||||
type="number"
|
||||
sx={{
|
||||
width: "200px"
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
}
|
||||
}}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
}
|
||||
}}
|
||||
value={addedValueField}
|
||||
onChange={e => setAddedValueField(Number(e.target.value) >= 0 ? e.target.value : "")}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<List sx={{
|
||||
border: "1px solid",
|
||||
borderColor: theme.palette.secondary.main,
|
||||
maxWidth: '745px',
|
||||
width: '100%',
|
||||
}}>
|
||||
<ListItem>
|
||||
<ListItemText
|
||||
primary="Название"
|
||||
sx={{
|
||||
textAlign: "center",
|
||||
// minWidth: "250px",
|
||||
maxWidth: "250px",
|
||||
padding: '0 10px'
|
||||
}}
|
||||
/>
|
||||
<ListItemText
|
||||
primary="Цена"
|
||||
sx={{
|
||||
textAlign: "center",
|
||||
// minWidth: "200px",
|
||||
padding: '0 10px',
|
||||
maxWidth: "200px"
|
||||
}}
|
||||
/>
|
||||
<ListItemText
|
||||
primary="Скидки"
|
||||
sx={{
|
||||
textAlign: "center",
|
||||
// minWidth: "200px",
|
||||
padding: '0 10px',
|
||||
maxWidth: "400px"
|
||||
}}
|
||||
/>
|
||||
<IconButton edge="end" aria-label="delete">
|
||||
<DeleteIcon sx={{
|
||||
color: theme.palette.grayDisabled.main,
|
||||
display: "none"
|
||||
}} />
|
||||
</IconButton>
|
||||
</ListItem>
|
||||
|
||||
{calculatedCartRowData.map((cartRow) => (
|
||||
<ListItem key={cartRow.id}>
|
||||
<ListItemAvatar>
|
||||
<Avatar sx={{ backgroundColor: theme.palette.secondary.main }}>
|
||||
<ShoppingCartIcon sx={{
|
||||
color: theme.palette.content.main,
|
||||
}} />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={cartRow.name}
|
||||
sx={{ maxWidth: "250px" }}
|
||||
/>
|
||||
<ListItemText
|
||||
primary={`${separator(cartRow.price)} ₽`}
|
||||
sx={{ textAlign: "center", maxWidth: "200px" }}
|
||||
/>
|
||||
<ListItemText
|
||||
primary={`${(cartRow.appliedDiscounts.map(e => (e * 100).toFixed(2)).join(' × '))} = ${(100 - cartRow.appliedDiscounts.reduce((a: number, cv: number) => a * (1 - cv), 100)).toFixed(2)}%`}
|
||||
sx={{ textAlign: "center", maxWidth: "400px" }}
|
||||
/>
|
||||
<IconButton edge="end" aria-label="delete" onClick={() => handleRemoveBasket(cartRow.id)}>
|
||||
<DeleteIcon sx={{
|
||||
color: theme.palette.secondary.main,
|
||||
}} />
|
||||
</IconButton>
|
||||
</ListItem>
|
||||
))}
|
||||
|
||||
<Typography id="transition-modal-title" variant="h6" sx={{
|
||||
fontWeight: "normal",
|
||||
textAlign: "center",
|
||||
marginTop: "15px",
|
||||
fontSize: "16px"
|
||||
}}>
|
||||
Скидки:   {discountText}
|
||||
</Typography>
|
||||
|
||||
<Typography id="transition-modal-title" variant="h6" sx={{
|
||||
fontWeight: "normal",
|
||||
textAlign: "center",
|
||||
marginTop: "10px"
|
||||
}}>
|
||||
ИТОГО:   {resultPrice} ₽
|
||||
</Typography>
|
||||
|
||||
</List>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@ -1,81 +0,0 @@
|
||||
import * as React from "react";
|
||||
import { Box, Typography, Button } from "@mui/material";
|
||||
import theme from "../../../../../theme";
|
||||
|
||||
|
||||
export interface MWProps {
|
||||
openModal: (type:number, num: number) => void
|
||||
}
|
||||
|
||||
const Contractor: React.FC<MWProps> = ({ openModal }) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
sx={{
|
||||
width: "90%",
|
||||
height: "60px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
color: theme.palette.secondary.main
|
||||
}}>
|
||||
Сокращатель ссылок
|
||||
</Typography>
|
||||
|
||||
<Box sx={{
|
||||
marginTop: "35px",
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(2, 1fr)",
|
||||
gridGap: "20px",
|
||||
marginBottom: "120px",
|
||||
}}>
|
||||
<Button
|
||||
variant = "contained"
|
||||
onClick={ () => openModal(3, 1) }
|
||||
sx={{
|
||||
backgroundColor: theme.palette.menu.main,
|
||||
padding: '11px 65px',
|
||||
fontWeight: "normal",
|
||||
fontSize: "17px",
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.grayMedium.main
|
||||
}
|
||||
}}>
|
||||
Создать тариф <br /> на аналитику время
|
||||
</Button>
|
||||
<Button
|
||||
variant = "contained"
|
||||
onClick={ () => openModal(3, 1) }
|
||||
sx={{
|
||||
backgroundColor: theme.palette.menu.main,
|
||||
padding: '11px 65px',
|
||||
fontWeight: "normal",
|
||||
fontSize: "17px",
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.grayMedium.main
|
||||
}
|
||||
}}>
|
||||
Создать тариф <br /> на a/b тесты время
|
||||
</Button>
|
||||
<Button
|
||||
variant = "contained"
|
||||
sx={{
|
||||
backgroundColor: theme.palette.menu.main,
|
||||
padding: '11px 65px',
|
||||
fontWeight: "normal",
|
||||
fontSize: "17px",
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.grayMedium.main
|
||||
}
|
||||
}}>
|
||||
Изменить тариф
|
||||
</Button>
|
||||
</Box>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export default Contractor;
|
||||
32
src/pages/dashboard/Content/Tariffs/CustomButton.tsx
Normal file
32
src/pages/dashboard/Content/Tariffs/CustomButton.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import { Button, SxProps, Theme, useTheme } from "@mui/material";
|
||||
import { MouseEventHandler, ReactNode } from "react";
|
||||
|
||||
|
||||
interface Props {
|
||||
onClick?: MouseEventHandler<HTMLButtonElement>;
|
||||
children: ReactNode;
|
||||
sx?: SxProps<Theme>;
|
||||
}
|
||||
|
||||
export default function CustomButton({ onClick, children, sx }: Props) {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={onClick}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.menu.main,
|
||||
padding: '11px 25px',
|
||||
fontWeight: "normal",
|
||||
fontSize: "17px",
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.grayMedium.main
|
||||
},
|
||||
...sx,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
28
src/pages/dashboard/Content/Tariffs/CustomHeader.tsx
Normal file
28
src/pages/dashboard/Content/Tariffs/CustomHeader.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import { Typography, useTheme } from "@mui/material";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export default function CustomHeader({ children }: Props) {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
sx={{
|
||||
width: "90%",
|
||||
height: "60px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
color: theme.palette.secondary.main
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,185 +1,187 @@
|
||||
import * as React from "react";
|
||||
import { Box, Modal, Fade, Backdrop, Typography, Button, TextField } from "@mui/material";
|
||||
import { ArrayProps } from "../types";
|
||||
import useStore, { StoreState } from "../../../../../store";
|
||||
import { Box, Modal, Fade, Backdrop, Button, TextField } from "@mui/material";
|
||||
import theme from "../../../../../theme";
|
||||
import { ArrayProps } from "../../../../../model/tariff";
|
||||
import { useTariffStore } from "../../../../../stores/tariffs";
|
||||
|
||||
|
||||
export interface MWProps {
|
||||
open: boolean
|
||||
type: number
|
||||
variant: number
|
||||
close: () => void
|
||||
open: boolean;
|
||||
type: number;
|
||||
variant: number;
|
||||
close: () => void;
|
||||
}
|
||||
|
||||
const ModalMini = ({open, type, variant, close}: MWProps ) => {
|
||||
let tariffsArray:Array<ArrayProps> = useStore((state) => state.tariffsArray);
|
||||
const { tariffsArraySet } = useStore<StoreState>((state) => state);
|
||||
const ModalMini = ({ open, type, variant, close }: MWProps) => {
|
||||
let tariffsArray = useTariffStore(state => state.tariffs);
|
||||
const tariffsArraySet = useTariffStore(state => state.setTariffs);
|
||||
|
||||
const types = [ "", "Шаблонизатор документов", "Опросник", "Сокращатель ссылок" ];
|
||||
const variants = [ "Количество", "Срок (дней)", "Количество (гб)" ];
|
||||
const types = ["", "Шаблонизатор документов", "Опросник", "Сокращатель ссылок"];
|
||||
const variants = ["Количество", "Срок (дней)", "Количество (гб)"];
|
||||
|
||||
const fieldName = React.useRef<HTMLInputElement | null>(null);
|
||||
const fieldTime = React.useRef<HTMLInputElement | null>(null);
|
||||
const fieldPrice = React.useRef<HTMLInputElement | null>(null);
|
||||
const fieldName = React.useRef<HTMLInputElement | null>(null);
|
||||
const fieldTime = React.useRef<HTMLInputElement | null>(null);
|
||||
const fieldPrice = React.useRef<HTMLInputElement | null>(null);
|
||||
|
||||
const checkTariff = () => {
|
||||
if( fieldName.current != null && fieldTime.current != null && fieldPrice.current != null ) {
|
||||
if( fieldName.current.value && fieldTime.current.value && fieldPrice.current.value ) {
|
||||
const getData = localStorage.getItem("tariffs");
|
||||
|
||||
if( getData != null ) { tariffsArray = JSON.parse(getData); }
|
||||
const checkTariff = () => {
|
||||
if (fieldName.current != null && fieldTime.current != null && fieldPrice.current != null) {
|
||||
if (fieldName.current.value && fieldTime.current.value && fieldPrice.current.value) {
|
||||
|
||||
const data = [ 0, 0, 0 ];
|
||||
const data = [0, 0, 0];
|
||||
|
||||
if( variant == 0 ) { data[ 0 ] = parseInt(fieldTime.current.value); }
|
||||
if( variant == 1 ) { data[ 1 ] = parseInt(fieldTime.current.value); }
|
||||
if( variant == 2 ) { data[ 2 ] = parseInt(fieldTime.current.value); }
|
||||
if (variant === 0) { data[0] = parseInt(fieldTime.current.value); }
|
||||
if (variant === 1) { data[1] = parseInt(fieldTime.current.value); }
|
||||
if (variant === 2) { data[2] = parseInt(fieldTime.current.value); }
|
||||
|
||||
const tariffsArrayNew = [...tariffsArray, {
|
||||
"id": new Date().getTime(),
|
||||
"name": fieldName.current.value,
|
||||
"type": "tariff",
|
||||
"service": types[ type ],
|
||||
"disk": data[ 2 ],
|
||||
"time": data[ 1 ],
|
||||
"points": data[ 0 ],
|
||||
"price": +fieldPrice.current.value
|
||||
} ];
|
||||
const tariffsArrayNew = [...tariffsArray, {
|
||||
"id": new Date().getTime(),
|
||||
"name": fieldName.current.value,
|
||||
"type": "tariff",
|
||||
"service": types[type],
|
||||
"disk": data[2],
|
||||
"time": data[1],
|
||||
"points": data[0],
|
||||
"price": +fieldPrice.current.value
|
||||
} as ArrayProps];
|
||||
|
||||
tariffsArraySet( tariffsArrayNew );
|
||||
|
||||
localStorage.setItem( "tariffs", JSON.stringify( tariffsArrayNew ) );
|
||||
close();
|
||||
tariffsArraySet(tariffsArrayNew);
|
||||
|
||||
console.log( tariffsArrayNew );
|
||||
}
|
||||
}
|
||||
}
|
||||
close();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Modal
|
||||
aria-labelledby="transition-modal-title"
|
||||
aria-describedby="transition-modal-description"
|
||||
open={ open }
|
||||
onClose={ () => close() }
|
||||
closeAfterTransition
|
||||
BackdropComponent={Backdrop}
|
||||
BackdropProps={{
|
||||
timeout: 500,
|
||||
}}
|
||||
>
|
||||
<Fade in={open}>
|
||||
<Box sx={{
|
||||
position: "absolute" as "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
width: "350px",
|
||||
height: "350px",
|
||||
bgcolor: theme.palette.menu.main,
|
||||
boxShadow: 24,
|
||||
color: theme.palette.secondary.main,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}>
|
||||
<TextField
|
||||
id = "standard-basic"
|
||||
label = { types[ type ] }
|
||||
disabled={ true }
|
||||
variant = "filled"
|
||||
color = "secondary"
|
||||
sx = {{ width: "80%", marginTop: theme.spacing(1) }}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
} }}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
} }}
|
||||
/>
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Modal
|
||||
aria-labelledby="transition-modal-title"
|
||||
aria-describedby="transition-modal-description"
|
||||
open={open}
|
||||
onClose={() => close()}
|
||||
closeAfterTransition
|
||||
BackdropComponent={Backdrop}
|
||||
BackdropProps={{
|
||||
timeout: 500,
|
||||
}}
|
||||
>
|
||||
<Fade in={open}>
|
||||
<Box sx={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
width: "350px",
|
||||
height: "350px",
|
||||
bgcolor: theme.palette.menu.main,
|
||||
boxShadow: 24,
|
||||
color: theme.palette.secondary.main,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}>
|
||||
<TextField
|
||||
id="standard-basic"
|
||||
label={types[type]}
|
||||
disabled={true}
|
||||
variant="filled"
|
||||
color="secondary"
|
||||
sx={{ width: "80%", marginTop: theme.spacing(1) }}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
}
|
||||
}}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
id = "standard-basic"
|
||||
label = { "Название тарифа" }
|
||||
variant = "filled"
|
||||
color = "secondary"
|
||||
sx = {{ width: "80%", marginTop: theme.spacing(1) }}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
} }}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
} }}
|
||||
inputRef={ fieldName }
|
||||
/>
|
||||
<TextField
|
||||
id="standard-basic"
|
||||
label={"Название тарифа"}
|
||||
variant="filled"
|
||||
color="secondary"
|
||||
sx={{ width: "80%", marginTop: theme.spacing(1) }}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
}
|
||||
}}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
}
|
||||
}}
|
||||
inputRef={fieldName}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
id = "standard-basic"
|
||||
label = { variants[ variant ] }
|
||||
variant = "filled"
|
||||
color = "secondary"
|
||||
sx = {{ width: "80%", marginTop: theme.spacing(1) }}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
} }}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
} }}
|
||||
inputRef={ fieldTime }
|
||||
/>
|
||||
<TextField
|
||||
id="standard-basic"
|
||||
label={variants[variant]}
|
||||
variant="filled"
|
||||
color="secondary"
|
||||
sx={{ width: "80%", marginTop: theme.spacing(1) }}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
}
|
||||
}}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
}
|
||||
}}
|
||||
inputRef={fieldTime}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
id = "standard-basic"
|
||||
label = "Цена"
|
||||
variant = "filled"
|
||||
color = "secondary"
|
||||
sx = {{ width: "80%", marginTop: theme.spacing(1) }}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
} }}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
} }}
|
||||
inputRef={ fieldPrice }
|
||||
/>
|
||||
<TextField
|
||||
id="standard-basic"
|
||||
label="Цена"
|
||||
variant="filled"
|
||||
color="secondary"
|
||||
sx={{ width: "80%", marginTop: theme.spacing(1) }}
|
||||
InputProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.content.main,
|
||||
color: theme.palette.secondary.main,
|
||||
}
|
||||
}}
|
||||
InputLabelProps={{
|
||||
style: {
|
||||
color: theme.palette.secondary.main
|
||||
}
|
||||
}}
|
||||
inputRef={fieldPrice}
|
||||
/>
|
||||
|
||||
|
||||
<Button
|
||||
variant = "contained"
|
||||
onClick={ () => checkTariff() }
|
||||
sx={{
|
||||
backgroundColor: theme.palette.grayDark.main,
|
||||
marginTop: "30px",
|
||||
height: "42px",
|
||||
fontWeight: "normal",
|
||||
fontSize: "17px",
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.grayMedium.main
|
||||
}
|
||||
}}>
|
||||
Применить
|
||||
</Button>
|
||||
</Box>
|
||||
</Fade>
|
||||
</Modal>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => checkTariff()}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.grayDark.main,
|
||||
marginTop: "30px",
|
||||
height: "42px",
|
||||
fontWeight: "normal",
|
||||
fontSize: "17px",
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.grayMedium.main
|
||||
}
|
||||
}}>
|
||||
Применить
|
||||
</Button>
|
||||
</Box>
|
||||
</Fade>
|
||||
</Modal>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export default ModalMini;
|
||||
|
||||
@ -1,81 +0,0 @@
|
||||
import * as React from "react";
|
||||
import { Box, Typography, Button } from "@mui/material";
|
||||
import theme from "../../../../../theme";
|
||||
|
||||
|
||||
export interface MWProps {
|
||||
openModal: (type:number, num: number) => void
|
||||
}
|
||||
|
||||
const Quiz: React.FC<MWProps> = ({ openModal }) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
sx={{
|
||||
width: "90%",
|
||||
height: "60px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
color: theme.palette.secondary.main
|
||||
}}>
|
||||
Опросник
|
||||
</Typography>
|
||||
|
||||
<Box sx={{
|
||||
marginTop: "35px",
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(2, 1fr)",
|
||||
gridGap: "20px",
|
||||
marginBottom: "120px",
|
||||
}}>
|
||||
<Button
|
||||
variant = "contained"
|
||||
onClick={ () => openModal(2, 1) }
|
||||
sx={{
|
||||
backgroundColor: theme.palette.menu.main,
|
||||
padding: "11px 43px",
|
||||
fontWeight: "normal",
|
||||
fontSize: "17px",
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.grayMedium.main
|
||||
}
|
||||
}}>
|
||||
Создать тариф на время
|
||||
</Button>
|
||||
<Button
|
||||
variant = "contained"
|
||||
onClick={ () => openModal(2, 0) }
|
||||
sx={{
|
||||
backgroundColor: theme.palette.menu.main,
|
||||
padding: '11px 43px',
|
||||
fontWeight: "normal",
|
||||
fontSize: "17px",
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.grayMedium.main
|
||||
}
|
||||
}}>
|
||||
Создать тариф на объем
|
||||
</Button>
|
||||
<Button
|
||||
variant = "contained"
|
||||
sx={{
|
||||
backgroundColor: theme.palette.menu.main,
|
||||
padding: '11px 43px',
|
||||
fontWeight: "normal",
|
||||
fontSize: "17px",
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.grayMedium.main
|
||||
}
|
||||
}}>
|
||||
Изменить тариф
|
||||
</Button>
|
||||
</Box>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export default Quiz;
|
||||
@ -1,95 +0,0 @@
|
||||
import * as React from "react";
|
||||
import { Box, Typography, Button } from "@mui/material";
|
||||
import theme from "../../../../../theme";
|
||||
|
||||
|
||||
export interface MWProps {
|
||||
openModal: (type:number, num: number) => void
|
||||
}
|
||||
|
||||
const Templater: React.FC<MWProps> = ({ openModal }) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
sx={{
|
||||
width: "90%",
|
||||
height: "60px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
color: theme.palette.secondary.main
|
||||
}}>
|
||||
Шаблонизатор документов
|
||||
</Typography>
|
||||
|
||||
<Box sx={{
|
||||
marginTop: "35px",
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(2, 1fr)",
|
||||
gridGap: "20px",
|
||||
marginBottom: "120px",
|
||||
}}>
|
||||
<Button
|
||||
variant = "contained"
|
||||
onClick={ () => openModal(1, 1) }
|
||||
sx={{
|
||||
backgroundColor: theme.palette.menu.main,
|
||||
fontWeight: "normal",
|
||||
fontSize: "17px",
|
||||
padding: '11px 25px',
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.grayMedium.main
|
||||
}
|
||||
}}>
|
||||
Создать тариф на время
|
||||
</Button>
|
||||
<Button
|
||||
variant = "contained"
|
||||
onClick={ () => openModal(1, 0) }
|
||||
sx={{
|
||||
backgroundColor: theme.palette.menu.main,
|
||||
padding: '11px 25px',
|
||||
fontWeight: "normal",
|
||||
fontSize: "17px",
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.grayMedium.main
|
||||
}
|
||||
}}>
|
||||
Создать тариф на объем
|
||||
</Button>
|
||||
<Button
|
||||
variant = "contained"
|
||||
onClick={ () => openModal(1, 2) }
|
||||
sx={{
|
||||
backgroundColor: theme.palette.menu.main,
|
||||
padding: '11px 25px',
|
||||
fontWeight: "normal",
|
||||
fontSize: "17px",
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.grayMedium.main
|
||||
}
|
||||
}}>
|
||||
Создать тариф на гигабайты
|
||||
</Button>
|
||||
<Button
|
||||
variant = "contained"
|
||||
sx={{
|
||||
backgroundColor: theme.palette.menu.main,
|
||||
padding: '11px 25px',
|
||||
fontWeight: "normal",
|
||||
fontSize: "17px",
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.grayMedium.main
|
||||
}
|
||||
}}>
|
||||
Изменить тариф
|
||||
</Button>
|
||||
</Box>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export default Templater;
|
||||
@ -1,97 +1,127 @@
|
||||
import * as React from "react";
|
||||
import { Box } from "@mui/material";
|
||||
import Templater from "./Templater";
|
||||
import Quiz from "./Quiz";
|
||||
import Contractor from "./Contractor";
|
||||
import DataGridElement from "./DataGridElement";
|
||||
import ModalMini from "./ModalMini";
|
||||
import ModalPackage from "./ModalPackage";
|
||||
import { ArrayProps, Tariff } from "./types";
|
||||
import useStore from "../../../../store";
|
||||
import theme from "../../../../theme";
|
||||
import { ReactNode } from "react";
|
||||
import CustomButton from "./CustomButton";
|
||||
import CustomHeader from "./CustomHeader";
|
||||
import { ArrayProps, Tariff } from "../../../../model/tariff";
|
||||
import { useTariffStore } from "../../../../stores/tariffs";
|
||||
|
||||
|
||||
const Tariffs: React.FC = () => {
|
||||
const [openModalMini, setOpenModalMini] = React.useState(false);
|
||||
const ButtonContainer: React.FC<{ children: ReactNode; }> = ({ children }) => {
|
||||
return <Box sx={{
|
||||
marginTop: "35px",
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(2, 1fr)",
|
||||
gridGap: "20px",
|
||||
marginBottom: "120px",
|
||||
}}>
|
||||
{children}
|
||||
</Box>;
|
||||
};
|
||||
|
||||
const handleOpenModalMini = () => { setOpenModalMini(true); };
|
||||
const handleCloseModalMini = () => { setOpenModalMini(false); };
|
||||
const Tariffs: React.FC = () => {
|
||||
const [openModalMini, setOpenModalMini] = React.useState(false);
|
||||
|
||||
const [type, setType] = React.useState( 100 );
|
||||
const [variant, setVariant] = React.useState( 100 );
|
||||
const setUpModalMini = (type:number, num:number) => {
|
||||
setType( type );
|
||||
setVariant( num );
|
||||
handleOpenModalMini();
|
||||
}
|
||||
const handleOpenModalMini = () => { setOpenModalMini(true); };
|
||||
const handleCloseModalMini = () => { setOpenModalMini(false); };
|
||||
|
||||
const getData = localStorage.getItem("tariffs");
|
||||
const store = useStore( (state) => state );
|
||||
const [type, setType] = React.useState(100);
|
||||
const [variant, setVariant] = React.useState(100);
|
||||
const setUpModalMini = (type: number, num: number) => {
|
||||
setType(type);
|
||||
setVariant(num);
|
||||
handleOpenModalMini();
|
||||
};
|
||||
|
||||
if( getData && !store.tariffsArray.length ) {
|
||||
const rows:Array<ArrayProps> = JSON.parse(getData);
|
||||
if( rows.length ) { store.tariffsArraySet( rows ); };
|
||||
}
|
||||
const tariffsArray = useTariffStore(state => state.tariffs);
|
||||
const tariffsArraySet = useTariffStore(state => state.setTariffs);
|
||||
const tariffsSelectedRowsData = useTariffStore(state => state.tariffsSelectedRowsData);
|
||||
|
||||
const [openModalPackage, setOpenModalPackage] = React.useState(false);
|
||||
|
||||
|
||||
const handleOpenModalPackage = () => { setOpenModalPackage(true); };
|
||||
const handleCloseModalPackage = () => { setOpenModalPackage(false); };
|
||||
const [openModalPackage, setOpenModalPackage] = React.useState(false);
|
||||
|
||||
const newPackage = ( name:string ) => {
|
||||
const tariffs:Array<Tariff> = [];
|
||||
const handleOpenModalPackage = () => { setOpenModalPackage(true); };
|
||||
const handleCloseModalPackage = () => { setOpenModalPackage(false); };
|
||||
|
||||
store.tariffsSelectedRowsData.forEach( (item) => {
|
||||
if( item.type === "package" && item.tariffs ) {
|
||||
tariffs.push( ...item.tariffs );
|
||||
} else {
|
||||
tariffs.push( item );
|
||||
}
|
||||
} );
|
||||
const newPackage = (name: string) => {
|
||||
const tariffs: Array<Tariff> = [];
|
||||
|
||||
const uniqueArray:Array<Tariff> = [];
|
||||
tariffs.forEach( (tariff) => {
|
||||
if( uniqueArray.findIndex( (a) => a.id === tariff.id ) < 0 ) {
|
||||
uniqueArray.push( tariff );
|
||||
}
|
||||
} );
|
||||
tariffsSelectedRowsData.forEach((item) => {
|
||||
if (item.type === "package" && item.tariffs) {
|
||||
tariffs.push(...item.tariffs);
|
||||
} else {
|
||||
tariffs.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
const packageCreated:ArrayProps = {
|
||||
name,
|
||||
id: new Date().getTime(),
|
||||
type: "package",
|
||||
tariffs: uniqueArray,
|
||||
service: "",
|
||||
disk: 0,
|
||||
time: 0,
|
||||
points: 0,
|
||||
price: 0
|
||||
}
|
||||
const uniqueArray: Array<Tariff> = [];
|
||||
tariffs.forEach((tariff) => {
|
||||
if (uniqueArray.findIndex((a) => a.id === tariff.id) < 0) {
|
||||
uniqueArray.push(tariff);
|
||||
}
|
||||
});
|
||||
|
||||
store.tariffsArraySet( [...store.tariffsArray, packageCreated] );
|
||||
handleCloseModalPackage();
|
||||
}
|
||||
const packageCreated: ArrayProps = {
|
||||
name,
|
||||
id: new Date().getTime(),
|
||||
type: "package",
|
||||
tariffs: uniqueArray,
|
||||
service: "",
|
||||
disk: 0,
|
||||
time: 0,
|
||||
points: 0,
|
||||
price: 0
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Box sx={{
|
||||
width: "90%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}>
|
||||
<Templater openModal={ setUpModalMini } />
|
||||
<Quiz openModal={ setUpModalMini } />
|
||||
<Contractor openModal={ setUpModalMini } />
|
||||
<DataGridElement openModal={ handleOpenModalPackage } />
|
||||
</Box>
|
||||
tariffsArraySet([...tariffsArray, packageCreated]);
|
||||
handleCloseModalPackage();
|
||||
};
|
||||
|
||||
<ModalMini open={ openModalMini } type={ type } variant={ variant } close={ handleCloseModalMini } />
|
||||
<ModalPackage open={ openModalPackage } newPackage={ newPackage } close={ handleCloseModalPackage } />
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Box sx={{
|
||||
width: "90%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}>
|
||||
<CustomHeader>Шаблонизатор документов</CustomHeader>
|
||||
<ButtonContainer>
|
||||
<CustomButton onClick={() => setUpModalMini(1, 1)}>Создать тариф на время</CustomButton>
|
||||
<CustomButton onClick={() => setUpModalMini(1, 0)}>Создать тариф на объем</CustomButton>
|
||||
<CustomButton onClick={() => setUpModalMini(1, 2)}>Создать тариф на гигабайты</CustomButton>
|
||||
<CustomButton>Изменить тариф</CustomButton>
|
||||
</ButtonContainer>
|
||||
<CustomHeader>Опросник</CustomHeader>
|
||||
<ButtonContainer>
|
||||
<CustomButton sx={{ padding: '11px 43px' }} onClick={() => setUpModalMini(2, 1)}>Создать тариф на время</CustomButton>
|
||||
<CustomButton sx={{ padding: '11px 43px' }} onClick={() => setUpModalMini(2, 0)}>Создать тариф на объем</CustomButton>
|
||||
<CustomButton sx={{ padding: '11px 43px' }}>Изменить тариф </CustomButton>
|
||||
</ButtonContainer>
|
||||
<CustomHeader>Сокращатель ссылок</CustomHeader>
|
||||
<ButtonContainer>
|
||||
<CustomButton onClick={() => setUpModalMini(3, 1)} sx={{ padding: '11px 65px' }}>
|
||||
Создать тариф <br /> на аналитику время
|
||||
</CustomButton>
|
||||
<CustomButton onClick={() => setUpModalMini(3, 1)} sx={{ padding: '11px 65px' }}>
|
||||
Создать тариф <br /> на a/b тесты время
|
||||
</CustomButton>
|
||||
<CustomButton sx={{ padding: '11px 65px' }}>
|
||||
Изменить тариф
|
||||
</CustomButton>
|
||||
</ButtonContainer>
|
||||
<DataGridElement openModal={handleOpenModalPackage} />
|
||||
</Box>
|
||||
<ModalMini open={openModalMini} type={type} variant={variant} close={handleCloseModalMini} />
|
||||
<ModalPackage open={openModalPackage} newPackage={newPackage} close={handleCloseModalPackage} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export default Tariffs;
|
||||
|
||||
@ -1,28 +0,0 @@
|
||||
export interface Tariff {
|
||||
id: number
|
||||
name: string
|
||||
type: string
|
||||
service: string
|
||||
disk: number
|
||||
time: number
|
||||
points: number
|
||||
price: number
|
||||
}
|
||||
|
||||
export interface ArrayProps {
|
||||
id: number
|
||||
name: string
|
||||
type: string
|
||||
service: string
|
||||
disk: number
|
||||
time: number
|
||||
points: number
|
||||
price: number
|
||||
tariffs?: Array<Tariff>
|
||||
}
|
||||
|
||||
export interface CartSummary {
|
||||
mbs: number
|
||||
points: number
|
||||
days: number
|
||||
}
|
||||
255
src/pages/dashboard/Content/Tariffs/utils.ts
Normal file
255
src/pages/dashboard/Content/Tariffs/utils.ts
Normal file
@ -0,0 +1,255 @@
|
||||
import { CartSummary, Discount, Promocode } from "../../../../model/cart";
|
||||
import { ArrayProps, Tariff } from "../../../../model/tariff";
|
||||
|
||||
|
||||
export function calcFitDiscounts(discountsArray: Discount[], discountsActiveArray: number[], cartSummary: { [key: string]: CartSummary; }, fieldAddedValue: string) {
|
||||
const result = discountsActiveArray.filter(e => {
|
||||
const discount = discountsArray[e];
|
||||
const summary = cartSummary[discount.privileges[0].good];
|
||||
return (discount.incomeMore * 100 < parseInt(fieldAddedValue) && discount.incomeMore > 0) ||
|
||||
(discount.toTime < (summary ? summary.days : 0) && discount.toTime > 0 && discount.toCapacity === 0) ||
|
||||
(discount.toCapacity > 0 && discount.toCapacity < (summary ? summary.points : 0) && discount.toTime === 0) ||
|
||||
(discount.toCapacity > 0 && discount.toTime > 0 && discount.toCapacity < (summary ? summary.points : 0) && discount.toTime < (summary ? summary.days : 0)) ||
|
||||
(!discount.toCapacity && !discount.toTime && !discount.incomeMore && !discount.basketMore) ||
|
||||
discount.basketMore;
|
||||
}).filter((e, i, a) => {
|
||||
const discount: Discount = discountsArray[e];
|
||||
if (discount.incomeMore) {
|
||||
return discount.incomeMore === a.reduce((a, e) => Math.max(a, discountsArray[e].incomeMore || 0), 0);
|
||||
}
|
||||
if (discount.toTime && discount.toCapacity) {
|
||||
return discount.toTime === a.reduce((a, e) => Math.max(a, (discountsArray[e].toTime && discountsArray[e].toCapacity) ? discountsArray[e].toTime : 0), 0) && discount.toCapacity === a.reduce((a, e) => Math.max(a, (discountsArray[e].toCapacity && discountsArray[e].toTime) ? discountsArray[e].toCapacity : 0), 0);
|
||||
}
|
||||
if (discount.toTime && !discount.toCapacity) {
|
||||
return discount.toTime === a.reduce((a, e) => Math.max(a, discountsArray[e].toTime && !discountsArray[e].toCapacity ? discountsArray[e].toTime : 0), 0);
|
||||
}
|
||||
if (!discount.toTime && discount.toCapacity) {
|
||||
return discount.toCapacity === a.reduce((a, e) => Math.max(a, discountsArray[e].toCapacity && !discountsArray[e].toTime ? discountsArray[e].toCapacity : 0), 0);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function separator(amount: number) {
|
||||
if (String(amount).length < 4) { return amount; }
|
||||
|
||||
let result: Array<string> = [];
|
||||
const arrs = String(amount).split('.');
|
||||
const arr = arrs[0].split('').reverse();
|
||||
|
||||
arr.forEach((item, i: number) => {
|
||||
result.push(String(arr[i]));
|
||||
if (((i + 1) / 3) - Math.round((i + 1) / 3) === 0) result.push(" ");
|
||||
});
|
||||
|
||||
if (arrs.length > 1) { return result.reverse().join("") + "." + arrs[1]; }
|
||||
else { return result.reverse().join(""); }
|
||||
};
|
||||
|
||||
export function formatPromocodePriveleges(promocode: Promocode) {
|
||||
return promocode.privileges.map(privelege => `${privelege.good} - ${Math.round(privelege.discount * 100)}%`).join(", ");
|
||||
}
|
||||
|
||||
export function calcTotalAndRowData(
|
||||
cartRowsData: ArrayProps[],
|
||||
isNonCommercial: boolean,
|
||||
discountsArray: Discount[],
|
||||
discountsActiveArray: number[],
|
||||
fitDiscounts: number[],
|
||||
addedValueField: string,
|
||||
cartSummary: { [key: string]: CartSummary; },
|
||||
promocode?: Promocode,
|
||||
) {
|
||||
let totalPrice = 0;
|
||||
|
||||
const calculatedCartRowData = cartRowsData.map(cartRow => {
|
||||
let price = cartRow.price;
|
||||
const appliedDiscounts: number[] = [];
|
||||
if (!isNonCommercial) {
|
||||
let percents = 0;
|
||||
if (cartRow.type === "package") {
|
||||
// считаем цену в ПАКЕТАХ
|
||||
price = 0;
|
||||
|
||||
cartRow.tariffs?.forEach((tariff) => {
|
||||
let tariffPrice = tariff.price;
|
||||
percents = 0;
|
||||
|
||||
// применяем скидки по промокоду
|
||||
if (promocode) {
|
||||
promocode.privileges.forEach(privilege => {
|
||||
if (tariff.service === privilege.good) {
|
||||
percents = percents + privilege.discount;
|
||||
}
|
||||
});
|
||||
} else {// применяем активные скидки
|
||||
percents = applyActiveDiscounts(
|
||||
percents,
|
||||
tariff,
|
||||
discountsArray,
|
||||
discountsActiveArray,
|
||||
addedValueField,
|
||||
);
|
||||
}
|
||||
|
||||
// применяем активные скидки по времени объему
|
||||
if (!promocode) {
|
||||
discountsActiveArray.forEach(activeDiscount => {
|
||||
discountsArray.forEach((discount, i) => {
|
||||
if (i === activeDiscount) {
|
||||
if (tariff.time) {
|
||||
const dTime = 0.1;
|
||||
percents = percents + dTime;
|
||||
}
|
||||
|
||||
if (tariff.points) {
|
||||
//const cTime = discountCapacity( tariff.points );
|
||||
//percents = percents + cTime;
|
||||
|
||||
//if( discounts ) discounts += " × ";
|
||||
//if( cTime != 0 ) discounts += `${ Math.round(cTime * 100) }%`;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// применяем активные скидки на продукт
|
||||
if (!promocode) {
|
||||
discountsActiveArray.forEach(activeDiscount => {
|
||||
discountsArray.forEach((discount, i) => {
|
||||
if (i === activeDiscount) {
|
||||
if (tariff.time && tariff.points) {
|
||||
// const dProduct = discountProduct( tariff.time, tariff.points );
|
||||
//percents = percents + dProduct;
|
||||
|
||||
//if( discounts ) discounts += " × ";
|
||||
//if( dProduct != 0 ) discounts += `${ Math.round(dProduct * 100) }%`;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
tariffPrice = tariffPrice - (tariffPrice * percents);
|
||||
|
||||
price += tariffPrice;
|
||||
});
|
||||
} else {
|
||||
// считаем цену в ТАРИФАХ
|
||||
price = cartRow.price;
|
||||
percents = 0;
|
||||
|
||||
// применяем скидки по промокоду
|
||||
if (promocode) {
|
||||
promocode.privileges.forEach(privilege => {
|
||||
if (cartRow.service === privilege.good) {
|
||||
appliedDiscounts.push(privilege.discount);
|
||||
price *= (1 - privilege.discount);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// применяем активные скидки
|
||||
fitDiscounts.forEach(activeDiscount => {
|
||||
const discount = discountsArray[activeDiscount];
|
||||
discount.privileges.forEach((p) => {
|
||||
const svcName = cartRow.service;
|
||||
if (p.good === svcName) {
|
||||
const summary = cartSummary[svcName] || { mbs: 0, points: 0, days: 0 };
|
||||
if (
|
||||
(discount.toCapacity === 0 && discount.toTime === 0 && discount.basketMore === 0 && !(discount.incomeMore)) ||
|
||||
(discount.toCapacity > 0 && summary.points > discount.toCapacity && cartRow.points > 0 && discount.toTime === 0) ||
|
||||
(discount.toTime > 0 && summary.days > discount.toTime * 100 && cartRow.time > 0 && discount.toCapacity === 0) ||
|
||||
(discount.toTime > 0 && discount.toCapacity > 0 && summary.days > discount.toTime * 100 && summary.points > discount.toCapacity)
|
||||
) {
|
||||
price *= (1 - p.discount);
|
||||
appliedDiscounts.push(p.discount);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
percents = Number(percents.toFixed(2));
|
||||
|
||||
price = price - (price * percents);
|
||||
}
|
||||
}
|
||||
totalPrice += price;
|
||||
|
||||
return {
|
||||
...cartRow,
|
||||
price,
|
||||
appliedDiscounts,
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
totalPrice,
|
||||
calculatedCartRowData
|
||||
};
|
||||
}
|
||||
|
||||
function applyActiveDiscounts(
|
||||
percents: number,
|
||||
tariff: Tariff,
|
||||
discountsArray: Discount[],
|
||||
discountsActiveArray: number[],
|
||||
addedValueField: string,
|
||||
) {
|
||||
discountsActiveArray.forEach(activeDiscountIndex => {
|
||||
discountsArray[activeDiscountIndex].privileges.forEach((privilege) => {
|
||||
if (privilege.discount !== 0) {
|
||||
if (addedValueField) { // внесено
|
||||
const addedValue = Number(addedValueField);
|
||||
let minDiscount = 100;
|
||||
let minI = -1;
|
||||
discountsArray.forEach((discount, index) => {
|
||||
discount.privileges.forEach((y) => {
|
||||
if (
|
||||
discount.active &&
|
||||
addedValue - y.discount * 100 < minDiscount &&
|
||||
addedValue - y.discount * 100 > 0
|
||||
) {
|
||||
minDiscount = addedValue - y.discount * 100;
|
||||
minI = index;
|
||||
}
|
||||
});
|
||||
});
|
||||
if (minI >= 0) {
|
||||
discountsArray[minI].privileges.forEach((y) => {
|
||||
percents = percents + y.discount / discountsActiveArray.length; // костыль
|
||||
});
|
||||
}
|
||||
} else { // не внесено
|
||||
if (tariff.service === privilege.good) {
|
||||
percents = percents + privilege.discount;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return percents;
|
||||
}
|
||||
|
||||
export function convertTariffs(tariffsArray: ArrayProps[]) {
|
||||
return tariffsArray.map((item) => {
|
||||
if (item.type === "package" && item.tariffs) {
|
||||
const result = item.tariffs.reduce((acc, tariff) => {
|
||||
acc.service = acc.service ? `${acc.service}, ${tariff.service}` : tariff.service;
|
||||
acc.disk = acc.disk + tariff.disk;
|
||||
acc.time = acc.time + tariff.time;
|
||||
acc.points = acc.points + tariff.points;
|
||||
acc.price = acc.price + tariff.price;
|
||||
|
||||
return acc;
|
||||
}, { service: "", disk: "", time: "", points: "", price: 0 });
|
||||
|
||||
return { id: item.id, name: item.name, type: item.type, ...result };
|
||||
} else {
|
||||
return item;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -4,7 +4,7 @@ import Users from "./Users";
|
||||
import Entities from "./Entities";
|
||||
import Tariffs from "./Tariffs";
|
||||
import Discounts from "./Discounts";
|
||||
import Promocode from "./Promocode";
|
||||
import Promocodes from "./Promocode";
|
||||
|
||||
|
||||
import Support from "./Support";
|
||||
@ -22,7 +22,7 @@ const Content: React.FC<MWProps> = ({ section }) => {
|
||||
<Entities />,
|
||||
<Tariffs />,
|
||||
<Discounts />,
|
||||
<Promocode />,
|
||||
<Promocodes />,
|
||||
<Error404 />,
|
||||
<Error404 />,
|
||||
<Support />
|
||||
@ -32,13 +32,13 @@ const Content: React.FC<MWProps> = ({ section }) => {
|
||||
<React.Fragment>
|
||||
<Box sx={{
|
||||
width: "100%",
|
||||
height: "calc(100vh - 85px)",
|
||||
height: "100vh",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
overflow: "auto",
|
||||
overflowY: "auto",
|
||||
padding: "60px 5px"
|
||||
padding: "160px 5px"
|
||||
}}>
|
||||
|
||||
{ componentsArray[ section ] }
|
||||
|
||||
@ -12,8 +12,6 @@ const Header: React.FC = () => {
|
||||
backgroundColor: theme.palette.menu.main,
|
||||
width: "100%",
|
||||
minHeight: "85px",
|
||||
borderBottom: "1px solid",
|
||||
borderColor: theme.palette.grayDark.main,
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
flexWrap: 'wrap',
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
import * as React from "react";
|
||||
import { Box } from "@mui/material";
|
||||
import { useNavigate, useLocation } from "react-router-dom";
|
||||
import MenuOutlinedIcon from '@mui/icons-material/MenuOutlined';
|
||||
import PersonOutlineOutlinedIcon from '@mui/icons-material/PersonOutlineOutlined';
|
||||
import SettingsOutlinedIcon from '@mui/icons-material/SettingsOutlined';
|
||||
import BathtubOutlinedIcon from '@mui/icons-material/BathtubOutlined';
|
||||
@ -11,174 +9,234 @@ import SettingsIcon from '@mui/icons-material/Settings';
|
||||
import CameraIcon from '@mui/icons-material/Camera';
|
||||
import HeadsetMicOutlinedIcon from '@mui/icons-material/HeadsetMicOutlined';
|
||||
import theme from "../../../theme";
|
||||
import CssBaseline from "@mui/material/CssBaseline";
|
||||
import Toolbar from "@mui/material/Toolbar";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import MenuIcon from "@mui/icons-material/Menu";
|
||||
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
|
||||
import ChevronLeftIcon from "@mui/icons-material/ChevronLeft";
|
||||
import Divider from "@mui/material/Divider";
|
||||
import List from "@mui/material/List";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import ListItemButton from "@mui/material/ListItemButton";
|
||||
import ListItemIcon from "@mui/material/ListItemIcon";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import {CSSObject, styled, Theme, useTheme} from "@mui/material/styles";
|
||||
import MuiDrawer from '@mui/material/Drawer';
|
||||
import MuiAppBar, { AppBarProps as MuiAppBarProps } from '@mui/material/AppBar';
|
||||
import Header from "../Header/index";
|
||||
import {Link} from 'react-router-dom';
|
||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||
import Paper from "@mui/material/Paper";
|
||||
|
||||
const drawerWidth = 240;
|
||||
|
||||
const openedMixin = (theme: Theme): CSSObject => ({
|
||||
width: drawerWidth,
|
||||
background: '#2f3339',
|
||||
transition: theme.transitions.create('width', {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.enteringScreen,
|
||||
}),
|
||||
overflowX: 'hidden',
|
||||
});
|
||||
|
||||
const closedMixin = (theme: Theme): CSSObject => ({
|
||||
transition: theme.transitions.create('width', {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen,
|
||||
}),
|
||||
overflowX: 'hidden',
|
||||
width: `calc(${theme.spacing(7)} + 1px)`,
|
||||
background: '#2f3339',
|
||||
// marginTop: '20px',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
width: `calc(${theme.spacing(8)} + 1px)`,
|
||||
},
|
||||
});
|
||||
|
||||
const DrawerHeader = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
padding: theme.spacing(0, 1),
|
||||
...theme.mixins.toolbar,
|
||||
}));
|
||||
|
||||
interface AppBarProps extends MuiAppBarProps {
|
||||
open?: boolean;
|
||||
}
|
||||
|
||||
const AppBar = styled(MuiAppBar, {
|
||||
shouldForwardProp: (prop) => prop !== 'open',
|
||||
})<AppBarProps>(({ theme, open }) => ({
|
||||
zIndex: theme.zIndex.drawer + 1,
|
||||
transition: theme.transitions.create(['width', 'margin'], {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen,
|
||||
}),
|
||||
...(open && {
|
||||
marginLeft: drawerWidth,
|
||||
width: `calc(100% - ${drawerWidth}px)`,
|
||||
transition: theme.transitions.create(['width', 'margin'], {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.enteringScreen,
|
||||
}),
|
||||
}),
|
||||
}));
|
||||
|
||||
const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== 'open' })(
|
||||
({ theme, open }) => ({
|
||||
width: drawerWidth,
|
||||
flexShrink: 0,
|
||||
boxSizing: 'border-box',
|
||||
...(open && {
|
||||
...openedMixin(theme),
|
||||
'& .MuiDrawer-paper': openedMixin(theme),
|
||||
}),
|
||||
...(!open && {
|
||||
...closedMixin(theme),
|
||||
'& .MuiDrawer-paper': closedMixin(theme),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
const links: {path: string; element: JSX.Element; title: string} [] =[
|
||||
{path: '/users', element: <PersonOutlineOutlinedIcon/>, title: 'Информация о проекте'},
|
||||
{path: '/entities', element: <SettingsOutlinedIcon/>, title: 'Юридические лица'},
|
||||
{path: '/tariffs', element: <BathtubOutlinedIcon/>, title: 'Шаблонизатор документов'},
|
||||
{path: '/discounts', element: <AddPhotoAlternateOutlinedIcon/>, title: 'Скидки'},
|
||||
{path: '/promocode', element: <NaturePeopleOutlinedIcon/>, title: 'Промокод'},
|
||||
{path: '/kkk', element: <SettingsIcon/>, title: 'Настройки'},
|
||||
{path: '/jjj', element: <CameraIcon/>, title: 'Камера' },
|
||||
{path: '/support', element: <HeadsetMicOutlinedIcon/>, title: 'Служба поддержки'},
|
||||
]
|
||||
|
||||
|
||||
const Navigation = (props:any) => {
|
||||
return (
|
||||
<List
|
||||
sx={{
|
||||
background: '#2f3339'
|
||||
}}
|
||||
>
|
||||
{links.map((e, i) => (
|
||||
<ListItem key={i} disablePadding sx={{ display: 'block' }}>
|
||||
<Link to={e.path} style={{textDecoration: 'none'}}>
|
||||
<ListItemButton onClick={props.SladeMobileHC}
|
||||
sx={{
|
||||
minHeight: 48,
|
||||
height: '50px',
|
||||
justifyContent: props.visible ? 'initial' : 'center',
|
||||
px: 2.5,
|
||||
margin: '20px 0'
|
||||
}}
|
||||
>
|
||||
<ListItemIcon
|
||||
sx={{
|
||||
minWidth: 0,
|
||||
mr: props.visible ? 3 : 'auto',
|
||||
justifyContent: 'center',
|
||||
color: '#eaba5b',
|
||||
transform: 'scale(1.2)'
|
||||
}}
|
||||
>
|
||||
{e.element}
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={e.title}
|
||||
sx={{
|
||||
opacity: props.visible ? 1 : 0,
|
||||
color: '#eaba5b',
|
||||
}} />
|
||||
</ListItemButton>
|
||||
</Link>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
const Menu: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const pages = [ "null", "users", "entities", "tariffs", "discounts", "promocode", "null", "null", "support" ];
|
||||
const colors = [
|
||||
"null",
|
||||
theme.palette.golden.main,
|
||||
theme.palette.golden.main,
|
||||
theme.palette.golden.main,
|
||||
theme.palette.golden.main,
|
||||
theme.palette.golden.main,
|
||||
theme.palette.golden.main,
|
||||
theme.palette.golden.main,
|
||||
theme.palette.golden.main ];
|
||||
const tablet = useMediaQuery('(max-width:600px)');
|
||||
|
||||
const location = useLocation();
|
||||
pages.forEach( (item, i) => {
|
||||
if( location.pathname == `/${pages[i]}` ) colors[ i ] = theme.palette.secondary.main
|
||||
} );
|
||||
const mobile = useMediaQuery('(max-width:340px)');
|
||||
|
||||
const changePage = (page:number) => { navigate(`/${pages[ page ]}`); }
|
||||
const theme = useTheme();
|
||||
const [open, setOpen] = React.useState(tablet? false : true);
|
||||
|
||||
const handleDrawerOpen = () => {
|
||||
if (!mobile) {setOpen(true)}
|
||||
else {SladeMobileHC()}
|
||||
};
|
||||
|
||||
const handleDrawerClose = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const [sladeMobile, setSladeMobile] = React.useState(false)
|
||||
const SladeMobileHC = () => {
|
||||
if (mobile) {setSladeMobile(old=>!old)}
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Box sx={{
|
||||
backgroundColor: theme.palette.menu.main,
|
||||
width: "96px",
|
||||
height: "100vh",
|
||||
borderRight: "1px solid #45494c",
|
||||
overflow: "auto"
|
||||
}}>
|
||||
<Box sx={{
|
||||
width: "100%",
|
||||
height: "85px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
cursor: "pointer"
|
||||
}}>
|
||||
<MenuOutlinedIcon sx={{
|
||||
transform: "scale(1.4)"
|
||||
}} />
|
||||
</Box>
|
||||
|
||||
<Box sx={{
|
||||
height: "calc( 100vh - 85px )",
|
||||
overflowY: "auto",
|
||||
'&::-webkit-scrollbar': {
|
||||
display: "none"
|
||||
}
|
||||
}}>
|
||||
<Box sx={{
|
||||
width: "100%",
|
||||
height: "85px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
cursor: "pointer"
|
||||
}} onClick={ () => changePage( 1 ) }>
|
||||
<PersonOutlineOutlinedIcon sx={{
|
||||
color: colors[ 1 ],
|
||||
transform: "scale(1.2)"
|
||||
}} />
|
||||
</Box>
|
||||
<Box sx={{
|
||||
width: "100%",
|
||||
height: "85px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
cursor: "pointer"
|
||||
}} onClick={ () => changePage( 2 ) }>
|
||||
<SettingsOutlinedIcon sx={{
|
||||
color: colors[ 2 ],
|
||||
transform: "scale(1.2)"
|
||||
}} />
|
||||
</Box>
|
||||
<Box sx={{
|
||||
width: "100%",
|
||||
height: "85px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
cursor: "pointer"
|
||||
}} onClick={ () => changePage( 3 ) }>
|
||||
<BathtubOutlinedIcon sx={{
|
||||
color: colors[ 3 ],
|
||||
transform: "scale(1.2)"
|
||||
}} />
|
||||
</Box>
|
||||
<Box sx={{
|
||||
width: "100%",
|
||||
height: "85px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
cursor: "pointer"
|
||||
}} onClick={ () => changePage( 4 ) }>
|
||||
<AddPhotoAlternateOutlinedIcon sx={{
|
||||
color: colors[ 4 ],
|
||||
transform: "scale(1.2)"
|
||||
}} />
|
||||
</Box>
|
||||
<Box sx={{
|
||||
width: "100%",
|
||||
height: "85px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
cursor: "pointer"
|
||||
}} onClick={ () => changePage( 5 ) }>
|
||||
<NaturePeopleOutlinedIcon sx={{
|
||||
color: colors[ 5 ],
|
||||
transform: "scale(1.2)"
|
||||
}} />
|
||||
</Box>
|
||||
<Box sx={{
|
||||
width: "100%",
|
||||
height: "85px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
cursor: "pointer"
|
||||
}} onClick={ () => changePage( 6 ) }>
|
||||
<SettingsIcon sx={{
|
||||
color: colors[ 6 ],
|
||||
transform: "scale(1.2)"
|
||||
}} />
|
||||
</Box>
|
||||
<Box sx={{
|
||||
width: "100%",
|
||||
height: "85px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
cursor: "pointer"
|
||||
}} onClick={ () => changePage( 7 ) }>
|
||||
<CameraIcon sx={{
|
||||
color: colors[ 7 ],
|
||||
transform: "scale(1.2)"
|
||||
}} />
|
||||
</Box>
|
||||
<Box sx={{
|
||||
width: "100%",
|
||||
height: "85px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
cursor: "pointer"
|
||||
}} onClick={ () => changePage( 8 ) }>
|
||||
<HeadsetMicOutlinedIcon sx={{
|
||||
color: colors[ 8 ],
|
||||
transform: "scale(1.2)"
|
||||
}} />
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ display: 'flex' }}>
|
||||
<CssBaseline />
|
||||
<AppBar open={open}>
|
||||
<Toolbar
|
||||
sx={{
|
||||
background: '#2f3339',
|
||||
borderBottom: '1px solid',
|
||||
borderColor: '#45494c',
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
aria-label="open drawer"
|
||||
onClick={handleDrawerOpen}
|
||||
edge="start"
|
||||
sx={{
|
||||
marginRight: 5,
|
||||
...(open && { display: 'none' }),
|
||||
color: "#eaba5b",
|
||||
background: '#2f3339'
|
||||
}}
|
||||
>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
<Header />
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
{!mobile ? <Drawer variant="permanent" open={open}>
|
||||
<DrawerHeader style={{minHeight: '86px',}}>
|
||||
<IconButton onClick={handleDrawerClose} sx={{color: "#eaba5b"}}>
|
||||
<ChevronLeftIcon />
|
||||
</IconButton>
|
||||
</DrawerHeader>
|
||||
<Divider />
|
||||
<Navigation visible={open}/>
|
||||
</Drawer> : null}
|
||||
</Box>
|
||||
{sladeMobile ? <Paper
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
background: '#2f3339',
|
||||
zIndex: theme.zIndex.drawer + 3
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{display: 'flex',
|
||||
justifyContent: 'end',
|
||||
padding: '10px'
|
||||
}}
|
||||
>
|
||||
<IconButton onClick={SladeMobileHC} sx={{color: "#eaba5b"}}>
|
||||
<ChevronLeftIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Divider />
|
||||
<Navigation visible={true} SladeMobileHC={SladeMobileHC}/></Paper> : null}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ const LoggedIn: React.FC<MWProps> = ({ section }) => {
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center"
|
||||
}}>
|
||||
<Header />
|
||||
{/*<Header />*/}
|
||||
<Content section={ section } />
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
63
src/store.ts
63
src/store.ts
@ -1,63 +0,0 @@
|
||||
import create from "zustand";
|
||||
import { persist } from "zustand/middleware"
|
||||
import { ArrayProps } from "./pages/dashboard/Content/Tariffs/types";
|
||||
import { PromocodeProps } from "./pages/dashboard/Content/Promocode/types";
|
||||
import { DiscountProps } from "./pages/dashboard/Content/Discounts/types";
|
||||
|
||||
|
||||
const useStore = create<StoreState>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
tariffsArray: [],
|
||||
tariffsArraySet: (array:Array<ArrayProps>) => set({ tariffsArray: array }),
|
||||
|
||||
tariffsSelectedRowsData: [],
|
||||
tariffsSelectedRowsDataSet: (array:Array<ArrayProps>) => set({ tariffsSelectedRowsData: array }),
|
||||
|
||||
cartRowsData: [],
|
||||
cartRowsDataSet: (array:Array<ArrayProps>) => set({ cartRowsData: array }),
|
||||
|
||||
promocodeArray: [],
|
||||
promocodeArraySet: (array:Array<PromocodeProps>) => set({ promocodeArray: array }),
|
||||
|
||||
discountsArray: [],
|
||||
discountsArraySet: (array:Array<DiscountProps>) => set({ discountsArray: array }),
|
||||
|
||||
discountsActiveArray: [],
|
||||
discountsActiveArraySet: (array:Array<number>) => set({ discountsActiveArray: array }),
|
||||
|
||||
discountsSelectedRowsData: [],
|
||||
discountsSelectedRowsDataSet: (array:Array<DiscountProps>) => set({ discountsSelectedRowsData: array }),
|
||||
}),
|
||||
{
|
||||
name: "arrays-storage",
|
||||
getStorage: () => localStorage,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
export interface StoreState {
|
||||
tariffsArray: Array<ArrayProps>,
|
||||
tariffsArraySet: (array:Array<ArrayProps>) => void,
|
||||
|
||||
tariffsSelectedRowsData: Array<ArrayProps>,
|
||||
tariffsSelectedRowsDataSet: (array:Array<ArrayProps>) => void,
|
||||
|
||||
cartRowsData: Array<ArrayProps>,
|
||||
cartRowsDataSet: (array:Array<ArrayProps>) => void,
|
||||
|
||||
promocodeArray: Array<PromocodeProps>,
|
||||
promocodeArraySet: (array:Array<PromocodeProps>) => void,
|
||||
|
||||
discountsArray: Array<DiscountProps>,
|
||||
discountsArraySet: (array:Array<DiscountProps>) => void,
|
||||
|
||||
discountsActiveArray: Array<number>,
|
||||
discountsActiveArraySet: (array:Array<number>) => void,
|
||||
|
||||
discountsSelectedRowsData: Array<DiscountProps>,
|
||||
discountsSelectedRowsDataSet: (array:Array<DiscountProps>) => void,
|
||||
}
|
||||
|
||||
|
||||
export default useStore;
|
||||
27
src/stores/cart.ts
Normal file
27
src/stores/cart.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import create from "zustand";
|
||||
import { devtools, persist } from "zustand/middleware";
|
||||
import { ArrayProps } from "../model/tariff";
|
||||
|
||||
|
||||
interface CartStore {
|
||||
cartRowsData: Array<ArrayProps>,
|
||||
setCartRowsData: (array: Array<ArrayProps>) => void,
|
||||
}
|
||||
|
||||
export const useCartStore = create<CartStore>()(
|
||||
devtools(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
cartRowsData: [],
|
||||
setCartRowsData: (array: Array<ArrayProps>) => set({ cartRowsData: array }),
|
||||
}),
|
||||
{
|
||||
name: "cart-storage",
|
||||
getStorage: () => localStorage,
|
||||
}
|
||||
),
|
||||
{
|
||||
name: "Cart store"
|
||||
}
|
||||
)
|
||||
);
|
||||
36
src/stores/discounts.ts
Normal file
36
src/stores/discounts.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import create from "zustand";
|
||||
import { devtools, persist } from "zustand/middleware";
|
||||
import { Discount } from "../model/cart";
|
||||
import { testDiscounts } from "./mocks/discounts";
|
||||
|
||||
|
||||
interface DiscountStore {
|
||||
discountsArray: Array<Discount>,
|
||||
setDiscountsArray: (array: Array<Discount>) => void,
|
||||
discountsActiveArray: Array<number>,
|
||||
setDiscountsActiveArray: (array: Array<number>) => void,
|
||||
discountsSelectedRowsData: Array<Discount>,
|
||||
setDiscountsSelectedRowsData: (array: Array<Discount>) => void,
|
||||
}
|
||||
|
||||
export const useDiscountStore = create<DiscountStore>()(
|
||||
devtools(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
discountsArray: testDiscounts,
|
||||
setDiscountsArray: (array: Array<Discount>) => set({ discountsArray: array }),
|
||||
discountsActiveArray: [],
|
||||
setDiscountsActiveArray: (array: Array<number>) => set({ discountsActiveArray: array }),
|
||||
discountsSelectedRowsData: [],
|
||||
setDiscountsSelectedRowsData: (array: Array<Discount>) => set({ discountsSelectedRowsData: array }),
|
||||
}),
|
||||
{
|
||||
name: "discount-storage",
|
||||
getStorage: () => localStorage,
|
||||
}
|
||||
),
|
||||
{
|
||||
name: "Discount store"
|
||||
}
|
||||
)
|
||||
);
|
||||
52
src/stores/mocks/discounts.ts
Normal file
52
src/stores/mocks/discounts.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { Discount } from "../../model/cart";
|
||||
|
||||
export const testDiscounts: Discount[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Скидка 1",
|
||||
endless: false,
|
||||
from: "",
|
||||
dueTo: "",
|
||||
privileges: [
|
||||
{ good: "Опросник", discount: 0.3 },
|
||||
{ good: "Опросник", discount: 0.2 },
|
||||
],
|
||||
active: false,
|
||||
incomeMore: 1,
|
||||
basketMore: 10,
|
||||
toTime: 20,
|
||||
toCapacity: 30,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Скидка 2",
|
||||
endless: false,
|
||||
from: "",
|
||||
dueTo: "",
|
||||
privileges: [
|
||||
{ good: "Опросник", discount: 0.3 },
|
||||
{ good: "Опросник", discount: 0.2 },
|
||||
],
|
||||
active: true,
|
||||
incomeMore: 1,
|
||||
basketMore: 10,
|
||||
toTime: 20,
|
||||
toCapacity: 30,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Скидка 3",
|
||||
endless: false,
|
||||
from: "",
|
||||
dueTo: "",
|
||||
privileges: [
|
||||
{ good: "Опросник", discount: 0.3 },
|
||||
{ good: "Опросник", discount: 0.2 },
|
||||
],
|
||||
active: false,
|
||||
incomeMore: 1,
|
||||
basketMore: 10,
|
||||
toTime: 20,
|
||||
toCapacity: 30,
|
||||
},
|
||||
];
|
||||
17
src/stores/mocks/promocodes.ts
Normal file
17
src/stores/mocks/promocodes.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { Promocode } from "../../model/cart";
|
||||
|
||||
|
||||
export const testPromocodes: Promocode[] = [
|
||||
{
|
||||
id: 1, name: "Промокод 1", endless: false, from: "", dueTo: "", privileges: [
|
||||
{ good: "Шаблонизатор документов", discount: 0.15 },
|
||||
{ good: "Опросник", discount: 0.3 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 1, name: "Промокод 2", endless: false, from: "", dueTo: "", privileges: [
|
||||
{ good: "Шаблонизатор документов", discount: 0.4 },
|
||||
{ good: "Опросник", discount: 0.6 }
|
||||
]
|
||||
}
|
||||
];
|
||||
28
src/stores/promocodes.ts
Normal file
28
src/stores/promocodes.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import create from "zustand";
|
||||
import { devtools, persist } from "zustand/middleware";
|
||||
import { Promocode } from "../model/cart";
|
||||
import { testPromocodes } from "./mocks/promocodes";
|
||||
|
||||
|
||||
interface PromocodeStore {
|
||||
promocodeArray: Array<Promocode>,
|
||||
setPromocodeArray: (array: Array<Promocode>) => void,
|
||||
}
|
||||
|
||||
export const usePromocodeStore = create<PromocodeStore>()(
|
||||
devtools(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
promocodeArray: testPromocodes,
|
||||
setPromocodeArray: (array: Array<Promocode>) => set({ promocodeArray: array }),
|
||||
}),
|
||||
{
|
||||
name: "promocode-storage",
|
||||
getStorage: () => localStorage,
|
||||
}
|
||||
),
|
||||
{
|
||||
name: "Promocode store"
|
||||
}
|
||||
)
|
||||
);
|
||||
31
src/stores/tariffs.ts
Normal file
31
src/stores/tariffs.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import create from "zustand";
|
||||
import { devtools, persist } from "zustand/middleware";
|
||||
import { ArrayProps } from "../model/tariff";
|
||||
|
||||
|
||||
interface TariffStore {
|
||||
tariffs: Array<ArrayProps>;
|
||||
setTariffs: (array: Array<ArrayProps>) => void;
|
||||
tariffsSelectedRowsData: Array<ArrayProps>;
|
||||
setTariffsSelectedRowsData: (array: Array<ArrayProps>) => void;
|
||||
}
|
||||
|
||||
export const useTariffStore = create<TariffStore>()(
|
||||
devtools(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
tariffs: [],
|
||||
setTariffs: (array: Array<ArrayProps>) => set({ tariffs: array }),
|
||||
tariffsSelectedRowsData: [],
|
||||
setTariffsSelectedRowsData: (array: Array<ArrayProps>) => set({ tariffsSelectedRowsData: array }),
|
||||
}),
|
||||
{
|
||||
name: "tariff-storage",
|
||||
getStorage: () => localStorage,
|
||||
}
|
||||
),
|
||||
{
|
||||
name: "Tariff store"
|
||||
}
|
||||
)
|
||||
);
|
||||
Loading…
Reference in New Issue
Block a user