add "order details" bottom sheet

This commit is contained in:
2025-10-31 19:33:11 +03:00
parent ab5d0da47e
commit 42d0b2cacf
3 changed files with 217 additions and 3 deletions

View File

@@ -0,0 +1,138 @@
import { useTranslation } from "react-i18next";
import { ProBottomSheet } from "../ProBottomSheet/ProBottomSheet";
import { useAppSelector } from "redux/hooks";
import ProText from "components/ProText";
import { OrderType } from "pages/checkout/hooks/types";
interface OrderDetailsBottomSheetProps {
isOpen: boolean;
onClose: () => void;
}
export function OrderDetailsBottomSheet({
isOpen,
onClose,
}: OrderDetailsBottomSheetProps) {
const { t } = useTranslation();
const { orderType, restaurant, specialRequest, items } = useAppSelector(
(state) => state.order,
);
// const { isRTL } = useAppSelector((state) => state.locale);
return (
<ProBottomSheet
isOpen={isOpen}
onClose={onClose}
title={t("Order Details")}
height={500}
snapPoints={["60vh"]}
>
<div style={{ padding: "16px 0" }}>
{/* Order Type */}
<div style={{ marginBottom: 16 }}>
<ProText style={{ fontSize: 18, fontWeight: 600 }}>
{t("Order Type")}: {orderType && t(orderType)}
</ProText>
</div>
{/* Items List */}
<div style={{ marginBottom: 16 }}>
<ProText style={{ fontSize: 16, fontWeight: 600, marginBottom: 8 }}>
{t("Items")}
</ProText>
{items.length > 0 ? (
<div>
{items.map((item) => (
<div
key={item.id}
style={{
display: "flex",
justifyContent: "space-between",
marginBottom: 8,
padding: "8px 0",
borderBottom: "1px solid #eee",
}}
>
<div>
<ProText style={{ fontWeight: 500 }}>
{item.quantity} x {item.name}
</ProText>
</div>
<ProText style={{ fontWeight: 500 }}>{}</ProText>
</div>
))}
</div>
) : (
<ProText>{t("No items in cart")}</ProText>
)}
</div>
{/* Special Request */}
{specialRequest && (
<div style={{ marginBottom: 16 }}>
<ProText style={{ fontSize: 16, fontWeight: 600, marginBottom: 8 }}>
{t("Special Request")}
</ProText>
<ProText>{specialRequest}</ProText>
</div>
)}
{/* Order Summary */}
<div style={{ marginTop: 24 }}>
<ProText style={{ fontSize: 16, fontWeight: 600, marginBottom: 8 }}>
{t("Order Summary")}
</ProText>
<div
style={{
display: "flex",
justifyContent: "space-between",
marginBottom: 8,
}}
>
<ProText>{t("Subtotal")}</ProText>
<ProText>{}</ProText>
</div>
<div
style={{
display: "flex",
justifyContent: "space-between",
marginBottom: 8,
}}
>
<ProText>{t("Tax")}</ProText>
<ProText>{}</ProText>
</div>
{orderType !== OrderType.DineIn && restaurant?.delivery_fees && (
<div
style={{
display: "flex",
justifyContent: "space-between",
marginBottom: 8,
}}
>
<ProText>{t("Delivery Fee")}</ProText>
<ProText>{Number(restaurant.delivery_fees)}</ProText>
</div>
)}
<div
style={{
display: "flex",
justifyContent: "space-between",
marginTop: 16,
paddingTop: 16,
borderTop: "1px solid #eee",
fontWeight: 600,
}}
>
<ProText style={{ fontWeight: 600 }}>{t("Total")}</ProText>
<ProText style={{ fontWeight: 600 }}>{}</ProText>
</div>
</div>
</div>
</ProBottomSheet>
);
}

52
src/hooks/useSwipeUp.ts Normal file
View File

