330 lines
10 KiB
TypeScript
330 lines
10 KiB
TypeScript
import { Button, Card, Divider, Flex, Image, Progress, Tooltip } from "antd";
|
|
import Ads2 from "components/Ads/Ads2";
|
|
import { CancelOrderBottomSheet } from "components/CustomBottomSheet/CancelOrderBottomSheet";
|
|
import LocationIcon from "components/Icons/LocationIcon";
|
|
import InvoiceIcon from "components/Icons/order/InvoiceIcon";
|
|
import TimeIcon from "components/Icons/order/TimeIcon";
|
|
import OrderDishIcon from "components/Icons/OrderDishIcon";
|
|
import PaymentDetails from "components/PaymentDetails/PaymentDetails";
|
|
import ProHeader from "components/ProHeader/ProHeader";
|
|
import ProInputCard from "components/ProInputCard/ProInputCard";
|
|
import ProText from "components/ProText";
|
|
import ProTitle from "components/ProTitle";
|
|
import dayjs from "dayjs";
|
|
import { useEffect, useRef, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { useNavigate, useParams } from "react-router-dom";
|
|
import {
|
|
useGetOrderDetailsQuery,
|
|
useGetRestaurantDetailsQuery,
|
|
} from "redux/api/others";
|
|
import { useAppSelector } from "redux/hooks";
|
|
import Stepper from "./components/Stepper";
|
|
import styles from "./order.module.css";
|
|
import BackIcon from "components/Icons/BackIcon";
|
|
import NextIcon from "components/Icons/NextIcon";
|
|
import { RateBottomSheet } from "components/CustomBottomSheet/RateBottomSheet";
|
|
|
|
export default function OrderPage() {
|
|
const { t } = useTranslation();
|
|
const { orderId } = useParams();
|
|
const { isRTL } = useAppSelector((state) => state.locale);
|
|
const { restaurant } = useAppSelector((state) => state.order);
|
|
const navigate = useNavigate();
|
|
const hasRefetchedRef = useRef(false);
|
|
|
|
const { data: orderDetails } = useGetOrderDetailsQuery(
|
|
{
|
|
orderID: orderId || "",
|
|
restaurantID: localStorage.getItem("restaurantID") || "",
|
|
},
|
|
{
|
|
skip: !orderId,
|
|
// return it t0 60000 after finish testing
|
|
pollingInterval: 10000,
|
|
refetchOnMountOrArgChange: true,
|
|
},
|
|
);
|
|
|
|
// Get restaurant subdomain for refetching
|
|
const restaurantSubdomain = restaurant?.subdomain;
|
|
const { refetch: refetchRestaurantDetails } = useGetRestaurantDetailsQuery(
|
|
restaurantSubdomain || "",
|
|
{
|
|
skip: !restaurantSubdomain,
|
|
},
|
|
);
|
|
|
|
// Reset refetch flag when orderId changes
|
|
useEffect(() => {
|
|
hasRefetchedRef.current = false;
|
|
}, [orderId]);
|
|
|
|
// Refetch restaurant details when order status has alias "closed"
|
|
useEffect(() => {
|
|
if (orderDetails?.status && !hasRefetchedRef.current) {
|
|
const hasClosedStatus = orderDetails.status.some(
|
|
(status) => status?.alias === "closed",
|
|
);
|
|
|
|
if (hasClosedStatus && restaurantSubdomain) {
|
|
refetchRestaurantDetails();
|
|
hasRefetchedRef.current = true;
|
|
}
|
|
}
|
|
}, [orderDetails?.status, restaurantSubdomain, refetchRestaurantDetails]);
|
|
|
|
// Check if order is in progress (check last status alias)
|
|
const lastStatus = orderDetails?.status?.[orderDetails.status.length - 1];
|
|
const isInProgress = lastStatus?.alias === "accepted_by_restaurant";
|
|
|
|
// Calculate timer and progress
|
|
const [remainingSeconds, setRemainingSeconds] = useState<number>(0);
|
|
const [progressPercent, setProgressPercent] = useState<number>(0);
|
|
|
|
useEffect(() => {
|
|
if (
|
|
!isInProgress ||
|
|
!orderDetails?.order?.time_to_prepare ||
|
|
!orderDetails?.order?.created_at
|
|
) {
|
|
return;
|
|
}
|
|
|
|
const updateTimer = () => {
|
|
const orderCreatedAt = dayjs(orderDetails.order.created_at);
|
|
const now = dayjs();
|
|
const elapsedSeconds = now.diff(orderCreatedAt, "second");
|
|
// time_to_prepare is in minutes, convert to seconds
|
|
const totalSeconds =
|
|
(Number(orderDetails.order.time_to_prepare) || 0) * 60;
|
|
const remaining = Math.max(0, totalSeconds - elapsedSeconds);
|
|
|
|
setRemainingSeconds(remaining);
|
|
const percent =
|
|
totalSeconds > 0
|
|
? Math.min(100, Math.max(0, (elapsedSeconds / totalSeconds) * 100))
|
|
: 0;
|
|
setProgressPercent(percent);
|
|
};
|
|
|
|
updateTimer();
|
|
const interval = setInterval(updateTimer, 1000);
|
|
|
|
return () => clearInterval(interval);
|
|
}, [
|
|
isInProgress,
|
|
orderDetails?.order?.time_to_prepare,
|
|
orderDetails?.status,
|
|
]);
|
|
|
|
// Format remaining time as MM:SS
|
|
const formatTimer = (totalSeconds: number): string => {
|
|
const mins = Math.floor(totalSeconds / 60);
|
|
const secs = Math.floor(totalSeconds % 60);
|
|
return `${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<ProHeader>{t("order.title")}</ProHeader>
|
|
<div
|
|
style={{
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
height: "92vh",
|
|
padding: 16,
|
|
gap: 16,
|
|
overflow: "auto",
|
|
scrollbarWidth: "none",
|
|
}}
|
|
>
|
|
<Card className={styles.orderCard}>
|
|
<div
|
|
style={{
|
|
display: "flex",
|
|
flexDirection: "row",
|
|
gap: "1rem",
|
|
backgroundColor: "rgba(255, 183, 0, 0.08)",
|
|
borderRadius: "12px",
|
|
padding: 16,
|
|
}}
|
|
>
|
|
<Button
|
|
type="text"
|
|
shape="circle"
|
|
style={{
|
|
backgroundColor: "rgba(255, 183, 0, 0.08)",
|
|
}}
|
|
>
|
|
<Image
|
|
src={orderDetails?.restaurant_iimage}
|
|
className={styles.profileImage}
|
|
width={50}
|
|
height={50}
|
|
preview={false}
|
|
/>
|
|
</Button>
|
|
<div>
|
|
<ProText style={{ fontSize: "1rem" }}>
|
|
{t("order.yourOrderFromFascanoRestaurant")}
|
|
</ProText>
|
|
<br />
|
|
<ProText type="secondary">
|
|
<LocationIcon className={styles.locationIcon} />{" "}
|
|
{isRTL ? orderDetails?.restaurantAR : orderDetails?.restaurant}
|
|
</ProText>
|
|
</div>
|
|
</div>
|
|
|
|
{isInProgress ? (
|
|
<Flex gap="small" wrap justify="center">
|
|
<Tooltip title="3 done / 3 in progress / 4 to do">
|
|
<Progress
|
|
percent={progressPercent}
|
|
format={() => formatTimer(remainingSeconds)}
|
|
// success={{ percent: progressPercent }}
|
|
strokeColor="#FFB700"
|
|
size={120}
|
|
type="dashboard"
|
|
/>
|
|
</Tooltip>
|
|
</Flex>
|
|
) : (
|
|
<OrderDishIcon className={styles.orderDishIcon} />
|
|
)}
|
|
|
|
<div>
|
|
<ProTitle
|
|
level={5}
|
|
style={{
|
|
fontWeight: 600,
|
|
fontSize: "18px",
|
|
marginBottom: "0.75rem",
|
|
}}
|
|
>
|
|
{t("order.inProgressOrder")} (1)
|
|
</ProTitle>
|
|
<div style={{ display: "flex", flexDirection: "row", gap: 8 }}>
|
|
<InvoiceIcon className={styles.invoiceIcon} />
|
|
<ProText type="secondary" style={{ fontSize: "14px" }}>
|
|
#{orderDetails?.order.id}
|
|
</ProText>
|
|
<TimeIcon className={styles.timeIcon} />
|
|
<ProText type="secondary" style={{ fontSize: "14px" }}>
|
|
ordered :- Today -{" "}
|
|
{dayjs(orderDetails?.status[0]?.pivot?.created_at).format(
|
|
"h:mm A",
|
|
)}
|
|
</ProText>
|
|
</div>
|
|
|
|
<Divider style={{ margin: "12px 0" }} />
|
|
|
|
<Stepper statuses={orderDetails?.status} />
|
|
</div>
|
|
</Card>
|
|
|
|
<Ads2 />
|
|
|
|
<ProInputCard
|
|
title={
|
|
<div style={{ marginBottom: 7 }}>
|
|
<ProText style={{ fontSize: "1rem" }}>
|
|
{t("order.yourOrderFrom")}
|
|
</ProText>
|
|
<br />
|
|
<ProText type="secondary">
|
|
<LocationIcon className={styles.locationIcon} />{" "}
|
|
{t("order.muscat")}
|
|
</ProText>
|
|
</div>
|
|
}
|
|
>
|
|
<div
|
|
style={{ display: "flex", flexDirection: "column", gap: "1rem" }}
|
|
>
|
|
{orderDetails?.orderItems.map((item, index) => (
|
|
<div key={item.id}>
|
|
<div
|
|
style={{
|
|
display: "flex",
|
|
flexDirection: "row",
|
|
justifyContent: "flex-start",
|
|
gap: "1rem",
|
|
}}
|
|
>
|
|
<Button
|
|
type="text"
|
|
shape="circle"
|
|
style={{
|
|
backgroundColor: "rgba(255, 183, 0, 0.08)",
|
|
}}
|
|
>
|
|
{index + 1}X
|
|
</Button>
|
|
<div>
|
|
<ProText
|
|
style={{ fontSize: "1rem", position: "relative", top: 8 }}
|
|
>
|
|
{item.name}
|
|
</ProText>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</ProInputCard>
|
|
|
|
<PaymentDetails order={orderDetails?.order} />
|
|
|
|
<Card
|
|
className={styles.backToHomePage}
|
|
onClick={() => navigate(`/${restaurant?.subdomain}`)}
|
|
>
|
|
<div
|
|
style={{
|
|
display: "flex",
|
|
flexDirection: "row",
|
|
justifyContent: "space-between",
|
|
marginTop: 1,
|
|
}}
|
|
>
|
|
<Image
|
|
src={restaurant?.restautantImage}
|
|
width={30}
|
|
height={30}
|
|
preview={false}
|
|
style={{
|
|
borderRadius: "50%",
|
|
objectFit: "cover",
|
|
position: "relative",
|
|
top: -4,
|
|
}}
|
|
/>
|
|
|
|
<ProTitle
|
|
level={5}
|
|
style={{
|
|
fontSize: 14,
|
|
}}
|
|
>
|
|
{isRTL ? restaurant?.nameAR : restaurant?.restautantName}
|
|
</ProTitle>
|
|
|
|
{isRTL ? (
|
|
<BackIcon className={styles.serviceIcon} />
|
|
) : (
|
|
<NextIcon className={styles.serviceIcon} />
|
|
)}
|
|
</div>
|
|
</Card>
|
|
|
|
<RateBottomSheet />
|
|
|
|
<CancelOrderBottomSheet />
|
|
</div>
|
|
</>
|
|
);
|
|
}
|