pay flow: initial commit

This commit is contained in:
2025-12-03 01:31:30 +03:00
parent 359d7820dd
commit c43708be6d
12 changed files with 148 additions and 17 deletions

View File

@@ -46,6 +46,7 @@
"dineIn": "تناول",
"pickup": "استلام",
"car": "سيارة",
"pay": "الدفع",
"sendGift": "إرسال هدية",
"roomService": "خدمة الغرف",
"officeDelivery": "توصيل للمكتب",
@@ -392,5 +393,13 @@
"validation": {
"phoneRequired": "رقم الهاتف مطلوب",
"invalidPhone": "رقم الهاتف غير صالح"
},
"pay": {
"title": "الدفع",
"description": "الدفع للطلب",
"pay": "الدفع",
"payDescription": "الدفع للطلب",
"payButton": "الدفع",
"payButtonDescription": "الدفع للطلب"
}
}

View File

@@ -46,6 +46,7 @@
"dineIn": "Dine-in",
"pickup": "Pickup",
"car": "Car",
"pay": "Pay",
"sendGift": "Send a Gift",
"roomService": "Room Service",
"officeDelivery": "Office Delivery",
@@ -404,5 +405,13 @@
"validation": {
"phoneRequired": "Phone number is required",
"invalidPhone": "Invalid phone number"
},
"pay": {
"title": "Pay",
"description": "Pay for your order",
"pay": "Pay",
"payDescription": "Pay for your order",
"payButton": "Pay",
"payButtonDescription": "Pay for your order"
}
}

View File

