Compare commits

...

2 Commits

2 changed files with 123 additions and 30 deletions

View File

@@ -13,29 +13,68 @@ interface StepperProps {
export default function Stepper({ statuses = [] }: StepperProps) { export default function Stepper({ statuses = [] }: StepperProps) {
const { t } = useTranslation(); const { t } = useTranslation();
// Check if order has canceled_by_customer status
const hasCanceledStatus = useMemo(
() => statuses.some((status) => status?.alias === "canceled_by_customer"),
[statuses],
);
// Get the canceled status if it exists
const canceledStatus = useMemo(
() => statuses.find((status) => status?.alias === "canceled_by_customer"),
[statuses],
);
const labels = useMemo( const labels = useMemo(
() => [t("order.reserved"), t("order.prepare"), t("order.ready")], () => [t("order.reserved"), t("order.prepare"), t("order.ready")],
[t], [t],
); );
// Determine number of steps: 2 if canceled, 3 otherwise
const stepCount = hasCanceledStatus ? 2 : 3;
const activeIndex = useMemo(() => { const activeIndex = useMemo(() => {
if (hasCanceledStatus) {
// For canceled orders, the last step (index 1) is always active
return 1;
}
const reachedStatuses = statuses.length > 0 ? statuses.length - 1 : 0; const reachedStatuses = statuses.length > 0 ? statuses.length - 1 : 0;
return Math.min(Math.max(reachedStatuses, 0), 2); return Math.min(Math.max(reachedStatuses, 0), 2);
}, [statuses.length]); }, [statuses.length, hasCanceledStatus]);
const steps = useMemo( const steps = useMemo(
() => () =>
Array.from({ length: 3 }, (_, index) => { Array.from({ length: stepCount }, (_, index) => {
const status = statuses[index]; let status;
const label = status?.name || status?.alias || labels[index] || ""; let label;
let isCanceled = false;
if (hasCanceledStatus) {
if (index === 0) {
// First step: show first status or default label
status = statuses[0];
label = status?.name || status?.alias || labels[0] || "";
} else if (index === 1) {
// Second step: show canceled status
status = canceledStatus;
label = status?.name || status?.alias || t("order.canceled") || "";
isCanceled = true;
}
} else {
// Normal flow: 3 steps
status = statuses[index];
label = status?.name || status?.alias || labels[index] || "";
}
const isPending = index > activeIndex; const isPending = index > activeIndex;
return { return {
key: status ? `status-${status.id}` : `placeholder-${index}`, key: status ? `status-${status.id}` : `placeholder-${index}`,
label, label,
isPending, isPending,
isCanceled,
}; };
}), }),
[activeIndex, labels, statuses], [activeIndex, labels, statuses, stepCount, hasCanceledStatus, canceledStatus, t],
); );
return ( return (
@@ -57,7 +96,16 @@ export default function Stepper({ statuses = [] }: StepperProps) {
}} }}
> >
{steps.map((step, index) => { {steps.map((step, index) => {
const circleStyle: CSSProperties = step.isPending // Determine circle style based on state
const circleStyle: CSSProperties = step.isCanceled
? {
width: 12,
height: 12,
borderRadius: "50%",
backgroundColor: "#EF4444", // Red for canceled
border: "2px solid #EF4444",
}
: step.isPending
? { ? {
width: 12, width: 12,
height: 12, height: 12,
@@ -72,11 +120,21 @@ export default function Stepper({ statuses = [] }: StepperProps) {
backgroundColor: colors.primary, backgroundColor: colors.primary,
}; };
const lineStyle: CSSProperties = { // Determine line style based on state
height: 1.5, // Line should be red if the next step is canceled
backgroundColor: step.isPending ? "#BDBDBD" : colors.primary, const nextStep = index < steps.length - 1 ? steps[index + 1] : null;
margin: "0 8px", const lineStyle: CSSProperties =
}; nextStep?.isCanceled
? {
height: 1.5,
backgroundColor: "#EF4444", // Red for canceled
margin: "0 8px",
}
: {
height: 1.5,
backgroundColor: step.isPending ? "#BDBDBD" : colors.primary,
margin: "0 8px",
};
return ( return (
<Fragment key={step.key}> <Fragment key={step.key}>
@@ -120,7 +178,11 @@ export default function Stepper({ statuses = [] }: StepperProps) {
lineHeight: "140%", lineHeight: "140%",
letterSpacing: "0%", letterSpacing: "0%",
textAlign: "center", textAlign: "center",
color: step.isPending ? "#99A2AE" : "#333333", color: step.isCanceled
? "#EF4444" // Red for canceled
: step.isPending
? "#99A2AE"
: "#333333",
}} }}
> >
{step.label} {step.label}

View File

@@ -8,7 +8,6 @@ import {
Progress, Progress,
Tooltip, Tooltip,
} from "antd"; } from "antd";
import Ads2 from "components/Ads/Ads2";
import { CancelOrderBottomSheet } from "components/CustomBottomSheet/CancelOrderBottomSheet"; import { CancelOrderBottomSheet } from "components/CustomBottomSheet/CancelOrderBottomSheet";
import LocationIcon from "components/Icons/LocationIcon"; import LocationIcon from "components/Icons/LocationIcon";
import InvoiceIcon from "components/Icons/order/InvoiceIcon"; import InvoiceIcon from "components/Icons/order/InvoiceIcon";
@@ -19,7 +18,7 @@ import ProHeader from "components/ProHeader/ProHeader";
import ProText from "components/ProText"; import ProText from "components/ProText";
import ProTitle from "components/ProTitle"; import ProTitle from "components/ProTitle";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { useEffect, useRef, useState } from "react"; import { useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import { import {
@@ -37,7 +36,6 @@ import { QRBottomSheet } from "pages/pay/components/splitBill/QRBottomSheet";
import NewRateIcon from "components/Icons/order/NewRateIcon"; import NewRateIcon from "components/Icons/order/NewRateIcon";
import NoteIcon from "components/Icons/NoteIcon"; import NoteIcon from "components/Icons/NoteIcon";
import SuccessIcon from "components/Icons/SuccessIcon"; import SuccessIcon from "components/Icons/SuccessIcon";
import ProInputCard from "components/ProInputCard/ProInputCard";
import { SplitBillParticipantsBottomSheet } from "./components/SplitBillParticipantsBottomSheet"; import { SplitBillParticipantsBottomSheet } from "./components/SplitBillParticipantsBottomSheet";
export default function OrderPage() { export default function OrderPage() {
@@ -49,10 +47,26 @@ export default function OrderPage() {
const hasRefetchedRef = useRef(false); const hasRefetchedRef = useRef(false);
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [isRateOrderOpen, setIsRateOrderOpen] = useState(false); const [isRateOrderOpen, setIsRateOrderOpen] = useState(false);
const [
isSplitBillParticipantsBottomSheetOpen, // Track order details in state to trigger re-evaluation of polling
setIsSplitBillParticipantsBottomSheetOpen, const [lastOrderDetails, setLastOrderDetails] = useState<any>(null);
] = useState(false);
// Compute polling interval based on order status
const pollingInterval = useMemo(() => {
const orderDetailsToCheck = lastOrderDetails;
if (orderDetailsToCheck?.status) {
const hasClosedOrCanceled = orderDetailsToCheck.status.some(
(status: any) =>
status?.alias === "closed" ||
status?.alias === "canceled_by_customer",
);
if (hasClosedOrCanceled) {
return 0; // Stop polling
}
}
return 10000; // Continue polling
}, [lastOrderDetails]);
const { data: orderDetails } = useGetOrderDetailsQuery( const { data: orderDetails } = useGetOrderDetailsQuery(
{ {
orderID: orderId || "", orderID: orderId || "",
@@ -61,11 +75,32 @@ export default function OrderPage() {
{ {
skip: !orderId, skip: !orderId,
// return it t0 60000 after finish testing // return it t0 60000 after finish testing
pollingInterval: 10000, // Stop polling if order is closed or canceled
pollingInterval,
refetchOnMountOrArgChange: true, refetchOnMountOrArgChange: true,
}, },
); );
// Update state when orderDetails changes to trigger polling re-evaluation
useEffect(() => {
if (orderDetails) {
setLastOrderDetails(orderDetails);
}
}, [orderDetails]);
const hasClosedStatus = orderDetails?.status?.some(
(status) => status?.alias === "closed",
);
const hasCanceledByCustomerStatus = orderDetails?.status?.some(
(status) => status?.alias === "canceled_by_customer",
);
const [
isSplitBillParticipantsBottomSheetOpen,
setIsSplitBillParticipantsBottomSheetOpen,
] = useState(false);
// Get restaurant subdomain for refetching // Get restaurant subdomain for refetching
const restaurantSubdomain = restaurant?.subdomain; const restaurantSubdomain = restaurant?.subdomain;
const { refetch: refetchRestaurantDetails } = useGetRestaurantDetailsQuery( const { refetch: refetchRestaurantDetails } = useGetRestaurantDetailsQuery(
@@ -74,13 +109,6 @@ export default function OrderPage() {
skip: !restaurantSubdomain, skip: !restaurantSubdomain,
}, },
); );
const hasClosedStatus = orderDetails?.status?.some(
(status) => status?.alias === "closed",
);
const hasCanceledByCustomerStatus = orderDetails?.status?.some(
(status) => status?.alias === "canceled_by_customer",
);
// Reset refetch flag when orderId changes // Reset refetch flag when orderId changes
useEffect(() => { useEffect(() => {
@@ -90,7 +118,10 @@ export default function OrderPage() {
// Refetch restaurant details when order status has alias "closed" // Refetch restaurant details when order status has alias "closed"
useEffect(() => { useEffect(() => {
if (orderDetails?.status && !hasRefetchedRef.current) { if (orderDetails?.status && !hasRefetchedRef.current) {
if (hasClosedStatus && restaurantSubdomain) { if (
(hasClosedStatus || hasCanceledByCustomerStatus) &&
restaurantSubdomain
) {
refetchRestaurantDetails(); refetchRestaurantDetails();
hasRefetchedRef.current = true; hasRefetchedRef.current = true;
} }
@@ -289,7 +320,7 @@ export default function OrderPage() {
</div> </div>
{isInProgress ? ( {isInProgress ? (
<Flex gap="small" wrap justify="center"> <Flex gap="small" wrap justify="center" style={{ marginTop: 16 }}>
<Tooltip title="3 done / 3 in progress / 4 to do"> <Tooltip title="3 done / 3 in progress / 4 to do">
<div <div
style={{ style={{
@@ -671,7 +702,7 @@ export default function OrderPage() {
onClose={() => setIsRateOrderOpen(false)} onClose={() => setIsRateOrderOpen(false)}
/> />
{(!hasClosedStatus && !hasCanceledByCustomerStatus) && ( {!hasClosedStatus && !hasCanceledByCustomerStatus && (
<CancelOrderBottomSheet /> <CancelOrderBottomSheet />
)} )}
</Layout.Content> </Layout.Content>