added shipping method

This commit is contained in:
@jcarymuhammedow
2026-03-02 17:46:18 +05:00
parent bf5980e3b3
commit c13a4655bf
7 changed files with 282 additions and 71 deletions

View File

@@ -23,9 +23,10 @@ import {
MapPin,
CreditCard,
ShoppingBag,
Banknote,
} from "lucide-react";
import { toast } from "sonner";
import { useOrders, useCancelOrder } from "@/lib/hooks";
import { useOrders, useCancelOrder, useOrderDeliveries } from "@/lib/hooks";
import { useTranslations } from "next-intl";
import type { Order } from "@/lib/types/api";
import EmptyOrders from "./EmptyOrders";
@@ -42,6 +43,8 @@ export default function OrdersPageClient({ locale }: OrdersPageClientProps) {
const t = useTranslations();
const { data: orders, isLoading, isError } = useOrders();
const { data: orderDeliveries, isLoading: deliveriesLoading } =
useOrderDeliveries();
const { mutate: cancelOrder, isPending: isCancellingOrder } =
useCancelOrder();
@@ -83,7 +86,7 @@ export default function OrdersPageClient({ locale }: OrdersPageClientProps) {
if (
lowerStatus.includes("ожидается") ||
lowerStatus.includes("pending") ||
lowerStatus.includes("garaşlama")
lowerStatus.includes("garaşylýar")
) {
return (
<Badge
@@ -147,20 +150,89 @@ export default function OrdersPageClient({ locale }: OrdersPageClientProps) {
const activeOrders = useMemo(
() => orders?.filter((o) => isActiveOrder(o.status)) || [],
[orders, isActiveOrder]
[orders, isActiveOrder],
);
const completedOrders = useMemo(
() => orders?.filter((o) => !isActiveOrder(o.status)) || [],
[orders, isActiveOrder]
[orders, isActiveOrder],
);
const calculateTotal = useCallback((order: Order) => {
return order.orderItems.reduce((sum, item) => {
return sum + parseFloat(item.unit_price_amount) * item.quantity;
}, 0);
}, []);
const getShippingPrice = useCallback(
(order: Order) => {
if (order.shipping_price !== undefined && order.shipping_price !== null) {
return Number(order.shipping_price);
}
if (isLoading) {
if (!orderDeliveries || orderDeliveries.length === 0) return 0;
const methodFromOrder = order.shipping_method.toLowerCase();
// Find delivery method by matching internal name, translated name, or common keywords
const delivery = orderDeliveries.find((d) => {
const internalName = d.name.toLowerCase();
const translatedName = t(internalName).toLowerCase(); // d.name should be used for translation
// Direct match
if (
internalName === methodFromOrder ||
translatedName === methodFromOrder
) {
return true;
}
// Keyword based matching for "region"
if (
(internalName === "region" || internalName === "çapar") &&
(methodFromOrder.includes("welaýat") ||
methodFromOrder.includes("region") ||
methodFromOrder.includes("регион") ||
methodFromOrder.includes("çapar") ||
methodFromOrder.includes("welayat"))
) {
return true;
}
// Keyword based matching for "self_pickup"
if (
internalName === "self_pickup" &&
(methodFromOrder.includes("özüm") ||
methodFromOrder.includes("özüň") ||
methodFromOrder.includes("pickup") ||
methodFromOrder.includes("самовывоз"))
) {
return true;
}
// Keyword based matching for "standart"
if (
internalName === "standart" &&
(methodFromOrder.includes("standart") ||
methodFromOrder.includes("standard"))
) {
return true;
}
return false;
});
return delivery ? Number(delivery.price) : 0;
},
[orderDeliveries, t],
);
const calculateTotal = useCallback(
(order: Order) => {
const itemsTotal = order.orderItems.reduce((sum, item) => {
return sum + parseFloat(item.unit_price_amount) * item.quantity;
}, 0);
const shippingPrice = getShippingPrice(order);
return itemsTotal + shippingPrice;
},
[getShippingPrice],
);
if (isLoading || deliveriesLoading) {
return (
<div className="mx-auto p-2 lg:p-6 md:p-4 mb-16 min-h-screen">
<h1 className="text-xl md:text-2xl lg:text-3xl font-bold mb-6">
@@ -247,8 +319,10 @@ export default function OrdersPageClient({ locale }: OrdersPageClientProps) {
isCancelling={isCancellingOrder}
getStatusBadge={getStatusBadge}
calculateTotal={calculateTotal}
getShippingPrice={getShippingPrice}
showCancelButton
t={t}
orderDeliveries={orderDeliveries || []}
/>
))}
</div>
@@ -270,12 +344,13 @@ export default function OrdersPageClient({ locale }: OrdersPageClientProps) {
order={order}
isExpanded={expandedOrders.has(order.id)}
onToggle={() => toggleOrderExpand(order.id)}
onCancel={handleCancelOrder}
isCancelling={isCancellingOrder}
getStatusBadge={getStatusBadge}
calculateTotal={calculateTotal}
getShippingPrice={getShippingPrice}
showCancelButton={false}
t={t}
orderDeliveries={orderDeliveries || []}
/>
))}
</div>
@@ -319,12 +394,14 @@ interface CompactOrderCardProps {
order: Order;
isExpanded: boolean;
onToggle: () => void;
onCancel: (order: Order) => void;
onCancel?: (order: Order) => void;
isCancelling: boolean;
getStatusBadge: (status: string) => React.ReactNode;
calculateTotal: (order: Order) => number;
getShippingPrice: (order: Order) => number;
showCancelButton: boolean;
t: any;
orderDeliveries: any[];
}
function CompactOrderCard({
@@ -335,12 +412,19 @@ function CompactOrderCard({
isCancelling,
getStatusBadge,
calculateTotal,
getShippingPrice,
showCancelButton,
t,
orderDeliveries,
}: CompactOrderCardProps) {
const total = useMemo(() => calculateTotal(order), [calculateTotal, order]);
const itemCount = order.orderItems.length;
const shippingPrice = useMemo(
() => getShippingPrice(order),
[order, getShippingPrice],
);
return (
<Card className="overflow-hidden transition-all py-2 md:py-4 lg:py-6 hover:shadow-md">
{/* Compact Header - Always Visible */}
@@ -430,6 +514,20 @@ function CompactOrderCard({
<p className="text-sm text-gray-900">{order.shipping_method}</p>
</div>
</div>
<div className="flex items-start gap-3 border-t md:border-t-0 pt-2 md:pt-0">
<Banknote className="h-5 w-5 text-orange-500 mt-0.5" />
<div>
<p className="text-sm font-medium text-gray-700">
{t("shipping_price")}
</p>
<p className="text-sm font-bold text-green-600">
{shippingPrice === 0
? t("free")
: `${shippingPrice.toFixed(2)} TMT`}
</p>
</div>
</div>
</div>
{/* Products List */}
@@ -476,7 +574,22 @@ function CompactOrderCard({
{/* Footer with Total and Actions */}
<div className="border-t p-4 bg-gray-50">
<div className="flex items-center justify-between mb-3">
<div className="space-y-2 mb-4">
<div className="flex justify-between text-sm text-gray-600">
<span>{t("products")}:</span>
<span>{(total - shippingPrice).toFixed(2)} TMT</span>
</div>
<div className="flex justify-between text-sm text-gray-600">
<span>{t("shipping_method")}:</span>
<span>
{shippingPrice === 0
? t("free")
: `${shippingPrice.toFixed(2)} TMT`}
</span>
</div>
</div>
<div className="flex items-center justify-between mb-3 pt-2 border-t">
<span className="text-base font-semibold text-gray-700">
{t("total_price")}:
</span>
@@ -490,7 +603,7 @@ function CompactOrderCard({
variant="destructive"
onClick={(e) => {
e.stopPropagation();
onCancel(order);
onCancel?.(order);
}}
disabled={isCancelling}
className="w-full cursor-pointer"