297 lines
9.6 KiB
TypeScript
297 lines
9.6 KiB
TypeScript
import { PlusOutlined } from "@ant-design/icons";
|
|
import { Space } from "antd";
|
|
import ArabicPrice from "components/ArabicPrice";
|
|
import BackIcon from "components/Icons/BackIcon";
|
|
import NextIcon from "components/Icons/NextIcon";
|
|
import ImageWithFallback from "components/ImageWithFallback";
|
|
import { ItemDescriptionIcons } from "components/ItemDescriptionIcons/ItemDescriptionIcons.tsx";
|
|
import ProText from "components/ProText.tsx";
|
|
import { menuItems } from "data/menuItems.ts";
|
|
import { addItem } from "features/order/orderSlice.ts";
|
|
import useBreakPoint from "hooks/useBreakPoint";
|
|
import { useEffect, useRef, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { useAppDispatch, useAppSelector } from "redux/hooks.ts";
|
|
import { colors } from "ThemeConstants.ts";
|
|
import { default_image } from "utils/constants.ts";
|
|
import { Product } from "utils/types/appTypes.ts";
|
|
import styles from "./YouMayAlsoLike.module.css";
|
|
|
|
export default function YouMightAlsoLike() {
|
|
const { t } = useTranslation();
|
|
const dispatch = useAppDispatch();
|
|
const { isRTL } = useAppSelector((state) => state.locale);
|
|
const { isMobile, isTablet, isDesktop } = useBreakPoint();
|
|
|
|
const [currentIndex, setCurrentIndex] = useState(0);
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
|
|
// Check if scrolling is needed based on container width
|
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
|
|
useEffect(() => {
|
|
const updateContainerWidth = () => {
|
|
if (containerRef.current) {
|
|
setContainerWidth(containerRef.current.offsetWidth);
|
|
}
|
|
};
|
|
|
|
updateContainerWidth();
|
|
window.addEventListener("resize", updateContainerWidth);
|
|
return () => window.removeEventListener("resize", updateContainerWidth);
|
|
}, []);
|
|
|
|
// Calculate actual visible items based on container width
|
|
const itemWidth = isMobile ? 95 + 16 : isTablet ? 120 + 16 : 140 + 16;
|
|
const totalItemsWidth = menuItems.length * itemWidth;
|
|
const needsScrolling = totalItemsWidth > containerWidth;
|
|
|
|
const canNavigateLeft = currentIndex > 0;
|
|
const canNavigateRight =
|
|
needsScrolling &&
|
|
currentIndex * itemWidth + containerWidth < totalItemsWidth;
|
|
|
|
const scrollToIndex = (index: number) => {
|
|
if (containerRef.current) {
|
|
const scrollLeft = isRTL ? -(index * itemWidth) : index * itemWidth;
|
|
containerRef.current.scrollTo({
|
|
left: scrollLeft,
|
|
behavior: "smooth",
|
|
});
|
|
}
|
|
};
|
|
|
|
const handlePrevious = () => {
|
|
if (canNavigateLeft) {
|
|
const newIndex = Math.max(0, currentIndex - 1);
|
|
setCurrentIndex(newIndex);
|
|
scrollToIndex(newIndex);
|
|
}
|
|
};
|
|
|
|
const handleNext = () => {
|
|
if (canNavigateRight) {
|
|
const maxPossibleIndex = Math.floor(
|
|
(totalItemsWidth - containerWidth) / itemWidth,
|
|
);
|
|
const newIndex = Math.min(maxPossibleIndex, currentIndex + 1);
|
|
setCurrentIndex(newIndex);
|
|
scrollToIndex(newIndex);
|
|
}
|
|
};
|
|
|
|
// RTL-specific handlers
|
|
const handleRTLPrevious = () => {
|
|
if (canNavigateRight) {
|
|
const maxPossibleIndex = Math.floor(
|
|
(totalItemsWidth - containerWidth) / itemWidth,
|
|
);
|
|
const newIndex = Math.min(maxPossibleIndex, currentIndex + 1);
|
|
setCurrentIndex(newIndex);
|
|
scrollToIndex(newIndex);
|
|
}
|
|
};
|
|
|
|
const handleRTLNext = () => {
|
|
if (canNavigateLeft) {
|
|
const newIndex = Math.max(0, currentIndex - 1);
|
|
setCurrentIndex(newIndex);
|
|
scrollToIndex(newIndex);
|
|
}
|
|
};
|
|
const handleQuickAdd = (item: Product) => {
|
|
dispatch(
|
|
addItem({
|
|
item: {
|
|
id: Number(item.id),
|
|
name: item.name,
|
|
price: item.price,
|
|
image: item.image,
|
|
description: item.description,
|
|
variant: "None",
|
|
isHasLoyalty: item.isHasLoyalty,
|
|
no_of_stamps_give: item.no_of_stamps_give,
|
|
},
|
|
quantity: 1,
|
|
}),
|
|
);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<div className={styles.youMightAlsoLikeTitle}>
|
|
<ProText
|
|
strong
|
|
style={{
|
|
fontSize: isMobile ? 18 : isTablet ? 18 : 20,
|
|
}}
|
|
>
|
|
{t("cart.youMightAlsoLike")}
|
|
</ProText>
|
|
</div>
|
|
|
|
<div
|
|
style={{
|
|
position: "relative",
|
|
padding: isTablet || isDesktop ? "0 16px" : "0",
|
|
}}
|
|
>
|
|
{/* Left Arrow - Tablet and Desktop only */}
|
|
{(isTablet || isDesktop) && (
|
|
<button
|
|
className={styles.arrowButton}
|
|
style={{
|
|
position: "absolute",
|
|
left: isRTL ? "auto" : -25,
|
|
right: isRTL ? -25 : "auto",
|
|
top: "25%",
|
|
transform: "translateY(-50%)",
|
|
zIndex: 10,
|
|
}}
|
|
onClick={isRTL ? handleRTLPrevious : handlePrevious}
|
|
aria-label="Previous items"
|
|
>
|
|
{isRTL ? (
|
|
<NextIcon className={styles.arrowIcon} />
|
|
) : (
|
|
<BackIcon className={styles.arrowIcon} />
|
|
)}
|
|
</button>
|
|
)}
|
|
|
|
{/* Right Arrow - Tablet and Desktop only */}
|
|
{(isTablet || isDesktop) && (
|
|
<button
|
|
className={styles.arrowButton}
|
|
style={{
|
|
position: "absolute",
|
|
left: isRTL ? -10 : "auto",
|
|
right: isRTL ? "auto" : -10,
|
|
top: "25%",
|
|
transform: "translateY(-50%)",
|
|
zIndex: 10,
|
|
}}
|
|
onClick={isRTL ? handleRTLNext : handleNext}
|
|
aria-label="Next items"
|
|
>
|
|
{isRTL ? (
|
|
<BackIcon className={styles.arrowIcon} />
|
|
) : (
|
|
<NextIcon className={styles.arrowIcon} />
|
|
)}
|
|
</button>
|
|
)}
|
|
|
|
<div ref={containerRef} className={styles.youMightAlsoLikeContainer}>
|
|
{menuItems.map((item: Product) => (
|
|
<div key={item.id}>
|
|
<div
|
|
style={{
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
alignItems: "center",
|
|
justifyContent: "space-between",
|
|
width: isMobile ? "120px" : isTablet ? "120px" : "140px",
|
|
position: "relative",
|
|
overflow: "hidden",
|
|
}}
|
|
>
|
|
<div
|
|
style={{
|
|
width: isMobile ? 28 : 24,
|
|
height: isMobile ? 28 : 24,
|
|
borderRadius: "50%",
|
|
top: isMobile ? 65 : isTablet ? 60 : 80,
|
|
position: "absolute",
|
|
[isRTL ? "left" : "right"]: isMobile ? 10 : 20,
|
|
display: "flex",
|
|
flexDirection: "row",
|
|
justifyContent: "center",
|
|
cursor: "pointer",
|
|
backgroundColor: "white",
|
|
zIndex: 999,
|
|
}}
|
|
>
|
|
<PlusOutlined
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
handleQuickAdd(item);
|
|
}}
|
|
style={{
|
|
color: colors.primary,
|
|
fontSize: isMobile ? 14 : 16,
|
|
}}
|
|
/>
|
|
</div>
|
|
|
|
<ImageWithFallback
|
|
src={item.image}
|
|
alt={item.name}
|
|
className={`${styles.popularMenuItemImage} ${
|
|
isMobile
|
|
? styles.popularMenuItemImageMobile
|
|
: isTablet
|
|
? styles.popularMenuItemImageTablet
|
|
: styles.popularMenuItemImageDesktop
|
|
}`}
|
|
width={isMobile ? 106 : isTablet ? 90 : 110}
|
|
height={isMobile ? 96 : isTablet ? 90 : 110}
|
|
fallbackSrc={default_image}
|
|
/>
|
|
|
|
<Space
|
|
direction="vertical"
|
|
size="small"
|
|
style={{
|
|
flex: 1,
|
|
rowGap: 0,
|
|
}}
|
|
>
|
|
<div style={{ height: 25 }}>
|
|
<ProText
|
|
style={{
|
|
whiteSpace: "nowrap",
|
|
overflow: "hidden",
|
|
textOverflow: "ellipsis",
|
|
padding: isMobile ? 3 : isTablet ? "0 8px" : "0 10px",
|
|
fontSize: isMobile ? 12 : isTablet ? 14 : 16,
|
|
width: isMobile ? 80 : isTablet ? 100 : 120,
|
|
display: "inline-block",
|
|
fontWeight: 500,
|
|
fontStyle: "Medium",
|
|
lineHeight: "140%",
|
|
letterSpacing: "0%",
|
|
marginTop: 8,
|
|
}}
|
|
>
|
|
{item.name}
|
|
</ProText>
|
|
</div>
|
|
|
|
<ArabicPrice
|
|
price={item.price}
|
|
style={{
|
|
margin: 0,
|
|
WebkitLineClamp: 1,
|
|
WebkitBoxOrient: "vertical",
|
|
overflow: "hidden",
|
|
textOverflow: "ellipsis",
|
|
padding: isMobile ? 3 : isTablet ? "0 8px" : "0 10px",
|
|
fontSize: isMobile ? 12 : isTablet ? 14 : 16,
|
|
fontWeight: 600,
|
|
fontStyle: "SemiBold",
|
|
lineHeight: "140%",
|
|
letterSpacing: "0%",
|
|
marginTop: 4,
|
|
}}
|
|
/>
|
|
</Space>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|