@@ -0,0 +1,52 @@
import { useRef, useState, useCallback } from "react";
type Props = { isEnabled: boolean; swipeAction: () => void };
export default function useSwipeUp({ isEnabled, swipeAction }: Props) {
// Swipe detection
const startYRef = useRef(0);
const [isSwiping, setIsSwiping] = useState(false);
const containerRef = useRef<HTMLDivElement>(null);
// Touch event handlers for swipe detection
const handleTouchStart = useCallback(
(e: React.TouchEvent) => {
if (!isEnabled) return;
startYRef.current = e.touches[0].clientY;
setIsSwiping(true);
},
[isEnabled],
);
/*
const handleTouchMove = useCallback(
(e: React.TouchEvent) => {
if (!isSwiping) return;
},
[isSwiping],
);
*/
const handleTouchEnd = useCallback(
(e: React.TouchEvent) => {
if (!isSwiping || !isEnabled) return;
const endY = e.changedTouches[0].clientY;
const deltaY = startYRef.current - endY;
const threshold = 70; // Threshold to detect a swipe up
if (deltaY > threshold) {
// Swipe up detected
swipeAction();
}
setIsSwiping(false);
},
[isSwiping, isEnabled],
);
return {
containerRef,
handleTouchStart,
handleTouchEnd,
};
}

View File

@@ -21,13 +21,16 @@ import {
} from "react-router-dom";
import { useGetRestaurantDetailsQuery } from "redux/api/others";
import LocalStorageHandler from "../menu/components/LocalStorageHandler";
import { useEffect } from "react";
import { useEffect, useState } from "react";
import {
updateOrderType,
CART_STORAGE_KEYS,
} from "features/order/orderSlice.ts";
import { OrderType } from "pages/checkout/hooks/types.ts";
import { useTranslation } from "react-i18next";
import { OrderDetailsBottomSheet } from "components/CustomBottomSheet/OrderDetailsBottomSheet";
import useBreakPoint from "hooks/useBreakPoint.ts";
import useSwipeUp from "hooks/useSwipeUp.ts";
const storedOrderType = localStorage.getItem(CART_STORAGE_KEYS.ORDER_TYPE);
@@ -35,13 +38,22 @@ export default function RestaurantPage() {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const param = useParams();
const { isMobile } = useBreakPoint();
const [searchParams] = useSearchParams();
const { pathname } = useLocation();
const { orderType } = useAppSelector((state) => state.order);
const { orderType, items: cartItems } = useAppSelector(
(state) => state.order,
);
const { isRTL } = useAppSelector((state) => state.locale);
const { data: restaurant, isLoading } = useGetRestaurantDetailsQuery("595", {
skip: !param.id,
});
const [isOrderDetailsOpen, setIsOrderDetailsOpen] = useState(false);
const { containerRef, handleTouchEnd, handleTouchStart } = useSwipeUp({
swipeAction: () => setIsOrderDetailsOpen(true),
isEnabled: isMobile && cartItems.length > 0,
});
// Automatically load restaurant taxes when restaurant data is available
useRestaurant(restaurant);
@@ -102,7 +114,13 @@ export default function RestaurantPage() {
<div className={styles.promotionContainer}>
<Ads1 />
</div>
<div className={styles.socialIconsContainer}>
<div
ref={containerRef}
onTouchStart={handleTouchStart}
onTouchEnd={handleTouchEnd}
className={styles.socialIconsContainer}
>
<InstagramIcon className={styles.socialIcon} />
<XIcon className={styles.socialIcon} />
<SnapIcon className={styles.socialIcon} />
@@ -110,6 +128,12 @@ export default function RestaurantPage() {
</div>
</div>
{/* Order Details Bottom Sheet - Moved outside the container */}
<OrderDetailsBottomSheet
isOpen={isOrderDetailsOpen}
onClose={() => setIsOrderDetailsOpen(false)}
/>
<LocalStorageHandler
restaurantID={restaurant.restautantId}
restaurantName={restaurant.restautantName}