Merge branch 'dev' into 'main'

Dev

See merge request frontend/admin!17
This commit is contained in:
Mikhail 2023-05-18 20:41:36 +00:00
commit 58fa8e8154
6 changed files with 74 additions and 108 deletions

@ -95,11 +95,13 @@ export default function Cart({ selectedTariffs }: Props) {
if (!isFinite(loyaltyValue)) loyaltyValue = 0; if (!isFinite(loyaltyValue)) loyaltyValue = 0;
const activeDiscounts = discounts.filter(discount => !discount.disabled);
const cartData = calcCartData({ const cartData = calcCartData({
user: testUser, user: testUser,
purchasesAmount: loyaltyValue, purchasesAmount: loyaltyValue,
cartItems, cartItems,
discounts, discounts: activeDiscounts,
isNonCommercial, isNonCommercial,
coupon: couponField, coupon: couponField,
}); });
@ -316,7 +318,7 @@ function DiscountTooltip({ discount, cartItemTotal }: {
discount: AnyDiscount; discount: AnyDiscount;
cartItemTotal?: CartItemTotal; cartItemTotal?: CartItemTotal;
}) { }) {
const discountText = formatDiscountFactor(findDiscountFactor(discount, cartItemTotal?.tariff.privilege.privilegeId)); const discountText = formatDiscountFactor(findDiscountFactor(discount));
return ( return (
<Tooltip title={ <Tooltip title={

@ -223,20 +223,20 @@ export function createCartItem(tariff: Tariff): CartItem {
return { tariff, price, id: "someId" }; return { tariff, price, id: "someId" };
} }
export function findDiscountFactor(discount: AnyDiscount, privilegeId?: string) { export function findDiscountFactor(discount: AnyDiscount): number {
switch (discount.conditionType) { switch (discount.conditionType) {
case "cartPurchasesAmount": case "cartPurchasesAmount":
return discount.factor; return discount.factor;
case "purchasesAmount": case "purchasesAmount":
return discount.factor; return discount.factor;
case "privilege": { case "privilege": {
const product = discount.target.products.find(product => product.privilegeId === privilegeId); const product = discount.target.products[0];
if (!product) throw new Error("Discount target product not found"); if (!product) throw new Error("Discount target product not found");
return product.factor; return product.factor;
} }
case "user": { case "user": {
const product = discount.target.products.find(product => product.privilegeId === privilegeId); const product = discount.target.products[0];
if (!product) throw new Error("Discount target product not found"); if (!product) throw new Error("Discount target product not found");
return product.factor; return product.factor;

@ -7,6 +7,7 @@ interface DiscountBase {
description: string; description: string;
/** Этап применения скидки */ /** Этап применения скидки */
layer: number; layer: number;
disabled?: boolean;
} }
export interface PurchasesAmountDiscount extends DiscountBase { export interface PurchasesAmountDiscount extends DiscountBase {

@ -13,10 +13,11 @@ import MenuItem from "@mui/material/MenuItem";
import Select, { SelectChangeEvent } from "@mui/material/Select"; import Select, { SelectChangeEvent } from "@mui/material/Select";
import theme from "../../../theme"; import theme from "../../../theme";
import { styled } from "@mui/material/styles"; import { styled } from "@mui/material/styles";
import { useDiscountStore } from "../../../stores/discounts"; import { activateDiscounts, deactivateDiscounts, setSelectedDiscountIds, useDiscountStore } from "../../../stores/discounts";
import { useState } from "react"; import { useState } from "react";
import { DiscountConditionType } from "@root/model/cart"; import { DiscountConditionType } from "@root/model/cart";
import { ServiceType } from "@root/model/tariff"; import { ServiceType } from "@root/model/tariff";
import { findDiscountFactor, formatDiscountFactor } from "@root/kitUI/Cart/calc";
const BoxButton = styled('div')(({ theme }) => ({ const BoxButton = styled('div')(({ theme }) => ({
@ -27,56 +28,6 @@ const BoxButton = styled('div')(({ theme }) => ({
const columns: GridColDef[] = [ const columns: GridColDef[] = [
// {
// 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: "active",
// headerName: "Активна",
// width: 100,
// sortable: false,
// },
// {
// field: "basketMore",
// headerName: "Корзина больше",
// width: 140,
// sortable: false,
// }
// ,
// {
// field: "toTime",
// headerName: "На время",
// width: 140,
// sortable: false,
// }
// ,
// {
// field: "toCapacity",
// headerName: "На объем",
// width: 140,
// sortable: false,
// },
{ {
field: "id", field: "id",
headerName: "ID", headerName: "ID",
@ -101,11 +52,24 @@ const columns: GridColDef[] = [
width: 120, width: 120,
sortable: false, sortable: false,
}, },
{
field: "factor",
headerName: "Процент скидки",
width: 120,
sortable: false,
},
{
field: "active",
headerName: "Активна",
width: 120,
sortable: false,
},
]; ];
const DiscountManagement: React.FC = () => { const DiscountManagement: React.FC = () => {
const discounts = useDiscountStore(state => state.discounts); const discounts = useDiscountStore(state => state.discounts);
const selectedDiscountIds = useDiscountStore(state => state.selectedDiscountIds);
const [isInfinite, setIsInfinite] = useState<boolean>(false); const [isInfinite, setIsInfinite] = useState<boolean>(false);
const [serviceType, setServiceType] = useState<ServiceType>("templategen"); const [serviceType, setServiceType] = useState<ServiceType>("templategen");
const [startDate, setStartDate] = useState<Date>(new Date()); const [startDate, setStartDate] = useState<Date>(new Date());
@ -127,14 +91,6 @@ const DiscountManagement: React.FC = () => {
// TODO // TODO
} }
function activateDiscounts() {
// TODO
}
function deactivateDiscounts() {
// TODO
}
// const discountsArrayConverted = discounts.map((item) => { // const discountsArrayConverted = discounts.map((item) => {
// const basketMorePerc = Math.round(Number(item.basketMore) * 100) + "%"; // const basketMorePerc = Math.round(Number(item.basketMore) * 100) + "%";
// const toTimePerc = Math.round(Number(item.toTime) * 100) + "%"; // const toTimePerc = Math.round(Number(item.toTime) * 100) + "%";
@ -173,16 +129,16 @@ const DiscountManagement: React.FC = () => {
// }); // });
const discountsGridData: GridRowsProp = discounts.map(discount => { const discountsGridData: GridRowsProp = discounts.map(discount => {
return { // TODO return {
id: discount._id, id: discount._id,
name: discount.name, name: discount.name,
description: discount.description, description: discount.description,
conditionType: discount.conditionType, conditionType: discount.conditionType,
factor: formatDiscountFactor(findDiscountFactor(discount)),
active: discount.disabled ? "🚫" : "✅",
}; };
}); });
console.log(discountsGridData);
return ( return (
<> <>
<LocalizationProvider dateAdapter={AdapterDayjs}> <LocalizationProvider dateAdapter={AdapterDayjs}>
@ -263,10 +219,9 @@ const DiscountManagement: React.FC = () => {
} }
}} }}
> >
<MenuItem value={"Шаблонизатор"}>Шаблонизатор</MenuItem> <MenuItem value={"templategen"}>Шаблонизатор</MenuItem>
<MenuItem value={"Опросник"}>Опросник</MenuItem> <MenuItem value={"squiz"}>Опросник</MenuItem>
<MenuItem value={"Аналитика сокращателя"}>Аналитика сокращателя</MenuItem> <MenuItem value={"dwarfener"}>Аналитика сокращателя</MenuItem>
<MenuItem value={"АБ тесты"}>АБ тесты</MenuItem>
</Select> </Select>
<TextField <TextField
@ -515,11 +470,13 @@ const DiscountManagement: React.FC = () => {
</Box> </Box>
<Box style={{ width: "80%", marginTop: "55px" }}> <Box style={{ width: "80%", marginTop: "55px" }}>
<Box style={{ height: 400 }}> <Box style={{ height: 600 }}>
<DataGrid <DataGrid
checkboxSelection={true} checkboxSelection={true}
rows={discountsGridData} rows={discountsGridData}
columns={columns} columns={columns}
selectionModel={selectedDiscountIds}
onSelectionModelChange={setSelectedDiscountIds}
sx={{ sx={{
color: theme.palette.secondary.main, color: theme.palette.secondary.main,
"& .MuiDataGrid-iconSeparator": { "& .MuiDataGrid-iconSeparator": {
@ -563,7 +520,7 @@ const DiscountManagement: React.FC = () => {
}}> }}>
<Button <Button
variant="contained" variant="contained"
onClick={activateDiscounts} onClick={deactivateDiscounts}
sx={{ sx={{
backgroundColor: theme.palette.menu.main, backgroundColor: theme.palette.menu.main,
width: "200px", width: "200px",
@ -580,7 +537,7 @@ const DiscountManagement: React.FC = () => {
<Button <Button
variant="contained" variant="contained"
onClick={deactivateDiscounts} onClick={activateDiscounts}
sx={{ sx={{
backgroundColor: theme.palette.menu.main, backgroundColor: theme.palette.menu.main,
width: "200px", width: "200px",
@ -591,7 +548,7 @@ const DiscountManagement: React.FC = () => {
backgroundColor: theme.palette.grayMedium.main backgroundColor: theme.palette.grayMedium.main
} }
}}> }}>
Применить Активировать
</Button> </Button>
</BoxButton> </BoxButton>
</Box> </Box>

@ -102,7 +102,7 @@ const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== "open"
const links: { path: string; element: JSX.Element; title: string; className: string }[] = [ const links: { path: string; element: JSX.Element; title: string; className: string }[] = [
{ path: "/users", element: <PersonOutlineOutlinedIcon />, title: "Информация о проекте", className: "menu" }, { path: "/users", element: <PersonOutlineOutlinedIcon />, title: "Информация о проекте", className: "menu" },
{ path: "/entities", element: <SettingsOutlinedIcon />, title: "Юридические лица", className: "menu" }, { path: "/entities", element: <SettingsOutlinedIcon />, title: "Юридические лица", className: "menu" },
{ path: "/tariffs", element: <BathtubOutlinedIcon />, title: "Шаблонизатор документов", className: "menu" }, { path: "/tariffs", element: <BathtubOutlinedIcon />, title: "Тарифы", className: "menu" },
{ path: "/discounts", element: <AddPhotoAlternateOutlinedIcon />, title: "Скидки", className: "menu" }, { path: "/discounts", element: <AddPhotoAlternateOutlinedIcon />, title: "Скидки", className: "menu" },
{ path: "/promocode", element: <NaturePeopleOutlinedIcon />, title: "Промокод", className: "menu" }, { path: "/promocode", element: <NaturePeopleOutlinedIcon />, title: "Промокод", className: "menu" },
{ path: "/settingRoles", element: <SettingsIcon />, title: "Настройки", className: "menu" }, { path: "/settingRoles", element: <SettingsIcon />, title: "Настройки", className: "menu" },

@ -1,49 +1,55 @@
import { GridSelectionModel } from "@mui/x-data-grid"; import { GridSelectionModel } from "@mui/x-data-grid";
import { AnyDiscount } from "@root/model/cart"; import { AnyDiscount } from "@root/model/cart";
import { create } from "zustand"; import { create } from "zustand";
import { devtools, persist } from "zustand/middleware"; import { devtools } from "zustand/middleware";
import { exampleCartValues } from "./mocks/exampleCartValues"; import { exampleCartValues } from "./mocks/exampleCartValues";
interface DiscountStore { interface DiscountStore {
discounts: AnyDiscount[]; discounts: AnyDiscount[];
selectedDiscountIds: GridSelectionModel, selectedDiscountIds: GridSelectionModel,
addDiscounts: (newDiscounts: AnyDiscount[]) => void;
deleteDiscounts: (discountIds: string[]) => void;
activateDiscounts: (discountIds: string[]) => void;
deactivateDiscounts: (discountIds: string[]) => void;
} }
export const useDiscountStore = create<DiscountStore>()( export const useDiscountStore = create<DiscountStore>()(
devtools( devtools(
// persist( (set, get) => ({
(set, get) => ({ discounts: exampleCartValues.discounts,
discounts: exampleCartValues.discounts, selectedDiscountIds: [],
selectedDiscountIds: [], }),
addDiscounts: newDiscounts => set(state => ({ discounts: [...state.discounts, ...newDiscounts] })),
deleteDiscounts: discountIdsToDelete => set(state => (
{ discounts: state.discounts.filter(discount => !discountIdsToDelete.includes(discount._id)) }
)),
activateDiscounts: discountIds => set(state => {
const filteredDiscounts = state.discounts.filter(discount => discountIds.includes(discount._id));
// TODO activate discounts, use immer js?
throw new Error("unimplemented");
}),
deactivateDiscounts: discountIds => set(state => {
const filteredDiscounts = state.discounts.filter(discount => discountIds.includes(discount._id));
// TODO deactivate discounts, use immer js?
throw new Error("unimplemented");
}),
}),
// {
// name: "discounts",
// getStorage: () => localStorage,
// }
// ),
{ {
name: "Discount store" name: "Discount store"
} }
) )
); );
export const addDiscounts = (newDiscounts: AnyDiscount[]) => useDiscountStore.setState(state => ({ discounts: [...state.discounts, ...newDiscounts] }));
export const setSelectedDiscountIds = (selectedDiscountIds: DiscountStore["selectedDiscountIds"]) => useDiscountStore.setState({ selectedDiscountIds });
export const activateDiscounts = () => useDiscountStore.setState(state => {
const discounts: AnyDiscount[] = [];
state.discounts.forEach(discount => {
if (!state.selectedDiscountIds.includes(discount._id)) return discounts.push(discount);
discounts.push({
...discount,
disabled: false,
});
});
return { discounts };
});
export const deactivateDiscounts = () => useDiscountStore.setState(state => {
const discounts: AnyDiscount[] = [];
state.discounts.forEach(discount => {
if (!state.selectedDiscountIds.includes(discount._id)) return discounts.push(discount);
discounts.push({
...discount,
disabled: true,
});
});
return { discounts };
});