Merge branch 'dev' into 'staging'

Dev

See merge request frontend/squiz!297
This commit is contained in:
Nastya 2024-05-04 00:42:40 +00:00
commit 4e457e9294
7 changed files with 179 additions and 63 deletions

22
src/api/discounts.ts Normal file

@ -0,0 +1,22 @@
import { makeRequest } from "@frontend/kitui";
import { parseAxiosError } from "@utils/parse-error";
import type { Discount } from "@model/discounts";
const API_URL = process.env.REACT_APP_DOMAIN + "/price/discount";
export async function getDiscounts(
userId: string,
): Promise<[Discount[] | null, string?]> {
try {
const { Discounts } = await makeRequest<unknown, { Discounts: Discount[] }>(
{ method: "GET", url: `${API_URL}/user/${userId}` },
);
return [Discounts];
} catch (nativeError) {
const [error] = parseAxiosError(nativeError);
return [null, `Не удалось получить скидки. ${error}`];
}
}

49
src/model/discounts.ts Normal file

@ -0,0 +1,49 @@
type Audit = {
UpdatedAt: Date;
CreatedAt: Date;
Deleted: boolean;
};
type Condition = {
Period: Period;
User: string;
UserType: string;
Coupon: string;
PurchasesAmount: string;
CartPurchasesAmount: string;
Product: string;
Term: string;
Usage: string;
PriceFrom: string;
Group: string;
};
type Period = {
From: Date;
To: Date;
};
type Target = {
Products: Product[];
Factor: number;
TargetScope: string;
TargetGroup: string;
Overhelm: boolean;
};
type Product = {
ID: string;
Factor: number;
Overhelm: boolean;
};
export type Discount = {
ID: string;
Name: string;
Layer: number;
Description: string;
Condition: Condition;
Target: Target;
Audit: Audit;
Deprecated: boolean;
};

