крако, привелегии в зустанд, корзина в ките

This commit is contained in:
krokodilka 2023-02-28 11:30:57 +03:00
parent 8a5ff4441b
commit 3741c78391
26 changed files with 8579 additions and 1608 deletions

17
craco.config.js Normal file

@ -0,0 +1,17 @@
const CracoAlias = require("craco-alias");
module.exports = {
plugins: [
{
plugin: CracoAlias,
options: {
source: "tsconfig",
// baseUrl SHOULD be specified
// plugin does not take it from tsconfig
baseUrl: "./src",
// tsConfigPath should point to the file where "baseUrl" and "paths" are specified
tsConfigPath: "./tsconfig.extend.json"
}
}
]
};

6684
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -21,6 +21,7 @@
"@types/node": "^16.11.56", "@types/node": "^16.11.56",
"@types/react": "^18.0.18", "@types/react": "^18.0.18",
"@types/react-dom": "^18.0.6", "@types/react-dom": "^18.0.6",
"craco": "^0.0.3",
"dayjs": "^1.11.5", "dayjs": "^1.11.5",
"moment": "^2.29.4", "moment": "^2.29.4",
"numeral": "^2.0.6", "numeral": "^2.0.6",
@ -35,10 +36,10 @@
"zustand": "^4.1.1" "zustand": "^4.1.1"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "craco start",
"build": "react-scripts build", "build": "craco build",
"test": "react-scripts test", "test": "craco test",
"eject": "react-scripts eject" "eject": "craco eject"
}, },
"eslintConfig": { "eslintConfig": {
"extends": [ "extends": [
@ -57,5 +58,8 @@
"last 1 firefox version", "last 1 firefox version",
"last 1 safari version" "last 1 safari version"
] ]
},
"devDependencies": {
"craco-alias": "^3.0.1"
} }
} }

@ -0,0 +1,12 @@
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://news.ycombinator.com', {
waitUntil: 'networkidle2',
});
await page.pdf({ path: 'hn.pdf', format: 'a4' });
await browser.close();
})();

87
src/kitUI/basket/calc.ts Normal file

@ -0,0 +1,87 @@
// export const setPromocode = (name: string) => {
// let codeNumber = -1;
//
// promocodeArray.forEach((item, i) => {
// if (name != "" && item.name == name) codeNumber = i;
// });
//
// setSelectedPromocode(codeNumber);
// }
export const PositiveInput = (event: any) => {
const numberInput = parseInt(event.target.value);
if (isNaN(numberInput) || numberInput < 0) {
event.target.value = '0'
}
}
// export const calcPriseTariff () => {
//
// // считаем цену в ТАРИФАХ
// price = item.price;
// percents = 0;
// discounts = "";
//
// // применяем скидки по промокоду
// if (selectedPromocode >= 0) {
// promocodeArray[selectedPromocode].privileges.forEach((privilege) => {
// console.log(item.service)
// console.log(privilege.good)
// if (item.service == privilege.good) {
// appliedDscnts.push(privilege.discount)
// price *= (1 - privilege.discount)
// }
// })
// } else {
// // применяем активные скидки
// if (fitDiscounts.length >= 0) {
// fitDiscounts.forEach((activeDiscount) => {
// const discount = discountsArray[activeDiscount]
// discount.privileges.forEach((p) => {
// const svcName = item.service.split(' ')[0]
// if (p.good == svcName) {
// const summary = cartSummary.get(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 && item.points > 0 && discount.toTime == 0
// || discount.toTime > 0 && summary.days > discount.toTime * 100 && item.time > 0 && discount.toCapacity == 0 ||
// discount.toTime > 0 && discount.toCapacity > 0 && summary.days > discount.toTime * 100 && summary.points > discount.toCapacity) {
//
// price *= (1 - p.discount)
// appliedDscnts.push(p.discount)
// }
//
//
// }
// })
// });
// }
//
// }
// percents = Number(percents.toFixed(2));
//
// priceBefore = price;
// price = price - (price * percents);
// prices += price;
// }
//
// // применяем активные скидки за объем корзины
// const discountsAfter = (priceBefore: number) => {
// const discounts: Array<number> = [];
//
//
// if (fitDiscounts.length >= 0 && !nonCommercial && selectedPromocode < 0) {
// fitDiscounts.forEach((activeDiscount) => {
// const d = discountsArray[activeDiscount]
// console.log(d)
// console.log(fieldAddedValue)
// if (d.basketMore > 0 && getPrice > d.basketMore && d.basketMore === fitDiscounts.reduce((a, e) => Math.max(a, discountsArray[e].basketMore), 0) ||
// d.incomeMore > 0 && parseInt(fieldAddedValue) > d.incomeMore) {
// prices *= (1 - d.privileges[0].discount)
// discounts.push(d.privileges[0].discount)
// }
// });
// }

372
src/kitUI/basket/index.tsx Normal file

