orders details: initial commit

This commit is contained in:
2025-12-30 00:01:12 +03:00
parent d77047fdb2
commit e9dc1aa580
9 changed files with 705 additions and 156 deletions

View File

@@ -172,6 +172,11 @@
z-index: 999;
}
/* Dark theme styles for checkout */
:global(.darkApp) .checkoutButtonContainer {
background-color: #000000 !important;
}
.splitBillButton {
border-radius: 100px;
height: 48px;
@@ -221,10 +226,7 @@
font-size: 14px;
}
/* Dark theme styles for checkout */
:global(.darkApp) .checkoutButtonContainer {
background-color: #000000 !important;
}
:global(.darkApp) .splitBillButton {
color: #ffffff !important;

View File

@@ -24,6 +24,7 @@ export default function CheckoutPage() {
const { phone, order, orderType, collectionMethod } =
useAppSelector(selectCart);
const { token } = useAppSelector((state) => state.auth);
return (
<>
<Form

View File

@@ -0,0 +1,300 @@
.orderSummary :global(.ant-card-body) {
padding: 16px !important;
}
.profileImage {
border-radius: 50%;
width: 50px;
height: 50px;
}
/* Enhanced responsive order summary */
@media (min-width: 769px) and (max-width: 1024px) {
.orderSummary {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
}
.fascanoIcon {
position: relative;
top: 3px;
}
.locationIcon {
position: relative;
top: 3px;
}
.orderDishIcon {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
}
.orderCard :global(.ant-card-body) {
display: flex;
flex-direction: column;
justify-content: flex-start;
}
.orderCard :global(.ant-card-body) > *:not(:last-child) {
margin-bottom: 3.5rem;
}
.orderSummary {
transition: all 0.3s ease;
}
.invoiceIcon {
position: relative;
top: 3px;
}
.timeIcon {
position: relative;
top: 3px;
}
/* Enhanced responsive order summary */
@media (min-width: 769px) and (max-width: 1024px) {
.orderSummary {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
}
.summaryRow {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 16px;
}
/* Enhanced responsive summary rows */
@media (min-width: 769px) and (max-width: 1024px) {
.summaryRow {
padding: 12px 0;
font-size: 16px;
}
}
@media (min-width: 1025px) {
.summaryRow {
padding: 16px 0;
font-size: 18px;
}
}
.summaryDivider {
margin: 8px 0 !important;
}
/* Enhanced responsive summary divider */
@media (min-width: 769px) and (max-width: 1024px) {
.summaryDivider {
margin: 20px 0 !important;
}
}
@media (min-width: 1025px) {
.summaryDivider {
margin: 24px 0 !important;
}
}
.totalRow {
font-weight: bold;
font-size: 16px;
}
/* Enhanced responsive total row */
@media (min-width: 769px) and (max-width: 1024px) {
.totalRow {
font-size: 18px;
padding-top: 20px;
margin-top: 12px;
}
}
@media (min-width: 1025px) {
.totalRow {
font-size: 20px;
padding-top: 24px;
margin-top: 16px;
}
}
.desktopOrderSummary {
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
border: 1px solid rgba(0, 0, 0, 0.08);
}
.desktopSummaryRow {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
font-size: 16px;
}
.desktopTotalRow {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 0;
margin-top: 16px;
}
[data-theme="dark"] .orderSummary {
background-color: #181818 !important;
border-color: #363636 !important;
}
[data-theme="dark"] .orderSummary:hover {
background-color: #363636 !important;
border-color: #424242 !important;
}
[data-theme="dark"] .summaryRow {
color: #b0b0b0;
}
[data-theme="dark"] .totalRow {
color: #ffffff;
border-top-color: #424242;
}
/* Enhanced responsive animations */
@media (prefers-reduced-motion: no-preference) {
.orderSummary {
animation: fadeInUp 0.8s ease-out;
}
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Enhanced responsive focus states */
.orderSummary:focus {
outline: 2px solid var(--primary);
outline-offset: 2px;
}
@media (min-width: 768px) {
.orderSummary:focus {
outline-offset: 4px;
}
}
/* Enhanced responsive print styles */
@media print {
.orderSummary {
box-shadow: none !important;
border: 1px solid #ccc !important;
}
}
/* Enhanced responsive hover effects */
@media (hover: hover) {
.orderSummary:hover {
transform: translateY(-2px);
}
.menuItemImage:hover {
transform: scale(1.05);
}
[data-theme="dark"] .orderSummary:hover {
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
}
}
.backToHomePage {
width: 100%;
height: 48px;
display: flex;
justify-content: flex-start;
padding: 12px 18px !important;
row-gap: 10px;
transition: all 0.3s ease;
border-radius: 50px;
}
.backToHomePage :global(.ant-card-body) {
padding: 0px !important;
text-align: start;
width: 100%;
}
.nextIcon {
width: 24px;
height: 24px;
}
.backIcon {
width: 24px;
height: 24px;
}
.inviteToBillCard {
width: 100%;
height: 48px;
display: flex;
justify-content: flex-start;
padding: 12px 18px !important;
row-gap: 10px;
transition: all 0.3s ease;
border-radius: 50px;
}
.inviteToBillCard :global(.ant-card-body) {
padding: 0px !important;
text-align: start;
width: 100%;
}
.orderDetailsContainer {
display: flex;
flex-direction: column;
padding: 16px;
gap: 16px;
overflow: auto;
scrollbar-width: none;
height: calc(92vh - 80px);
}
/* CheckoutButton Styles */
.orderDetailsButtonContainer {
width: 100%;
padding: 16px 16px 0;
position: sticky;
bottom: 0;
left: 0;
height: 80px;
display: flex;
flex-direction: row;
justify-content: space-around;
gap: 1rem;
background-color: var(--secondary-background);
box-shadow: none;
z-index: 999;
}
/* Dark theme styles for checkout */
:global(.darkApp) .orderDetailsButtonContainer {
background-color: #000000 !important;
}
.button {
width: 100%;
height: 48px;
margin-bottom: 16px;
box-shadow: none;
}

View File

@@ -0,0 +1,229 @@
import { Button, Card, Divider, Form, Image, Layout } from "antd";
import LocationIcon from "components/Icons/LocationIcon";
import InvoiceIcon from "components/Icons/order/InvoiceIcon";
import TimeIcon from "components/Icons/order/TimeIcon";
import PaymentDetails from "components/PaymentDetails/PaymentDetails";
import ProText from "components/ProText";
import ProTitle from "components/ProTitle";
import dayjs from "dayjs";
import { useTranslation } from "react-i18next";
import { useGetOrderDetailsQuery } from "redux/api/others";
import { useAppSelector } from "redux/hooks";
import styles from "./OrderDetails.module.css";
import ProInputCard from "components/ProInputCard/ProInputCard";
import ProHeader from "components/ProHeader/ProHeader";
export default function OrderDetails({ orderId }: { orderId: string }) {
const { t } = useTranslation();
const { isRTL } = useAppSelector((state) => state.locale);
const { data: orderDetails } = useGetOrderDetailsQuery(
{
orderID: orderId || "",
restaurantID: localStorage.getItem("restaurantID") || "",
},
{
skip: !orderId,
},
);
// Determine button styles based on order status
const getStatusButtonStyles = () => {
const status = orderDetails?.order.status?.toLowerCase() || "";
if (status.includes("close") || status === "closed") {
return {
backgroundColor: "var(--color-error-12, #EA1F221F)",
color: "#EA1F22",
};
} else if (status.includes("completed") || status === "complete") {
return {
backgroundColor: "var(--color-success-12, #28A7451F)",
color: "#28A745",
};
} else if (status.includes("preparing") || status.includes("prepare")) {
return {
backgroundColor: "var(--Color-Primary-100, #FFB7001F)",
color: "#FFB700",
};
}
// Default fallback
return {
backgroundColor: "var(--color-success-12, #28A7451F)",
color: "#28A745",
};
};
const statusButtonStyles = getStatusButtonStyles();
return (
<Form layout="vertical">
<Layout>
<ProHeader>{t("orders.title")}</ProHeader>
<Layout.Content className={styles.orderDetailsContainer}>
<Card className={styles.orderCard}>
<div
style={{
display: "flex",
flexDirection: "row",
gap: "1rem",
borderRadius: "12px",
padding: 16,
}}
>
<Button type="text" shape="circle" style={{}}>
<Image
src={orderDetails?.restaurant_iimage}
className={styles.profileImage}
width={50}
height={50}
preview={false}
/>
</Button>
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
<ProText
type="secondary"
style={{
fontWeight: 500,
fontStyle: "Medium",
fontSize: "16px",
lineHeight: "140%",
letterSpacing: "0%",
color: "#333333",
}}
>
{/* <LocationIcon className={styles.locationIcon} />{" "} */}
{isRTL
? orderDetails?.restaurantAR
: orderDetails?.restaurant}
</ProText>
<ProText
type="secondary"
style={{
fontSize: "14px",
color: "#5F6C7B",
fontWeight: 400,
fontStyle: "Regular",
lineHeight: "140%",
letterSpacing: "0%",
}}
>
{dayjs(orderDetails?.order.created_at).format(
"YYYY-MM-DD h:mm A",
)}
</ProText>
<ProText
type="secondary"
style={{
fontSize: "14px",
color: "#5F6C7B",
fontWeight: 400,
fontStyle: "Regular",
lineHeight: "140%",
letterSpacing: "0%",
}}
>
Order ID: #{orderDetails?.order.id}
</ProText>
<Button
style={{
height: 28,
gap: 10,
opacity: 1,
borderRadius: "Spacing/4XL",
padding: "4px 8px",
border: "none",
width: "fit-content",
...statusButtonStyles,
}}
>
{orderDetails?.order.status}
</Button>
</div>
</div>
</Card>
<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} />
{/* <CancelOrderBottomSheet /> */}
</Layout.Content>
<Layout.Footer className={styles.orderDetailsButtonContainer}>
<Button
className={styles.button}
onClick={() => {}}
style={{
border: "1px solid #333333",
}}
>
{t("orders.rateOrder")}
</Button>
<Button
type="primary"
shape="round"
className={styles.button}
onClick={() => {}}
>
{t("orders.reOrder")}
</Button>
</Layout.Footer>
{/* <RateBottomSheet /> */}
</Layout>
</Form>
);
}

View File

@@ -9,6 +9,7 @@ import { useTranslation } from "react-i18next";
import { useGetOrdersQuery } from "redux/api/others";
import { colors } from "ThemeConstants";
import styles from "./orders.module.css";
import { Link, useParams } from "react-router-dom";
// Utility function to format date
const formatDate = (dateString: string): string => {
@@ -29,7 +30,7 @@ const formatDate = (dateString: string): string => {
export default function OrdersList() {
const { data: orders = [], isLoading, error, refetch } = useGetOrdersQuery();
const { subdomain } = useParams();
const { t } = useTranslation();
if (isLoading) {
@@ -44,7 +45,7 @@ export default function OrdersList() {
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
height: "90vh",
height: "100vh",
}}
>
<ProTitle level={4}>{t("orders.errorLoadingOrders")}</ProTitle>
@@ -53,6 +54,34 @@ export default function OrdersList() {
);
}
// Determine button styles based on order status
const getStatusButtonStyles = (order: any) => {
const status = order?.status?.toLowerCase() || "";
if (status.includes("close") || status === "closed") {
return {
backgroundColor: "var(--color-error-12, #EA1F221F)",
color: "#EA1F22",
};
} else if (status.includes("completed") || status === "complete") {
return {
backgroundColor: "var(--color-success-12, #28A7451F)",
color: "#28A745",
};
} else if (status.includes("preparing") || status.includes("prepare")) {
return {
backgroundColor: "var(--Color-Primary-100, #FFB7001F)",
color: "#FFB700",
};
}
// Default fallback
return {
backgroundColor: "var(--color-success-12, #28A7451F)",
color: "#28A745",
};
};
return (
<>
{orders.length === 0 ? (
@@ -64,94 +93,99 @@ export default function OrdersList() {
justifyContent: "center",
alignItems: "center",
height: "90vh",
padding: "1rem"
padding: "1rem",
}}
>
<EmptyOrdersIcon />
<ProTitle level={4}>{t("orders.noOrdersFound")}</ProTitle>
<ProText>
{t("orders.youHavenTPlacedAnyOrdersYet")}
</ProText>
<ProText>{t("orders.youHavenTPlacedAnyOrdersYet")}</ProText>
</div>
) : (
// Orders list
<div style={{ padding: "16px" }}>
{orders.map((order: any) => (
<Card
key={order.id}
style={{
borderRadius: "12px",
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.1)",
marginBottom: 12,
}}
>
<div
<Link to={`/${subdomain}/orders/${order.id}`} key={order.id}>
<Card
key={order.id}
style={{
display: "flex",
flexDirection: "row",
justifyContent: "flex-start",
gap: "1rem",
borderRadius: "12px",
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.1)",
marginBottom: 12,
}}
>
<Button
type="text"
shape="circle"
<div
style={{
backgroundColor: "rgba(255, 183, 0, 0.08)",
display: "flex",
flexDirection: "row",
justifyContent: "flex-start",
gap: "1rem",
}}
>
{order.restaurant_name[0]}
</Button>
<div
style={{ display: "flex", flexDirection: "column", gap: 8 }}
>
<ProText style={{ fontSize: "1rem" }}>
{order.restaurant_name}
</ProText>
<ProText type="secondary">
{formatDate(order?.created_at)}
</ProText>
<ProText type="secondary">
{t("orders.orderID")} {order.id}
</ProText>
<Button
type="text"
shape="circle"
style={{
borderRadius: 888,
height: 24,
border: "none",
backgroundColor: colors.primary,
color: "white",
padding: "0 8px",
width: "fit-content",
backgroundColor: "rgba(255, 183, 0, 0.08)",
}}
>
{order.status}
{order.restaurant_name[0]}
</Button>
<div
style={{ display: "flex", flexDirection: "column", gap: 8 }}
>
<ProText style={{ fontSize: "1rem" }}>
{order.restaurant_name}
</ProText>
<ProText type="secondary">
{formatDate(order?.created_at)}
</ProText>
<ProText type="secondary">
{t("orders.orderID")} {order.id}
</ProText>
<Button
style={{
height: 28,
gap: 10,
opacity: 1,
borderRadius: "Spacing/4XL",
padding: "4px 8px",
border: "none",
width: "fit-content",
...getStatusButtonStyles(order),
}}
>
{order.status}
</Button>
</div>
</div>
</div>
<Divider style={{ margin: "15px 0 10px 0" }} />
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-around",
}}
>
<div>
<ProText style={{ fontSize: "1rem", color: colors.primary }}>
<RateIcon className={styles.rateIcon} />
{t("orders.rateOrder")}
</ProText>{" "}
</div>
<Divider style={{ margin: "15px 0 10px 0" }} />
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-around",
}}
>
<div>
<ProText
style={{ fontSize: "1rem", color: colors.primary }}
>
<RateIcon className={styles.rateIcon} />
{t("orders.rateOrder")}
</ProText>{" "}
</div>
<div>
<ProText style={{ fontSize: "1rem", color: colors.primary }}>
<ReOrderIcon className={styles.reorderIcon} />
{t("orders.reOrder")}
</ProText>
<div>
<ProText
style={{ fontSize: "1rem", color: colors.primary }}
>
<ReOrderIcon className={styles.reorderIcon} />
{t("orders.reOrder")}
</ProText>
</div>
</div>
</div>
</Card>
</Card>
</Link>
))}
</div>
)}

View File

@@ -1,25 +1,23 @@
import { Button, Row } from "antd";
import ProHeader from "components/ProHeader/ProHeader";
import { useTranslation } from "react-i18next";
import { Link, useParams } from "react-router-dom";
import styles from "./orders.module.css";
import { useParams } from "react-router-dom";
import OrdersList from "./OrdersList";
import OrderDetails from "./OrderDetails";
export default function OrdersPage() {
const { t } = useTranslation();
const { subdomain } = useParams();
const { orderId } = useParams();
return (
<>
<ProHeader>{t("orders.title")}</ProHeader>
<OrdersList />
<Row className={styles.row}>
<Link to={`/${subdomain}/menu`} style={{ width: "100%" }}>
<Button type="primary" shape="round" className={styles.button}>
{t("orders.browseMenu")}
</Button>
</Link>
</Row>
{orderId ? (
<OrderDetails orderId={orderId} />
) : (
<>
<ProHeader>{t("orders.title")}</ProHeader>
<OrdersList />
</>
)}
</>
);
}