Merge branch 'dev' into 'staging'
Dev See merge request frontend/squiz!297
This commit is contained in:
commit
4e457e9294
22
src/api/discounts.ts
Normal file
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
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,6 +233,7 @@ const SettingField = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{quiz.config.formContact.fields[type].innerText || placeholder}
|
{quiz.config.formContact.fields[type].innerText || placeholder}
|
||||||
|
{type === "text" && (
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={() => drawerNewFieldHC({ field: type, isEdit: true })}
|
onClick={() => drawerNewFieldHC({ field: type, isEdit: true })}
|
||||||
sx={{
|
sx={{
|
||||||
@ -242,6 +243,7 @@ const SettingField = ({
|
|||||||
>
|
>
|
||||||
<GearIcon height="20px" width="20px" color="#7e2aea" />
|
<GearIcon height="20px" width="20px" color="#7e2aea" />
|
||||||
</IconButton>
|
</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,6 +204,17 @@ export const MediaSelectionAndDisplay: FC<Iprops> = ({
|
|||||||
</Box>
|
</Box>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Box>
|
</Box>
|
||||||
|
{backgroundUploding ? (
|
||||||
|
<Skeleton
|
||||||
|
sx={{
|
||||||
|
width: "48px",
|
||||||
|
height: "48px",
|
||||||
|
transform: "none",
|
||||||
|
margin: "20px 0",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
<ButtonBase
|
<ButtonBase
|
||||||
component="label"
|
component="label"
|
||||||
sx={{
|
sx={{
|
||||||
@ -214,10 +227,11 @@ export const MediaSelectionAndDisplay: FC<Iprops> = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
onChange={(event) => {
|
onChange={async (event) => {
|
||||||
|
setBackgroundUploading(true);
|
||||||
const file = event.target.files?.[0];
|
const file = event.target.files?.[0];
|
||||||
if (file) {
|
if (file) {
|
||||||
uploadQuestionImage(
|
await uploadQuestionImage(
|
||||||
resultData.id,
|
resultData.id,
|
||||||
quizQid,
|
quizQid,
|
||||||
file,
|
file,
|
||||||
@ -226,6 +240,7 @@ export const MediaSelectionAndDisplay: FC<Iprops> = ({
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
setBackgroundUploading(false);
|
||||||
}}
|
}}
|
||||||
hidden
|
hidden
|
||||||
accept=".mp4"
|
accept=".mp4"
|
||||||
@ -241,6 +256,8 @@ export const MediaSelectionAndDisplay: FC<Iprops> = ({
|
|||||||
/>
|
/>
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
</>
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<VideoElement
|
<VideoElement
|
||||||
videoSrc={resultData.content.video}
|
videoSrc={resultData.content.video}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user