@ -75,7 +75,7 @@ const FunnelItem = ({ title, percent, index, funnel }: FunnelItemProps) => {
<Box sx={{ display: "flex", alignItems: "center", flexGrow: 1 }}> <Box sx={{ display: "flex", alignItems: "center", flexGrow: 1 }}>
<LinearProgress <LinearProgress
variant="determinate" variant="determinate"
value={percent * 100 > 100 ? 100 : percent * 10} value={percent * 100 > 100 ? 100 : percent * 100}
sx={{ sx={{
width: "100%", width: "100%",
marginRight: "15px", marginRight: "15px",

@ -233,15 +233,17 @@ const SettingField = ({
}} }}
> >
{quiz.config.formContact.fields[type].innerText || placeholder} {quiz.config.formContact.fields[type].innerText || placeholder}
<IconButton {type === "text" && (
onClick={() => drawerNewFieldHC({ field: type, isEdit: true })} <IconButton
sx={{ onClick={() => drawerNewFieldHC({ field: type, isEdit: true })}
position: "absolute", sx={{
right: "0", position: "absolute",
}} right: "0",
> }}
<GearIcon height="20px" width="20px" color="#7e2aea" /> >
</IconButton> <GearIcon height="20px" width="20px" color="#7e2aea" />
</IconButton>
)}
</Typography> </Typography>
{(type !== "email" || quiz?.config.resultInfo.when !== "email") && ( {(type !== "email" || quiz?.config.resultInfo.when !== "email") && (
<IconButton <IconButton

@ -32,6 +32,9 @@ import { createTariffElements } from "./tariffsUtils/createTariffElements";
import { currencyFormatter } from "./tariffsUtils/currencyFormatter"; import { currencyFormatter } from "./tariffsUtils/currencyFormatter";
import { useWallet, setCash } from "@root/cash"; import { useWallet, setCash } from "@root/cash";
import { handleLogoutClick } from "@utils/HandleLogoutClick"; import { handleLogoutClick } from "@utils/HandleLogoutClick";
import { getDiscounts } from "@api/discounts";
import type { Discount } from "@model/discounts";
const StepperText: Record<string, string> = { const StepperText: Record<string, string> = {
day: "Тарифы на время", day: "Тарифы на время",
@ -48,7 +51,7 @@ function TariffPage() {
const navigate = useNavigate(); const navigate = useNavigate();
const [tariffs, setTariffs] = useState<Tariff[]>([]); const [tariffs, setTariffs] = useState<Tariff[]>([]);
const [user, setUser] = useState(); const [user, setUser] = useState();
const [discounts, setDiscounts] = useState(); const [discounts, setDiscounts] = useState<Discount[]>([]);
const [openModal, setOpenModal] = useState({}); const [openModal, setOpenModal] = useState({});
const { cashString, cashCop, cashRub } = useWallet(); const { cashString, cashCop, cashRub } = useWallet();
const [selectedItem, setSelectedItem] = useState<"count" | "day" | "dop">( const [selectedItem, setSelectedItem] = useState<"count" | "day" | "dop">(
@ -90,13 +93,18 @@ function TariffPage() {
url: process.env.REACT_APP_DOMAIN + "/customer/account", url: process.env.REACT_APP_DOMAIN + "/customer/account",
}); });
const tariffsList = await getTariffsList(); const tariffsList = await getTariffsList();
const discounts = await makeRequest({
method: "GET", if (userId) {
url: `${process.env.REACT_APP_DOMAIN}/price/discount/user/${userId}`, const [discounts] = await getDiscounts(userId);
});
if (discounts?.length) {
setDiscounts(discounts);
}
}
setUser(user); setUser(user);
setTariffs(tariffsList); setTariffs(tariffsList);
setDiscounts(discounts.Discounts);
let cs = currencyFormatter.format(Number(user.wallet.cash) / 100); let cs = currencyFormatter.format(Number(user.wallet.cash) / 100);
let cc = Number(user.wallet.cash); let cc = Number(user.wallet.cash);
let cr = Number(user.wallet.cash) / 100; let cr = Number(user.wallet.cash) / 100;
@ -178,11 +186,19 @@ function TariffPage() {
.then(async (greetings) => { .then(async (greetings) => {
enqueueSnackbar(greetings); enqueueSnackbar(greetings);
const discounts = await makeRequest({ if (!userId) {
method: "GET", return;
url: `${process.env.REACT_APP_DOMAIN}/price/discount/user/${userId}`, }
});
setDiscounts(discounts.Discounts); const [discounts, discountsError] = await getDiscounts(userId);
if (discountsError) {
throw new Error(discountsError);
}
if (discounts?.length) {
setDiscounts(discounts);
}
}) })
.catch(enqueueSnackbar); .catch(enqueueSnackbar);
} }

@ -1,25 +1,35 @@
import { useEffect, useLayoutEffect, useState } from "react"; import { useEffect, useLayoutEffect, useState } from "react";
import { useUserStore } from "@root/user";
import { Box, Button, Modal, Typography } from "@mui/material"; import { Box, Button, Modal, Typography } from "@mui/material";
import { mutate } from "swr";
import { enqueueSnackbar } from "notistack"; import { enqueueSnackbar } from "notistack";
import { mutate } from "swr";
import makeRequest from "@api/makeRequest"; import makeRequest from "@api/makeRequest";
import { getDiscounts } from "@api/discounts";
import { useUserStore } from "@root/user";
import { parseAxiosError } from "@utils/parse-error"; import { parseAxiosError } from "@utils/parse-error";
import type { Discount } from "@model/discounts";
export function CheckFastlink() { export function CheckFastlink() {
const userId = useUserStore((state) => state.userId); const userId = useUserStore((state) => state.userId);
const [discounts, setDiscounts] = useState(); const [discounts, setDiscounts] = useState<Discount[]>([]);
const [askToChange, setAskToChange] = useState(false); const [askToChange, setAskToChange] = useState(false);
const [promocode, setPromocode] = useState(""); const [promocode, setPromocode] = useState("");
useEffect(() => { useEffect(() => {
const get = async () => { const get = async () => {
const discounts = await makeRequest({ if (!userId) {
method: "GET", return;
url: `${process.env.REACT_APP_DOMAIN}/price/discount/user/${userId}`, }
});
setDiscounts(discounts.Discounts); const [discounts] = await getDiscounts(userId);
if (discounts?.length) {
setDiscounts(discounts);
}
}; };
get(); get();
}, []); }, []);

@ -3,6 +3,7 @@ import {
Box, Box,
Button, Button,
ButtonBase, ButtonBase,
Skeleton,
Tooltip, Tooltip,
Typography, Typography,
useTheme, useTheme,
@ -33,6 +34,7 @@ export const MediaSelectionAndDisplay: FC<Iprops> = ({
cropAspectRatio, cropAspectRatio,
}) => { }) => {
const [pictureUploding, setPictureUploading] = useState<boolean>(false); const [pictureUploding, setPictureUploading] = useState<boolean>(false);
const [backgroundUploding, setBackgroundUploading] = useState<boolean>(false);
const quizQid = useCurrentQuiz()?.qid; const quizQid = useCurrentQuiz()?.qid;
const theme = useTheme(); const theme = useTheme();
const { const {
@ -202,44 +204,59 @@ export const MediaSelectionAndDisplay: FC<Iprops> = ({
</Box> </Box>
</Tooltip> </Tooltip>
</Box> </Box>
<ButtonBase {backgroundUploding ? (
component="label" <Skeleton
sx={{
justifyContent: "center",
height: "48px",
width: "48px",
display: "flex",
alignItems: "center",
my: "20px",
}}
>
<input
onChange={(event) => {
const file = event.target.files?.[0];
if (file) {
uploadQuestionImage(
resultData.id,
quizQid,
file,
(question, url) => {
question.content.video = url;
},
);
}
}}
hidden
accept=".mp4"
multiple
type="file"
/>
<UploadBox
icon={<UploadIcon />}
sx={{ sx={{
height: "48px",
width: "48px", width: "48px",
height: "48px",
transform: "none",
margin: "20px 0",
}} }}
/> />
</ButtonBase> ) : (
<>
<ButtonBase
component="label"
sx={{
justifyContent: "center",
height: "48px",
width: "48px",
display: "flex",
alignItems: "center",
my: "20px",
}}
>
<input
onChange={async (event) => {
setBackgroundUploading(true);
const file = event.target.files?.[0];
if (file) {
await uploadQuestionImage(
resultData.id,
quizQid,
file,
(question, url) => {
question.content.video = url;
},
);
}
setBackgroundUploading(false);
}}
hidden
accept=".mp4"
multiple
type="file"
/>
<UploadBox
icon={<UploadIcon />}
sx={{
height: "48px",
width: "48px",
}}
/>
</ButtonBase>
</>
)}
</> </>
) : ( ) : (
<VideoElement <VideoElement