From d5148aaf0671b505988a002e615aed460fc868c5 Mon Sep 17 00:00:00 2001 From: Samandar Turgunboyev Date: Fri, 13 Feb 2026 15:50:53 +0500 Subject: [PATCH] order status update --- src/features/cart/lib/api.ts | 3 +- src/features/cart/ui/CartPage.tsx | 244 ++++++++++++--------- src/features/cart/ui/OrderPage.tsx | 2 +- src/features/product/ui/Product.tsx | 153 +++++++------ src/shared/config/api/product/type.ts | 10 +- src/widgets/categories/ui/product-card.tsx | 180 ++++++++------- 6 files changed, 331 insertions(+), 261 deletions(-) diff --git a/src/features/cart/lib/api.ts b/src/features/cart/lib/api.ts index ed6c3bc..5f4b326 100644 --- a/src/features/cart/lib/api.ts +++ b/src/features/cart/lib/api.ts @@ -13,7 +13,7 @@ export interface CartItem { image: string; }[]; liked: boolean; - meansurement: null | string; + meansurement: null | { id: number; name: string }; inventory_id: null | string; product_id: string; code: string; @@ -31,6 +31,7 @@ export interface CartItem { marketing_group_code: null | string; inventory_kinds: { id: number; name: string }[]; sector_codes: { id: number; code: string }[]; + balance: number; prices: { id: number; price: string; diff --git a/src/features/cart/ui/CartPage.tsx b/src/features/cart/ui/CartPage.tsx index da0a2e7..e08141d 100644 --- a/src/features/cart/ui/CartPage.tsx +++ b/src/features/cart/ui/CartPage.tsx @@ -39,6 +39,12 @@ const CartPage = () => { const [quantities, setQuantities] = useState>({}); const debounceRef = useRef>({}); + // O'lchov birligini formatlash uchun yordamchi funksiya + const getQuantityMessage = (qty: number, measurement: string | null) => { + if (!measurement) return `${qty} dona`; + return `${qty} ${measurement}`; + }; + useEffect(() => { if (!cartItems) return; const initialQuantities: Record = {}; @@ -57,8 +63,24 @@ const CartPage = () => { body: { quantity: number }; cart_item_id: string; }) => cart_api.update_cart_item({ body, cart_item_id }), - onSuccess: () => - queryClient.invalidateQueries({ queryKey: ['cart_items', cart_id] }), + onSuccess: (_, variables) => { + queryClient.invalidateQueries({ queryKey: ['cart_items', cart_id] }); + + // Qaysi mahsulot yangilanganini topish + const item = cartItems?.find( + (i) => String(i.id) === variables.cart_item_id, + ); + if (item) { + const measurementName = item.product.meansurement?.name || null; + toast.success( + `${t('Miqdor')} ${getQuantityMessage(variables.body.quantity, measurementName)} ${t('ga yangilandi')}`, + { + richColors: true, + position: 'top-center', + }, + ); + } + }, onError: (err: AxiosError) => toast.error(err.message, { richColors: true, position: 'top-center' }), }); @@ -66,8 +88,13 @@ const CartPage = () => { const { mutate: deleteCartItem } = useMutation({ mutationFn: ({ cart_item_id }: { cart_item_id: string }) => cart_api.delete_cart_item(cart_item_id), - onSuccess: () => - queryClient.invalidateQueries({ queryKey: ['cart_items', cart_id] }), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['cart_items', cart_id] }); + toast.success(t("Savatdan o'chirildi"), { + richColors: true, + position: 'top-center', + }); + }, onError: (err: AxiosError) => toast.error(err.message, { richColors: true, position: 'top-center' }), }); @@ -104,15 +131,14 @@ const CartPage = () => { const subtotal = cartItems?.reduce((sum, item) => { - if (item.product.prices.length === 0) return sum; // narx yo'q bo'lsa qo'shmaymiz + if (item.product.prices.length === 0) return sum; - // Eng yuqori narxni olish const maxPrice = Math.max( ...item.product.prices.map((p) => Number(p.price)), ); return sum + maxPrice * item.quantity; - }, 0) || 0; // cartItems bo'sh bo'lsa 0 qaytaradi + }, 0) || 0; const handleQuantityChange = (itemId: string, value: number) => { setQuantities((prev) => ({ @@ -143,107 +169,125 @@ const CartPage = () => {
- {cartItems.map((item, index) => ( -
- - -
- 0 - ? item.product.images[0].image.includes(BASE_URL) - ? item.product.images[0].image - : BASE_URL + item.product.images[0].image - : ProductBanner +
+ className="absolute right-2 w-7 h-7 top-2 cursor-pointer" + > + + -
-

- {item.product.name} -

-
- - {formatPrice( - item.product.prices.length !== 0 - ? Math.max( - ...item.product.prices.map((e) => - Number(e.price), - ), - ) - : 0, - true, - )} - -
- -
- - - { - const val = e.target.value.replace(/\D/g, ''); // faqat raqam - setQuantities((prev) => ({ - ...prev, - [item.id]: val, - })); - - // Debounce bilan update - const valNum = Number(val); - if (!isNaN(valNum)) - handleQuantityChange(String(item.id), valNum); - }} - type="text" - className="w-16 text-center" + alt={item.product.name} + width={500} + height={500} + unoptimized + className="object-cover" + style={{ width: '100%', height: '100%' }} /> +
- +
+

+ {item.product.name} +

+
+ + {formatPrice( + item.product.prices.length !== 0 + ? Math.max( + ...item.product.prices.map((e) => + Number(e.price), + ), + ) + : 0, + true, + )} + + + /{measurementDisplay} + +
+ + {/* O'lchov ko'rsatkichi */} +

+ {t('Miqdor')}: {quantities[item.id]} {measurementDisplay} +

+ +
+ + +
+ { + const val = e.target.value.replace(/\D/g, ''); + const valNum = Number(val); + + setQuantities((prev) => ({ + ...prev, + [item.id]: val, + })); + + if (!isNaN(valNum)) + handleQuantityChange(String(item.id), valNum); + }} + type="text" + className="w-14 text-center border-none p-0" + /> + + {measurementDisplay} + +
+ + +
-
- ))} + ); + })}
diff --git a/src/features/cart/ui/OrderPage.tsx b/src/features/cart/ui/OrderPage.tsx index 08f7efd..823f129 100644 --- a/src/features/cart/ui/OrderPage.tsx +++ b/src/features/cart/ui/OrderPage.tsx @@ -298,7 +298,7 @@ const OrderPage = () => { room_code: process.env.NEXT_PUBLIC_ROOM_CODE!, deal_time: formatDate.format(new Date(), 'DD.MM.YYYY'), robot_code: process.env.NEXT_PUBLIC_ROBOT_CODE!, - status: 'B#N', + status: 'D', sales_manager_code: process.env.NEXT_PUBLIC_SALES_MANAGER_CODE!, person_code: user?.username, currency_code: '860', diff --git a/src/features/product/ui/Product.tsx b/src/features/product/ui/Product.tsx index 92084a6..08d9500 100644 --- a/src/features/product/ui/Product.tsx +++ b/src/features/product/ui/Product.tsx @@ -48,6 +48,12 @@ const ProductDetail = () => { const [selectedImage, setSelectedImage] = useState(0); const debounceRef = useRef(null); + // O'lchov birligini formatlash uchun yordamchi funksiya + const getQuantityMessage = (qty: number, measurement: string | null) => { + if (!measurement) return `${qty} dona`; + return `${qty} ${measurement}`; + }; + /* ---------------- CART ITEMS ---------------- */ const { data: cartItems } = useQuery({ queryKey: ['cart_items', cart_id], @@ -72,7 +78,8 @@ const ProductDetail = () => { /* ---------------- DERIVED DATA ---------------- */ const price = Number(data?.prices?.[0]?.price || 0); - const maxBalance = data?.balance ?? 0; // <-- balance limit + const maxBalance = data?.balance ?? 0; + const measurementDisplay = data?.meansurement?.name || 'шт.'; /* ---------------- SYNC CART QUANTITY ---------------- */ useEffect(() => { @@ -113,9 +120,16 @@ const ProductDetail = () => { const { mutate: addToCart } = useMutation({ mutationFn: (body: { product: string; quantity: number; cart: string }) => cart_api.cart_item(body), - onSuccess: () => { + onSuccess: (_, variables) => { queryClient.refetchQueries({ queryKey: ['cart_items'] }); - toast.success(t("Mahsulot savatga qo'shildi"), { richColors: true }); + const measurementName = data?.meansurement?.name || null; + toast.success( + `${getQuantityMessage(variables.quantity, measurementName)} ${t("savatga qo'shildi")}`, + { + richColors: true, + position: 'top-center', + }, + ); }, onError: (err: AxiosError) => { const msg = @@ -129,7 +143,17 @@ const ProductDetail = () => { cart_item_id: string; body: { quantity: number }; }) => cart_api.update_cart_item(payload), - onSuccess: () => queryClient.refetchQueries({ queryKey: ['cart_items'] }), + onSuccess: (_, variables) => { + queryClient.refetchQueries({ queryKey: ['cart_items'] }); + const measurementName = data?.meansurement?.name || null; + toast.success( + `${t('Miqdor')} ${getQuantityMessage(variables.body.quantity, measurementName)} ${t('ga yangilandi')}`, + { + richColors: true, + position: 'top-center', + }, + ); + }, }); /* ---------------- FAVOURITE ---------------- */ @@ -149,25 +173,12 @@ const ProductDetail = () => { /* ---------------- HANDLERS ---------------- */ const handleAddToCart = () => { - if (quantity >= maxBalance) { - toast.warning(t(`only_available`, { maxBalance }), { - richColors: true, - position: 'top-center', - }); - return; - } if (!data || !cart_id) return; const cartItem = cartItems?.data.cart_item.find( (i) => Number(i.product.id) === data.id, ); - if (quantity > maxBalance) { - toast.error(t(`Faqat ${maxBalance} dona mavjud`), { richColors: true }); - setQuantity(maxBalance); - return; - } - if (cartItem) { updateCartItem({ cart_item_id: cartItem.id.toString(), @@ -183,13 +194,6 @@ const ProductDetail = () => { }; const handleIncrease = () => { - if (quantity >= maxBalance) { - toast.warning(t(`Faqat ${maxBalance} dona mavjud`), { - richColors: true, - position: 'top-center', - }); - return; - } setQuantity((q) => q + 1); }; @@ -281,40 +285,65 @@ const ProductDetail = () => { {/* INFO */}

{data?.name}

-
- {formatPrice(price, true)} + + {/* Narx va o'lchov birligi */} +
+ + {formatPrice(price, true)} + + /{measurementDisplay}
+

{data?.short_name}

{/* QUANTITY */} -
- +
+ +
+ - { - let v = Number(e.target.value); - if (v < 1) v = 1; - if (v > maxBalance) { - toast.warning(t(`Faqat ${maxBalance} dona mavjud`), { - richColors: true, - position: 'top-center', - }); - v = maxBalance; - } - setQuantity(v); - }} - className="w-16 text-center" - /> +
+ { + let v = Number(e.target.value); + if (v < 1) v = 1; + if (v > maxBalance) { + toast.warning( + `${t('Maksimal')} ${maxBalance} ${measurementDisplay}`, + { + richColors: true, + position: 'top-center', + }, + ); + v = maxBalance; + } + setQuantity(v); + }} + className="w-20 text-center" + /> + + {measurementDisplay} + +
- + +
-
+
{t('Jami')}: {formatPrice(price * quantity, true)}
@@ -322,7 +351,7 @@ const ProductDetail = () => {
- {/* IMPROVED UPDATED_AT WARNING */} + + {/* UPDATED_AT WARNING */} {data?.updated_at && data.payment_type === 'cash' && (

@@ -352,21 +384,12 @@ const ProductDetail = () => {

- {t('Bepul yetkazib berish')} +

{t('Bepul yetkazib berish')}

- {t('Kafolat')} +

{t('Kafolat')}

- {/* {data?.payment_type && ( -
- - - {data.payment_type === 'cash' - ? t('Naqd bilan olinadi') - : t("Pul o'tkazish yo'li bilan olinadi")} -
- )} */}
diff --git a/src/shared/config/api/product/type.ts b/src/shared/config/api/product/type.ts index 7685a8b..bdedf8b 100644 --- a/src/shared/config/api/product/type.ts +++ b/src/shared/config/api/product/type.ts @@ -12,7 +12,10 @@ export interface ProductListResult { id: number; images: { id: number; image: string }[]; liked: boolean; - meansurement: null | string; + meansurement: { + id: number; + name: string; + } | null; inventory_id: null | string; product_id: string; code: string; @@ -99,7 +102,10 @@ export interface FavouriteProductRes { id: number; images: { id: number; image: string }[]; liked: boolean; - meansurement: null | string; + meansurement: { + id: number; + name: string; + } | null; inventory_id: null | string; product_id: string; code: string; diff --git a/src/widgets/categories/ui/product-card.tsx b/src/widgets/categories/ui/product-card.tsx index e89ddd9..aa7cef2 100644 --- a/src/widgets/categories/ui/product-card.tsx +++ b/src/widgets/categories/ui/product-card.tsx @@ -18,7 +18,7 @@ import { Input } from '@/shared/ui/input'; import { FlyingAnimationPortal } from '@/widgets/animation/FlyingAnimationPortal'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { AxiosError } from 'axios'; -import { Heart, Minus, Plus, ShoppingCart } from 'lucide-react'; +import { Heart, Minus, Plus } from 'lucide-react'; import { useTranslations } from 'next-intl'; import Image from 'next/image'; import { MouseEvent, useEffect, useRef, useState } from 'react'; @@ -42,12 +42,28 @@ export function ProductCard({ const debounceRef = useRef(null); const imageRef = useRef(null); + // O'lchov birligini formatlash uchun yordamchi funksiya + const getQuantityMessage = (qty: number, measurement: string | null) => { + if (!measurement) return `${qty} dona`; + return `${qty} ${measurement}`; + }; + const { mutate: addToCart } = useMutation({ mutationFn: (body: { product: string; quantity: number; cart: string }) => cart_api.cart_item(body), - onSuccess: () => { + onSuccess: (_, variables) => { queryClient.refetchQueries({ queryKey: ['cart_items'] }); setAnimated(true); + + // Muvaffaqiyatli qo'shilganda xabar + const measurementName = product.meansurement?.name || null; + toast.success( + `${getQuantityMessage(variables.quantity, measurementName)} ${t("savatga qo'shildi")}`, + { + richColors: true, + position: 'top-center', + }, + ); }, onError: (err: AxiosError) => { const detail = (err.response?.data as { detail: string }).detail; @@ -57,7 +73,6 @@ export function ProductCard({ }); }, }); - const maxBalance = product.balance ?? 0; const { mutate: updateCartItem } = useMutation({ mutationFn: ({ @@ -158,13 +173,6 @@ export function ProductCard({ const current = quantity === '' ? 0 : quantity; - if (current >= maxBalance) { - toast.warning(t(`Faqat ${maxBalance} dona mavjud`), { - richColors: true, - }); - return; - } - const newQty = current + 1; setQuantity(newQty); @@ -181,13 +189,16 @@ export function ProductCard({ return ( - Xatolik - {t('Mahsulotni yuklab bo‘lmadi')} + {t('Xatolik')} + {t("Mahsulotni yuklab bo'lmadi")} ); } + // O'lchov birligini ko'rsatish + const measurementDisplay = product.meansurement?.name || 'шт.'; + return ( <>
- {/* {product. > 0 && ( -
- -{product.discount}% -
- )} */} -
-
- {/*
- - - {product.rating} - -
*/} +
+ {/* Narx va o'lchov birligi */} +
+ {product.prices.length > 0 && ( + <> +

+ {formatPrice( + Math.max(...product.prices.map((p) => Number(p.price))), + true, + )} + + /{measurementDisplay} + +

+ + )} +
-

+

{product.name}

- -
- {product.prices.length > 0 && ( - - {formatPrice( - Math.max(...product.prices.map((p) => Number(p.price))), - true, - )} - - )} - - {/* {product. && ( -
- {formatPrice(product.oldPrice, true)} -
- )} */} -
-
+ +
{quantity === 0 ? ( ) : (
e.stopPropagation()} - className="flex items-center justify-between border border-green-500 rounded-lg h-10" + className="flex items-center justify-between bg-white border border-slate-300 rounded-lg h-10 overflow-hidden" > - - { - const v = e.target.value; - if (!/^\d*$/.test(v)) return; +
+ { + const v = e.target.value; + if (!/^\d*$/.test(v)) return; - if (debounceRef.current) { - clearTimeout(debounceRef.current); - } + if (debounceRef.current) { + clearTimeout(debounceRef.current); + } - if (v === '') { - setQuantity(''); - return; - } + if (v === '') { + setQuantity(''); + return; + } - let num = Number(v); - if (num > maxBalance) { - num = maxBalance; - toast.warning(t(`Maksimal ${maxBalance} dona`), { - richColors: true, - }); - } + const num = Number(v); - setQuantity(num); + setQuantity(num); - const id = getCartItemId(); - if (!id) return; + const id = getCartItemId(); + if (!id) return; - if (num === 0) { - deleteCartItem({ cart_item_id: id.toString() }); - return; - } + if (num === 0) { + deleteCartItem({ cart_item_id: id.toString() }); + return; + } - debounceRef.current = setTimeout(() => { - updateCartItem({ - cart_item_id: id.toString(), - body: { quantity: num }, - }); - }, 500); - }} - /> + debounceRef.current = setTimeout(() => { + updateCartItem({ + cart_item_id: id.toString(), + body: { quantity: num }, + }); + }, 500); + }} + /> + + {measurementDisplay} + +
)}