diff --git a/src/pages/dashboard/Content/Discounts/index.tsx b/src/pages/dashboard/Content/Discounts/index.tsx index 0bbc6fa..0990691 100644 --- a/src/pages/dashboard/Content/Discounts/index.tsx +++ b/src/pages/dashboard/Content/Discounts/index.tsx @@ -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,144 @@ const columns: GridColDef[] = [ } ]; -const rows:Array = [ - { 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 rows: Array = [ + { + 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 + }, ]; -const Discounts: React.FC = () => { +const Discounts: React.FC = () => { const [checkboxState, setCheckboxState] = React.useState(false); - const toggleCheckbox = () => { setCheckboxState( !checkboxState ); } + const toggleCheckbox = () => { setCheckboxState(!checkboxState); }; - const [value1, setValue1] = React.useState( new Date() ); - const [value2, setValue2] = React.useState( new Date() ); + const [value1, setValue1] = React.useState(new Date()); + const [value2, setValue2] = React.useState(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((state) => state); - //discountsArraySet( rows ); - const { discountsActiveArray, discountsActiveArraySet } = useStore((state) => state); - let discountsActiveArrayUpdated:Array; + const discountsActiveArray = useDiscountStore(state => state.discountsActiveArray); + const discountsActiveArraySet = useDiscountStore(state => state.setDiscountsActiveArray); + let discountsActiveArrayUpdated: Array; - const findActiveDiscounts = () => { - const actives:Array = []; - - discountsArray.forEach( (item, i) => { - if( item.active == true ) { actives.push( i ); } - } ); + const findActiveDiscounts = () => { + const actives: Array = []; - 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(null); const fieldDiscount = React.useRef(null); @@ -232,61 +243,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((state) => state); - - const onRowsSelectionHandler = ( ids:GridSelectionModel ) => { - const result:Array = []; - 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 = []; + 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 ( - - + { justifyContent: "center", alignItems: "center", color: theme.palette.secondary.main - }}> + }}> СКИДКИ { style: { backgroundColor: theme.palette.content.main, color: theme.palette.secondary.main, - } }} + } + }} InputLabelProps={{ style: { color: theme.palette.secondary.main - } }} - inputRef={ fieldName } + } + }} + inputRef={fieldName} /> - { color: theme.palette.grayDisabled.main, marginTop: "75px", paddingLeft: '10px', - }}> + }}> Условия: @@ -347,7 +361,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 +380,10 @@ 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} /> { style: { backgroundColor: theme.palette.content.main, color: theme.palette.secondary.main, - } }} + } + }} InputLabelProps={{ style: { color: theme.palette.secondary.main - } }} - inputRef={ fieldAddedMore } + } + }} + inputRef={fieldAddedMore} onChange={PositiveInput} /> { style: { backgroundColor: theme.palette.content.main, color: theme.palette.secondary.main, - } }} - InputLabelProps={{ - style: { - color: theme.palette.secondary.main - } }} - inputRef={ basketMore } - onChange={ PositiveInput } - /> - - - - - + + + + @@ -494,24 +518,24 @@ const Discounts: React.FC = () => { - + }}> Дата действия: - + { { if(e) { setValue1(e) } } } + value={value1} + onChange={(e) => { if (e) { setValue1(e); } }} renderInput={(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 } + } + }} /> { { if(e) { setValue2(e) } } } + value={value2} + onChange={(e) => { if (e) { setValue2(e); } }} renderInput={(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 } + } + }} /> @@ -564,7 +592,7 @@ const Discounts: React.FC = () => { width: "90%", marginTop: theme.spacing(2), }}> - { alignItems: "left", marginRight: theme.spacing(1) }}> - toggleCheckbox() } /> + }} onClick={() => toggleCheckbox()} /> - { - { justifyContent: "center", alignItems: "center" }}> - - + - { }, }} components={{ Toolbar: GridToolbar }} - onSelectionModelChange={ (ids) => onRowsSelectionHandler( ids ) } + onSelectionModelChange={(ids) => onRowsSelectionHandler(ids)} /> - { justifyContent: "space-between", flexWrap: 'wrap', }}> - - - + ); -} +}; export default Discounts; diff --git a/src/pages/dashboard/Content/Promocode/index.tsx b/src/pages/dashboard/Content/Promocode/index.tsx index dd6677f..76c810b 100644 --- a/src/pages/dashboard/Content/Promocode/index.tsx +++ b/src/pages/dashboard/Content/Promocode/index.tsx @@ -9,444 +9,460 @@ 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 = [ - { id: 1, name: "Промокод 1", endless: false, from: "", dueTo: "", privileges: [ - { - good: "Товар 1", - discount: 0.3 +const testPromocodes: Array = [ + { + id: 1, name: "Промокод 1", endless: false, from: "", dueTo: "", privileges: [ + { + good: "Шаблонизатор документов", + discount: 0.3 + }, + { + good: "Опросник", + discount: 0.2 + } + ] }, - { - good: "Товар 2", - discount: 0.2 - } - ] }, - { id: 2, name: "Промокод 2", endless: false, from: "", dueTo: "", privileges: [ - { - good: "Товар 3", - discount: 0.3 + { + id: 2, name: "Промокод 2", endless: false, from: "", dueTo: "", privileges: [ + { + good: "Шаблонизатор документов", + discount: 0.3 + }, + { + good: "Опросник", + discount: 0.2 + } + ] }, - { - good: "Товар 4", - discount: 0.2 - } - ] }, - { id: 3, name: "Промокод 3", endless: false, from: "", dueTo: "", privileges: [ - { - good: "Товар 5", - discount: 0.3 + { + id: 3, name: "Промокод 3", endless: false, from: "", dueTo: "", privileges: [ + { + good: "Шаблонизатор документов", + discount: 0.3 + }, + { + good: "Опросник", + discount: 0.2 + } + ] }, - { - good: "Товар 6", - discount: 0.2 - } - ] }, ]; -const Promocode: React.FC = () => { - const [checkboxState, setCheckboxState] = React.useState(false); - const toggleCheckbox = () => { setCheckboxState( !checkboxState ); } +const Promocodes: React.FC = () => { + const [checkboxState, setCheckboxState] = React.useState(false); + const toggleCheckbox = () => { setCheckboxState(!checkboxState); }; - const [value1, setValue1] = React.useState( new Date() ); - const [value2, setValue2] = React.useState( new Date() ); + const [value1, setValue1] = React.useState(new Date()); + const [value2, setValue2] = React.useState(new Date()); - const [service, setService] = React.useState("Шаблонизатор"); - const handleChange = (event: SelectChangeEvent) => { - setService(event.target.value as string); - }; + const [service, setService] = React.useState("Шаблонизатор документов"); + const handleChange = (event: SelectChangeEvent) => { + setService(event.target.value as ServiceType); + }; - const { promocodeArray, promocodeArraySet } = useStore((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 promocodeArray = usePromocodeStore(state => state.promocodeArray); + const promocodeArraySet = usePromocodeStore(state => state.setPromocodeArray); - const strFrom = dateFrom - ? `${dateFrom.getDate()}.${dateFrom.getMonth()}.${dateFrom.getFullYear()}` - : "-" + const promocodeArrayConverted = promocodeArray.map((item) => { + const dateFrom = item.from ? new Date(Number(item.from)) : ""; + const dateDueTo = item.from ? new Date(Number(item.dueTo)) : ""; - const strDueTo = dateDueTo - ? `${dateDueTo.getDate()}.${dateDueTo.getMonth()}.${dateDueTo.getFullYear()}` - : "-" + const strFrom = dateFrom + ? `${dateFrom.getDate()}.${dateFrom.getMonth()}.${dateFrom.getFullYear()}` + : "-"; - if( item.privileges.length ) { - const result = item.privileges.reduce( (acc, privilege) => { - acc = acc - ? `${acc}, ${privilege.good} - ${privilege.discount}%` - : `${privilege.good} - ${privilege.discount * 100}%`; + const strDueTo = dateDueTo + ? `${dateDueTo.getDate()}.${dateDueTo.getMonth()}.${dateDueTo.getFullYear()}` + : "-"; - return acc; - }, "" ); + if (item.privileges.length) { + const result = item.privileges.reduce((acc, privilege) => { + acc = acc + ? `${acc}, ${privilege.good} - ${privilege.discount}%` + : `${privilege.good} - ${privilege.discount * 100}%`; - return { ...item, privileges: result, from: strFrom, dueTo: strDueTo } - } else { - return { ...item, from: strFrom, dueTo: strDueTo } - } - } ); + return acc; + }, ""); - 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 - }] - } + return { ...item, privileges: result, from: strFrom, dueTo: strDueTo }; + } else { + return { ...item, from: strFrom, dueTo: strDueTo }; + } + }); - const promocodeArrayUpdated = [ ...promocodeArray, newPromocode ]; - promocodeArraySet( promocodeArrayUpdated ); - } + 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 fieldName = React.useRef(null); - const fieldDiscount = React.useRef(null); + const promocodeArrayUpdated = [...promocodeArray, newPromocode]; + promocodeArraySet(promocodeArrayUpdated); + }; - const checkFields = () => { - if( fieldName.current != null && fieldDiscount.current != null ) { - createPromocode( fieldName.current.value, Number(fieldDiscount.current.value) ); - } - } + const fieldName = React.useRef(null); + const fieldDiscount = React.useRef(null); - return ( - - - - ПРОМОКОД - + const checkFields = () => { + if (fieldName.current != null && fieldDiscount.current != null) { + createPromocode(fieldName.current.value, Number(fieldDiscount.current.value)); + } + }; - - {/**/} - {/* Название:*/} - {/**/} - + return ( + + + + ПРОМОКОД + - - Условия: - + + {/**/} + {/* Название:*/} + {/**/} + - + + Условия: + - + - - - - - - Работает, если заплатите 100500 денег - - - - - Вы должны будете продать душу дьяволу - - - -
-
+ - - Дата действия: - - - - С + + + + + + Работает, если заплатите 100500 денег + + + + + Вы должны будете продать душу дьяволу + + + +
+
- { if(e) { setValue1(e) } } } - renderInput={(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 } - } }} - /> + + Дата действия: + - по + + С - { if(e) { setValue2(e) } } } - renderInput={(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 } - } }} - /> - + { if (e) { setValue1(e); } }} + renderInput={(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 } + } + }} + /> + + по + + { if (e) { setValue2(e); } }} + renderInput={(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 } + } + }} + /> +
- - - toggleCheckbox() } /> - - - Бессрочно - - + + + toggleCheckbox()} /> + + + Бессрочно + + - - - - -
+ + + - - - console.log("datagrid select") } - /> - - - -
-
- ); -} +
+ + + + console.log("datagrid select")} + /> + + + +
+
+ ); +}; -export default Promocode; +export default Promocodes; diff --git a/src/pages/dashboard/Content/Tariffs/Cart.tsx b/src/pages/dashboard/Content/Tariffs/Cart.tsx new file mode 100644 index 0000000..294f6b0 --- /dev/null +++ b/src/pages/dashboard/Content/Tariffs/Cart.tsx @@ -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 = []; + + 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 ( + + + Корзина + + + + setIsNonCommercial(prev => !prev)} + />} + /> + + setAddedValueField(Number(e.target.value) >= 0 ? e.target.value : "")} + /> + + + + + + + + + + + + + {calculatedCartRowData.map((cartRow) => ( + + + + + + + + + (e * 100).toFixed(2)).join(' × '))} = ${(100 - cartRow.appliedDiscounts.reduce((a: number, cv: number) => a * (1 - cv), 100)).toFixed(2)}%`} + sx={{ textAlign: "center", maxWidth: "400px" }} + /> + handleRemoveBasket(cartRow.id)}> + + + + ))} + + + Скидки:   {discountText} + + + + ИТОГО:   {resultPrice} ₽ + + + + + ); +} \ No newline at end of file diff --git a/src/pages/dashboard/Content/Tariffs/Contractor/index.tsx b/src/pages/dashboard/Content/Tariffs/Contractor/index.tsx deleted file mode 100644 index 2a0e466..0000000 --- a/src/pages/dashboard/Content/Tariffs/Contractor/index.tsx +++ /dev/null @@ -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 = ({ openModal }) => { - return ( - - - Сокращатель ссылок - - - - - - - - - ); -} - - -export default Contractor; \ No newline at end of file diff --git a/src/pages/dashboard/Content/Tariffs/CustomButton.tsx b/src/pages/dashboard/Content/Tariffs/CustomButton.tsx new file mode 100644 index 0000000..37b5e3e --- /dev/null +++ b/src/pages/dashboard/Content/Tariffs/CustomButton.tsx @@ -0,0 +1,32 @@ +import { Button, SxProps, Theme, useTheme } from "@mui/material"; +import { MouseEventHandler, ReactNode } from "react"; + + +interface Props { + onClick?: MouseEventHandler; + children: ReactNode; + sx?: SxProps; +} + +export default function CustomButton({ onClick, children, sx }: Props) { + const theme = useTheme(); + + return ( + + ); +} \ No newline at end of file diff --git a/src/pages/dashboard/Content/Tariffs/CustomHeader.tsx b/src/pages/dashboard/Content/Tariffs/CustomHeader.tsx new file mode 100644 index 0000000..492d011 --- /dev/null +++ b/src/pages/dashboard/Content/Tariffs/CustomHeader.tsx @@ -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 ( + + {children} + + ); +} \ No newline at end of file diff --git a/src/pages/dashboard/Content/Tariffs/DataGridElement/index.tsx b/src/pages/dashboard/Content/Tariffs/DataGridElement/index.tsx index 5886a3c..c917530 100644 --- a/src/pages/dashboard/Content/Tariffs/DataGridElement/index.tsx +++ b/src/pages/dashboard/Content/Tariffs/DataGridElement/index.tsx @@ -1,799 +1,269 @@ import * as React from "react"; -import { Box, Button, Typography, TextField } from "@mui/material"; +import { Box, 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"; +import { useEffect, useMemo, useState } from "react"; +import { convertTariffs, formatPromocodePriveleges } from "../utils"; +import CustomButton from "../CustomButton"; +import Cart from "../Cart"; +import type { ArrayProps } from "../../../../../model/tariff"; +import { Promocode } from "../../../../../model/cart"; +import { usePromocodeStore } from "../../../../../stores/promocodes"; +import { useTariffStore } from "../../../../../stores/tariffs"; +import { useCartStore } from "../../../../../stores/cart"; export interface MWProps { - openModal: () => void + 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 = [ - { id: 1, name: "Промокод 1", endless: false, from: "", dueTo: "", privileges: [ - { - good: "Шаблонизатор", - discount: 0.15 + { + field: "id", + headerName: "ID", + width: 30, + sortable: false, }, - { - good: "Опросник", - discount: 0.3 - } - ] }, - { id: 1, name: "Промокод 2", endless: false, from: "", dueTo: "", privileges: [ - { - good: "Шаблонизатор", - discount: 0.4 + { + 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, }, - { - good: "Опросник", - discount: 0.6 - } - ] } ]; -const DataGridElement: React.FC = ({ openModal }) => { - let priceBefore = 0; - let price = 0; - let prices = 0; - let percents = 0; - let discounts = ""; - let discountsSum = ""; +/* const testTariffArray: ArrayProps[] = [ + { 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 { discountsArray, discountsArraySet } = useStore((state) => state); - const { discountsActiveArray, discountsActiveArraySet } = useStore((state) => state); - - const [nonCommercial, setNonCommercial] = React.useState(false); - - const { data } = useDemoData({ - dataSet: "Commodity", - rowLength: 10, - maxColumns: 5, - }); - - const { tariffsArray } = useStore((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 testPromocodeArray: Array = [ + { + 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 { tariffsSelectedRowsData, tariffsSelectedRowsDataSet } = useStore((state) => state); - const onRowsSelectionHandler = ( ids:GridSelectionModel ) => { - const result:Array = []; - ids.forEach((id) => tariffsArray.forEach( (row) => { - if(row.id === id) result.push(row); - } ) ); +const DataGridElement: React.FC = ({ openModal }) => { + const promocodeArray = usePromocodeStore(state => state.promocodeArray); + const promocodeArraySet = usePromocodeStore(state => state.setPromocodeArray); + const tariffsSelectedRowsData = useTariffStore(state => state.tariffsSelectedRowsData); + const tariffsSelectedRowsDataSet = useTariffStore(state => state.setTariffsSelectedRowsData); + const tariffsArray = useTariffStore(state => state.tariffs); + const cartRowsDataSet = useCartStore(state => state.setCartRowsData); + const [selectedPromocodeIndex, setSelectedPromocodeIndex] = useState(-1); + const [promocodeField, setPromocodeField] = useState(""); - tariffsSelectedRowsDataSet( result ); - }; + useEffect(function setTestPromocodeArray() { + promocodeArraySet(testPromocodeArray); + }, [promocodeArraySet]); - const { cartRowsData, cartRowsDataSet } = useStore((state) => state); - const handleToBasket = () => { - cartRowsDataSet( tariffsSelectedRowsData ); - } + const onRowsSelectionHandler = (ids: GridSelectionModel) => { + const result: Array = []; + ids.forEach((id) => tariffsArray.forEach((row) => { + if (row.id === id) result.push(row); + })); - const handleRemoveBasket = ( id:number ) => { - const cartFiltered = cartRowsData.filter( (row) => row.id != id ); - cartRowsDataSet( cartFiltered ); - } + tariffsSelectedRowsDataSet(result); + }; - const fieldPromocode = React.useRef(null); - const checkPromocode = () => { - if( fieldPromocode.current != null ) { - setPromocode( fieldPromocode.current.value ); - } - } - - let { promocodeArray, promocodeArraySet } = useStore((state) => state); - const [selectedPromocode, setSelectedPromocode] = React.useState( -1 ); + const handleToBasket = () => cartRowsDataSet(tariffsSelectedRowsData); - //promocodeArray = [ ...rowz ]; + const checkPromocode = () => setSelectedPromocodeIndex(promocodeArray.findIndex(promocode => promocode.name === promocodeField)); - const setPromocode = ( name:string ) => { - let codeNumber = -1; - - promocodeArray.forEach( (item, i) => { - if( name != "" && item.name == name ) codeNumber = i; - } ); + const tariffsArrayConverted = useMemo(() => convertTariffs(tariffsArray), [tariffsArray]); - setSelectedPromocode( codeNumber ); - } + return ( + + + onRowsSelectionHandler(ids)} + /> - const PositiveInput = (event:any) => { - const numberInput = parseInt(event.target.value); - if(isNaN(numberInput) || numberInput < 0) {event.target.value = '0'} - } + + openModal()} + sx={{ + padding: "6px 30px", + marginTop: "45px", + marginBottom: "15px", + maxWidth: '320px', + width: '100%', + }}> + Пакетизировать + - const fieldAdded = React.useRef(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 = []; - 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 = []; - 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 ( - - - onRowsSelectionHandler( ids ) } - /> - - - - - - - - - - - - - - - { - selectedPromocode >= 0 - ? ( - - - - Введен промокод: - - - { promocodeArray[ selectedPromocode ].name } - - - - - - Привилегии:   - - - - { - 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}` - } - - ) - } ) - } - - + handleToBasket()} + sx={{ + padding: "6px 69px", + marginBottom: "95px", + }}> + Сложить в корзину + - ) : null - } - - - - - Корзина - - - - setNonCommercial(!nonCommercial) } /> - } label="НКО" /> - - - - - - - - - - - - - - - { 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) }% `; - } - } + + + setPromocodeField(e.target.value)} + /> + + checkPromocode()} + sx={{ + width: "200px", + height: "48px", + }}> + Готово + + + + {selectedPromocodeIndex >= 0 && + + + + Введен промокод: + + + {promocodeArray[selectedPromocodeIndex].name} + + + + + + Привилегии:   + + + + + {formatPromocodePriveleges(promocodeArray[selectedPromocodeIndex])} + + + + } + - // применяем активные скидки по времени объему - 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( - - - - - - - - - (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" }} - /> - handleRemoveBasket( item.id ) }> - - - - ) - } ) } - - - Скидки:   { discountsAfter(prices) } - - - - ИТОГО:   { separator( prices ) } ₽ - - - + - - - - ); -} + ); +}; export default DataGridElement; diff --git a/src/pages/dashboard/Content/Tariffs/ModalMini/index.tsx b/src/pages/dashboard/Content/Tariffs/ModalMini/index.tsx index 2e74a13..c3b3f61 100644 --- a/src/pages/dashboard/Content/Tariffs/ModalMini/index.tsx +++ b/src/pages/dashboard/Content/Tariffs/ModalMini/index.tsx @@ -1,185 +1,193 @@ 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 = useStore((state) => state.tariffsArray); - const { tariffsArraySet } = useStore((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(null); - const fieldTime = React.useRef(null); - const fieldPrice = React.useRef(null); + const fieldName = React.useRef(null); + const fieldTime = React.useRef(null); + const fieldPrice = React.useRef(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 getData = localStorage.getItem("tariffs"); - const data = [ 0, 0, 0 ]; + if (getData != null) { tariffsArray = JSON.parse(getData); } - 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 data = [0, 0, 0]; - 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 - } ]; + 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); } - tariffsArraySet( tariffsArrayNew ); - - localStorage.setItem( "tariffs", JSON.stringify( tariffsArrayNew ) ); - close(); + 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]; - console.log( tariffsArrayNew ); - } - } - } + tariffsArraySet(tariffsArrayNew); - return ( - - close() } - closeAfterTransition - BackdropComponent={Backdrop} - BackdropProps={{ - timeout: 500, - }} - > - - - + localStorage.setItem("tariffs", JSON.stringify(tariffsArrayNew)); + close(); - + console.log(tariffsArrayNew); + } + } + }; - + return ( + + close()} + closeAfterTransition + BackdropComponent={Backdrop} + BackdropProps={{ + timeout: 500, + }} + > + + + - + + + + + - - - - - - ); -} + + + + + + ); +}; export default ModalMini; diff --git a/src/pages/dashboard/Content/Tariffs/Quiz/index.tsx b/src/pages/dashboard/Content/Tariffs/Quiz/index.tsx deleted file mode 100644 index 30ccc25..0000000 --- a/src/pages/dashboard/Content/Tariffs/Quiz/index.tsx +++ /dev/null @@ -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 = ({ openModal }) => { - return ( - - - Опросник - - - - - - - - - ); -} - - -export default Quiz; \ No newline at end of file diff --git a/src/pages/dashboard/Content/Tariffs/Templater/index.tsx b/src/pages/dashboard/Content/Tariffs/Templater/index.tsx deleted file mode 100644 index 4a5b9e7..0000000 --- a/src/pages/dashboard/Content/Tariffs/Templater/index.tsx +++ /dev/null @@ -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 = ({ openModal }) => { - return ( - - - Шаблонизатор документов - - - - - - - - - - ); -} - - -export default Templater; \ No newline at end of file diff --git a/src/pages/dashboard/Content/Tariffs/index.tsx b/src/pages/dashboard/Content/Tariffs/index.tsx index 00cdc5d..a910a3c 100644 --- a/src/pages/dashboard/Content/Tariffs/index.tsx +++ b/src/pages/dashboard/Content/Tariffs/index.tsx @@ -1,97 +1,131 @@ 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 + {children} + ; +}; - 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 = JSON.parse(getData); - if( rows.length ) { store.tariffsArraySet( rows ); }; - } + const getData = localStorage.getItem("tariffs"); + 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 newPackage = ( name:string ) => { - const tariffs:Array = []; - - store.tariffsSelectedRowsData.forEach( (item) => { - if( item.type === "package" && item.tariffs ) { - tariffs.push( ...item.tariffs ); - } else { - tariffs.push( item ); - } - } ); - - const uniqueArray:Array = []; - tariffs.forEach( (tariff) => { - if( uniqueArray.findIndex( (a) => a.id === tariff.id ) < 0 ) { - uniqueArray.push( tariff ); - } - } ); - - const packageCreated:ArrayProps = { - name, - id: new Date().getTime(), - type: "package", - tariffs: uniqueArray, - service: "", - disk: 0, - time: 0, - points: 0, - price: 0 + if (getData && !tariffsArray.length) { + const rows: Array = JSON.parse(getData); + if (rows.length) { tariffsArraySet(rows); }; } - store.tariffsArraySet( [...store.tariffsArray, packageCreated] ); - handleCloseModalPackage(); - } + const [openModalPackage, setOpenModalPackage] = React.useState(false); - return ( - - - - - - - + const handleOpenModalPackage = () => { setOpenModalPackage(true); }; + const handleCloseModalPackage = () => { setOpenModalPackage(false); }; - - - - ); -} + const newPackage = (name: string) => { + const tariffs: Array = []; + + tariffsSelectedRowsData.forEach((item) => { + if (item.type === "package" && item.tariffs) { + tariffs.push(...item.tariffs); + } else { + tariffs.push(item); + } + }); + + const uniqueArray: Array = []; + tariffs.forEach((tariff) => { + if (uniqueArray.findIndex((a) => a.id === tariff.id) < 0) { + uniqueArray.push(tariff); + } + }); + + const packageCreated: ArrayProps = { + name, + id: new Date().getTime(), + type: "package", + tariffs: uniqueArray, + service: "", + disk: 0, + time: 0, + points: 0, + price: 0 + }; + + tariffsArraySet([...tariffsArray, packageCreated]); + handleCloseModalPackage(); + }; + + return ( + <> + + Шаблонизатор документов + + setUpModalMini(1, 1)}>Создать тариф на время + setUpModalMini(1, 0)}>Создать тариф на объем + setUpModalMini(1, 2)}>Создать тариф на гигабайты + Изменить тариф + + Опросник + + setUpModalMini(2, 1)}>Создать тариф на время + setUpModalMini(2, 0)}>Создать тариф на объем + Изменить тариф + + Сокращатель ссылок + + setUpModalMini(3, 1)} sx={{ padding: '11px 65px' }}> + Создать тариф
на аналитику время +
+ setUpModalMini(3, 1)} sx={{ padding: '11px 65px' }}> + Создать тариф
на a/b тесты время +
+ + Изменить тариф + +
+ +
+ + + + ); +}; export default Tariffs; diff --git a/src/pages/dashboard/Content/Tariffs/utils.ts b/src/pages/dashboard/Content/Tariffs/utils.ts new file mode 100644 index 0000000..3f7a8b0 --- /dev/null +++ b/src/pages/dashboard/Content/Tariffs/utils.ts @@ -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 = []; + 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; + } + }); +} \ No newline at end of file diff --git a/src/pages/dashboard/Content/index.tsx b/src/pages/dashboard/Content/index.tsx index ebbd3cf..40238a2 100644 --- a/src/pages/dashboard/Content/index.tsx +++ b/src/pages/dashboard/Content/index.tsx @@ -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 = ({ section }) => { , , , - , + , , ,