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