Merge branch 'deploy' into 'main'
fix: design See merge request frontend/marketplace!20
This commit is contained in:
commit
b572f162a0
@ -1,26 +1,69 @@
|
||||
import { Slider, SliderProps, styled } from "@mui/material";
|
||||
import { useState, useEffect } from "react";
|
||||
import { Slider, useTheme } from "@mui/material";
|
||||
|
||||
type CustomSliderProps = {
|
||||
value: number;
|
||||
min: number;
|
||||
max: number;
|
||||
onChange: (value: number | number[]) => void;
|
||||
};
|
||||
|
||||
export default styled(Slider)<SliderProps>(({ theme }) => ({
|
||||
color: theme.palette.brightPurple.main,
|
||||
height: "12px",
|
||||
"& .MuiSlider-track": {
|
||||
border: "none",
|
||||
},
|
||||
"& .MuiSlider-rail": {
|
||||
backgroundColor: "#F2F3F7",
|
||||
border: `1px solid ${theme.palette.grey2.main}`,
|
||||
},
|
||||
"& .MuiSlider-thumb": {
|
||||
height: 32,
|
||||
width: 32,
|
||||
border: `6px solid ${theme.palette.brightPurple.main}`,
|
||||
backgroundColor: "white",
|
||||
boxShadow: `0px 0px 0px 3px white,
|
||||
0px 4px 4px 3px #C3C8DD`,
|
||||
"&:focus, &:hover, &.Mui-active, &.Mui-focusVisible": {
|
||||
boxShadow: `0px 0px 0px 3px white,
|
||||
0px 4px 4px 3px #C3C8DD`,
|
||||
export const CustomSlider = ({
|
||||
value,
|
||||
min = 0,
|
||||
max = 100,
|
||||
onChange,
|
||||
}: CustomSliderProps) => {
|
||||
const theme = useTheme();
|
||||
const [step, setStep] = useState<number>(1);
|
||||
|
||||
useEffect(() => {
|
||||
if (value < 100) {
|
||||
return setStep(10);
|
||||
}
|
||||
|
||||
if (value < 500) {
|
||||
return setStep(20);
|
||||
}
|
||||
|
||||
if (value < 2000) {
|
||||
return setStep(50);
|
||||
}
|
||||
|
||||
setStep(150);
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<Slider
|
||||
value={value}
|
||||
defaultValue={0}
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
onChange={(_, newValue) => onChange(newValue)}
|
||||
sx={{
|
||||
color: theme.palette.brightPurple.main,
|
||||
height: "12px",
|
||||
"& .MuiSlider-track": {
|
||||
border: "none",
|
||||
},
|
||||
},
|
||||
}));
|
||||
"& .MuiSlider-rail": {
|
||||
backgroundColor: "#F2F3F7",
|
||||
border: `1px solid ${theme.palette.grey2.main}`,
|
||||
},
|
||||
"& .MuiSlider-thumb": {
|
||||
height: 32,
|
||||
width: 32,
|
||||
border: `6px solid ${theme.palette.brightPurple.main}`,
|
||||
backgroundColor: "white",
|
||||
boxShadow: `0px 0px 0px 3px white,
|
||||
0px 4px 4px 3px #C3C8DD`,
|
||||
"&:focus, &:hover, &.Mui-active, &.Mui-focusVisible": {
|
||||
boxShadow: `0px 0px 0px 3px white,
|
||||
0px 4px 4px 3px #C3C8DD`,
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -2,7 +2,6 @@ import { useState } from "react";
|
||||
import { Box, SvgIcon, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||
|
||||
import ClearIcon from "@mui/icons-material/Clear";
|
||||
import { cardShadow } from "@root/utils/themes/shadow";
|
||||
import { currencyFormatter } from "@root/utils/currencyFormatter";
|
||||
import { removeTariffFromCart } from "@root/stores/user";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
@ -35,7 +34,6 @@ export default function CustomWrapperDrawer({ serviceData }: Props) {
|
||||
sx={{
|
||||
overflow: "hidden",
|
||||
borderRadius: "12px",
|
||||
boxShadow: cardShadow,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
|
@ -25,12 +25,9 @@ import {
|
||||
useCartStore,
|
||||
} from "@root/stores/cart";
|
||||
import { useCustomTariffsStore } from "@root/stores/customTariffs";
|
||||
import { useUserStore } from "@root/stores/user";
|
||||
|
||||
type DrawersProps = {
|
||||
cartItemsAmount?: number;
|
||||
};
|
||||
|
||||
export default function Drawers({ cartItemsAmount = 0 }: DrawersProps) {
|
||||
export default function Drawers() {
|
||||
const navigate = useNavigate();
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
@ -42,6 +39,7 @@ export default function Drawers({ cartItemsAmount = 0 }: DrawersProps) {
|
||||
const summaryPriceAfterDiscountsMap = useCustomTariffsStore(
|
||||
(state) => state.summaryPriceAfterDiscountsMap
|
||||
);
|
||||
const userAccount = useUserStore((state) => state.userAccount);
|
||||
|
||||
const basePrice = Object.values(summaryPriceBeforeDiscountsMap).reduce(
|
||||
(a, e) => a + e,
|
||||
@ -74,7 +72,7 @@ export default function Drawers({ cartItemsAmount = 0 }: DrawersProps) {
|
||||
}}
|
||||
>
|
||||
<Badge
|
||||
badgeContent={cartItemsAmount}
|
||||
badgeContent={userAccount?.cart.length}
|
||||
sx={{
|
||||
"& .MuiBadge-badge": {
|
||||
color: "#FFFFFF",
|
||||
@ -132,8 +130,8 @@ export default function Drawers({ cartItemsAmount = 0 }: DrawersProps) {
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
pt: "20px",
|
||||
pb: "20px",
|
||||
pt: "12px",
|
||||
pb: "12px",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
bgcolor: "#F2F3F7",
|
||||
@ -202,7 +200,6 @@ export default function Drawers({ cartItemsAmount = 0 }: DrawersProps) {
|
||||
color: theme.palette.grey3.main,
|
||||
pb: "100px",
|
||||
pt: "38px",
|
||||
pl: upMd ? "20px" : undefined,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
|
@ -152,7 +152,7 @@ export default function Chat({ sx }: Props) {
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
height: "clamp(250px, 100dvh - 90px, 600px)",
|
||||
height: "clamp(250px, calc(100vh - 90px), 600px)",
|
||||
backgroundColor: "#944FEE",
|
||||
borderRadius: "8px",
|
||||
...sx,
|
||||
|
@ -28,6 +28,7 @@ export default function Menu() {
|
||||
},
|
||||
{ name: "Вопросы и ответы", url: "/faq" },
|
||||
{ name: "Корзина", url: "/basket" },
|
||||
{ name: "История", url: "/history" },
|
||||
];
|
||||
|
||||
return (
|
||||
|
@ -42,6 +42,7 @@ const arrayMenu: MenuItem[] = [
|
||||
},
|
||||
{ name: "Вопросы и ответы", url: "/faq" },
|
||||
{ name: "Корзина", url: "/basket" },
|
||||
{ name: "История", url: "/history" },
|
||||
];
|
||||
|
||||
const Transition = React.forwardRef(function Transition(
|
||||
|
@ -1,25 +1,23 @@
|
||||
import { useState } from "react";
|
||||
import { Badge, IconButton, useTheme } from "@mui/material";
|
||||
import MenuIcon from "@mui/icons-material/Menu";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import SectionWrapper from "../SectionWrapper";
|
||||
import { useUserStore } from "@root/stores/user";
|
||||
|
||||
import PenaLogo from "../PenaLogo";
|
||||
import DialogMenu from "./DialogMenu";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import cartIcon from "@root/assets/Icons/cart.svg";
|
||||
|
||||
interface Props {
|
||||
isLoggedIn: boolean;
|
||||
cartItemsAmount?: number;
|
||||
}
|
||||
|
||||
export default function NavbarCollapsed({
|
||||
isLoggedIn,
|
||||
cartItemsAmount = 5,
|
||||
}: Props) {
|
||||
export default function NavbarCollapsed({ isLoggedIn }: Props) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const userAccount = useUserStore((state) => state.userAccount);
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
@ -73,7 +71,7 @@ export default function NavbarCollapsed({
|
||||
}}
|
||||
>
|
||||
<Badge
|
||||
badgeContent={cartItemsAmount}
|
||||
badgeContent={userAccount?.cart.length}
|
||||
sx={{
|
||||
"& .MuiBadge-badge": {
|
||||
color: "#FFFFFF",
|
||||
|
@ -70,7 +70,7 @@ export default function NavbarFull({ isLoggedIn }: Props) {
|
||||
ml: "auto",
|
||||
}}
|
||||
>
|
||||
<Drawers cartItemsAmount={3} />
|
||||
<Drawers />
|
||||
<IconButton
|
||||
sx={{ p: 0, ml: "8px" }}
|
||||
onClick={() => navigate("/wallet")}
|
||||
|
@ -1,87 +1,98 @@
|
||||
import { InputAdornment, TextField, Typography, useTheme } from "@mui/material";
|
||||
import { useState } from "react";
|
||||
import { InputAdornment, TextField, Typography, useTheme } from "@mui/material";
|
||||
|
||||
import type { ChangeEvent } from "react";
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
adornmentText: string;
|
||||
onChange: (value: number) => void;
|
||||
id: string;
|
||||
adornmentText: string;
|
||||
onChange: (value: number) => void;
|
||||
}
|
||||
|
||||
export default function NumberInputWithUnitAdornment({ id, adornmentText, onChange }: Props) {
|
||||
const theme = useTheme();
|
||||
const [valueField, setValueField] = useState<string>("");
|
||||
export default function NumberInputWithUnitAdornment({
|
||||
id,
|
||||
adornmentText,
|
||||
onChange,
|
||||
}: Props) {
|
||||
const theme = useTheme();
|
||||
const [valueField, setValueField] = useState<string>("");
|
||||
|
||||
return (
|
||||
<TextField
|
||||
type="number"
|
||||
size="small"
|
||||
placeholder="Введите вручную"
|
||||
id={id}
|
||||
value={valueField}
|
||||
onChange={e => {
|
||||
let n = parseInt(e.target.value);
|
||||
return (
|
||||
<TextField
|
||||
type="number"
|
||||
size="small"
|
||||
placeholder="Введите вручную"
|
||||
id={id}
|
||||
value={valueField}
|
||||
onChange={({ target }: ChangeEvent<HTMLInputElement>) => {
|
||||
const newNumber = parseInt(target.value);
|
||||
|
||||
if (!isFinite(n)) n = 0;
|
||||
if (!isFinite(newNumber) || newNumber < 0) {
|
||||
onChange(0);
|
||||
setValueField(String(0));
|
||||
|
||||
onChange(n);
|
||||
setValueField(n.toString());
|
||||
}}
|
||||
return;
|
||||
}
|
||||
|
||||
onChange(newNumber);
|
||||
setValueField(String(newNumber));
|
||||
}}
|
||||
sx={{
|
||||
maxWidth: "200px",
|
||||
minWidth: "200px",
|
||||
".MuiInputBase-root": {
|
||||
pr: 0,
|
||||
height: "48px",
|
||||
borderRadius: "8px",
|
||||
backgroundColor: "#F2F3F7",
|
||||
fieldset: {
|
||||
border: "1px solid" + theme.palette.grey2.main,
|
||||
},
|
||||
"&.Mui-focused fieldset": {
|
||||
borderColor: theme.palette.brightPurple.main,
|
||||
},
|
||||
input: {
|
||||
height: "31px",
|
||||
borderRight: !valueField ? "none" : "1px solid #9A9AAF",
|
||||
},
|
||||
"&.Mui-focused input": {
|
||||
borderRight: "1px solid #9A9AAF",
|
||||
},
|
||||
"&:not(.Mui-focused) .MuiInputAdornment-root": {
|
||||
display: !valueField ? "none" : undefined,
|
||||
},
|
||||
"&.Mui-focused ::-webkit-input-placeholder": {
|
||||
color: "transparent",
|
||||
},
|
||||
|
||||
// Hiding arrows
|
||||
"input::-webkit-outer-spin-button, input::-webkit-inner-spin-button":
|
||||
{
|
||||
WebkitAppearance: "none",
|
||||
margin: 0,
|
||||
},
|
||||
"input[type = number]": {
|
||||
MozAppearance: "textfield",
|
||||
},
|
||||
},
|
||||
}}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment
|
||||
position="end"
|
||||
sx={{
|
||||
maxWidth: "200px",
|
||||
minWidth: "200px",
|
||||
".MuiInputBase-root": {
|
||||
pr: 0,
|
||||
height: "48px",
|
||||
borderRadius: "8px",
|
||||
backgroundColor: "#F2F3F7",
|
||||
"fieldset": {
|
||||
border: "1px solid" + theme.palette.grey2.main,
|
||||
},
|
||||
"&.Mui-focused fieldset": {
|
||||
borderColor: theme.palette.brightPurple.main,
|
||||
},
|
||||
"input": {
|
||||
height: "31px",
|
||||
borderRight: !valueField ? "none" : "1px solid #9A9AAF",
|
||||
},
|
||||
"&.Mui-focused input": {
|
||||
borderRight: "1px solid #9A9AAF",
|
||||
},
|
||||
"&:not(.Mui-focused) .MuiInputAdornment-root": {
|
||||
display: !valueField ? "none" : undefined,
|
||||
},
|
||||
"&.Mui-focused ::-webkit-input-placeholder": {
|
||||
color: "transparent",
|
||||
},
|
||||
|
||||
// Hiding arrows
|
||||
"input::-webkit-outer-spin-button, input::-webkit-inner-spin-button": {
|
||||
"WebkitAppearance": "none",
|
||||
margin: 0,
|
||||
},
|
||||
"input[type = number]": {
|
||||
"MozAppearance": "textfield",
|
||||
}
|
||||
},
|
||||
userSelect: "none",
|
||||
pointerEvents: "none",
|
||||
pl: "2px",
|
||||
pr: "13px",
|
||||
}}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment
|
||||
position="end"
|
||||
sx={{
|
||||
userSelect: "none",
|
||||
pointerEvents: "none",
|
||||
pl: "2px",
|
||||
pr: "13px",
|
||||
}}
|
||||
>
|
||||
<Typography variant="body2" color="#4D4D4D">
|
||||
{adornmentText}
|
||||
</Typography>
|
||||
</InputAdornment>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
);
|
||||
>
|
||||
<Typography variant="body2" color="#4D4D4D">
|
||||
{adornmentText}
|
||||
</Typography>
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
import {Box, Button, Typography, useMediaQuery, useTheme} from "@mui/material";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import ArrowForwardIcon from "@mui/icons-material/ArrowForward";
|
||||
import CardWithLink from "@components/CardWithLink";
|
||||
import UnderlinedLink from "@components/UnderlinedLink";
|
||||
@ -9,73 +15,76 @@ import card3Image from "@root/assets/landing/card3.png";
|
||||
import cardImageBig from "@root/assets/landing/card1big.png";
|
||||
|
||||
export default function () {
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));console.log("я узкий")
|
||||
return <Box sx={{
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
console.log("я узкий");
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
mt: upMd ? "93px" : "55px",
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
justifyContent: "space-evenly",
|
||||
columnGap: "40px",
|
||||
rowGap: "50px",
|
||||
backgroundColor: "inherit"
|
||||
}}>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
flexGrow: 1,
|
||||
alignItems: "start",
|
||||
p: "20px",
|
||||
maxWidth: "360px",
|
||||
backgroundColor: " #E6E6EB",
|
||||
borderRadius: "12px",
|
||||
boxShadow: `
|
||||
0px 100px 309px rgba(37, 39, 52, 0.24),
|
||||
0px 41.7776px 129.093px rgba(37, 39, 52, 0.172525),
|
||||
0px 22.3363px 69.0192px rgba(37, 39, 52, 0.143066),
|
||||
0px 12.5216px 38.6916px rgba(37, 39, 52, 0.12),
|
||||
0px 6.6501px 20.5488px rgba(37, 39, 52, 0.0969343),
|
||||
0px 2.76726px 8.55082px rgba(37, 39, 52, 0.0674749)
|
||||
`,
|
||||
color: "black",
|
||||
height: "520px",
|
||||
justifyContent: "space-between"
|
||||
}}
|
||||
backgroundColor: "inherit",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
flexGrow: 1,
|
||||
alignItems: "start",
|
||||
p: "20px",
|
||||
maxWidth: "360px",
|
||||
backgroundColor: " #E6E6EB",
|
||||
borderRadius: "12px",
|
||||
boxShadow: "0 10px 0 -5px #BABBC8",
|
||||
color: "black",
|
||||
height: "520px",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={card1Image}
|
||||
alt=""
|
||||
style={{
|
||||
objectFit: "contain",
|
||||
width: "100%",
|
||||
display: "block",
|
||||
pointerEvents: "none",
|
||||
}}
|
||||
/>
|
||||
<Typography variant="h5">Шаблонизатор</Typography>
|
||||
<Typography mt="20px" mb="20px">
|
||||
"Текст- это текст, который имеет некоторые характеристики реального
|
||||
письменного текст"
|
||||
</Typography>
|
||||
|
||||
<Button
|
||||
sx={{
|
||||
width: "180px",
|
||||
paddingTop: "10px",
|
||||
paddingBottom: "10px",
|
||||
borderRadius: "8px",
|
||||
boxShadow: "none",
|
||||
backgroundColor: "white",
|
||||
color: "black",
|
||||
"&:hover": {
|
||||
backgroundColor: "#581CA7",
|
||||
color: "white",
|
||||
},
|
||||
"&:active": {
|
||||
backgroundColor: "black",
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
variant="contained"
|
||||
>
|
||||
<img
|
||||
src={card1Image}
|
||||
alt=""
|
||||
style={{
|
||||
objectFit: "contain",
|
||||
width: "100%",
|
||||
display: "block",
|
||||
pointerEvents: "none",
|
||||
}}
|
||||
/>
|
||||
<Typography variant="h5">Шаблонизатор</Typography>
|
||||
<Typography mt="20px" mb="20px">"Текст- это текст, который имеет некоторые характеристики реального письменного текст"</Typography>
|
||||
|
||||
<Button
|
||||
sx={{
|
||||
width: "180px",
|
||||
paddingTop: "10px",
|
||||
paddingBottom: "10px",
|
||||
borderRadius: "8px",
|
||||
boxShadow: "none",
|
||||
backgroundColor: "white",
|
||||
color: "black",
|
||||
"&:hover": {
|
||||
backgroundColor: "#581CA7",
|
||||
color: "white"
|
||||
},
|
||||
"&:active": {
|
||||
backgroundColor: "black",
|
||||
color: "white"
|
||||
}
|
||||
}}
|
||||
variant="contained">Подробнее</Button>
|
||||
|
||||
</Box>
|
||||
Подробнее
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,12 @@
|
||||
import {Box, Typography, useMediaQuery, useTheme, Button, SxProps, Theme} from "@mui/material";
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
Button,
|
||||
SxProps,
|
||||
Theme,
|
||||
} from "@mui/material";
|
||||
import ArrowForwardIcon from "@mui/icons-material/ArrowForward";
|
||||
import CardWithLink from "@components/CardWithLink";
|
||||
import UnderlinedLink from "@components/UnderlinedLink";
|
||||
@ -9,15 +17,17 @@ import card3Image from "@root/assets/landing/card3.png";
|
||||
import cardImageBig from "@root/assets/landing/card1big.png";
|
||||
|
||||
interface Props {
|
||||
light?: boolean;
|
||||
sx?: SxProps<Theme>;
|
||||
light?: boolean;
|
||||
sx?: SxProps<Theme>;
|
||||
}
|
||||
|
||||
export default function ({light = true, sx}: Props) {
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
export default function ({ light = true, sx }: Props) {
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
|
||||
return <Box sx={{
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: "relative",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
@ -25,68 +35,70 @@ export default function ({light = true, sx}: Props) {
|
||||
px: "20px",
|
||||
backgroundColor: light ? "#E6E6EB" : "#434657",
|
||||
borderRadius: "12px",
|
||||
boxShadow: `
|
||||
0px 100px 309px rgba(37, 39, 52, 0.24),
|
||||
0px 41.7776px 129.093px rgba(37, 39, 52, 0.172525),
|
||||
0px 22.3363px 69.0192px rgba(37, 39, 52, 0.143066),
|
||||
0px 12.5216px 38.6916px rgba(37, 39, 52, 0.12),
|
||||
0px 6.6501px 20.5488px rgba(37, 39, 52, 0.0969343),
|
||||
0px 2.76726px 8.55082px rgba(37, 39, 52, 0.0674749)
|
||||
`,
|
||||
...sx
|
||||
}}>
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}>
|
||||
<Typography variant="h5">Шаблонизатор</Typography>
|
||||
<Typography mt="20px" maxWidth="552px">Текст- это текст, который имеет некоторые характеристики
|
||||
реального
|
||||
письменного текс</Typography>
|
||||
{
|
||||
light ?
|
||||
<Button
|
||||
sx={{
|
||||
mt:"28px",
|
||||
width: "180px",
|
||||
paddingTop: "10px",
|
||||
paddingBottom: "10px",
|
||||
borderRadius: "8px",
|
||||
boxShadow: "none",
|
||||
backgroundColor: "white",
|
||||
color: "black",
|
||||
"&:hover": {
|
||||
backgroundColor: "#581CA7",
|
||||
color: "white"
|
||||
},
|
||||
"&:active": {
|
||||
backgroundColor: "black",
|
||||
color: "white"
|
||||
}
|
||||
}}
|
||||
variant="contained">Подробнее</Button>
|
||||
:
|
||||
<UnderlinedLink
|
||||
linkHref="#"
|
||||
text="Подробнее"
|
||||
endIcon={<ArrowForwardIcon sx={{height: "20px", width: "20px"}}/>}
|
||||
sx={{
|
||||
mt: "auto",
|
||||
}}
|
||||
/>
|
||||
}
|
||||
</Box>
|
||||
<img
|
||||
src={cardImageBig}
|
||||
alt=""
|
||||
style={{
|
||||
display: "block",
|
||||
objectFit: "contain",
|
||||
pointerEvents: "none",
|
||||
marginBottom: "-40px",
|
||||
marginTop: "-110px",
|
||||
maxWidth: "390px",
|
||||
boxShadow: "0 10px 0 -5px #BABBC8",
|
||||
...sx,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<Typography variant="h5">Шаблонизатор</Typography>
|
||||
<Typography mt="20px" maxWidth="552px">
|
||||
Текст- это текст, который имеет некоторые характеристики реального
|
||||
письменного текс
|
||||
</Typography>
|
||||
{light ? (
|
||||
<Button
|
||||
sx={{
|
||||
mt: "28px",
|
||||
width: "180px",
|
||||
paddingTop: "10px",
|
||||
paddingBottom: "10px",
|
||||
borderRadius: "8px",
|
||||
boxShadow: "none",
|
||||
backgroundColor: "white",
|
||||
color: "black",
|
||||
"&:hover": {
|
||||
backgroundColor: "#581CA7",
|
||||
color: "white",
|
||||
},
|
||||
"&:active": {
|
||||
backgroundColor: "black",
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
variant="contained"
|
||||
>
|
||||
Подробнее
|
||||
</Button>
|
||||
) : (
|
||||
<UnderlinedLink
|
||||
linkHref="#"
|
||||
text="Подробнее"
|
||||
endIcon={
|
||||
<ArrowForwardIcon sx={{ height: "20px", width: "20px" }} />
|
||||
}
|
||||
sx={{
|
||||
mt: "auto",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
<img
|
||||
src={cardImageBig}
|
||||
alt=""
|
||||
style={{
|
||||
display: "block",
|
||||
objectFit: "contain",
|
||||
pointerEvents: "none",
|
||||
marginBottom: "-40px",
|
||||
marginTop: "-110px",
|
||||
maxWidth: "390px",
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,11 @@
|
||||
import { useState } from "react";
|
||||
import { Box, SvgIcon, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||
import {
|
||||
Box,
|
||||
SvgIcon,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import ExpandIcon from "@components/icons/ExpandIcon";
|
||||
import ClearIcon from "@mui/icons-material/Clear";
|
||||
import { cardShadow } from "@root/utils/themes/shadow";
|
||||
@ -8,168 +14,183 @@ import { removeTariffFromCart } from "@root/stores/user";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { ServiceCartData, getMessageFromFetchError } from "@frontend/kitui";
|
||||
|
||||
|
||||
const name: Record<string, string> = { templategen: "Шаблонизатор", squiz: "Опросник", reducer: "Сокращатель ссылок" };
|
||||
const name: Record<string, string> = {
|
||||
templategen: "Шаблонизатор",
|
||||
squiz: "Опросник",
|
||||
reducer: "Сокращатель ссылок",
|
||||
};
|
||||
|
||||
interface Props {
|
||||
serviceData: ServiceCartData;
|
||||
serviceData: ServiceCartData;
|
||||
}
|
||||
|
||||
export default function CustomWrapper({ serviceData }: Props) {
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const upSm = useMediaQuery(theme.breakpoints.up("sm"));
|
||||
const [isExpanded, setIsExpanded] = useState<boolean>(false);
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const upSm = useMediaQuery(theme.breakpoints.up("sm"));
|
||||
const [isExpanded, setIsExpanded] = useState<boolean>(false);
|
||||
|
||||
function handleItemDeleteClick(tariffId: string) {
|
||||
removeTariffFromCart(tariffId).then(() => {
|
||||
enqueueSnackbar("Тариф удален");
|
||||
}).catch(error => {
|
||||
const message = getMessageFromFetchError(error);
|
||||
if (message) enqueueSnackbar(message);
|
||||
});
|
||||
}
|
||||
function handleItemDeleteClick(tariffId: string) {
|
||||
removeTariffFromCart(tariffId)
|
||||
.then(() => {
|
||||
enqueueSnackbar("Тариф удален");
|
||||
})
|
||||
.catch((error) => {
|
||||
const message = getMessageFromFetchError(error);
|
||||
if (message) enqueueSnackbar(message);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
overflow: "hidden",
|
||||
borderRadius: "12px",
|
||||
boxShadow: cardShadow,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
backgroundColor: "white",
|
||||
"&:first-of-type": {
|
||||
borderTopLeftRadius: "12px",
|
||||
borderTopRightRadius: "12px",
|
||||
},
|
||||
"&:last-of-type": {
|
||||
borderBottomLeftRadius: "12px",
|
||||
borderBottomRightRadius: "12px",
|
||||
},
|
||||
"&:not(:last-of-type)": {
|
||||
borderBottom: `1px solid ${theme.palette.grey2.main}`,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
overflow: "hidden",
|
||||
borderRadius: "12px",
|
||||
boxShadow: cardShadow,
|
||||
}}
|
||||
onClick={() => setIsExpanded((prev) => !prev)}
|
||||
sx={{
|
||||
height: "72px",
|
||||
px: "20px",
|
||||
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
cursor: "pointer",
|
||||
userSelect: "none",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
backgroundColor: "white",
|
||||
"&:first-of-type": {
|
||||
borderTopLeftRadius: "12px",
|
||||
borderTopRightRadius: "12px",
|
||||
},
|
||||
"&:last-of-type": {
|
||||
borderBottomLeftRadius: "12px",
|
||||
borderBottomRightRadius: "12px",
|
||||
},
|
||||
"&:not(:last-of-type)": {
|
||||
borderBottom: `1px solid ${theme.palette.grey2.main}`,
|
||||
},
|
||||
}}
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: upMd ? "20px" : "16px",
|
||||
lineHeight: upMd ? undefined : "19px",
|
||||
fontWeight: 500,
|
||||
color: theme.palette.text.secondary,
|
||||
px: 0,
|
||||
}}
|
||||
>
|
||||
{name[serviceData.serviceKey]}
|
||||
</Typography>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
height: "100%",
|
||||
alignItems: "center",
|
||||
gap: upSm ? "111px" : "17px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
color: theme.palette.grey3.main,
|
||||
fontSize: upSm ? "20px" : "16px",
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
onClick={() => setIsExpanded((prev) => !prev)}
|
||||
sx={{
|
||||
height: "72px",
|
||||
px: "20px",
|
||||
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
cursor: "pointer",
|
||||
userSelect: "none",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: upMd ? "20px" : "16px",
|
||||
lineHeight: upMd ? undefined : "19px",
|
||||
fontWeight: 500,
|
||||
color: theme.palette.text.secondary,
|
||||
px: 0,
|
||||
}}
|
||||
>
|
||||
{name[serviceData.serviceKey]}
|
||||
</Typography>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
height: "100%",
|
||||
alignItems: "center",
|
||||
gap: upSm ? "111px" : "17px",
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ color: theme.palette.grey3.main, fontSize: upSm ? "20px" : "16px", fontWeight: 500 }}>
|
||||
{currencyFormatter.format(serviceData.price / 100)}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
borderLeft: upSm ? "1px solid #9A9AAF" : "none",
|
||||
paddingLeft: upSm ? "24px" : 0,
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<ExpandIcon isExpanded={isExpanded} />
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
{isExpanded &&
|
||||
serviceData.privileges.map(privilege => (
|
||||
<Box
|
||||
key={privilege.tariffId + privilege.privilegeId}
|
||||
sx={{
|
||||
px: "20px",
|
||||
py: upMd ? "25px" : undefined,
|
||||
pt: upMd ? undefined : "15px",
|
||||
pb: upMd ? undefined : "25px",
|
||||
backgroundColor: "#F1F2F6",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
gap: "15px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: upMd ? undefined : "16px",
|
||||
lineHeight: upMd ? undefined : "19px",
|
||||
color: theme.palette.grey3.main,
|
||||
}}
|
||||
>
|
||||
{privilege.description}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
gap: "10px",
|
||||
alignItems: "center",
|
||||
width: upSm ? "195px" : "123px",
|
||||
marginRight: upSm ? "65px" : 0,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
color: theme.palette.grey3.main,
|
||||
fontSize: upSm ? "20px" : "16px",
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
{currencyFormatter.format(privilege.price / 100)}
|
||||
</Typography>
|
||||
{upSm ? (
|
||||
<Typography
|
||||
component="div"
|
||||
onClick={() => handleItemDeleteClick(privilege.tariffId)}
|
||||
sx={{
|
||||
color: theme.palette.text.secondary,
|
||||
borderBottom: `1px solid ${theme.palette.text.secondary}`,
|
||||
width: "max-content",
|
||||
lineHeight: "19px",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
Удалить
|
||||
</Typography>
|
||||
) : (
|
||||
<SvgIcon onClick={() => handleItemDeleteClick(privilege.tariffId)} component={ClearIcon}></SvgIcon>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
))}
|
||||
{currencyFormatter.format(serviceData.price / 100)}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
borderLeft: upSm ? "1px solid #9A9AAF" : "none",
|
||||
paddingLeft: upSm ? "24px" : 0,
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<ExpandIcon isExpanded={isExpanded} />
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
{isExpanded &&
|
||||
serviceData.privileges.map((privilege) => (
|
||||
<Box
|
||||
key={privilege.tariffId + privilege.privilegeId}
|
||||
sx={{
|
||||
px: "20px",
|
||||
py: upMd ? "25px" : undefined,
|
||||
pt: upMd ? undefined : "15px",
|
||||
pb: upMd ? undefined : "25px",
|
||||
backgroundColor: "#F1F2F6",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
gap: "15px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: upMd ? undefined : "16px",
|
||||
lineHeight: upMd ? undefined : "19px",
|
||||
color: theme.palette.grey3.main,
|
||||
}}
|
||||
>
|
||||
{privilege.description}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
gap: "10px",
|
||||
alignItems: "center",
|
||||
width: upSm ? "195px" : "123px",
|
||||
marginRight: upSm ? "65px" : 0,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
color: theme.palette.grey3.main,
|
||||
fontSize: upSm ? "20px" : "16px",
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
{currencyFormatter.format(privilege.price / 100)}
|
||||
</Typography>
|
||||
{upSm ? (
|
||||
<Typography
|
||||
component="div"
|
||||
onClick={() => handleItemDeleteClick(privilege.tariffId)}
|
||||
sx={{
|
||||
color: theme.palette.text.secondary,
|
||||
borderBottom: `1px solid ${theme.palette.text.secondary}`,
|
||||
width: "max-content",
|
||||
lineHeight: "19px",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
Удалить
|
||||
</Typography>
|
||||
) : (
|
||||
<SvgIcon
|
||||
onClick={() => handleItemDeleteClick(privilege.tariffId)}
|
||||
component={ClearIcon}
|
||||
sx={{ fill: "#7E2AEA" }}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ export default function Faq() {
|
||||
<Box
|
||||
sx={{
|
||||
mt: "20px",
|
||||
mb: upMd ? "40px" : "20px",
|
||||
mb: "20px",
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
}}
|
||||
|
@ -36,7 +36,7 @@ export default function History() {
|
||||
<Box
|
||||
sx={{
|
||||
mt: "20px",
|
||||
mb: upMd ? "40px" : "20px",
|
||||
mb: "20px",
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
}}
|
||||
|
@ -11,88 +11,94 @@ import SectionWrapper from "@components/SectionWrapper";
|
||||
import ArrowForwardIcon from "@mui/icons-material/ArrowForward";
|
||||
|
||||
export default function Section3() {
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const downXs = useMediaQuery(theme.breakpoints.down("sm"));
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const downXs = useMediaQuery(theme.breakpoints.down("sm"));
|
||||
|
||||
return (
|
||||
<SectionWrapper
|
||||
component="section"
|
||||
maxWidth="lg"
|
||||
outerContainerSx={{
|
||||
backgroundColor: theme.palette.lightPurple.main,
|
||||
}}
|
||||
sx={{
|
||||
display: "flex",
|
||||
pt: upMd ? "170px" : "155px",
|
||||
pb: upMd ? "100px" : "70px",
|
||||
// width: "fit-content",
|
||||
margin: "auto",
|
||||
flexDirection: upMd ? "row" : "column",
|
||||
flexWrap: "wrap",
|
||||
rowGap: upMd ? "58px" : "30px",
|
||||
columnGap: "13.8%",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
return (
|
||||
<SectionWrapper
|
||||
component="section"
|
||||
maxWidth="lg"
|
||||
outerContainerSx={{
|
||||
backgroundColor: theme.palette.lightPurple.main,
|
||||
}}
|
||||
sx={{
|
||||
display: "flex",
|
||||
pt: upMd ? "170px" : "155px",
|
||||
pb: upMd ? "100px" : "70px",
|
||||
// width: "fit-content",
|
||||
margin: "auto",
|
||||
flexDirection: upMd ? "row" : "column",
|
||||
flexWrap: "wrap",
|
||||
rowGap: upMd ? "58px" : "30px",
|
||||
columnGap: "13.8%",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "start",
|
||||
maxWidth: "500px",
|
||||
width: upMd ? "43.1%" : undefined,
|
||||
mb: "10px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h4"
|
||||
sx={{
|
||||
mb: upMd ? "70px" : "30px",
|
||||
}}
|
||||
>
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "start",
|
||||
maxWidth: "500px",
|
||||
width: upMd ? "43.1%" : undefined,
|
||||
mb: "10px",
|
||||
}}>
|
||||
<Typography
|
||||
variant="h4"
|
||||
sx={{
|
||||
mb: upMd ? "70px" : "30px",
|
||||
}}
|
||||
>
|
||||
Узнайте, как наши сервисы решают ваши задачи
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
mb: upMd ? "20px" : "30px",
|
||||
}}
|
||||
>
|
||||
<Typography>Покажут эффективность рекламы</Typography>
|
||||
<Typography>Соберут все обращения клиентов</Typography>
|
||||
<Typography>Повысят конверсию сайта</Typography>
|
||||
</Box>
|
||||
<UnderlinedLink
|
||||
linkHref="#"
|
||||
text="Подробнее"
|
||||
endIcon={<ArrowForwardIcon sx={{ height: "20px", width: "20px", display: "inline" }} />}
|
||||
/>
|
||||
</Box>
|
||||
<PromoCard
|
||||
width={upMd ? "43.1%" : "100%"}
|
||||
headerText="Общий кабинет"
|
||||
text="Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного"
|
||||
textOrientation="column"
|
||||
small={downXs}
|
||||
backgroundImage={downXs ? cardPagesBackground4 : cardPagesBackground1}
|
||||
sx={{ alignSelf: "center" }}
|
||||
Узнайте, как наши сервисы решают ваши задачи
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
mb: upMd ? "20px" : "30px",
|
||||
}}
|
||||
>
|
||||
<Typography>Покажут эффективность рекламы</Typography>
|
||||
<Typography>Соберут все обращения клиентов</Typography>
|
||||
<Typography>Повысят конверсию сайта</Typography>
|
||||
</Box>
|
||||
<UnderlinedLink
|
||||
linkHref="#"
|
||||
text="Подробнее"
|
||||
endIcon={
|
||||
<ArrowForwardIcon
|
||||
sx={{ height: "20px", width: "20px", display: "inline" }}
|
||||
/>
|
||||
<PromoCard
|
||||
width={upMd ? "43.1%" : "100%"}
|
||||
headerText="Общий кабинет"
|
||||
text="Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного"
|
||||
textOrientation="row"
|
||||
small={downXs}
|
||||
backgroundImage={downXs ? cardPagesBackground5 : cardPagesBackground2}
|
||||
sx={{ alignSelf: "center" }}
|
||||
/>
|
||||
<PromoCard
|
||||
width={upMd ? "43.1%" : "100%"}
|
||||
headerText="Гибкие тарифы"
|
||||
text="Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного"
|
||||
textOrientation="column"
|
||||
small={downXs}
|
||||
backgroundImage={downXs ? cardPagesBackground6 : cardPagesBackground3}
|
||||
sx={{ mt: upMd ? "82px" : undefined, alignSelf: "center" }}
|
||||
/>
|
||||
</SectionWrapper>
|
||||
);
|
||||
}
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
<PromoCard
|
||||
width={upMd ? "43.1%" : "100%"}
|
||||
headerText="Общий кабинет"
|
||||
text="Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного"
|
||||
textOrientation="column"
|
||||
small={downXs}
|
||||
backgroundImage={downXs ? cardPagesBackground4 : cardPagesBackground1}
|
||||
sx={{ alignSelf: "center" }}
|
||||
/>
|
||||
<PromoCard
|
||||
width={upMd ? "43.1%" : "100%"}
|
||||
headerText="Общий кабинет"
|
||||
text="Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного"
|
||||
textOrientation="row"
|
||||
small={downXs}
|
||||
backgroundImage={downXs ? cardPagesBackground5 : cardPagesBackground2}
|
||||
sx={{ alignSelf: "center", mt: upMd ? "-82px" : null }}
|
||||
/>
|
||||
<PromoCard
|
||||
width={upMd ? "43.1%" : "100%"}
|
||||
headerText="Гибкие тарифы"
|
||||
text="Текст-заполнитель — это текст, который имеет некоторые характеристики реального письменного"
|
||||
textOrientation="column"
|
||||
small={downXs}
|
||||
backgroundImage={downXs ? cardPagesBackground6 : cardPagesBackground3}
|
||||
sx={{ mt: upMd ? "82px" : undefined, alignSelf: "center" }}
|
||||
/>
|
||||
</SectionWrapper>
|
||||
);
|
||||
}
|
||||
|
@ -1,120 +1,144 @@
|
||||
import { Box, Divider, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||
import {
|
||||
Box,
|
||||
Divider,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import CustomButton from "../../components/CustomButton";
|
||||
import { Privilege } from "@root/model/privilege";
|
||||
import TariffPrivilegeSlider from "./TariffItem";
|
||||
import { createAndSendTariff, useCustomTariffsStore } from "@root/stores/customTariffs";
|
||||
import {
|
||||
createAndSendTariff,
|
||||
useCustomTariffsStore,
|
||||
} from "@root/stores/customTariffs";
|
||||
import { cardShadow } from "@root/utils/themes/shadow";
|
||||
import { currencyFormatter } from "@root/utils/currencyFormatter";
|
||||
import { devlog, getMessageFromFetchError } from "@frontend/kitui";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
|
||||
|
||||
interface Props {
|
||||
serviceKey: string;
|
||||
privileges: Privilege[];
|
||||
serviceKey: string;
|
||||
privileges: Privilege[];
|
||||
}
|
||||
|
||||
export default function CustomTariffCard({ serviceKey, privileges }: Props) {
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const summaryPriceBeforeDiscounts = useCustomTariffsStore(state => state.summaryPriceBeforeDiscountsMap);
|
||||
const summaryPriceAfterDiscounts = useCustomTariffsStore(state => state.summaryPriceAfterDiscountsMap);
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const summaryPriceBeforeDiscounts = useCustomTariffsStore(
|
||||
(state) => state.summaryPriceBeforeDiscountsMap
|
||||
);
|
||||
const summaryPriceAfterDiscounts = useCustomTariffsStore(
|
||||
(state) => state.summaryPriceAfterDiscountsMap
|
||||
);
|
||||
|
||||
const priceBeforeDiscounts = summaryPriceBeforeDiscounts[serviceKey] ?? 0;
|
||||
const priceAfterDiscounts = summaryPriceAfterDiscounts[serviceKey] ?? 0;
|
||||
const priceBeforeDiscounts = summaryPriceBeforeDiscounts[serviceKey] ?? 0;
|
||||
const priceAfterDiscounts = summaryPriceAfterDiscounts[serviceKey] ?? 0;
|
||||
|
||||
async function handleConfirmClick() {
|
||||
createAndSendTariff(serviceKey).then(result => {
|
||||
devlog(result);
|
||||
enqueueSnackbar("Тариф создан");
|
||||
}).catch(error => {
|
||||
const message = getMessageFromFetchError(error, "Не удалось создать тариф");
|
||||
if (message) enqueueSnackbar(message);
|
||||
});
|
||||
}
|
||||
async function handleConfirmClick() {
|
||||
createAndSendTariff(serviceKey)
|
||||
.then((result) => {
|
||||
devlog(result);
|
||||
enqueueSnackbar("Тариф создан");
|
||||
})
|
||||
.catch((error) => {
|
||||
const message = getMessageFromFetchError(
|
||||
error,
|
||||
"Не удалось создать тариф"
|
||||
);
|
||||
if (message) enqueueSnackbar(message);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Box sx={{
|
||||
backgroundColor: "white",
|
||||
width: "100%",
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
backgroundColor: "white",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: upMd ? "row" : "column",
|
||||
borderRadius: "12px",
|
||||
boxShadow: cardShadow,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
p: "20px",
|
||||
pr: upMd ? "35px" : undefined,
|
||||
display: "flex",
|
||||
flexBasis: 0,
|
||||
flexGrow: 2.37,
|
||||
flexWrap: "wrap",
|
||||
flexDirection: "column",
|
||||
gap: "25px",
|
||||
}}
|
||||
>
|
||||
{privileges.map((privilege) => (
|
||||
<TariffPrivilegeSlider key={privilege._id} privilege={privilege} />
|
||||
))}
|
||||
</Box>
|
||||
{!upMd && (
|
||||
<Divider
|
||||
sx={{ mx: "20px", my: "10px", borderColor: theme.palette.grey2.main }}
|
||||
/>
|
||||
)}
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexBasis: 0,
|
||||
flexGrow: 1,
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "start",
|
||||
color: theme.palette.grey3.main,
|
||||
p: "20px",
|
||||
pl: upMd ? "33px" : undefined,
|
||||
borderLeft: upMd
|
||||
? `1px solid ${theme.palette.grey2.main}`
|
||||
: undefined,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: upMd ? "row" : "column",
|
||||
borderRadius: "12px",
|
||||
boxShadow: cardShadow,
|
||||
}}>
|
||||
<Box sx={{
|
||||
p: "20px",
|
||||
pr: upMd ? "35px" : undefined,
|
||||
display: "flex",
|
||||
flexBasis: 0,
|
||||
flexGrow: 2.37,
|
||||
flexWrap: "wrap",
|
||||
flexDirection: "column",
|
||||
gap: "25px",
|
||||
}}>
|
||||
{privileges.map(privilege =>
|
||||
<TariffPrivilegeSlider
|
||||
key={privilege._id}
|
||||
privilege={privilege}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
{!upMd && <Divider sx={{ mx: "20px", my: "10px", borderColor: theme.palette.grey2.main }} />}
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
flexBasis: 0,
|
||||
flexGrow: 1,
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "start",
|
||||
color: theme.palette.grey3.main,
|
||||
p: "20px",
|
||||
pl: upMd ? "33px" : undefined,
|
||||
borderLeft: upMd ? `1px solid ${theme.palette.grey2.main}` : undefined,
|
||||
}}>
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
gap: "15%",
|
||||
mb: "auto",
|
||||
width: "100%",
|
||||
}}>
|
||||
<Typography>Чем больше пакеты, тем дешевле подписки и опции </Typography>
|
||||
<Box sx={{
|
||||
px: "6.7px",
|
||||
height: "36px",
|
||||
color: "white",
|
||||
backgroundColor: theme.palette.orange.main,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
borderRadius: "8px",
|
||||
}}>
|
||||
{"-60%"}
|
||||
</Box>
|
||||
</Box>
|
||||
<Typography mb="20px" mt="18px">
|
||||
Сумма с учетом скидки
|
||||
</Typography>
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "20px",
|
||||
mb: "30px",
|
||||
}}>
|
||||
<Typography variant="price">{currencyFormatter.format(priceAfterDiscounts / 100)}</Typography>
|
||||
<Typography variant="oldPrice" pt="3px">{currencyFormatter.format(priceBeforeDiscounts / 100)}</Typography>
|
||||
</Box>
|
||||
<CustomButton
|
||||
variant="contained"
|
||||
onClick={handleConfirmClick}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.brightPurple.main,
|
||||
}}
|
||||
>
|
||||
Выбрать
|
||||
</CustomButton>
|
||||
</Box>
|
||||
justifyContent: "space-between",
|
||||
gap: "15%",
|
||||
mb: "auto",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Typography>
|
||||
Чем больше пакеты, тем дешевле подписки и опции{" "}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
<Typography mb="20px" mt="18px">
|
||||
Сумма с учетом скидки
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "20px",
|
||||
mb: "30px",
|
||||
}}
|
||||
>
|
||||
<Typography variant="price">
|
||||
{currencyFormatter.format(priceAfterDiscounts / 100)}
|
||||
</Typography>
|
||||
<Typography variant="oldPrice" pt="3px">
|
||||
{currencyFormatter.format(priceBeforeDiscounts / 100)}
|
||||
</Typography>
|
||||
</Box>
|
||||
<CustomButton
|
||||
variant="contained"
|
||||
onClick={handleConfirmClick}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.brightPurple.main,
|
||||
}}
|
||||
>
|
||||
Выбрать
|
||||
</CustomButton>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
@ -9,82 +9,96 @@ import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
||||
import TotalPrice from "@root/components/TotalPrice";
|
||||
import { serviceNameByKey } from "@root/utils/serviceKeys";
|
||||
|
||||
|
||||
export default function TariffConstructor() {
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const customTariffs = useCustomTariffsStore(state => state.customTariffsMap);
|
||||
const summaryPriceBeforeDiscountsMap = useCustomTariffsStore(state => state.summaryPriceBeforeDiscountsMap);
|
||||
const summaryPriceAfterDiscountsMap = useCustomTariffsStore(state => state.summaryPriceAfterDiscountsMap);
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const customTariffs = useCustomTariffsStore(
|
||||
(state) => state.customTariffsMap
|
||||
);
|
||||
const summaryPriceBeforeDiscountsMap = useCustomTariffsStore(
|
||||
(state) => state.summaryPriceBeforeDiscountsMap
|
||||
);
|
||||
const summaryPriceAfterDiscountsMap = useCustomTariffsStore(
|
||||
(state) => state.summaryPriceAfterDiscountsMap
|
||||
);
|
||||
|
||||
const basePrice = Object.values(summaryPriceBeforeDiscountsMap).reduce((a, e) => a + e, 0);
|
||||
const discountedPrice = Object.values(summaryPriceAfterDiscountsMap).reduce((a, e) => a + e, 0);
|
||||
const basePrice = Object.values(summaryPriceBeforeDiscountsMap).reduce(
|
||||
(a, e) => a + e,
|
||||
0
|
||||
);
|
||||
const discountedPrice = Object.values(summaryPriceAfterDiscountsMap).reduce(
|
||||
(a, e) => a + e,
|
||||
0
|
||||
);
|
||||
|
||||
return (
|
||||
<SectionWrapper
|
||||
maxWidth="lg"
|
||||
sx={{
|
||||
mt: upMd ? "25px" : "20px",
|
||||
mb: upMd ? "93px" : "48px",
|
||||
}}
|
||||
>
|
||||
{upMd && <ComplexNavText text1="Все тарифы — " text2="Кастомный тариф" />}
|
||||
<Box
|
||||
return (
|
||||
<SectionWrapper
|
||||
maxWidth="lg"
|
||||
sx={{
|
||||
mt: upMd ? "25px" : "20px",
|
||||
mb: upMd ? "93px" : "48px",
|
||||
}}
|
||||
>
|
||||
{upMd && <ComplexNavText text1="Все тарифы — " text2="Кастомный тариф" />}
|
||||
<Box
|
||||
sx={{
|
||||
mt: "20px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "80px",
|
||||
}}
|
||||
>
|
||||
{Object.entries(customTariffs).map(
|
||||
([serviceKey, privileges], index) => (
|
||||
<Box key={serviceKey}>
|
||||
<Box
|
||||
sx={{
|
||||
mt: "20px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "80px",
|
||||
mb: "40px",
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
{Object.entries(customTariffs).map(([serviceKey, privileges], index) => (
|
||||
<Box key={serviceKey}>
|
||||
<Box
|
||||
sx={{
|
||||
mb: "40px",
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
{!upMd && index === 0 && (
|
||||
<IconButton
|
||||
sx={{
|
||||
p: 0,
|
||||
height: "28px",
|
||||
width: "28px",
|
||||
color: "black",
|
||||
}}
|
||||
>
|
||||
<ArrowBackIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
<ComplexHeader
|
||||
text1="Кастомный тариф "
|
||||
text2={serviceNameByKey[serviceKey]}
|
||||
/>
|
||||
</Box>
|
||||
<CustomTariffCard serviceKey={serviceKey} privileges={privileges} />
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
{upMd && (
|
||||
<Link
|
||||
to="/tariffconstructor/savedtariffs"
|
||||
style={{
|
||||
display: "block",
|
||||
marginTop: "60px",
|
||||
textUnderlinePosition: "under",
|
||||
color: theme.palette.brightPurple.main,
|
||||
textDecorationColor: theme.palette.brightPurple.main,
|
||||
>
|
||||
{!upMd && index === 0 && (
|
||||
<IconButton
|
||||
sx={{
|
||||
p: 0,
|
||||
height: "28px",
|
||||
width: "28px",
|
||||
color: "black",
|
||||
}}
|
||||
>
|
||||
Ваши сохраненные тарифы
|
||||
</Link>
|
||||
)}
|
||||
<TotalPrice
|
||||
priceBeforeDiscounts={basePrice}
|
||||
priceAfterDiscounts={discountedPrice}
|
||||
/>
|
||||
</SectionWrapper>
|
||||
);
|
||||
>
|
||||
<ArrowBackIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
<ComplexHeader
|
||||
text1="Кастомный тариф "
|
||||
text2={serviceNameByKey[serviceKey]}
|
||||
/>
|
||||
</Box>
|
||||
<CustomTariffCard
|
||||
serviceKey={serviceKey}
|
||||
privileges={privileges}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
)}
|
||||
</Box>
|
||||
<Link
|
||||
to="/tariffconstructor/savedtariffs"
|
||||
style={{
|
||||
display: "block",
|
||||
marginTop: "60px",
|
||||
textUnderlinePosition: "under",
|
||||
color: theme.palette.brightPurple.main,
|
||||
textDecorationColor: theme.palette.brightPurple.main,
|
||||
}}
|
||||
>
|
||||
Ваши сохраненные тарифы
|
||||
</Link>
|
||||
<TotalPrice
|
||||
priceBeforeDiscounts={basePrice}
|
||||
priceAfterDiscounts={discountedPrice}
|
||||
/>
|
||||
</SectionWrapper>
|
||||
);
|
||||
}
|
||||
|
@ -1,135 +1,169 @@
|
||||
import { useThrottle } from "@frontend/kitui";
|
||||
import { Box, SliderProps, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||
import CustomSlider from "@root/components/CustomSlider";
|
||||
import {
|
||||
Box,
|
||||
SliderProps,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import { CustomSlider } from "@root/components/CustomSlider";
|
||||
import NumberInputWithUnitAdornment from "@root/components/NumberInputWithUnitAdornment";
|
||||
import CalendarIcon from "@root/components/icons/CalendarIcon";
|
||||
import PieChartIcon from "@root/components/icons/PieChartIcon";
|
||||
import { Privilege, PrivilegeValueType } from "@root/model/privilege";
|
||||
import { useCartStore } from "@root/stores/cart";
|
||||
import { setCustomTariffsUserValue, useCustomTariffsStore } from "@root/stores/customTariffs";
|
||||
import {
|
||||
setCustomTariffsUserValue,
|
||||
useCustomTariffsStore,
|
||||
} from "@root/stores/customTariffs";
|
||||
import { useDiscountStore } from "@root/stores/discounts";
|
||||
import { useUserStore } from "@root/stores/user";
|
||||
import { getDeclension } from "@root/utils/declension";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
|
||||
const sliderSettingsByType: Record<PrivilegeValueType, Partial<SliderProps>> = {
|
||||
"день": {
|
||||
max: 365,
|
||||
step: 1,
|
||||
},
|
||||
"шаблон": {
|
||||
max: 1000000,
|
||||
step: 1000,
|
||||
},
|
||||
"МБ": {
|
||||
max: 1000000,
|
||||
step: 1000,
|
||||
},
|
||||
день: { max: 365 },
|
||||
шаблон: { max: 1000000 },
|
||||
МБ: { max: 1000000 },
|
||||
};
|
||||
|
||||
interface Props {
|
||||
privilege: Privilege;
|
||||
privilege: Privilege;
|
||||
}
|
||||
|
||||
export default function TariffPrivilegeSlider({ privilege }: Props) {
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const userValue = useCustomTariffsStore(state => state.userValuesMap[privilege.serviceKey]?.[privilege._id]) ?? 0;
|
||||
const discounts = useDiscountStore(state => state.discounts);
|
||||
const currentCartTotal = useCartStore(state => state.cart.priceAfterDiscounts);
|
||||
const purchasesAmount = useUserStore(state => state.userAccount?.wallet.purchasesAmount) ?? 0;
|
||||
const [value, setValue] = useState<number>(userValue);
|
||||
const throttledValue = useThrottle(value, 200);
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const userValue =
|
||||
useCustomTariffsStore(
|
||||
(state) => state.userValuesMap[privilege.serviceKey]?.[privilege._id]
|
||||
) ?? 0;
|
||||
const discounts = useDiscountStore((state) => state.discounts);
|
||||
const currentCartTotal = useCartStore(
|
||||
(state) => state.cart.priceAfterDiscounts
|
||||
);
|
||||
const purchasesAmount =
|
||||
useUserStore((state) => state.userAccount?.wallet.purchasesAmount) ?? 0;
|
||||
const [value, setValue] = useState<number>(userValue);
|
||||
const throttledValue = useThrottle(value, 200);
|
||||
|
||||
useEffect(function setStoreValue() {
|
||||
setCustomTariffsUserValue(
|
||||
privilege.serviceKey,
|
||||
privilege._id,
|
||||
throttledValue,
|
||||
discounts,
|
||||
currentCartTotal,
|
||||
purchasesAmount
|
||||
);
|
||||
}, [currentCartTotal, discounts, purchasesAmount, privilege._id, privilege.serviceKey, throttledValue]);
|
||||
useEffect(
|
||||
function setStoreValue() {
|
||||
setCustomTariffsUserValue(
|
||||
privilege.serviceKey,
|
||||
privilege._id,
|
||||
throttledValue,
|
||||
discounts,
|
||||
currentCartTotal,
|
||||
purchasesAmount
|
||||
);
|
||||
},
|
||||
[
|
||||
currentCartTotal,
|
||||
discounts,
|
||||
purchasesAmount,
|
||||
privilege._id,
|
||||
privilege.serviceKey,
|
||||
throttledValue,
|
||||
]
|
||||
);
|
||||
|
||||
function handleSliderChange(event: Event, value: number | number[]) {
|
||||
if (Array.isArray(value)) throw new Error("Slider uses multiple values instead of one");
|
||||
function handleSliderChange(value: number | number[]) {
|
||||
if (Array.isArray(value))
|
||||
throw new Error("Slider uses multiple values instead of one");
|
||||
|
||||
setValue(value);
|
||||
}
|
||||
setValue(value);
|
||||
}
|
||||
|
||||
const quantityText = `${value} ${getDeclension(value, privilege.value)}`;
|
||||
const quantityText = `${value} ${getDeclension(value, privilege.value)}`;
|
||||
|
||||
const quantityElement = (
|
||||
<Box sx={{
|
||||
const quantityElement = (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "15px",
|
||||
alignItems: "center",
|
||||
justifyContent: upMd ? "end" : undefined,
|
||||
flexWrap: "wrap",
|
||||
mt: upMd ? undefined : "12px",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="p1"
|
||||
color={theme.palette.brightPurple.main}
|
||||
textAlign="end"
|
||||
>
|
||||
{quantityText}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "15px",
|
||||
alignItems: "center",
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ fontSize: "16px", lineHeight: "19px", mt: "1px" }}>
|
||||
или
|
||||
</Typography>
|
||||
<NumberInputWithUnitAdornment
|
||||
id={"privilege_input_" + privilege._id}
|
||||
adornmentText={getDeclension(0, privilege.value)}
|
||||
onChange={(value) => setValue(value)}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
const icon =
|
||||
privilege.type === "day" ? (
|
||||
<CalendarIcon color={theme.palette.orange.main} bgcolor="#FEDFD0" />
|
||||
) : (
|
||||
<PieChartIcon color={theme.palette.orange.main} bgcolor="#FEDFD0" />
|
||||
);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography sx={{ color: theme.palette.grey3.main, mb: "auto" }}>
|
||||
{privilege.description}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
mt: "40px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "15px",
|
||||
// flexWrap: "wrap",
|
||||
alignItems: "center",
|
||||
justifyContent: upMd ? "end" : undefined,
|
||||
flexWrap: "wrap",
|
||||
mt: upMd ? undefined : "12px",
|
||||
}}>
|
||||
<Typography variant="p1" color={theme.palette.brightPurple.main} textAlign="end">
|
||||
{quantityText}
|
||||
</Typography>
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
gap: "15px",
|
||||
alignItems: "center",
|
||||
flexWrap: "wrap",
|
||||
}}>
|
||||
<Typography sx={{ fontSize: "16px", lineHeight: "19px", mt: "1px" }}>или</Typography>
|
||||
<NumberInputWithUnitAdornment
|
||||
id={"privilege_input_" + privilege._id}
|
||||
adornmentText={getDeclension(0, privilege.value)}
|
||||
onChange={value => setValue(value)}
|
||||
/>
|
||||
</Box>
|
||||
mb: "8px",
|
||||
justifyContent: "space-between",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "22px",
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
<Typography variant="h5">{privilege.name}</Typography>
|
||||
</Box>
|
||||
{upMd && quantityElement}
|
||||
</Box>
|
||||
);
|
||||
|
||||
const icon = privilege.type === "day"
|
||||
? <CalendarIcon color={theme.palette.orange.main} bgcolor="#FEDFD0" />
|
||||
: <PieChartIcon color={theme.palette.orange.main} bgcolor="#FEDFD0" />;
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography sx={{ color: theme.palette.grey3.main, mb: "auto" }}>
|
||||
{privilege.description}
|
||||
</Typography>
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
mt: "40px",
|
||||
}}>
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
// flexWrap: "wrap",
|
||||
alignItems: "center",
|
||||
mb: "8px",
|
||||
justifyContent: "space-between",
|
||||
gap: "10px",
|
||||
}}>
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "22px",
|
||||
}}>
|
||||
{icon}
|
||||
<Typography variant="h5">{privilege.name}</Typography>
|
||||
</Box>
|
||||
{upMd && quantityElement}
|
||||
</Box>
|
||||
<CustomSlider
|
||||
value={value}
|
||||
defaultValue={0}
|
||||
min={0}
|
||||
onChange={handleSliderChange}
|
||||
{...sliderSettingsByType[privilege.value]}
|
||||
/>
|
||||
{!upMd && quantityElement}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
<CustomSlider
|
||||
value={value}
|
||||
min={0}
|
||||
max={sliderSettingsByType[privilege.value].max || 100}
|
||||
onChange={handleSliderChange}
|
||||
/>
|
||||
{!upMd && quantityElement}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ export default function TariffCard({ icon, headerText, text, sx, price, buttonPr
|
||||
sx={{
|
||||
color: theme.palette.brightPurple.main,
|
||||
borderColor: theme.palette.brightPurple.main,
|
||||
mt: "auto",
|
||||
mt: "30px",
|
||||
...buttonProps.sx,
|
||||
}}
|
||||
>
|
||||
|
@ -89,7 +89,7 @@ export default function Tariffs() {
|
||||
</Typography>
|
||||
|
||||
{upMd ?
|
||||
<WideTemplCard sx={{ marginTop: "76px" }} />
|
||||
<WideTemplCard sx={{ marginTop: "60px" }} />
|
||||
:
|
||||
<TemplCardPhoneLight />}
|
||||
{/*<Box sx={{*/}
|
||||
|
@ -23,17 +23,20 @@ import { useCartStore } from "@root/stores/cart";
|
||||
const subPages = ["Шаблонизатор", "Опросник", "Сокращатель ссылок"];
|
||||
|
||||
export default function TariffPage() {
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
const location = useLocation();
|
||||
const tariffs = useTariffStore(state => state.tariffs);
|
||||
const [selectedItem, setSelectedItem] = useState<number>(0);
|
||||
const discounts = useDiscountStore(state => state.discounts);
|
||||
const customTariffs = useCustomTariffsStore(state => state.customTariffsMap);
|
||||
const purchasesAmount = useUserStore(state => state.userAccount?.wallet.purchasesAmount) ?? 0;
|
||||
const cart = useCartStore(state => state.cart);
|
||||
const unit: string = String(location.pathname).slice(9);
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
const location = useLocation();
|
||||
const tariffs = useTariffStore((state) => state.tariffs);
|
||||
const [selectedItem, setSelectedItem] = useState<number>(0);
|
||||
const discounts = useDiscountStore((state) => state.discounts);
|
||||
const customTariffs = useCustomTariffsStore(
|
||||
(state) => state.customTariffsMap
|
||||
);
|
||||
const purchasesAmount =
|
||||
useUserStore((state) => state.userAccount?.wallet.purchasesAmount) ?? 0;
|
||||
const cart = useCartStore((state) => state.cart);
|
||||
const unit: string = String(location.pathname).slice(9);
|
||||
|
||||
const StepperText: Record<string, string> = {
|
||||
volume: "Тарифы на объём",
|
||||
@ -155,7 +158,7 @@ export default function TariffPage() {
|
||||
>
|
||||
{tariffElements}
|
||||
</Box>
|
||||
<Typography variant="h4" sx={{ mt: "50px", mb: "40px" }}>
|
||||
<Typography variant="h4" sx={{ mt: "40px" }}>
|
||||
Ранее вы покупали
|
||||
</Typography>
|
||||
<Slider items={tariffElements} />
|
||||
|
@ -1,3 +1,7 @@
|
||||
.slider {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.slider .slick-slide {
|
||||
width: 100%;
|
||||
max-width: 360px;
|
||||
|
@ -59,7 +59,7 @@ export const Slider = ({ items }: SliderProps) => {
|
||||
display: "grid",
|
||||
gap: "40px",
|
||||
gridTemplateColumns: "repeat(auto-fit, minmax(300px, 360px))",
|
||||
margin: isTablet ? "auto" : null,
|
||||
margin: isTablet ? "40px auto" : null,
|
||||
}}
|
||||
>
|
||||
{items}
|
||||
|
Loading…
Reference in New Issue
Block a user