@@ -3,16 +3,19 @@ import ArabicPrice from "components/ArabicPrice";
import ProInputCard from "components/ProInputCard/ProInputCard";
import ProText from "components/ProText";
import { selectCart } from "features/order/orderSlice";
import { OrderType } from "pages/checkout/hooks/types.ts";
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { Link, useParams } from "react-router-dom";
import { useAppSelector } from "redux/hooks";
import { colors } from "ThemeConstants";
import styles from "../../address/address.module.css";
import { OrderType } from "pages/checkout/hooks/types.ts";
export default function BriefMenu() {
const { tables, items } = useAppSelector(selectCart);
const { t } = useTranslation();
const { orderType } = useAppSelector(selectCart);
const { subdomain } = useParams();
const menuItems = useMemo(
() =>
@@ -46,7 +49,18 @@ export default function BriefMenu() {
);
return (
<ProInputCard title={cardTitle}>
<ProInputCard
title={cardTitle}
titleRight={
orderType === OrderType.Pay ? (
<Link to={`/${subdomain}/menu?orderType=${OrderType.Pay}`} style={{ textDecoration: "none" }}>
<ProText style={{ color: colors.primary }}>
{t("menu.title")}
</ProText>
</Link>
) : undefined
}
>
<div className={styles.briefMenuContainer}>{menuItems}</div>
</ProInputCard>
);

View File

@@ -246,4 +246,5 @@ export enum OrderType {
ToRoom = "room",
ToOffice = "office",
Booking = "booking",
Pay = "pay"
}

View File

@@ -1,4 +1,5 @@
import { Form, Layout } from "antd";
import InputCard from "components/InputCard";
import OrderSummary from "components/OrderSummary/OrderSummary";
import PaymentMethods from "components/PaymentMethods/PaymentMethods";
import ProHeader from "components/ProHeader/ProHeader";
@@ -9,11 +10,9 @@ import styles from "../address/address.module.css";
import { AddressSummary } from "./components/AddressSummary";
import BriefMenu from "./components/BriefMenu";
import CheckoutButton from "./components/CheckoutButton";
import { GiftDetails } from "./components/GiftDetails";
import PhoneCard from "./components/phoneCard";
import InputCard from "components/InputCard";
import { OrderType } from "./hooks/types";
import { GiftCard } from "./components/GiftCard";
import PhoneCard from "./components/phoneCard";
import { OrderType } from "./hooks/types";
export default function CheckoutPage() {
const { t } = useTranslation();

View File

@@ -1,6 +1,7 @@
import { Badge, Button } from "antd";
import CartIcon from "components/Icons/cart/CartIcon";
import { selectCartItems } from "features/order/orderSlice";
import { OrderType } from "pages/checkout/hooks/types";
import { useCallback } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { useAppSelector } from "redux/hooks";
@@ -11,10 +12,17 @@ export function CartButton() {
const { subdomain } = useParams();
const navigate = useNavigate();
const items = useAppSelector(selectCartItems);
const { orderType } = useAppSelector((s) => s.order);
const onCartClick = useCallback(() => {
navigate(`/${subdomain}/cart`);
}, [navigate, subdomain]);
console.log(orderType);
navigate(
orderType === OrderType.Pay
? `/${subdomain}/pay`
: `/${subdomain}/menu?orderType=${orderType}`,
);
}, [navigate, subdomain, orderType]);
const totalItems = items.reduce((sum, item) => sum + item.quantity, 0);
@@ -26,7 +34,7 @@ export function CartButton() {
right: !isRTL ? "auto" : "20px",
left: !isRTL ? "20px" : "auto",
}}
className={'cart-button'}
className={"cart-button"}
>
<Badge count={totalItems} size="default" className={styles.badge}>
<Button

View File

@@ -3,6 +3,7 @@ import CartIcon from "components/Icons/cart/CartIcon";
import ProText from "components/ProText";
import { selectCartItems } from "features/order/orderSlice";
import useBreakPoint from "hooks/useBreakPoint";
import { OrderType } from "pages/checkout/hooks/types";
import { useTranslation } from "react-i18next";
import { Link, useParams } from "react-router-dom";
import { useAppSelector } from "redux/hooks";
@@ -14,9 +15,11 @@ export function MenuFooter() {
const { isMobile, isTablet } = useBreakPoint();
const { t } = useTranslation();
const { subdomain } = useParams();
const { orderType } = useAppSelector((s) => s.order);
const totalItems = items.length;
console.log(orderType);
return (
<>
{(isMobile || isTablet) && (
@@ -37,7 +40,11 @@ export function MenuFooter() {
}}
>
<Link
to={`/${subdomain}/cart`}
to={
orderType === OrderType.Pay
? `/${subdomain}/pay`
: `/${subdomain}/cart`
}
style={{
width: "100%",
padding: "0 16px",

66
src/pages/pay/page.tsx Normal file
View File

@@ -0,0 +1,66 @@
import { Form, Layout } from "antd";
import InputCard from "components/InputCard";
import OrderSummary from "components/OrderSummary/OrderSummary";
import PaymentMethods from "components/PaymentMethods/PaymentMethods";
import ProHeader from "components/ProHeader/ProHeader";
import { selectCart } from "features/order/orderSlice";
import { AddressSummary } from "pages/checkout/components/AddressSummary";
import BriefMenu from "pages/checkout/components/BriefMenu";
import CheckoutButton from "pages/checkout/components/CheckoutButton";
import { GiftCard } from "pages/checkout/components/GiftCard";
import PhoneCard from "pages/checkout/components/phoneCard";
import { OrderType } from "pages/checkout/hooks/types";
import { useTranslation } from "react-i18next";
import { useAppSelector } from "redux/hooks";
import styles from "../address/address.module.css";
export default function PayPage() {
const { t } = useTranslation();
const [form] = Form.useForm();
const { phone, order, orderType } = useAppSelector(selectCart);
return (
<>
<Form
form={form}
initialValues={{
phone,
}}
layout="vertical"
>
<Layout>
<ProHeader>{t("pay.title")}</ProHeader>
<Layout.Content className={styles.checkoutContainer}>
<AddressSummary />
{orderType === OrderType.ToRoom && (
<InputCard
title={t("address.roomNo")}
name="roomNumber"
placeholder={t("address.roomNo")}
value={order?.roomNumber}
/>
)}
{orderType === OrderType.ToOffice && (
<InputCard
title={t("address.officeNo")}
name="officeNumber"
placeholder={t("address.officeNo")}
value={order?.officeNumber}
/>
)}
{orderType === OrderType.Gift && <GiftCard />}
{/* <RoomDetails />
<OfficeDetails /> */}
{/* <GiftDetails /> */}
<BriefMenu />
<OrderSummary />
<PhoneCard />
<PaymentMethods />
</Layout.Content>
<CheckoutButton form={form} />
</Layout>
</Form>
</>
);
}

View File

View File

@@ -1,7 +1,6 @@
import { ScheduleFilled } from "@ant-design/icons";
import { Card } from "antd";
import BackIcon from "components/Icons/BackIcon";
import BookingIcon from "components/Icons/BookingIcon";
import DeliveryIcon from "components/Icons/DeliveryIcon";
import DineInIcon from "components/Icons/DineInIcon";
import NextIcon from "components/Icons/NextIcon";
@@ -33,7 +32,7 @@ export default function RestaurantServices() {
gift,
toRoom,
toOffice,
is_booking_enabled,
// is_booking_enabled,
delivery,
is_schedule_order_enabled,
pickup_type,
@@ -113,6 +112,7 @@ export default function RestaurantServices() {
},
]) ||
[]),
// ...((is_booking_enabled && [
// {
// id: OrderType.Booking,
@@ -158,6 +158,21 @@ export default function RestaurantServices() {
},
]) ||
[]),
...((true && [
{
id: OrderType.Pay,
title: t("common.pay"),
description: t("home.services.pay"),
icon: (
<ToOfficeIcon
className={styles.serviceIcon + " " + styles.officeIcon}
/>
),
color: "bg-orange-50 text-orange-600",
href: `/${subdomain}/pay`,
},
]) ||
[]),
];
// Determine grid class based on number of services

View File

@@ -31,9 +31,7 @@ export const branchApi = baseApi.injectEndpoints({
},
}),
transformResponse: (response: any) => {
return response?.result?.restaurants?.find(
(restaurant: RestaurantDetails) => restaurant.distance === 595,
);
return response?.result?.restaurants?.[0]
},
providesTags: ["Restaurant"],
}),

View File

@@ -12,6 +12,7 @@ import MenuPage from "pages/menu/page";
import OrderPage from "pages/order/page";
import OrdersPage from "pages/orders/page";
import OtpPage from "pages/otp/page";
import PayPage from "pages/pay/page";
import ProductDetailPage from "pages/product/page";
import RestaurantPage from "pages/restaurant/page";
import SearchPage from "pages/search/page";
@@ -136,12 +137,16 @@ export const router = createHashRouter([
element: <PageWrapper children={<OtpPage />} />,
errorElement: <ErrorPage />,
},
{
path: "order/:orderId",
element: <PageWrapper children={<OrderPage />} />,
errorElement: <ErrorPage />,
},
{
path: "pay",
element: <PageWrapper children={<PayPage />} />,
errorElement: <ErrorPage />,
}
],
},