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 }}>
<LinearProgress
variant="determinate"
value={percent * 100 > 100 ? 100 : percent * 10}
value={percent * 100 > 100 ? 100 : percent * 100}
sx={{
width: "100%",
marginRight: "15px",

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

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

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

@ -3,6 +3,7 @@ import {
Box,
Button,
ButtonBase,
Skeleton,
Tooltip,
Typography,
useTheme,
@ -33,6 +34,7 @@ export const MediaSelectionAndDisplay: FC<Iprops> = ({
cropAspectRatio,
}) => {
const [pictureUploding, setPictureUploading] = useState<boolean>(false);
const [backgroundUploding, setBackgroundUploading] = useState<boolean>(false);
const quizQid = useCurrentQuiz()?.qid;
const theme = useTheme();
const {
@ -202,44 +204,59 @@ export const MediaSelectionAndDisplay: FC<Iprops> = ({
</Box>
</Tooltip>
</Box>
<ButtonBase
component="label"
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 />}
{backgroundUploding ? (
<Skeleton
sx={{
height: "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