@ -0,0 +1,372 @@
import * as React from "react";
import theme from "@theme";
import {Button, Paper, ListItemText, IconButton, FormControlLabel, Box, TextField, Typography, Container, Checkbox, List, ListItem, ListItemAvatar, Avatar} from "@mui/material";
import ShoppingCartIcon from "@mui/icons-material/ShoppingCart";
import DeleteIcon from "@mui/icons-material/Delete";
import Input from "@kitUI/input";
import useStore, {StoreState} from "../../stores/store";
import {useDemoData} from "@mui/x-data-grid-generator";
import {GridSelectionModel} from "@mui/x-data-grid";
import {ArrayProps} from "../../pages/dashboard/Content/Tariffs/types";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import TableCell from "@mui/material/TableCell";
import TableBody from "@mui/material/TableBody";
import Radio from "@mui/material/Radio";
import Skeleton from "@mui/material/Skeleton";
import Table from "@mui/material/Table";
export default (props:any):any => {
// const [added, setAdded] = React.useState<number>(0)
// const [notCommercial, setNotCommercial] = React.useState<boolean>(false)
//
// const setPromocode = (name: string) => {
// let codeNumber = -1;
//
// promocodeArray.forEach((item, i) => {
// if (name != "" && item.name == name) codeNumber = i;
// });
//
// setSelectedPromocode(codeNumber);
// }
//
// const PositiveInput = (event: any) => {
// const numberInput = parseInt(event.target.value);
// if (isNaN(numberInput) || numberInput < 0) {
// event.target.value = '0'
// }
// }
//
// // const getData = localStorage.getItem("tariffs");
// // const store = useStore( (state) => state );
// //
// // if( getData && !store.tariffsArray.length ) {
// // const rows:Array<ArrayProps> = JSON.parse(getData);
// // if( rows.length ) { store.tariffsArraySet( rows ); };
// // }
// let priceBefore = 0;
// let price = 0;
// let prices = 0;
// let percents = 0;
// let discounts = "";
// let discountsSum = "";
//
// const { discountsArray, discountsArraySet } = useStore<StoreState>((state) => state);
// const { discountsActiveArray, discountsActiveArraySet } = useStore<StoreState>((state) => state);
//
// const [nonCommercial, setNonCommercial] = React.useState(false);
//
// const { data } = useDemoData({
// dataSet: "Commodity",
// rowLength: 10,
// maxColumns: 5,
// });
//
//
// const { tariffsArray } = useStore<StoreState>((state) => state);
// const tariffsArrayConverted = 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;
// }
// } );
//
// const { tariffsSelectedRowsData, tariffsSelectedRowsDataSet } = useStore<StoreState>((state) => state);
// const onRowsSelectionHandler = ( ids:GridSelectionModel ) => {
// const result:Array<ArrayProps> = [];
// ids.forEach((id) => tariffsArray.forEach( (row) => {
// if(row.id === id) result.push(row);
// } ) );
//
// tariffsSelectedRowsDataSet( result );
// };
//
// const { cartRowsData, cartRowsDataSet } = useStore<StoreState>((state) => state);
// const handleToBasket = () => {
// cartRowsDataSet( tariffsSelectedRowsData );
// }
//
// const handleRemoveBasket = ( id:number ) => {
// const cartFiltered = cartRowsData.filter( (row) => row.id != id );
// cartRowsDataSet( cartFiltered );
// }
//
// const fieldPromocode = React.useRef<HTMLInputElement | null>(null);
// const checkPromocode = () => {
// if( fieldPromocode.current != null ) {
// setPromocode( fieldPromocode.current.value );
// }
// }
//
// let { promocodeArray, promocodeArraySet } = useStore<StoreState>((state) => state);
// const [selectedPromocode, setSelectedPromocode] = React.useState( -1 );
//
// //promocodeArray = [ ...rowz ];
//
// const setPromocode = ( name:string ) => {
// let codeNumber = -1;
//
// promocodeArray.forEach( (item, i) => {
// if( name != "" && item.name == name ) codeNumber = i;
// } );
//
// setSelectedPromocode( codeNumber );
// }
//
// const PositiveInput = (event:any) => {
// const numberInput = parseInt(event.target.value);
// if(isNaN(numberInput) || numberInput < 0) {event.target.value = '0'}
// }
//
// const fieldAdded = React.useRef<HTMLInputElement | null>(null);
// const [ fieldAddedValue, setFieldAddedValue ] = React.useState("");
//
// const changeAdded = (event:any) => {
// if( fieldAdded.current != null ) {
// if( fieldAdded.current.value != null ) {
// setFieldAddedValue( fieldAdded.current.value );
// }
//
// }
// PositiveInput(event)
// };
//
// const separator = (amount: number) => {
// console.log(amount)
//
// 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(""); }
//
// }
//
//
// const cartSummary = new Map()
// cartRowsData.forEach((row) => {
// const svcName = row.service.split(" ")[0]
// const prev = cartSummary.get(svcName)
// console.log(row)
// console.log(prev)
// if (!prev) {
// cartSummary.set(svcName, {
// mbs: row.disk,
// points: row.points,
// days: row.time,
// })
// } else {
// cartSummary.set(svcName, {
// mbs: row.disk+prev.mbs,
// points: row.points+prev.points,
// days: row.time+prev.days,
// })
// }
// })
// console.log(cartSummary)
//
// const fitDiscounts = discountsActiveArray.filter(e => {
// const d = discountsArray[e]
// const summary = cartSummary.get(d.privileges[0].good.split(' ')[0])
// return d.incomeMore*100 < parseInt(fieldAddedValue) && d.incomeMore > 0 ||
// d.toTime < (summary ? summary.days : 0) && d.toTime > 0 && d.toCapacity === 0 ||
// d.toCapacity > 0 && d.toCapacity < (summary ? summary.points : 0) && d.toTime === 0 ||
// d.toCapacity > 0 && d.toTime > 0 && d.toCapacity < (summary ? summary.points : 0) && d.toTime < (summary ? summary.days : 0) ||
// !d.toCapacity && !d.toTime && !d.incomeMore && !d.basketMore ||
// d.basketMore
// }).filter((e,i,a)=>{
// const d = discountsArray[e]
// if (d.incomeMore) {
// return d.incomeMore === a.reduce((a, e) => Math.max(a, discountsArray[e].incomeMore || 0), 0 )
// }
// if (d.toTime && d.toCapacity) {
// return d.toTime === a.reduce((a, e) => Math.max(a, (discountsArray[e].toTime && discountsArray[e].toCapacity) ? discountsArray[e].toTime:0 ), 0 ) && d.toCapacity === a.reduce((a, e) => Math.max(a, (discountsArray[e].toCapacity && discountsArray[e].toTime) ? discountsArray[e].toCapacity : 0 ), 0 )
// }
// if (d.toTime && !d.toCapacity) {
// return d.toTime === a.reduce((a, e) => Math.max(a, discountsArray[e].toTime && !discountsArray[e].toCapacity ? discountsArray[e].toTime : 0), 0 )
// }
// if (!d.toTime && d.toCapacity) {
// return d.toCapacity === a.reduce((a, e) => Math.max(a, discountsArray[e].toCapacity && !discountsArray[e].toTime ? discountsArray[e].toCapacity : 0), 0 )
// }
// return true
// })
// console.log(fitDiscounts)
//
// const discountsAfter = ( getPrice:number ) => {
// const discounts:Array<number> = [];
// priceBefore = getPrice;
//
// prices = getPrice;
// console.log(getPrice)
//
// // применяем активные скидки за объем корзины
//
// if( fitDiscounts.length >= 0 && !nonCommercial && selectedPromocode < 0 ) {
// fitDiscounts.forEach( (activeDiscount) => {
// const d = discountsArray[activeDiscount]
// console.log(d)
// console.log(fieldAddedValue)
// if (d.basketMore > 0 && getPrice > d.basketMore && d.basketMore === fitDiscounts.reduce((a,e) => Math.max(a, discountsArray[e].basketMore), 0) ||
// d.incomeMore > 0 && parseInt(fieldAddedValue) > d.incomeMore) {
// prices *= (1-d.privileges[0].discount)
// discounts.push(d.privileges[0].discount)
// }
// });
// }
//
// if( nonCommercial ) {
// prices *= 0.2
// return `80%`;
// }
//
// return discounts.map(e => `${(e*100).toFixed(2)}%`).join(' × ') + ` = ${(100 - discounts.reduce((a : number,cv : number) => a*(1-cv), 100)) .toFixed(2)}%`;
// }
return (
<Box
component="section"
sx={{
border: "1px solid white",
display: "flex",
flexDirection: "column",
alignItems: "center",
width: "100%"
}}
>
<Typography variant="caption">
корзина
</Typography>
<Paper
variant="bar"
sx={{display:"flex", alignItems:"center", justifyContent: "space-between"}}
>
<FormControlLabel
label="НКО"
control={
<Checkbox
sx={{
color: theme.palette.secondary.main,
"&.Mui-checked": {
color: theme.palette.secondary.main,
},
}}
/>
}
sx={{
color: theme.palette.secondary.main,
}}
/>
<Input
label="внесено"
type="number"
size="small"
// onChange={(e:React.ChangeEvent<HTMLInputElement>) => setAdded(Number(e.target.value))}
/>
<Box
sx={{
border: "1px solid white",
padding: "3px",
display:"flex",
flexDirection:"column"
}}
>
<Input
label="промокод"
size="small"
/>
<Button sx={{maxWidth:"140px"}}>применить промокод</Button>
</Box>
<Button>рассчитать</Button>
</Paper>
{/*<Table sx={{*/}
{/* width: "90%",*/}
{/* margin: "5px",*/}
{/* border: "2px solid",*/}
{/* borderColor: theme.palette.secondary.main,*/}
{/*}} aria-label="simple table">*/}
{/* <TableHead>*/}
{/* <TableRow sx={{*/}
{/* borderBottom: "2px solid",*/}
{/* borderColor: theme.palette.grayLight.main,*/}
{/* height: "100px"*/}
{/* }}>*/}
{/* <TableCell>*/}
{/* <Typography*/}
{/* variant="h4"*/}
{/* sx={{*/}
{/* color: theme.palette.secondary.main,*/}
{/* }}>*/}
{/* Имя*/}
{/* </Typography>*/}
{/* </TableCell>*/}
{/* <TableCell>*/}
{/* <Typography*/}
{/* variant="h4"*/}
{/* sx={{*/}
{/* color: theme.palette.secondary.main,*/}
{/* }}>*/}
{/* Описание*/}
{/* </Typography>*/}
{/* </TableCell>*/}
{/* <TableCell>*/}
{/* <Typography*/}
{/* variant="h4"*/}
{/* sx={{*/}
{/* color: theme.palette.secondary.main,*/}
{/* }}>*/}
{/* стоимость*/}
{/* </Typography>*/}
{/* </TableCell>*/}
{/* </TableRow>*/}
{/* </TableHead>*/}
{/* <TableBody>*/}
{/* </TableBody>*/}
{/*</Table>*/}
<Typography id="transition-modal-title" variant="h6" sx={{
fontWeight: "normal",
textAlign: "center",
marginTop: "15px",
fontSize: "16px"
}}>
Скидки:
</Typography>
<Typography id="transition-modal-title" variant="h6" sx={{
fontWeight: "normal",
textAlign: "center",
marginTop: "10px"
}}>
ИТОГО:
</Typography>
</Box>
)
}

