From 02b6ec79e8d154483e7278ca9292864b4f022036 Mon Sep 17 00:00:00 2001 From: Tamara Date: Wed, 8 May 2024 05:10:07 +0300 Subject: [PATCH 1/2] =?UTF-8?q?=D1=84=D0=B8=D0=BB=D1=8C=D1=82=D1=80=D1=8B?= =?UTF-8?q?=20=D0=B0=D0=BA=D1=82=D0=B8=D0=B2=D0=BD=D0=BE=D1=81=D1=82=D0=B8?= =?UTF-8?q?=20=D0=BF=D1=80=D0=BE=D0=BC=D0=BE=D0=BA=D0=BE=D0=B4=D0=BE=D0=B2?= =?UTF-8?q?=20=D0=B8=20=D0=B8=D1=85=20=D1=81=D1=82=D0=B0=D1=82=D0=B8=D1=81?= =?UTF-8?q?=D1=82=D0=B8=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/promocode/requests.ts | 28 ++++++ src/api/promocode/swr.ts | 20 ++++- .../Content/PromocodeManagement/index.tsx | 34 ++++++- .../QuizStatistics/StastisticsPromocode.tsx | 90 ++++++++++++++++--- 4 files changed, 153 insertions(+), 19 deletions(-) diff --git a/src/api/promocode/requests.ts b/src/api/promocode/requests.ts index ed10142..debd1d2 100644 --- a/src/api/promocode/requests.ts +++ b/src/api/promocode/requests.ts @@ -71,6 +71,33 @@ export const getAllPromocodes = async () => { } }; +export const getNotActivePromocodes = async () => { + try { + const promocodes: Promocode[] = []; + + let page = 0; + while (true) { + const promocodeList = await getPromocodeList({ + limit: 100, + filter: { + active: false, + }, + page, + }); + + if (promocodeList.items.length === 0) break; + + promocodes.push(...promocodeList.items); + page++; + } + + return promocodes; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); + throw new Error(`Ошибка при получении списка промокодов. ${error}`); + } +}; + const createPromocode = async (body: CreatePromocodeBody) => { try { const createPromocodeResponse = await makeRequest< @@ -137,6 +164,7 @@ export const promocodeApi = { createPromocode, deletePromocode, getAllPromocodes, + getNotActivePromocodes, getPromocodeStatistics, createFastlink, }; diff --git a/src/api/promocode/swr.ts b/src/api/promocode/swr.ts index 2e58d67..7101514 100644 --- a/src/api/promocode/swr.ts +++ b/src/api/promocode/swr.ts @@ -13,16 +13,17 @@ export function usePromocodes( pageSize: number, promocodeId: string, to: number, - from: number + from: number, + active: boolean ) { const promocodesCountRef = useRef(0); const swrResponse = useSwr( - ["promocodes", page, pageSize], + ["promocodes", page, pageSize, active], async (key) => { const result = await promocodeApi.getPromocodeList({ limit: key[2], filter: { - active: true, + active: key[3], }, page: key[1], }); @@ -147,3 +148,16 @@ export function useAllPromocodes() { return data; } + +export function useNotActivePromocodes() { + const { data } = useSwr("notActivePromocodes", promocodeApi.getNotActivePromocodes, { + keepPreviousData: true, + suspense: true, + onError(err) { + console.error("Error fetching all promocodes", err); + enqueueSnackbar(err.message, { variant: "error" }); + }, + }); + + return data; +} diff --git a/src/pages/dashboard/Content/PromocodeManagement/index.tsx b/src/pages/dashboard/Content/PromocodeManagement/index.tsx index ba726f4..4e2a41b 100644 --- a/src/pages/dashboard/Content/PromocodeManagement/index.tsx +++ b/src/pages/dashboard/Content/PromocodeManagement/index.tsx @@ -1,5 +1,5 @@ import { useState } from "react"; -import { Box, Typography, useTheme } from "@mui/material"; +import {Box, Button, FormControl, InputLabel, MenuItem, Select, Typography, useTheme} from "@mui/material"; import { DataGrid, GridLoadingOverlay, GridToolbar } from "@mui/x-data-grid"; import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; @@ -9,6 +9,8 @@ import { CreatePromocodeForm } from "./CreatePromocodeForm"; import { usePromocodeGridColDef } from "./usePromocodeGridColDef"; import { StatisticsModal } from "./StatisticsModal"; import DeleteModal from "./DeleteModal"; +import {promocodeApi} from "@root/api/promocode/requests"; +import {SelectChangeEvent} from "@mui/material/Select"; export const PromocodeManagement = () => { const theme = useTheme(); @@ -22,6 +24,7 @@ export const PromocodeManagement = () => { const [to, setTo] = useState(0); const [from, setFrom] = useState(0); const [pageSize, setPageSize] = useState(10); + const [active, setActive] = useState(true) const { data, error, @@ -31,7 +34,7 @@ export const PromocodeManagement = () => { deletePromocode, createPromocode, createFastLink, - } = usePromocodes(page, pageSize, showStatisticsModalId, to, from); + } = usePromocodes(page, pageSize, showStatisticsModalId, to, from, active); const columns = usePromocodeGridColDef( setShowStatisticsModalId, deleteModalHC @@ -56,7 +59,32 @@ export const PromocodeManagement = () => { Создание промокода - + + + + + + { const [from, setFrom] = useState( moment(moment().subtract(4, "weeks")) ); const [to, setTo] = useState(moment()); + const [active, setActive] = useState(true) const promocodes = useAllPromocodes(); + const promocodesNot = useNotActivePromocodes(); const promocodeStatistics = usePromocodeStatistics({ to, from }); const theme = useTheme(); + const filterPromo = active ? promocodes : promocodesNot + console.log(promocodes, "active") + console.log(promocodesNot) + + const filteredPromoStatistics = () => { + let copyPromocodeStatistics:PropStatistic[] = [] + Object.entries(promocodeStatistics).map(([key, { Regs, Money }]) => { + + for(let i in filterPromo){ + if(filterPromo[i].id === key) { + copyPromocodeStatistics.push( + { + "id": key, + "Regs": Regs, + "Money": Money, + "codeword": filterPromo[i].codeword, + } + ) + + }} + + }) + return copyPromocodeStatistics + } + +const filteredStat = filteredPromoStatistics() + return ( <> Статистика промокодов + + + + + { - {Object.entries(promocodeStatistics).map(([key, { Regs, Money }]) => ( - + {filteredStat.map((stat) => ( + - {promocodes.find(({ id }) => id === key)?.codeword ?? ""} + {stat?.codeword} - {Regs} + {stat?.Regs} - {(Money / 100).toFixed(2)} + {(stat?.Money / 100).toFixed(2)} From dddc5a6b35ad0913b480f79c9ccaa75d994f73a4 Mon Sep 17 00:00:00 2001 From: Tamara Date: Wed, 15 May 2024 01:21:07 +0300 Subject: [PATCH 2/2] =?UTF-8?q?=D1=80=D0=B5=D0=B4=D0=B0=D0=BA=D1=82=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D0=BC=D0=BE=D0=BA=D0=BE=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/promocode/requests.ts | 17 +- src/api/promocode/swr.ts | 26 ++- src/model/promocodes.ts | 10 + .../Content/PromocodeManagement/EditModal.tsx | 202 ++++++++++++++++++ .../Content/PromocodeManagement/index.tsx | 11 + .../usePromocodeGridColDef.tsx | 22 +- 6 files changed, 284 insertions(+), 4 deletions(-) create mode 100644 src/pages/dashboard/Content/PromocodeManagement/EditModal.tsx diff --git a/src/api/promocode/requests.ts b/src/api/promocode/requests.ts index debd1d2..7ee5254 100644 --- a/src/api/promocode/requests.ts +++ b/src/api/promocode/requests.ts @@ -1,7 +1,7 @@ import makeRequest from "@root/api/makeRequest"; import type { - CreatePromocodeBody, + CreatePromocodeBody, EditPromocodeBody, GetPromocodeListBody, Promocode, PromocodeList, @@ -124,6 +124,20 @@ const createPromocode = async (body: CreatePromocodeBody) => { } }; +const editPromocode = async (body: EditPromocodeBody) => { + try { + const editPromocodeResponse = await makeRequest({ + url: process.env.REACT_APP_DOMAIN + "/codeword/promocode" + "/edit", + method: "PUT", + body, + }); + return [editPromocodeResponse] + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); + return [null, `Ошибка редактирования промокода. ${error}`]; + } +} + const deletePromocode = async (id: string): Promise => { try { await makeRequest({ @@ -162,6 +176,7 @@ const getPromocodeStatistics = async (id: string, from: number, to: number) => { export const promocodeApi = { getPromocodeList, createPromocode, + editPromocode, deletePromocode, getAllPromocodes, getNotActivePromocodes, diff --git a/src/api/promocode/swr.ts b/src/api/promocode/swr.ts index 7101514..300dd29 100644 --- a/src/api/promocode/swr.ts +++ b/src/api/promocode/swr.ts @@ -4,7 +4,7 @@ import { enqueueSnackbar } from "notistack"; import { promocodeApi } from "./requests"; import type { - CreatePromocodeBody, + CreatePromocodeBody, EditPromocodeBody, PromocodeList, } from "@root/model/promocodes"; @@ -55,6 +55,29 @@ export function usePromocodes( [page, pageSize] ); + const editPromocode = useCallback( + async function (body: EditPromocodeBody) { + try { + await promocodeApi.editPromocode(body); + mutate( + ["promocodes", page, pageSize, active], + ); + await promocodeApi.getPromocodeList({ + limit: pageSize, + filter: { + active: active, + }, + page: page, + }); + } catch (error) { + console.error("Error editing promocode", error); + if (error instanceof Error) + enqueueSnackbar(error.message, { variant: "error" }); + } + }, + [page, pageSize,] + ); + const deletePromocode = useCallback( async function (id: string) { try { @@ -130,6 +153,7 @@ export function usePromocodes( ...swrResponse, createPromocode, deletePromocode, + editPromocode, createFastLink, promocodeStatistics: promocodeStatistics.data, promocodesCount: promocodesCountRef.current, diff --git a/src/model/promocodes.ts b/src/model/promocodes.ts index d3805f6..7d490b7 100644 --- a/src/model/promocodes.ts +++ b/src/model/promocodes.ts @@ -49,3 +49,13 @@ export type PromocodeStatistics = { usageCount: number; usageMap: Record; }; + +export type EditPromocodeBody = { + id: string, + description: string, + greetings: string, + dueTo: number, + activationCount: number, + delete: boolean + +} diff --git a/src/pages/dashboard/Content/PromocodeManagement/EditModal.tsx b/src/pages/dashboard/Content/PromocodeManagement/EditModal.tsx new file mode 100644 index 0000000..c710623 --- /dev/null +++ b/src/pages/dashboard/Content/PromocodeManagement/EditModal.tsx @@ -0,0 +1,202 @@ +import { + Box, + Button, + Typography, + Modal, + TextField, + useTheme, + useMediaQuery, + TextFieldProps, +} from "@mui/material"; +import {CustomTextField} from "@kitUI/CustomTextField"; +import {DesktopDatePicker} from "@mui/x-date-pickers/DesktopDatePicker"; +import {useEffect, useState} from "react"; +import {EditPromocodeBody, Promocode} from "@root/model/promocodes"; +import {enqueueSnackbar} from "notistack"; + + +const host = window.location.hostname; +let isTest = host.includes("s"); + +type EditModalProps = { + id: string; + setId: (id: string) => void; + promocodes: Promocode[]; + editPromocode: (body: EditPromocodeBody) => Promise +}; + +export const EditModal = ({id, setId, promocodes, editPromocode}: EditModalProps) => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down(550)); + const promocode = promocodes.find((item) => item.id === id); + const [descriptionField, setDescriptionField] = useState(""); + const [greetingsField, setGreetingsField] = useState(""); + const [dueToField, setDueToField] = useState(0); + const [activationField, setActivationField] = useState(0); + + useEffect( + function setCurrentPromocodeFields() { + if (!promocode) return; + + setDescriptionField(promocode.description); + setGreetingsField(promocode.greetings); + setDueToField(promocode.dueTo) + setActivationField(promocode.activationCount) + }, + [promocode] + ); + + + async function handleEditClick() { + if (!promocode) return enqueueSnackbar(`Тариф ${id} не найден`); + + if (!descriptionField) return enqueueSnackbar('Поле "Описание" пустое'); + if (!greetingsField) return enqueueSnackbar('Поле "Приветственное сообщение" пустое'); + + const editPromocodeBody: EditPromocodeBody = { + id: id, + description: descriptionField, + greetings: greetingsField, + dueTo: dueToField, + activationCount: activationField, + delete: promocode.delete + } + + await editPromocode(editPromocodeBody); + setId("") + + + } + + + return ( + { + setId(""); + }} + sx={{ + "& > .MuiBox-root": { outline: "none", padding: "32px 32px 16px" }, + }} + > + + + + Редактировать промокод: {promocode?.codeword} + {promocode && ( + <> + setDescriptionField(event.target.value)} + /> + setGreetingsField(event.target.value)} + /> + + Время существования промокода + + { + setDueToField(event.$d.getTime() / 1000 || 0); + }} + renderInput={(params: TextFieldProps) => } + InputProps={{ + sx: { + height: "40px", + color: theme.palette.secondary.main, + border: "1px solid", + borderColor: theme.palette.secondary.main, + "& .MuiSvgIcon-root": { color: theme.palette.secondary.main }, + }, + }} + /> + + + setActivationField(Number(target.value.replace(/\D/g, ""))) + } + /> + + + )} + + + + + + + + + + ); +}; diff --git a/src/pages/dashboard/Content/PromocodeManagement/index.tsx b/src/pages/dashboard/Content/PromocodeManagement/index.tsx index 4e2a41b..c5cce6a 100644 --- a/src/pages/dashboard/Content/PromocodeManagement/index.tsx +++ b/src/pages/dashboard/Content/PromocodeManagement/index.tsx @@ -11,6 +11,7 @@ import { StatisticsModal } from "./StatisticsModal"; import DeleteModal from "./DeleteModal"; import {promocodeApi} from "@root/api/promocode/requests"; import {SelectChangeEvent} from "@mui/material/Select"; +import {EditModal} from "@pages/dashboard/Content/PromocodeManagement/EditModal"; export const PromocodeManagement = () => { const theme = useTheme(); @@ -20,6 +21,8 @@ export const PromocodeManagement = () => { const [showStatisticsModalId, setShowStatisticsModalId] = useState(""); + const [showEditModalId, setShowEditModalId] = + useState(""); const [page, setPage] = useState(0); const [to, setTo] = useState(0); const [from, setFrom] = useState(0); @@ -33,9 +36,11 @@ export const PromocodeManagement = () => { promocodeStatistics, deletePromocode, createPromocode, + editPromocode, createFastLink, } = usePromocodes(page, pageSize, showStatisticsModalId, to, from, active); const columns = usePromocodeGridColDef( + setShowEditModalId, setShowStatisticsModalId, deleteModalHC ); @@ -121,6 +126,12 @@ export const PromocodeManagement = () => { autoHeight /> + void, setStatistics: (id: string) => void, deletePromocode: (id: string) => void ) { @@ -64,6 +65,23 @@ export function usePromocodeGridColDef( sortable: false, valueGetter: ({ row }) => row.description, }, + { + field: "edit", + headerName: "", + width: 60, + sortable: false, + renderCell: (params) => { + return ( + { + setEdit(params.row.id); + }} + > + + + ); + }, + }, { field: "settings", headerName: "", @@ -96,6 +114,6 @@ export function usePromocodeGridColDef( }, }, ], - [deletePromocode, setStatistics] + [deletePromocode, setStatistics, setEdit] ); }