complated

This commit is contained in:
Samandar Turgunboyev
2026-02-06 19:59:47 +05:00
parent 2bdd6f17a2
commit 5e1430adf6
9 changed files with 664 additions and 333 deletions

View File

@@ -2,6 +2,7 @@
import { cart_api, OrderCreateBody } from '@/features/cart/lib/api';
import { orderForm } from '@/features/cart/lib/form';
import { BASE_URL } from '@/shared/config/api/URLs';
import formatDate from '@/shared/lib/formatDate';
import formatPrice from '@/shared/lib/formatPrice';
import { cn } from '@/shared/lib/utils';
@@ -33,7 +34,7 @@ import {
YMaps,
ZoomControl,
} from '@pbe/react-yandex-maps';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import {
Calendar as CalIcon,
CheckCircle2,
@@ -41,15 +42,18 @@ import {
Loader2,
LocateFixed,
MapPin,
Package,
ShoppingBag,
User,
} from 'lucide-react';
import { useTranslations } from 'next-intl';
import Image from 'next/image';
import { useSearchParams } from 'next/navigation';
import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { toast } from 'sonner';
import z from 'zod';
import useOrderStore from '../lib/order';
import { order_api } from '../lib/api';
const deliveryTimeSlots = [
{ id: 1, label: '09:00 - 11:00', start: '09:00', end: '11:00' },
@@ -69,46 +73,43 @@ interface CoordsData {
const RefreshOrder = () => {
const [deliveryDate, setDeliveryDate] = useState<Date>();
const [selectedTimeSlot, setSelectedTimeSlot] = useState<string>('');
const { order: initialValues } = useOrderStore();
const t = useTranslations();
const queryClient = useQueryClient();
const searchParams = useSearchParams();
const id = searchParams.get('id');
const initialCartItems = initialValues?.cart_item.map((item) => ({
id: item.id,
product_id: item.product.id,
product_name: item.product.name,
product_price: item.product.prices[0].price,
product_image: item.product.images[0].image || '/placeholder.svg',
quantity: item.quantity,
}));
const { data, isLoading } = useQuery({
queryKey: ['order_list'],
queryFn: () => order_api.list(),
select: (res) => res.data,
});
const cartItems = initialCartItems;
const initialValues = data?.find((e) => e.id === Number(id));
const form = useForm<z.infer<typeof orderForm>>({
resolver: zodResolver(orderForm),
defaultValues: {
comment: '',
comment: initialValues?.comment || '',
lat: '41.311081',
long: '69.240562',
},
});
// Update form when initialValues loads
useEffect(() => {
if (initialValues?.comment) {
form.setValue('comment', initialValues.comment);
}
}, [initialValues, form]);
const [orderSuccess, setOrderSuccess] = useState(false);
const subtotal = cartItems
? cartItems.reduce(
(sum, item) => sum + Number(item.product_price) * item.quantity,
0,
)
: 0;
const total = subtotal;
const { mutate, isPending } = useMutation({
mutationFn: (body: OrderCreateBody) => cart_api.createOrder(body),
onSuccess: () => {
setOrderSuccess(true);
queryClient.refetchQueries({ queryKey: ['cart_items'] });
queryClient.refetchQueries({ queryKey: ['order_list'] });
},
onError: () => {
toast.error(t('Xatolik yuz berdi'), {
@@ -164,7 +165,7 @@ const RefreshOrder = () => {
const handleShowMyLocation = () => {
if (!navigator.geolocation) {
alert('Sizning brauzeringiz geolokatsiyani qollab-quvvatlamaydi');
alert("Sizning brauzeringiz geolokatsiyani qo'llab-quvvatlamaydi");
return;
}
navigator.geolocation.getCurrentPosition(
@@ -217,15 +218,15 @@ const RefreshOrder = () => {
return;
}
if (initialValues === null) {
toast.error(t('Savatcha bosh'), {
if (!initialValues) {
toast.error(t('Buyurtma topilmadi'), {
richColors: true,
position: 'top-center',
});
return;
}
const order_products = initialValues.cart_item
const order_products = initialValues.items
.filter(
(item) =>
item.product.prices &&
@@ -263,6 +264,41 @@ const RefreshOrder = () => {
});
};
// Calculate total price
const totalPrice =
initialValues?.items.reduce(
(sum, item) => sum + Number(item.price) * item.quantity,
0,
) || 0;
const totalItems =
initialValues?.items.reduce((sum, item) => sum + item.quantity, 0) || 0;
if (isLoading) {
return (
<div className="flex items-center justify-center h-screen">
<Loader2 className="w-12 h-12 animate-spin text-blue-600" />
</div>
);
}
if (!initialValues) {
return (
<div className="flex flex-col items-center justify-center h-screen">
<Package className="w-20 h-20 text-gray-400 mb-4" />
<h2 className="text-2xl font-bold text-gray-800 mb-2">
{t('Buyurtma topilmadi')}
</h2>
<p className="text-gray-500 mb-6">
{t("Ushbu buyurtma mavjud emas yoki o'chirilgan")}
</p>
<Button onClick={() => (window.location.href = '/profile/history')}>
{t('Buyurtmalar tarixiga qaytish')}
</Button>
</div>
);
}
if (orderSuccess) {
return (
<div className="flex justify-center items-center h-screen">
@@ -274,7 +310,7 @@ const RefreshOrder = () => {
{t('Buyurtma qabul qilindi!')}
</h2>
<p className="text-gray-500 mb-6">
{t('Buyurtmangiz muvaffaqiyatli qabul qilindi')}
{t('Buyurtmangiz muvaffaqiyatli qayta qabul qilindi')}
</p>
<button
onClick={() => (window.location.href = '/')}
@@ -293,9 +329,12 @@ const RefreshOrder = () => {
{/* Header */}
<div className="mb-6">
<h1 className="text-3xl font-bold text-gray-800 mb-2">
{t('Buyurtmani rasmiylashtirish')}
{t('Buyurtmani qayta rasmiylashtirish')}
</h1>
<p className="text-gray-600">{t("Ma'lumotlaringizni to'ldiring")}</p>
<p className="text-gray-600">
{t('Buyurtma')} #{initialValues.id}{' '}
{t("uchun ma'lumotlarni yangilang")}
</p>
</div>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
@@ -409,7 +448,7 @@ const RefreshOrder = () => {
</div>
</div>
{/* Yetkazib berish vaqti - Yangilangan versiya */}
{/* Yetkazib berish vaqti */}
<div className="bg-white rounded-lg shadow-md p-6">
<div className="flex items-center gap-2 mb-4">
<Clock className="w-5 h-5 text-blue-600" />
@@ -510,44 +549,70 @@ const RefreshOrder = () => {
{/* Right Column - Order Summary */}
<div className="lg:col-span-1">
<div className="bg-white rounded-lg shadow-md p-6 sticky top-4">
<h3 className="text-xl font-bold mb-4">{t('Mahsulotlar')}</h3>
<div className="flex items-center gap-2 mb-4">
<ShoppingBag className="w-5 h-5 text-blue-600" />
<h3 className="text-xl font-bold">
{t('Buyurtma tafsilotlari')}
</h3>
</div>
{/* Cart Items */}
<div className="space-y-3 mb-4 max-h-60 overflow-y-auto">
{cartItems?.map((item) => (
<div key={item.id} className="flex gap-3 pb-3 border-b">
<Image
width={500}
height={500}
src={item.product_image}
alt={item.product_name}
unoptimized
className="w-16 h-16 object-contain bg-gray-100 rounded"
/>
<div className="flex-1">
<h4 className="font-medium text-sm">
{item.product_name}
</h4>
<p className="text-sm text-gray-500">
{item.quantity} x{' '}
{formatPrice(item.product_price, true)}
</p>
<p className="font-semibold text-sm">
{formatPrice(
Number(item.product_price) * item.quantity,
true,
)}
</p>
<div className="space-y-3 mb-4 max-h-96 overflow-y-auto">
{initialValues.items.map((item) => {
const productImage = item.product.images?.[0]?.images
? item.product.images[0].images.includes(BASE_URL)
? item.product.images[0].images
: BASE_URL + item.product.images[0].images
: '/placeholder.svg';
return (
<div
key={item.id}
className="flex gap-3 p-3 bg-gray-50 rounded-lg border border-gray-200"
>
<div className="w-16 h-16 flex-shrink-0 bg-white rounded-lg overflow-hidden border">
<Image
src={productImage}
alt={item.product.name}
width={64}
height={64}
className="w-full h-full object-contain"
unoptimized
/>
</div>
<div className="flex-1 min-w-0">
<h4 className="font-semibold text-sm text-gray-900 truncate">
{item.product.name}
</h4>
<p className="text-xs text-gray-500 mt-1">
{item.quantity} ×{' '}
{formatPrice(Number(item.price), true)}
</p>
<p className="text-sm font-bold text-blue-600 mt-1">
{formatPrice(
Number(item.price) * item.quantity,
true,
)}
</p>
</div>
</div>
</div>
))}
);
})}
</div>
{/* Pricing */}
<div className="space-y-2 mb-4 pt-4 border-t">
<div className="flex justify-between text-gray-600">
<span>{t('Mahsulotlar')}:</span>
<span>{subtotal && formatPrice(subtotal, true)}</span>
<span>{t('Mahsulotlar soni')}:</span>
<span className="font-semibold">
{totalItems} {t('dona')}
</span>
</div>
<div className="flex justify-between text-gray-600">
<span>{t('Mahsulotlar narxi')}:</span>
<span className="font-semibold">
{formatPrice(totalPrice, true)}
</span>
</div>
</div>
@@ -556,8 +621,8 @@ const RefreshOrder = () => {
<span className="text-lg font-semibold">
{t('Jami')}:
</span>
<span className="text-2xl font-bold text-blue-600">
{total && formatPrice(total, true)}
<span className="text-2xl font-bold text-green-600">
{formatPrice(totalPrice, true)}
</span>
</div>
</div>
@@ -570,6 +635,7 @@ const RefreshOrder = () => {
{isPending ? (
<span className="flex items-center justify-center gap-2">
<Loader2 className="animate-spin" />
{t('Yuklanmoqda...')}
</span>
) : (
t('Buyurtmani tasdiqlash')