front-hub/src/components/Drawers.tsx

342 lines
9.4 KiB
TypeScript
Raw Normal View History

2023-11-05 23:33:40 +00:00
import { useState, useRef } from "react"
2023-08-30 09:56:14 +00:00
import {
2023-11-05 23:33:40 +00:00
Typography,
Drawer,
useMediaQuery,
useTheme,
Box,
IconButton,
Badge,
Button,
} from "@mui/material"
import SectionWrapper from "./SectionWrapper"
import CustomWrapperDrawer from "./CustomWrapperDrawer"
import { NotificationsModal } from "./NotificationsModal"
import { Loader } from "./Loader"
import { useCart } from "@root/utils/hooks/useCart"
import { currencyFormatter } from "@root/utils/currencyFormatter"
2023-08-30 09:56:14 +00:00
import {
2023-11-05 23:33:40 +00:00
closeCartDrawer,
openCartDrawer,
useCartStore,
} from "@root/stores/cart"
import { setUserAccount, useUserStore } from "@root/stores/user"
import { useTicketStore } from "@root/stores/tickets"
2023-08-03 12:14:31 +00:00
2023-11-05 23:33:40 +00:00
import { ReactComponent as BellIcon } from "@root/assets/Icons/bell.svg"
import { ReactComponent as CartIcon } from "@root/assets/Icons/cart.svg"
import { ReactComponent as CrossIcon } from "@root/assets/Icons/cross.svg"
2023-03-27 12:45:44 +00:00
2023-11-05 23:33:40 +00:00
import { payCart } from "@root/api/cart"
import { enqueueSnackbar } from "notistack"
import { Link, useNavigate } from "react-router-dom"
import { withErrorBoundary } from "react-error-boundary"
import { handleComponentError } from "@root/utils/handleComponentError"
import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline"
function Drawers() {
2023-11-05 23:33:40 +00:00
const [openNotificationsModal, setOpenNotificationsModal] =
useState<boolean>(false)
const [loading, setLoading] = useState<boolean>(false)
const bellRef = useRef<HTMLButtonElement | null>(null)
const navigate = useNavigate()
const theme = useTheme()
const upMd = useMediaQuery(theme.breakpoints.up("md"))
const isTablet = useMediaQuery(theme.breakpoints.down(1000))
const isDrawerOpen = useCartStore((state) => state.isDrawerOpen)
const cart = useCart()
const userAccount = useUserStore((state) => state.userAccount)
const tickets = useTicketStore((state) => state.tickets)
const [notEnoughMoneyAmount, setNotEnoughMoneyAmount] = useState<number>(0)
2023-08-03 12:14:31 +00:00
2023-11-05 23:33:40 +00:00
const notificationsCount = tickets.filter(
({ user, top_message }) =>
user !== top_message.user_id && top_message.shown.me !== 1
).length
2023-08-07 15:16:34 +00:00
2023-11-05 23:33:40 +00:00
async function handlePayClick() {
setLoading(true)
2023-10-06 11:56:50 +00:00
2023-11-05 23:33:40 +00:00
const [payCartResponse, payCartError] = await payCart()
2023-08-30 09:56:14 +00:00
2023-11-05 23:33:40 +00:00
if (payCartError) {
if (payCartError.includes("insufficient funds: ")) {
const notEnoughMoneyAmount = parseInt(
payCartError.replace(/^.*insufficient\sfunds:\s(?=\d+$)/, "")
)
2023-08-30 09:56:14 +00:00
2023-11-05 23:33:40 +00:00
setNotEnoughMoneyAmount(notEnoughMoneyAmount)
}
2023-10-26 11:27:41 +00:00
2023-11-05 23:33:40 +00:00
setLoading(false)
2023-08-30 09:56:14 +00:00
2023-11-05 23:33:40 +00:00
return enqueueSnackbar(payCartError)
}
2023-08-30 09:56:14 +00:00
2023-11-05 23:33:40 +00:00
if (payCartResponse) {
setUserAccount(payCartResponse)
}
2023-10-06 11:56:50 +00:00
2023-11-05 23:33:40 +00:00
setLoading(false)
closeCartDrawer()
}
2023-08-27 13:14:17 +00:00
2023-11-05 23:33:40 +00:00
function handleReplenishWallet() {
navigate("/payment", { state: { notEnoughMoneyAmount } })
}
2023-08-27 13:14:17 +00:00
2023-11-05 23:33:40 +00:00
return (
<Box sx={{ display: "flex", gap: isTablet ? "10px" : "20px" }}>
<IconButton
ref={bellRef}
aria-label="cart"
onClick={() => setOpenNotificationsModal((isOpened) => !isOpened)}
sx={{
cursor: "pointer",
borderRadius: "6px",
background: openNotificationsModal
? theme.palette.purple.main
: theme.palette.background.default,
"& .MuiBadge-badge": {
background: openNotificationsModal
? theme.palette.background.default
: theme.palette.purple.main,
color: openNotificationsModal
? theme.palette.purple.main
: theme.palette.background.default,
},
"& svg > path:first-of-type": {
fill: openNotificationsModal ? "#FFFFFF" : "#9A9AAF",
},
"& svg > path:last-child": {
stroke: openNotificationsModal ? "#FFFFFF" : "#9A9AAF",
},
"&:hover": {
background: theme.palette.purple.main,
"& .MuiBox-root": {
background: theme.palette.purple.main,
},
"& .MuiBadge-badge": {
background: theme.palette.background.default,
color: theme.palette.purple.main,
},
"& svg > path:first-of-type": { fill: "#FFFFFF" },
"& svg > path:last-child": { stroke: "#FFFFFF" },
},
}}
>
<Badge
badgeContent={notificationsCount}
sx={{
"& .MuiBadge-badge": {
display: notificationsCount ? "flex" : "none",
color: "#FFFFFF",
background: theme.palette.purple.main,
transform: "scale(0.8) translate(50%, -50%)",
top: "2px",
right: "2px",
fontWeight: 400,
},
}}
>
<BellIcon />
</Badge>
</IconButton>
<NotificationsModal
open={openNotificationsModal}
setOpen={setOpenNotificationsModal}
anchorElement={bellRef.current}
notifications={tickets
.filter(({ user, top_message }) => user !== top_message.user_id)
.map((ticket) => ({
text: "У вас новое сообщение от техподдержки",
date: new Date(ticket.updated_at).toLocaleDateString(),
url: `/support/${ticket.id}`,
watched:
2023-08-30 09:56:14 +00:00
ticket.user === ticket.top_message.user_id ||
ticket.top_message.shown.me === 1,
2023-11-05 23:33:40 +00:00
}))}
/>
<IconButton
onClick={openCartDrawer}
component="div"
sx={{
cursor: "pointer",
background: theme.palette.background.default,
borderRadius: "6px",
"&:hover": {
background: theme.palette.purple.main,
"& .MuiBox-root": {
background: theme.palette.purple.main,
},
"& .MuiBadge-badge": {
background: theme.palette.background.default,
color: theme.palette.purple.main,
},
"& svg > path:nth-of-type(1)": { fill: "#FFFFFF" },
"& svg > path:nth-of-type(2)": { fill: "#FFFFFF" },
"& svg > path:nth-of-type(3)": { stroke: "#FFFFFF" },
},
}}
>
<Badge
badgeContent={userAccount?.cart.length}
sx={{
"& .MuiBadge-badge": {
display: userAccount?.cart.length ? "flex" : "none",
color: "#FFFFFF",
background: theme.palette.purple.main,
transform: "scale(0.8) translate(50%, -50%)",
top: "2px",
right: "2px",
fontWeight: 400,
},
}}
>
<CartIcon />
</Badge>
</IconButton>
<Drawer
anchor={"right"}
open={isDrawerOpen}
onClose={closeCartDrawer}
sx={{ background: "rgba(0, 0, 0, 0.55)" }}
>
<SectionWrapper
maxWidth="lg"
sx={{
pl: "0px",
pr: "0px",
width: "450px",
}}
>
<Box
sx={{
width: "100%",
pt: "12px",
pb: "12px",
display: "flex",
justifyContent: "space-between",
bgcolor: "#F2F3F7",
gap: "10px",
pl: "20px",
pr: "20px",
}}
>
<Typography
component="div"
sx={{
fontSize: "18px",
lineHeight: "21px",
font: "Rubick",
}}
>
2023-08-30 09:56:14 +00:00
Корзина
2023-11-05 23:33:40 +00:00
</Typography>
<IconButton onClick={closeCartDrawer} sx={{ p: 0 }}>
<CrossIcon />
</IconButton>
</Box>
<Box sx={{ pl: "20px", pr: "20px" }}>
{cart.services.map((serviceData) => (
<CustomWrapperDrawer
key={serviceData.serviceKey}
serviceData={serviceData}
/>
))}
<Box
sx={{
mt: "40px",
pt: upMd ? "30px" : undefined,
borderTop: upMd
? `1px solid ${theme.palette.gray.main}`
: undefined,
}}
>
<Box
sx={{
width: upMd ? "100%" : undefined,
display: "flex",
flexWrap: "wrap",
flexDirection: "column",
}}
>
<Typography variant="h4" mb={upMd ? "18px" : "30px"}>
2023-08-30 09:56:14 +00:00
Итоговая цена
2023-11-05 23:33:40 +00:00
</Typography>
<Typography color={theme.palette.gray.dark}>
2023-08-30 09:56:14 +00:00
Текст-заполнитель это текст, который имеет Текст-заполнитель
это текст, который имеет Текст-заполнитель это текст,
который имеет Текст-заполнитель это текст, который имеет
Текст-заполнитель
2023-11-05 23:33:40 +00:00
</Typography>
</Box>
<Box
sx={{
color: theme.palette.gray.dark,
pb: "100px",
pt: "38px",
}}
>
<Box
sx={{
display: "flex",
flexDirection: upMd ? "column" : "row",
alignItems: upMd ? "start" : "center",
mt: upMd ? "10px" : "30px",
gap: "15px",
}}
>
<Typography
color={theme.palette.orange.main}
sx={{
textDecoration: "line-through",
order: upMd ? 1 : 2,
}}
>
{currencyFormatter.format(cart.priceBeforeDiscounts / 100)}
</Typography>
<Typography
variant="p1"
sx={{
fontWeight: 500,
fontSize: "26px",
lineHeight: "31px",
order: upMd ? 2 : 1,
}}
>
{currencyFormatter.format(cart.priceAfterDiscounts / 100)}
</Typography>
</Box>
<Button
variant="pena-contained-dark"
onClick={() =>
notEnoughMoneyAmount === 0
? !loading && handlePayClick()
: handleReplenishWallet()
}
sx={{ mt: "25px" }}
>
{loading ? <Loader size={24} /> : "Оплатить"}
</Button>
</Box>
</Box>
</Box>
</SectionWrapper>
</Drawer>
</Box>
)
2023-03-27 12:45:44 +00:00
}
export default withErrorBoundary(Drawers, {
2023-11-05 23:33:40 +00:00
fallback: (
<Box sx={{
display: "flex",
alignItems: "center",
}}>
<ErrorOutlineIcon color="error" />
</Box>
),
onError: handleComponentError,
})