diff --git a/src/widgets/categories/ui/product-card.tsx b/src/widgets/categories/ui/product-card.tsx index aa7cef2..a6d2b38 100644 --- a/src/widgets/categories/ui/product-card.tsx +++ b/src/widgets/categories/ui/product-card.tsx @@ -42,31 +42,47 @@ export function ProductCard({ const debounceRef = useRef(null); const imageRef = useRef(null); - // O'lchov birligini formatlash uchun yordamchi funksiya + /** βœ… Measurement logic */ + const measurementName = product.meansurement?.name ?? null; + const isGram = measurementName === 'gr'; + + // default qoβ€˜shish miqdori + const defaultQty = isGram ? 100 : 1; + + // +/- qadam + const step = isGram ? 100 : 1; + + const measurementDisplay = measurementName || 'ΡˆΡ‚.'; + const getQuantityMessage = (qty: number, measurement: string | null) => { if (!measurement) return `${qty} dona`; return `${qty} ${measurement}`; }; + /** πŸ›’ Add to cart */ const { mutate: addToCart } = useMutation({ mutationFn: (body: { product: string; quantity: number; cart: string }) => cart_api.cart_item(body), + 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")}`, + `${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; + const detail = (err.response?.data as { detail?: string })?.detail; + toast.error(detail || err.message, { richColors: true, position: 'top-center', @@ -74,6 +90,7 @@ export function ProductCard({ }, }); + /** πŸ”„ Update */ const { mutate: updateCartItem } = useMutation({ mutationFn: ({ body, @@ -82,33 +99,40 @@ export function ProductCard({ body: { quantity: number }; cart_item_id: string; }) => cart_api.update_cart_item({ body, cart_item_id }), + onSuccess: () => { queryClient.refetchQueries({ queryKey: ['cart_items'] }); setAnimated(true); }, + onError: (err: AxiosError) => { - toast.error(err.message, { richColors: true, position: 'top-center' }); + toast.error(err.message, { richColors: true }); }, }); + /** ❌ Delete */ const { mutate: deleteCartItem } = useMutation({ mutationFn: ({ cart_item_id }: { cart_item_id: string }) => cart_api.delete_cart_item(cart_item_id), + onSuccess: () => { queryClient.refetchQueries({ queryKey: ['cart_items'] }); setAnimated(true); }, + onError: (err: AxiosError) => { - toast.error(err.message, { richColors: true, position: 'top-center' }); + toast.error(err.message, { richColors: true }); }, }); + /** πŸ“¦ Cart items */ const { data: cartItems } = useQuery({ queryKey: ['cart_items', cart_id], queryFn: () => cart_api.get_cart_items(cart_id!), enabled: !!cart_id, }); + /** πŸ” Sync quantity */ useEffect(() => { const item = cartItems?.data?.cart_item?.find( (item) => Number(item.product.id) === product.id, @@ -117,15 +141,62 @@ export function ProductCard({ setQuantity(item ? item.quantity : 0); }, [cartItems, product.id]); + const getCartItemId = () => + cartItems?.data.cart_item.find( + (item) => Number(item.product.id) === product.id, + )?.id; + + /** βž– Decrease */ + const decrease = (e: MouseEvent) => { + e.stopPropagation(); + + if (!cartItems) return; + + const currentQty = quantity === '' ? 0 : quantity; + const newQty = currentQty - step; + + const id = getCartItemId(); + if (!id) return; + + if (newQty <= 0) { + setQuantity(0); + deleteCartItem({ cart_item_id: id.toString() }); + return; + } + + setQuantity(newQty); + + updateCartItem({ + cart_item_id: id.toString(), + body: { quantity: newQty }, + }); + }; + + /** βž• Increase */ + const increase = (e: MouseEvent) => { + e.stopPropagation(); + + const currentQty = quantity === '' ? 0 : quantity; + const newQty = currentQty + step; + + setQuantity(newQty); + + const id = getCartItemId(); + if (!id) return; + + updateCartItem({ + cart_item_id: id.toString(), + body: { quantity: newQty }, + }); + }; + + /** ❀️ Favourite */ const favouriteMutation = useMutation({ mutationFn: (productId: string) => product_api.favourite(productId), onSuccess: () => { queryClient.refetchQueries({ queryKey: ['product_list'] }); - queryClient.refetchQueries({ queryKey: ['list'] }); queryClient.refetchQueries({ queryKey: ['favourite_product'] }); - queryClient.refetchQueries({ queryKey: ['search'] }); - queryClient.refetchQueries({ queryKey: ['product_detail', product] }); }, onError: () => { @@ -136,55 +207,7 @@ export function ProductCard({ }, }); - const decrease = (e: MouseEvent) => { - e.stopPropagation(); - - if (!cartItems) return; - - const currentQty = quantity === '' ? 0 : quantity; - const newQty = currentQty - 1; - - const cartItemId = cartItems.data.cart_item.find( - (item) => Number(item.product.id) === product.id, - )?.id; - - if (!cartItemId) return; - - if (newQty <= 0) { - setQuantity(0); - deleteCartItem({ cart_item_id: cartItemId.toString() }); - return; - } - - setQuantity(newQty); - updateCartItem({ - body: { quantity: newQty }, - cart_item_id: cartItemId.toString(), - }); - }; - - const getCartItemId = () => - cartItems?.data.cart_item.find( - (item) => Number(item.product.id) === product.id, - )?.id; - - const increase = (e: MouseEvent) => { - e.stopPropagation(); - - const current = quantity === '' ? 0 : quantity; - - const newQty = current + 1; - setQuantity(newQty); - - const id = getCartItemId(); - if (id) { - updateCartItem({ - cart_item_id: id.toString(), - body: { quantity: newQty }, - }); - } - }; - + /** ❌ Error state */ if (error) { return ( @@ -196,9 +219,6 @@ export function ProductCard({ ); } - // O'lchov birligini ko'rsatish - const measurementDisplay = product.meansurement?.name || 'ΡˆΡ‚.'; - return ( <> @@ -233,7 +251,7 @@ export function ProductCard({ fill src={ product.images.length > 0 - ? product?.images[0].image?.includes(BASE_URL) + ? product.images[0].image?.includes(BASE_URL) ? product.images[0].image : BASE_URL + product.images[0].image : LogosProduct @@ -246,26 +264,19 @@ export function ProductCard({
- {/* Narx va o'lchov birligi */} -
- {product.prices.length > 0 && ( - <> -

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

- - )} -
+ {product.prices.length > 0 && ( +

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

+ )} -

- {product.name} -

+

{product.name}

@@ -276,39 +287,33 @@ export function ProductCard({ addToCart({ product: String(product.id), - quantity: 1, + quantity: defaultQty, // βœ… 100gr yoki 1 dona cart: cart_id!, }); }} - className="w-full bg-white hover:bg-slate-50 text-slate-700 border border-slate-300 rounded-lg h-10 font-medium" + className="w-full bg-white border border-slate-300 text-slate-700" > {t('Savatga')} ) : (
e.stopPropagation()} - className="flex items-center justify-between bg-white border border-slate-300 rounded-lg h-10 overflow-hidden" + className="flex items-center justify-between border border-slate-300 rounded-lg h-10" > - -
+
{ const v = e.target.value; if (!/^\d*$/.test(v)) return; - if (debounceRef.current) { + if (debounceRef.current) clearTimeout(debounceRef.current); - } if (v === '') { setQuantity(''); @@ -316,13 +321,12 @@ export function ProductCard({ } const num = Number(v); - setQuantity(num); const id = getCartItemId(); if (!id) return; - if (num === 0) { + if (num <= 0) { deleteCartItem({ cart_item_id: id.toString() }); return; } @@ -335,17 +339,12 @@ export function ProductCard({ }, 500); }} /> - + {measurementDisplay}
-
@@ -353,6 +352,7 @@ export function ProductCard({
+