From c13a4655bf39be1f367acaf6580a2b326c48eee8 Mon Sep 17 00:00:00 2001 From: "@jcarymuhammedow" Date: Mon, 2 Mar 2026 17:46:18 +0500 Subject: [PATCH] added shipping method --- app/[locale]/cart/page.tsx | 51 +++++--- features/cart/components/OrderSummary.tsx | 91 +++++++++++--- features/cart/hooks/useCart.ts | 38 +++--- features/orders/components/OrderPage.tsx | 141 +++++++++++++++++++--- i18n/messages/ru.json | 7 +- i18n/messages/tm.json | 15 ++- lib/types/api.ts | 10 +- 7 files changed, 282 insertions(+), 71 deletions(-) diff --git a/app/[locale]/cart/page.tsx b/app/[locale]/cart/page.tsx index 34c2ab3..e17deff 100644 --- a/app/[locale]/cart/page.tsx +++ b/app/[locale]/cart/page.tsx @@ -11,18 +11,18 @@ import { useCreateOrder, useRegions, usePaymentTypes, + useOrderDeliveries, } from "@/lib/hooks"; import { useTranslations } from "next-intl"; import { useRouter } from "next/navigation"; -import type { DeliveryType, PaymentType } from "@/lib/types/api"; +import type { PaymentType, OrderDelivery } from "@/lib/types/api"; import EmptyCart from "@/features/cart/components/EmptyCart"; import ErrorPage from "@/components/ErrorPage"; export default function CartPage() { const [isClient, setIsClient] = useState(false); const [paymentType, setPaymentType] = useState(null); - const [deliveryType, setDeliveryType] = - useState("SELECTED_DELIVERY"); + const [selectedOrderDelivery, setSelectedOrderDelivery] = useState(null); const [selectedRegion, setSelectedRegion] = useState(""); const [selectedProvince, setSelectedProvince] = useState(null); const [notes, setNote] = useState(""); @@ -36,15 +36,22 @@ export default function CartPage() { const { data: provinces = [], isLoading: provincesLoading } = useRegions(); const { data: paymentTypes = [], isLoading: paymentTypesLoading } = usePaymentTypes(); + const { data: orderDeliveries = [], isLoading: deliveriesLoading } = useOrderDeliveries(); const { mutate: createOrder, isPending: isCreatingOrder } = useCreateOrder(); const cartItems = cartResponse?.data || []; - const isLoading = cartLoading || provincesLoading || paymentTypesLoading; + const isLoading = cartLoading || provincesLoading || paymentTypesLoading || deliveriesLoading; useEffect(() => { setIsClient(true); }, []); + const handleRegionChange = (region: string) => { + setSelectedRegion(region); + setSelectedProvince(null); + setSelectedOrderDelivery(null); + }; + const regionGroups = useMemo(() => { return provinces.reduce((acc, province) => { if (!acc[province.region]) { @@ -77,25 +84,25 @@ export default function CartPage() { }, [cartItems]); const totalAmount = useMemo(() => { - return cartItems.reduce((sum, item) => { + const productsTotal = cartItems.reduce((sum, item) => { const price = parseFloat(item.product.price_amount || "0"); return sum + price * item.product_quantity; }, 0); + return productsTotal; }, [cartItems]); - const handleDeliveryTypeChange = (type: DeliveryType) => { - setDeliveryType(type); - setSelectedProvince(null); - }; + const finalTotal = useMemo(() => { + const shippingPrice = selectedOrderDelivery?.price || 0; + return totalAmount + shippingPrice; + }, [totalAmount, selectedOrderDelivery]); - const formatPhoneForBackend = (phoneNumber: string): string => { return phoneNumber.replace(/^\+993\s*/, "").replace(/\s+/g, ""); }; const handleCompleteOrder = () => { - if (!selectedRegion || !selectedProvince || !paymentType || !phone || !name) { + if (!selectedRegion || !selectedProvince || !paymentType || !phone || !name || !selectedOrderDelivery) { console.warn("Missing required fields for order"); return; } @@ -112,9 +119,10 @@ export default function CartPage() { createOrder( { customer_name: `${name} ${lastName}`.trim(), - customer_phone: parseInt(phoneDigits, 10), + customer_phone: phoneDigits, customer_address: selectedProvinceData.name, - shipping_method: "standart", + shipping_method: selectedOrderDelivery.name, + shipping_price: selectedOrderDelivery.price, payment_type_id: paymentType.id, region: selectedRegion, notes: notes || undefined, @@ -226,15 +234,25 @@ export default function CartPage() { title: t("products"), value: `${totalAmount.toFixed(2)} TMT`, }, + ...(selectedOrderDelivery + ? [ + { + title: t("shipping_method"), + value: `${selectedOrderDelivery.price.toFixed(2)} TMT`, + }, + ] + : []), ], footer: { title: t("total_price"), - value: `${totalAmount.toFixed(2)} TMT`, + value: `${finalTotal.toFixed(2)} TMT`, }, }, }} paymentType={paymentType} - deliveryType={deliveryType} + orderDeliveries={orderDeliveries} + selectedOrderDelivery={selectedOrderDelivery} + onOrderDeliveryChange={setSelectedOrderDelivery} selectedRegion={selectedRegion} selectedProvince={selectedProvince} notes={notes} @@ -248,8 +266,7 @@ export default function CartPage() { onNameChange={setName} onLastNameChange={setLastName} onPaymentTypeChange={setPaymentType} - onDeliveryTypeChange={handleDeliveryTypeChange} - onRegionChange={setSelectedRegion} + onRegionChange={handleRegionChange} onProvinceChange={setSelectedProvince} onNoteChange={setNote} onCompleteOrder={handleCompleteOrder} diff --git a/features/cart/components/OrderSummary.tsx b/features/cart/components/OrderSummary.tsx index cf7485b..0fd94c0 100644 --- a/features/cart/components/OrderSummary.tsx +++ b/features/cart/components/OrderSummary.tsx @@ -13,9 +13,13 @@ import { SelectValue, } from "@/components/ui/select"; import { Input } from "@/components/ui/input"; -import DeliveryTypeSelector from "./DeliveryTypeSelector"; import { useTranslations } from "next-intl"; -import type { DeliveryType, PaymentType, Province } from "@/lib/types/api"; +import type { + DeliveryType, + PaymentType, + Province, + OrderDelivery, +} from "@/lib/types/api"; import { useState } from "react"; interface OrderBillingItem { @@ -37,7 +41,8 @@ interface OrderSummaryProps { billing: OrderBilling; }; paymentType: PaymentType | null; - deliveryType: DeliveryType; + orderDeliveries: OrderDelivery[]; + selectedOrderDelivery: OrderDelivery | null; selectedRegion: string; selectedProvince: number | null; notes: string; @@ -51,7 +56,7 @@ interface OrderSummaryProps { onNameChange: (name: string) => void; onLastNameChange: (lastName: string) => void; onPaymentTypeChange: (type: PaymentType) => void; - onDeliveryTypeChange: (type: DeliveryType) => void; + onOrderDeliveryChange: (delivery: OrderDelivery) => void; onRegionChange: (regionCode: string) => void; onProvinceChange: (provinceId: number) => void; onNoteChange: (notes: string) => void; @@ -62,7 +67,8 @@ interface OrderSummaryProps { export default function OrderSummary({ order, paymentType, - deliveryType, + orderDeliveries, + selectedOrderDelivery, selectedRegion, selectedProvince, notes, @@ -76,7 +82,7 @@ export default function OrderSummary({ onNameChange, onLastNameChange, onPaymentTypeChange, - onDeliveryTypeChange, + onOrderDeliveryChange, onRegionChange, onProvinceChange, onNoteChange, @@ -90,6 +96,15 @@ export default function OrderSummary({ ? regionGroups[selectedRegion] || [] : []; + const filteredDeliveries = orderDeliveries.filter((delivery) => { + if (!selectedRegion) return true; + if (selectedRegion === "ag") { + return delivery.name === "standart" || delivery.name === "self_pickup"; + } else { + return delivery.name === "region"; + } + }); + const phoneDigits = phone.replace(/\D/g, ""); const isPhoneValid = phoneDigits.length === 11; @@ -97,6 +112,7 @@ export default function OrderSummary({ selectedRegion && selectedProvince && paymentType && + selectedOrderDelivery && isPhoneValid && name.trim() !== "" && lastName.trim() !== ""; @@ -136,7 +152,7 @@ export default function OrderSummary({ return ( {/* Customer Information */} -
+

{t("customer_information")}

@@ -198,7 +214,7 @@ export default function OrderSummary({
{/* Payment Type */} -
+

{t("payment_type")}

{paymentTypes.map((type) => ( @@ -231,16 +247,13 @@ export default function OrderSummary({
{/* Region Selection */} -
+
{ - onRegionChange(value); - onProvinceChange(null as any); - }} + onValueChange={(value) => onRegionChange(value)} className="flex flex-wrap gap-4" > {availableRegions.map((regionCode) => ( @@ -270,7 +283,7 @@ export default function OrderSummary({ {/* Province Selection */} {selectedRegion && provincesForSelectedRegion.length > 0 && ( -
+
@@ -299,8 +312,56 @@ export default function OrderSummary({
)} + {/* Shipping Method */} + {selectedRegion && ( +
+

{t("shipping_method")}

+
+ {filteredDeliveries.map((delivery) => ( + onOrderDeliveryChange(delivery)} + > +
+
+ + {t(delivery.name)} + +
+ + {delivery.price === 0 ? t("free") : `${delivery.price} TMT`} + +
+
+ ))} +
+ {showValidation && !selectedOrderDelivery && ( +

{t("requiredField")}

+ )} +
+ )} + {/* Note */} -
+