26
src/kitUI/datagrid.tsx Normal file

@ -0,0 +1,26 @@
import { DataGrid } from "@mui/x-data-grid";
import { styled } from "@mui/material/styles";
export default styled(DataGrid)(({ theme }) => ({
width: "100%",
minHeight: "400px",
margin: "10px 0",
color: theme.palette.secondary.main,
"& .MuiDataGrid-iconSeparator": {
display: "none"
},
"& .css-levciy-MuiTablePagination-displayedRows": {
color: theme.palette.secondary.main
},
"& .MuiSvgIcon-root": {
color: theme.palette.secondary.main
},
"& .MuiTablePagination-selectLabel": {
color: theme.palette.secondary.main
},
"& .MuiInputBase-root": {
color: theme.palette.secondary.main
},
"& .MuiButton-text": {
color: theme.palette.secondary.main
},
}));

16
src/kitUI/input.tsx Normal file

@ -0,0 +1,16 @@
import {TextField} from "@mui/material";
import { styled } from "@mui/material/styles";
export default styled(TextField)(({ theme }) => ({
variant: "outlined",
height: "40px",
size: "small",
color: theme.palette.secondary.main,
width: "140px",
backgroundColor: theme.palette.content.main,
"& .MuiFormLabel-root": {
color: theme.palette.secondary.main,
},
"& .Mui-focused": {
color: theme.palette.secondary.main,
}
}));

@ -0,0 +1,11 @@
export type Privilege = {
serviceKey: string;
name: string;
description: string;
type: string;
price: number;
}
export interface State {
privileges: { [key: string]: Privilege } | {},
tariffsUpdate: (element:Privilege) => void
}

@ -13,7 +13,7 @@ import { DataGrid, GridColDef, GridSelectionModel, GridToolbar } from "@mui/x-da
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@mui/material/MenuItem";
import Select, { SelectChangeEvent } from "@mui/material/Select"; import Select, { SelectChangeEvent } from "@mui/material/Select";
import { PrivilegesProps, DiscountProps } from "./types"; import { PrivilegesProps, DiscountProps } from "./types";
import useStore, { StoreState } from "../../../../store"; import useStore, { StoreState } from "../../../../stores/store";
import theme from "../../../../theme"; import theme from "../../../../theme";
import {styled} from "@mui/material/styles"; import {styled} from "@mui/material/styles";

@ -13,7 +13,7 @@ import { DataGrid, GridColDef, GridSelectionModel, GridToolbar } from "@mui/x-da
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@mui/material/MenuItem";
import Select, { SelectChangeEvent } from "@mui/material/Select"; import Select, { SelectChangeEvent } from "@mui/material/Select";
import { PrivilegesProps, PromocodeProps } from "./types"; import { PrivilegesProps, PromocodeProps } from "./types";
import useStore, { StoreState } from "../../../../store"; import useStore, { StoreState } from "../../../../stores/store";
import theme from "../../../../theme"; import theme from "../../../../theme";

@ -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;

@ -1,807 +0,0 @@
import * as React from "react";
import { Box, Button, Typography, TextField } from "@mui/material";
import { DataGrid, GridColDef, GridSelectionModel, GridToolbar } from "@mui/x-data-grid";
import { useDemoData } from "@mui/x-data-grid-generator";
import useStore, { StoreState } from "../../../../../store";
import { ArrayProps, CartSummary } from "../types";
import List from "@mui/material/List";
import ListItem from "@mui/material/ListItem";
import ListItemAvatar from "@mui/material/ListItemAvatar";
import Avatar from "@mui/material/Avatar";
import ShoppingCartIcon from "@mui/icons-material/ShoppingCart";
import ListItemText from "@mui/material/ListItemText";
import IconButton from "@mui/material/IconButton";
import DeleteIcon from "@mui/icons-material/Delete";
import FormControlLabel from "@mui/material/FormControlLabel";
import Checkbox from "@mui/material/Checkbox";
import { PrivilegesProps, PromocodeProps } from "../../Promocode/types";
import { DiscountProps } from "../../Discounts/types";
import theme from "../../../../../theme";
export interface MWProps {
openModal: () => void
}
const columns: GridColDef[] = [
{
field: "id",
headerName: "ID",
width: 30,
sortable: false,
},
{
field: "name",
headerName: "Название тарифа",
width: 200,
sortable: false,
},
{
field: "service",
headerName: "Сервис",
width: 210,
sortable: false,
},{
field: "disk",
headerName: "Гигабайты",
type: "number",
width: 110,
sortable: false,
},
{
field: "time",
headerName: "Время",
type: "number",
width: 110,
sortable: false,
},
{
field: "points",
headerName: "Объем",
width: 110,
sortable: false,
},
{
field: "price",
headerName: "Стоимость",
width: 160,
sortable: false,
},
{
field: "conditions",
headerName: "Условия",
width: 110,
sortable: false,
},
];
const rows = [
{ id: 1, name: "Тариф 1", type: "tariff", service: "Шаблонизатор", disk: "100 гб", time: "200 дней", points: "300", price: 100500 },
{ id: 2, name: "Тариф 2", type: "tariff", service: "Шаблонизатор", disk: "100 гб", time: "200 дней", points: "300", price: 100500 },
{ id: 3, name: "Тариф 3", type: "tariff", service: "Шаблонизатор", disk: "100 гб", time: "200 дней", points: "300", price: 100500 },
{ id: 4, name: "Пакет 1", type: "package", tariffs: [
{ id: 1, name: "Тариф 1", type: "tariff", service: "Шаблонизатор", disk: "100 гб", time: "200 дней", points: "300", price: 100500 },
{ id: 2, name: "Тариф 2", type: "tariff", service: "Шаблонизатор", disk: "100 гб", time: "200 дней", points: "300", price: 100500 },
] },
];
const rowz:Array<PromocodeProps> = [
{ 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
}
] }
];
const DataGridElement: React.FC<MWProps> = ({ openModal }) => {
let priceBefore = 0;
let price = 0;
let prices = 0;
let percents = 0;
let discounts = "";
let discountsSum = "";
const { discountsArray, discountsArraySet } = useStore<StoreState>((state) => state);
const { discountsActiveArray, discountsActiveArraySet } = useStore<StoreState>((state) => state);
const [nonCommercial, setNonCommercial] = React.useState(false);
const { data } = useDemoData({
dataSet: "Commodity",
rowLength: 10,
maxColumns: 5,
});
const { tariffsArray } = useStore<StoreState>((state) => state);
const tariffsArrayConverted = 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;
}
} );
const { tariffsSelectedRowsData, tariffsSelectedRowsDataSet } = useStore<StoreState>((state) => state);
const onRowsSelectionHandler = ( ids:GridSelectionModel ) => {
const result:Array<ArrayProps> = [];
ids.forEach((id) => tariffsArray.forEach( (row) => {
if(row.id === id) result.push(row);
} ) );
tariffsSelectedRowsDataSet( result );
};
const { cartRowsData, cartRowsDataSet } = useStore<StoreState>((state) => state);
const handleToBasket = () => {
cartRowsDataSet( tariffsSelectedRowsData );
}
const handleRemoveBasket = ( id:number ) => {
const cartFiltered = cartRowsData.filter( (row) => row.id != id );
cartRowsDataSet( cartFiltered );
}
const fieldPromocode = React.useRef<HTMLInputElement | null>(null);
const checkPromocode = () => {
if( fieldPromocode.current != null ) {
setPromocode( fieldPromocode.current.value );
}
}
let { promocodeArray, promocodeArraySet } = useStore<StoreState>((state) => state);
const [selectedPromocode, setSelectedPromocode] = React.useState( -1 );
//promocodeArray = [ ...rowz ];
const setPromocode = ( name:string ) => {
let codeNumber = -1;
promocodeArray.forEach( (item, i) => {
if( name != "" && item.name == name ) codeNumber = i;
} );
setSelectedPromocode( codeNumber );
}
const PositiveInput = (event:any) => {
const numberInput = parseInt(event.target.value);
if(isNaN(numberInput) || numberInput < 0) {event.target.value = '0'}
}
const fieldAdded = React.useRef<HTMLInputElement | null>(null);
const [ fieldAddedValue, setFieldAddedValue ] = React.useState("");
const changeAdded = (event:any) => {
if( fieldAdded.current != null ) {
if( fieldAdded.current.value != null ) {
setFieldAddedValue( fieldAdded.current.value );
}
}
PositiveInput(event)
};
const separator = (amount: number) => {
console.log(amount)
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(""); }
}
const cartSummary = new Map()
cartRowsData.forEach((row) => {
const svcName = row.service.split(" ")[0]
const prev = cartSummary.get(svcName)
console.log(row)
console.log(prev)
if (!prev) {
cartSummary.set(svcName, {
mbs: row.disk,
points: row.points,
days: row.time,
})
} else {
cartSummary.set(svcName, {
mbs: row.disk+prev.mbs,
points: row.points+prev.points,
days: row.time+prev.days,
})
}
})
console.log(cartSummary)
const fitDiscounts = discountsActiveArray.filter(e => {
const d = discountsArray[e]
const summary = cartSummary.get(d.privileges[0].good.split(' ')[0])
return d.incomeMore*100 < parseInt(fieldAddedValue) && d.incomeMore > 0 ||
d.toTime < (summary ? summary.days : 0) && d.toTime > 0 && d.toCapacity === 0 ||
d.toCapacity > 0 && d.toCapacity < (summary ? summary.points : 0) && d.toTime === 0 ||
d.toCapacity > 0 && d.toTime > 0 && d.toCapacity < (summary ? summary.points : 0) && d.toTime < (summary ? summary.days : 0) ||
!d.toCapacity && !d.toTime && !d.incomeMore && !d.basketMore ||
d.basketMore
}).filter((e,i,a)=>{
const d = discountsArray[e]
if (d.incomeMore) {
return d.incomeMore === a.reduce((a, e) => Math.max(a, discountsArray[e].incomeMore || 0), 0 )
}
if (d.toTime && d.toCapacity) {
return d.toTime === a.reduce((a, e) => Math.max(a, (discountsArray[e].toTime && discountsArray[e].toCapacity) ? discountsArray[e].toTime:0 ), 0 ) && d.toCapacity === a.reduce((a, e) => Math.max(a, (discountsArray[e].toCapacity && discountsArray[e].toTime) ? discountsArray[e].toCapacity : 0 ), 0 )
}
if (d.toTime && !d.toCapacity) {
return d.toTime === a.reduce((a, e) => Math.max(a, discountsArray[e].toTime && !discountsArray[e].toCapacity ? discountsArray[e].toTime : 0), 0 )
}
if (!d.toTime && d.toCapacity) {
return d.toCapacity === a.reduce((a, e) => Math.max(a, discountsArray[e].toCapacity && !discountsArray[e].toTime ? discountsArray[e].toCapacity : 0), 0 )
}
return true
})
console.log(fitDiscounts)
const discountsAfter = ( getPrice:number ) => {
const discounts:Array<number> = [];
priceBefore = getPrice;
prices = getPrice;
console.log(getPrice)
// применяем активные скидки за объем корзины
if( fitDiscounts.length >= 0 && !nonCommercial && selectedPromocode < 0 ) {
fitDiscounts.forEach( (activeDiscount) => {
const d = discountsArray[activeDiscount]
console.log(d)
console.log(fieldAddedValue)
if (d.basketMore > 0 && getPrice > d.basketMore && d.basketMore === fitDiscounts.reduce((a,e) => Math.max(a, discountsArray[e].basketMore), 0) ||
d.incomeMore > 0 && parseInt(fieldAddedValue) > d.incomeMore) {
prices *= (1-d.privileges[0].discount)
discounts.push(d.privileges[0].discount)
}
});
}
if( nonCommercial ) {
prices *= 0.2
return `80%`;
}
return discounts.map(e => `${(e*100).toFixed(2)}%`).join(' × ') + ` = ${(100 - discounts.reduce((a : number,cv : number) => a*(1-cv), 100)) .toFixed(2)}%`;
}
return (
<Box style={{ width: "93%" }}>
<Box style={{ height: 400 }}>
<DataGrid
checkboxSelection={true}
rows={ tariffsArrayConverted }
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) => onRowsSelectionHandler( ids ) }
/>
<Box sx={{
width: "100%",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center"
}}>
<Button
variant = "contained"
onClick={ () => openModal() }
sx={{
backgroundColor: theme.palette.menu.main,
padding: "6px 30px",
fontWeight: "normal",
fontSize: "17px",
marginTop: "45px",
marginBottom: "15px",
maxWidth: '320px',
width: '100%',
"&:hover": {
backgroundColor: theme.palette.grayMedium.main
}
}}>
Пакетизировать
</Button>
<Button
variant = "contained"
onClick={ () => handleToBasket() }
sx={{
backgroundColor: theme.palette.menu.main,
padding: "6px 69px",
fontWeight: "normal",
fontSize: "17px",
marginBottom: "95px",
"&:hover": {
backgroundColor: theme.palette.grayMedium.main
}
}}>
Сложить в корзину
</Button>
</Box>
<Box sx={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
marginBottom: "45px",
}}>
<Box sx={{
maxWidth: "480px",
width: '100%',
display: "flex",
justifyContent: "space-between",
}}>
<TextField
id = "standard-basic"
label = { "Ввести промокод" }
variant = "filled"
size="small"
color = "secondary"
sx={{
width: "200px",
height: "30px",
}}
InputProps={{
style: {
backgroundColor: theme.palette.content.main,
color: theme.palette.secondary.main,
} }}
InputLabelProps={{
style: {
color: theme.palette.secondary.main
} }}
inputRef={ fieldPromocode }
/>
<Button
variant = "contained"
onClick={ () => checkPromocode() }
sx={{
backgroundColor: theme.palette.menu.main,
width: "200px",
height: "48px",
fontWeight: "normal",
fontSize: "17px",
"&:hover": {
backgroundColor: theme.palette.grayMedium.main
}
}}>
Готово
</Button>
</Box>
{
selectedPromocode >= 0
? (
<Box>
<Box sx={{ marginTop: "35px", display: "flex" }}>
<Typography sx={{ color: theme.palette.grayDisabled.main, minWidth: "150px" }}>
Введен промокод:
</Typography>
<Typography sx={{ width: "100%", textAlign: "center" }}>
{ promocodeArray[ selectedPromocode ].name }
</Typography>
</Box>
<Box sx={{ display: "flex" }}>
<Typography sx={{ color: theme.palette.grayDisabled.main, minWidth: "150px" }}>
Привилегии: &ensp;
</Typography>
<Typography sx={{ width: "100%", textAlign: "center" }}>
{
promocodeArray[ selectedPromocode ].privileges.map( (item, i) => {
let period;
i < promocodeArray[ selectedPromocode ].privileges.length - 1
? period = ", "
: period = ""
return(
<>
{
`${item.good} - ${ Math.round(item.discount * 100) }%${period}`
}
</>
)
} )
}
</Typography>
</Box>
</Box>
) : null
}
</Box>
<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 control={
<Checkbox sx={{
color: theme.palette.secondary.main,
"&.Mui-checked": {
color: theme.palette.secondary.main,
},
}} onClick={ () => setNonCommercial(!nonCommercial) } />
} label="НКО" />
<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
} }}
inputRef={ fieldAdded }
onChange={ changeAdded }
/>
</Box>
<List sx={{
border: "1px solid",
borderColor: theme.palette.secondary.main,
maxWidth: '745px',
width: '100%',
}}>
<ListItem>
<ListItemAvatar>
<Avatar sx={{ backgroundColor: theme.palette.content.main }}>
<ShoppingCartIcon sx={{
color: theme.palette.content.main,
display: "none"
}} />
</Avatar>
</ListItemAvatar>
<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>
{ cartRowsData.map( (item) => {
price = item.price
const appliedDscnts: number[] = [];
if (!nonCommercial) {
if( item.type == "package" ) {
// считаем цену в ПАКЕТАХ
price = 0;
discounts = "";
priceBefore = 0;
if( item.tariffs ) {
item.tariffs.forEach( (tariff) => {
let tariffPrice = tariff.price;
percents = 0;
// применяем скидки по промокоду
if( selectedPromocode >= 0 ) {
promocodeArray[ selectedPromocode ].privileges.forEach( (privilege) => {
if( tariff.service == privilege.good ) {
percents = percents + privilege.discount;
if( discounts ) { discounts += " × "; }
discounts += `${ Math.round(privilege.discount * 100) }%`;
}
} )
}
// применяем активные скидки
if( discountsActiveArray.length >= 0 && selectedPromocode < 0 ) {
discountsActiveArray.forEach( (activeDiscount) => {
discountsArray.forEach( (discount, i) => {
if( i == activeDiscount ) {
discount.privileges.forEach( (privilege) => {
if( privilege.discount != 0 ) {
if( fieldAddedValue ) { // внесено
const f = Number(fieldAddedValue);
let minDiscount = 100;
let minI = -1;
discountsArray.forEach( (x, ii) => {
x.privileges.forEach( (y) => {
if( x.active && f - y.discount * 100 < minDiscount
&& f - y.discount * 100 > 0 ) {
minDiscount = f - y.discount * 100;
minI = ii;
}
});
});
if( minI >= 0 ) {
discountsArray[ minI ].privileges.forEach( (y) => {
percents = percents + y.discount / discountsActiveArray.length; // костыль
if( discounts ) { discounts += " × "; }
discounts += `${y.discount / discountsActiveArray.length * 100}%`;
});
}
} else { // не внесено
if( tariff.service == privilege.good ) {
percents = percents + privilege.discount;
if( discounts ) { discounts += " × "; }
discounts += `${ Math.round(privilege.discount * 100) }% `;
}
}
}
});
}
});
});
}
// применяем активные скидки по времени объему
if( discountsActiveArray.length >= 0 && selectedPromocode < 0 ) {
discountsActiveArray.forEach( (activeDiscount) => {
discountsArray.forEach( (discount, i) => {
if( i == activeDiscount ) {
if( tariff.time ) {
const dTime = 0.1;
percents = percents + dTime;
if( discounts ) discounts += " × ";
//if( dTime != 0.0 ) discounts += `${ Math.round(dTime * 100) }%`;
}
if( tariff.points ) {
//const cTime = discountCapacity( tariff.points );
//percents = percents + cTime;
//if( discounts ) discounts += " × ";
//if( cTime != 0 ) discounts += `${ Math.round(cTime * 100) }%`;
}
}
});
});
}
// применяем активные скидки на продукт
if( discountsActiveArray.length >= 0 && selectedPromocode < 0 ) {
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) }%`;
}
}
});
});
}
percents = Number( percents.toFixed(2) );
priceBefore += tariffPrice;
tariffPrice = tariffPrice - (tariffPrice * percents);
price += tariffPrice;
} );
}
} else {
// считаем цену в ТАРИФАХ
price = item.price;
percents = 0;
discounts = "";
// применяем скидки по промокоду
if( selectedPromocode >= 0 ) {
promocodeArray[ selectedPromocode ].privileges.forEach( (privilege) => {
console.log(item.service)
console.log(privilege.good)
if( item.service == privilege.good ) {
appliedDscnts.push(privilege.discount)
price *= (1 - privilege.discount)
}
} )
} else {
// применяем активные скидки
if( fitDiscounts.length >= 0 ) {
fitDiscounts.forEach( (activeDiscount) => {
const discount = discountsArray[activeDiscount]
discount.privileges.forEach((p) => {
const svcName = item.service.split(' ')[0]
if (p.good == svcName) {
const summary = cartSummary.get(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 && item.points > 0 && discount.toTime == 0
|| discount.toTime >0 && summary.days > discount.toTime*100 && item.time > 0 && discount.toCapacity == 0 ||
discount.toTime > 0 && discount.toCapacity > 0 && summary.days > discount.toTime*100 && summary.points > discount.toCapacity ){
price *= (1-p.discount)
appliedDscnts.push(p.discount)
}
}
})
});
}
}
percents = Number( percents.toFixed(2) );
priceBefore = price;
price = price - (price * percents);
}
}
prices += price;
return(
<ListItem key={ item.id }>
<ListItemAvatar>
<Avatar sx={{ backgroundColor: theme.palette.secondary.main }}>
<ShoppingCartIcon sx={{
color: theme.palette.content.main,
}} />
</Avatar>
</ListItemAvatar>
<ListItemText
primary={ item.name }
sx={{ minWidth: "250px", maxWidth: "250px" }}
/>
<ListItemText
primary={ `${separator(price)}` }
sx={{ textAlign: "center", minWidth: "200px", maxWidth: "200px" }}
/>
<ListItemText
primary={ `${(appliedDscnts.map(e=>(e*100).toFixed(2)).join(' × '))} = ${(100 - appliedDscnts.reduce((a : number,cv : number) => a*(1-cv), 100)).toFixed(2)}%` }
sx={{ textAlign: "center", minWidth: "400px", maxWidth: "400px" }}
/>
<IconButton edge="end" aria-label="delete" onClick={ () => handleRemoveBasket( item.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"
}}>
Скидки: &ensp; { discountsAfter(prices) }
</Typography>
<Typography id="transition-modal-title" variant="h6" sx={{
fontWeight: "normal",
textAlign: "center",
marginTop: "10px"
}}>
ИТОГО: &ensp; { separator( prices ) }
</Typography>
</List>
</Box>
</Box>
</Box>
);
}
export default DataGridElement;

@ -1,185 +0,0 @@
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 theme from "../../../../../theme";
export interface MWProps {
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 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 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 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); }
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
} ];
tariffsArraySet( tariffsArrayNew );
localStorage.setItem( "tariffs", JSON.stringify( tariffsArrayNew ) );
close();
console.log( tariffsArrayNew );
}
}
}
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
} }}
/>
<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 = "Цена"
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>
);
}
export default ModalMini;

@ -1,92 +0,0 @@
import * as React from "react";
import { Box, Modal, Fade, Backdrop, Button, TextField } from "@mui/material";
import theme from "../../../../../theme";
export interface MWProps {
open: boolean
newPackage: (name: string) => void
close: () => void
}
const ModalPackage = ({open, newPackage, close}: MWProps ) => {
const fieldName = React.useRef<HTMLInputElement | null>(null);
const checkName = () => {
if( fieldName.current != null ) {
newPackage( fieldName.current.value );
}
}
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: "170px",
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 = { "Название пакета" }
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 }
/>
<Button
variant = "contained"
onClick={ () => checkName() }
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 ModalPackage;

@ -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;

File diff suppressed because it is too large Load Diff

@ -0,0 +1,29 @@
import * as React from "react";
import { GridColDef } from "@mui/x-data-grid";
import DataGrid from "@kitUI/datagrid";
import privilegesStore from "@stores/privileges";
const columns: GridColDef[] = [
{ field: 'name', headerName: 'Привелегия', width: 150 },
{ field: 'description', headerName: 'Описание', width: 550 },//инфо из гитлаба.
{ field: 'type', headerName: 'Тип', width: 150 },
{ field: 'price', headerName: 'Стоимость', width: 50 }
]
export default () => {
const {privileges} = privilegesStore()
let rows = []
if (Object.keys(privileges).length !== 0) {
for (let [id, value] of Object.entries(privileges)) {
rows.push({id:id,name:value.name,description:value.description,type:value.type,price:value.price})
}
}
return (
<DataGrid
checkboxSelection={true}
rows={ rows }
columns={columns}
/>
);
}

@ -0,0 +1,27 @@
import * as React from "react";
import { GridColDef, GridSelectionModel, GridToolbar } from "@mui/x-data-grid";
import DataGrid from "@kitUI/datagrid";
const columns: GridColDef[] = [
{ field: 'id', headerName: 'ID', width: 150 },
{ field: 'id', headerName: 'Название тарифа', width: 150 },
{ field: 'id', headerName: 'Сервис', width: 150 },//инфо из гитлаба.
{ field: 'id', headerName: 'Гигабайты', width: 150 },
{ field: 'id', headerName: 'Привелегия', width: 150 },
{ field: 'id', headerName: 'Количество привелегии', width: 150 },
{ field: 'id', headerName: 'Условия', width: 150 },
]
export default () => {
return (
<DataGrid
checkboxSelection={true}
rows={ [] }
columns={columns}
components={{ Toolbar: GridToolbar }}
// onSelectionModelChange={ (ids) => onRowsSelectionHandler( ids ) }
/>
);
}

60
src/stores/privileges.ts Normal file

@ -0,0 +1,60 @@
import create from "zustand";
import {Privilege, State} from "@kitUI/types/privileges";
const useStore = create<State>()(
(set, get) => ({
privileges: {
p1: {
"serviceKey": "templategen",
"name": "unlim",
"description":"привилегия безлимитного доступа к шаблонизатору на время. в днях",
"type":"day",
"price": 0.5
},
p2: {
"serviceKey": "templategen",
"name": "gencount",
"description":"привилегия на определённое количество генераций",
"type":"count",
"price": 0.1
},
p3: {
"serviceKey": "squiz",
"name": "unlim",
"description":"привилегия безлимитного доступа к опроснику. в днях",
"type":"day",
"price": 3.0
},
p4: {
"serviceKey": "squiz",
"name": "activequiz",
"description":"привилегия создания ограниченного количества опросов",
"type":"count",
"price": 1.0
},
p5: {
"serviceKey": "dwarfener",
"name": "unlim",
"description":"привилегия безлимитного доступа к сокращателю на время. в днях",
"type":"day",
"price": 0.1
},
p6: {
"serviceKey": "dwarfener",
"name": "abcount",
"description":"привилегия на количество активных ссылок в абтестах",
"type":"count",
"price": 0.7
},
p7: {
"serviceKey": "dwarfener",
"name": "extended",
"description":"привилегия расширенной статистики, в днях",
"type":"day",
"price": 2
},
},
tariffsUpdate: (element:Privilege) => {set((state:State) => ({ privileges: {...state.privileges, ...element} }))}
})
);
export default useStore;

@ -1,8 +1,8 @@
import create from "zustand"; import create from "zustand";
import { persist } from "zustand/middleware" import { persist } from "zustand/middleware"
import { ArrayProps } from "./pages/dashboard/Content/Tariffs/types"; import { ArrayProps } from "../pages/dashboard/Content/Tariffs/types";
import { PromocodeProps } from "./pages/dashboard/Content/Promocode/types"; import { PromocodeProps } from "../pages/dashboard/Content/Promocode/types";
import { DiscountProps } from "./pages/dashboard/Content/Discounts/types"; import { DiscountProps } from "../pages/dashboard/Content/Discounts/types";
const useStore = create<StoreState>()( const useStore = create<StoreState>()(

18
src/stores/tariffs.ts Normal file

@ -0,0 +1,18 @@
import create from "zustand";
import { persist } from "zustand/middleware"
const useStore = create<any>()(
persist((set, get) => ({
tariffs: {},
tariffsUpdate: (element:any) => {set((state:any) => ({ tariffs: {...state.tariffs, ...element} }))},
tariffsClear: () => set({tariffs: {}}),
}),
{
name: "tariffs",
getStorage: () => localStorage,
}
));
export default useStore;

@ -1,180 +1,217 @@
import { createTheme, PaletteColorOptions } from "@mui/material"; import { Theme } from '@mui/material/styles';
import {createTheme, PaletteColorOptions} from "@mui/material";
import { deepmerge } from '@mui/utils';
//import { createTheme } from "./types"; //import { createTheme } from "./types";
declare module '@mui/material/Button' { declare module '@mui/material/Button' {
interface ButtonPropsVariantOverrides { interface ButtonPropsVariantOverrides {
enter: true; enter: true;
} }
}
declare module '@mui/material/Paper' {
interface PaperPropsVariantOverrides {
bar: true;
}
}
declare module '@mui/material/styles' {
interface Theme {
palette: {
primary: {
main: string
},
secondary: {
main: string;
},
menu: {
main: string;
},
content: {
main: string;
},
grayLight: {
main: string;
},
grayDark: {
main: string;
},
grayMedium: {
main: string;
},
grayDisabled: {
main: string;
},
golden: {
main: string;
},
goldenDark: {
main: string;
},
goldenMedium: {
main: string;
},
caption: {
main: string;
}
}
}
interface PaletteOptions {
menu?: PaletteColorOptions;
content?: PaletteColorOptions;
grayLight?: PaletteColorOptions;
grayDark?: PaletteColorOptions;
grayMedium?: PaletteColorOptions;
grayDisabled?: PaletteColorOptions;
golden?: PaletteColorOptions;
goldenDark?: PaletteColorOptions;
goldenMedium?: PaletteColorOptions;
hover?: PaletteColorOptions;
}
// allow configuration using `createTheme`
interface TypographyVariants {
body1: React.CSSProperties;
subtitle1: React.CSSProperties;
subtitle2: React.CSSProperties;
caption: React.CSSProperties;
h5: React.CSSProperties;
h6: React.CSSProperties;
button: React.CSSProperties;
}
} }
const fontFamily: string = "GilroyRegular"; const fontFamily: string = "GilroyRegular";
const fontWeight: string = "600"; const fontWeight: string = "600";
const theme = createTheme({ const options1 = {
palette: {
primary: {
main: "#111217"
},
secondary: {
main: "#ffffff"
},
menu: {
main: "#2f3339"
},
content: {
main: "#26272c"
},
grayLight: {
main: "#707070"
},
grayDark: {
main: "#45494c"
},
grayMedium: {
main: "#424242"
},
grayDisabled: {
main: "#c0c1c3"
},
golden: {
main: "#eaba5b"
},
goldenDark: {
main: "#fe9903"
},
goldenMedium: {
main: "#2a2b1d"
}
},
typography: {
body1: {
fontFamily: fontFamily
},
subtitle1: {
fontFamily: fontFamily,
fontWeight: fontWeight,
fontSize: 25
},
subtitle2: {
fontFamily: fontFamily,
fontSize: 25,
textAlign: "center"
},
caption: {
fontFamily: fontFamily,
fontWeight: fontWeight,
fontSize: 21
},
h5: {
fontFamily: fontFamily,
fontWeight: fontWeight,
fontSize: 35
},
h6: {
fontFamily: fontFamily,
fontWeight: fontWeight,
fontSize: 18
},
button: {
fontFamily: fontFamily,
fontWeight: fontWeight,
fontSize: 22
},
h4: {
fontFamily: fontFamily,
fontWeight: fontWeight,
fontSize: 16
}
},
components: {
MuiButton: {
variants: [
{
props: {
variant: 'enter' },
style: {
backgroundColor: "#26272c",
padding: '12px 48px',
"&:hover": {
backgroundColor: "#2f3339"
}
},
},
],
},
},
});
declare module '@mui/material/styles' {
interface Theme {
palette: { palette: {
primary: { primary: {
main: string main: "#111217"
}, },
secondary: { secondary: {
main: string; main: "#e6e8ec"
}, },
menu: { menu: {
main: string; main: "#2f3339"
}, },
content: { content: {
main: string; main: "#26272c"
}, },
grayLight: { hover: {
main: string; main: "#191a1e"
}, },
grayDark: { grayLight: {
main: string; main: "#707070"
}, },
grayMedium: { grayDark: {
main: string; main: "#45494c"
}, },
grayDisabled: { grayMedium: {
main: string; main: "#424242"
}, },
golden: { grayDisabled: {
main: string; main: "#c0c1c3"
}, },
goldenDark: { golden: {
main: string; main: "#eaba5b"
}, },
goldenMedium: { goldenDark: {
main: string; main: "#fe9903"
}, },
caption: { goldenMedium: {
main: string; main: "#2a2b1d"
} }
} },
}
interface PaletteOptions {
menu?: PaletteColorOptions;
content?: PaletteColorOptions;
grayLight?: PaletteColorOptions;
grayDark?: PaletteColorOptions;
grayMedium?: PaletteColorOptions;
grayDisabled?: PaletteColorOptions;
golden?: PaletteColorOptions;
goldenDark?: PaletteColorOptions;
goldenMedium?: PaletteColorOptions;
}
// allow configuration using `createTheme`
interface TypographyVariants {
body1: React.CSSProperties;
subtitle1: React.CSSProperties;
subtitle2: React.CSSProperties;
caption: React.CSSProperties;
h5: React.CSSProperties;
h6: React.CSSProperties;
button: React.CSSProperties;
}
} }
const options2 = {
typography: {
body1: {
fontFamily: fontFamily
},
subtitle1: {
fontFamily: fontFamily,
fontWeight: fontWeight,
fontSize: 25
},
subtitle2: {
fontFamily: fontFamily,
fontSize: 25,
textAlign: "center"
},
caption: {
fontFamily: fontFamily,
fontWeight: fontWeight,
fontSize: 21
},
h5: {
fontFamily: fontFamily,
fontWeight: fontWeight,
fontSize: 35
},
h6: {
fontFamily: fontFamily,
fontWeight: fontWeight,
fontSize: 18
},
button: {
fontFamily: fontFamily,
fontWeight: fontWeight,
fontSize: 22
},
h4: {
fontFamily: fontFamily,
fontWeight: fontWeight,
fontSize: 16
}
},
components: {
MuiButton: {
styleOverrides: {
root: {
color: options1.palette.secondary.main,
backgroundColor: options1.palette.menu.main,
padding: "12px",
fontSize: "13px",
"&:hover": {
backgroundColor: options1.palette.hover.main,
}
}
},
variants: [
{
props: {
variant: 'enter'
},
style: {
color: options1.palette.secondary.main,
backgroundColor: options1.palette.content.main,
padding: '12px 48px',
"&:hover": {
backgroundColor: options1.palette.hover.main,
}
},
},
],
},
MuiPaper: {
variants: [
{
props: {
variant: "bar"
},
style: {
backgroundColor: options1.palette.grayMedium.main,
padding: "15px",
width: "100%"
}
}
]
}
},
};
const theme = createTheme(deepmerge(options1, options2));
export default theme; export default theme;

11
tsconfig.extend.json Normal file

@ -0,0 +1,11 @@
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"@theme": ["./theme.ts"],
"@root/*": ["./*"],
"@kitUI/*": ["./kitUI/*"],
"@stores/*": ["./stores/*"]
}
}
}

@ -1,4 +1,5 @@
{ {
"extends": "./tsconfig.extend.json",
"compilerOptions": { "compilerOptions": {
"target": "es5", "target": "es5",
"lib": [ "lib": [