refresh order update
This commit is contained in:
@@ -26,6 +26,7 @@ import {
|
||||
SelectValue,
|
||||
} from '@/shared/ui/select';
|
||||
import { Textarea } from '@/shared/ui/textarea';
|
||||
import { userStore } from '@/widgets/welcome/lib/hook';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import {
|
||||
Map,
|
||||
@@ -42,18 +43,21 @@ import {
|
||||
Loader2,
|
||||
LocateFixed,
|
||||
MapPin,
|
||||
Minus,
|
||||
Package,
|
||||
Plus,
|
||||
ShoppingBag,
|
||||
Trash2,
|
||||
User,
|
||||
} from 'lucide-react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import Image from 'next/image';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { toast } from 'sonner';
|
||||
import z from 'zod';
|
||||
import { order_api } from '../lib/api';
|
||||
import OrderItem, { order_api } from '../lib/api';
|
||||
|
||||
const deliveryTimeSlots = [
|
||||
{ id: 1, label: '10:00 - 12:00', start: '10:00', end: '12:00' },
|
||||
@@ -61,6 +65,7 @@ const deliveryTimeSlots = [
|
||||
{ id: 3, label: '14:00 - 16:00', start: '14:00', end: '16:00' },
|
||||
{ id: 4, label: '16:00 - 18:00', start: '16:00', end: '18:00' },
|
||||
];
|
||||
|
||||
interface CoordsData {
|
||||
lat: number;
|
||||
lon: number;
|
||||
@@ -70,9 +75,16 @@ interface CoordsData {
|
||||
const RefreshOrder = () => {
|
||||
const [deliveryDate, setDeliveryDate] = useState<Date>();
|
||||
const [selectedTimeSlot, setSelectedTimeSlot] = useState<string>('');
|
||||
const [orderItems, setOrderItems] = useState<OrderItem[]>([]);
|
||||
const { user } = userStore();
|
||||
const [quantityInputs, setQuantityInputs] = useState<Record<number, string>>(
|
||||
{},
|
||||
);
|
||||
|
||||
const t = useTranslations();
|
||||
const queryClient = useQueryClient();
|
||||
const searchParams = useSearchParams();
|
||||
const router = useRouter();
|
||||
const id = searchParams.get('id');
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
@@ -83,6 +95,18 @@ const RefreshOrder = () => {
|
||||
|
||||
const initialValues = data?.find((e) => e.id === Number(id));
|
||||
|
||||
useEffect(() => {
|
||||
if (initialValues?.items) {
|
||||
const items = initialValues.items.map((item: OrderItem) => ({ ...item }));
|
||||
setOrderItems(items);
|
||||
const inputs: Record<number, string> = {};
|
||||
items.forEach((item: OrderItem) => {
|
||||
inputs[item.id] = String(item.quantity);
|
||||
});
|
||||
setQuantityInputs(inputs);
|
||||
}
|
||||
}, [initialValues]);
|
||||
|
||||
const form = useForm<z.infer<typeof orderForm>>({
|
||||
resolver: zodResolver(orderForm),
|
||||
defaultValues: {
|
||||
@@ -92,7 +116,6 @@ const RefreshOrder = () => {
|
||||
},
|
||||
});
|
||||
|
||||
// Update form when initialValues loads
|
||||
useEffect(() => {
|
||||
if (initialValues?.comment) {
|
||||
form.setValue('comment', initialValues.comment);
|
||||
@@ -125,6 +148,66 @@ const RefreshOrder = () => {
|
||||
[number, number][][] | null
|
||||
>(null);
|
||||
|
||||
// Input o'zgarishi — foydalanuvchi "0.5", "1.75" yoza oladi
|
||||
const handleQuantityInput = (itemId: number, value: string) => {
|
||||
// Faqat raqam va nuqtaga ruxsat
|
||||
if (!/^(\d+\.?\d*)?$/.test(value)) return;
|
||||
setQuantityInputs((prev) => ({ ...prev, [itemId]: value }));
|
||||
|
||||
const parsed = parseFloat(value);
|
||||
if (!isNaN(parsed) && parsed > 0) {
|
||||
setOrderItems((prev) =>
|
||||
prev.map((item) =>
|
||||
item.id === itemId ? { ...item, quantity: parsed } : item,
|
||||
),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Blur — bo'sh yoki 0 qiymatni 1 ga qaytarish
|
||||
const handleQuantityBlur = (itemId: number) => {
|
||||
const val = parseFloat(quantityInputs[itemId]);
|
||||
if (!val || val <= 0) {
|
||||
setQuantityInputs((prev) => ({ ...prev, [itemId]: '1' }));
|
||||
setOrderItems((prev) =>
|
||||
prev.map((item) =>
|
||||
item.id === itemId ? { ...item, quantity: 1 } : item,
|
||||
),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// ± tugmalar — 0.5 qadamda o'zgartirish
|
||||
const updateQuantity = (itemId: number, delta: number) => {
|
||||
setOrderItems((prev) =>
|
||||
prev.map((item) => {
|
||||
if (item.id !== itemId) return item;
|
||||
const newQty = Math.max(
|
||||
0.5,
|
||||
Math.round((item.quantity + delta) * 100) / 100,
|
||||
);
|
||||
setQuantityInputs((inputs) => ({
|
||||
...inputs,
|
||||
[itemId]: String(newQty),
|
||||
}));
|
||||
return { ...item, quantity: newQty };
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
// Item o'chirish — agar 0 ta qolsa profile sahifaga yo'naltiradi
|
||||
const deleteItem = (itemId: number) => {
|
||||
const updated = orderItems.filter((item) => item.id !== itemId);
|
||||
setOrderItems(updated);
|
||||
if (updated.length === 0) {
|
||||
toast.info(t('Buyurtmada mahsulot qolmadi'), {
|
||||
richColors: true,
|
||||
position: 'top-center',
|
||||
});
|
||||
router.push('/profile');
|
||||
}
|
||||
};
|
||||
|
||||
const getCoords = async (name: string): Promise<CoordsData | null> => {
|
||||
const res = await fetch(
|
||||
`https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(
|
||||
@@ -186,11 +269,7 @@ const RefreshOrder = () => {
|
||||
const timeout = setTimeout(async () => {
|
||||
const result = await getCoords(cityValue);
|
||||
if (!result) return;
|
||||
setCoords({
|
||||
latitude: result.lat,
|
||||
longitude: result.lon,
|
||||
zoom: 12,
|
||||
});
|
||||
setCoords({ latitude: result.lat, longitude: result.lon, zoom: 12 });
|
||||
setPolygonCoords(result.polygon);
|
||||
form.setValue('lat', result.lat.toString(), { shouldDirty: true });
|
||||
form.setValue('long', result.lon.toString(), { shouldDirty: true });
|
||||
@@ -223,7 +302,7 @@ const RefreshOrder = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const order_products = initialValues.items
|
||||
const order_products = orderItems
|
||||
.filter(
|
||||
(item) =>
|
||||
item.product.prices &&
|
||||
@@ -237,42 +316,44 @@ const RefreshOrder = () => {
|
||||
on_balance: 'Y',
|
||||
order_quant: item.quantity,
|
||||
price_type_code: item.product.prices![0].price_type.code,
|
||||
product_price: item.product.prices![0].price,
|
||||
warehouse_code: 'wh1',
|
||||
product_price: item.price,
|
||||
warehouse_code: process.env.NEXT_PUBLIC_WARHOUSES_CODE!,
|
||||
}));
|
||||
if (user) {
|
||||
const dealTime = formatDate.format(deliveryDate, 'DD.MM.YYYY');
|
||||
|
||||
mutate({
|
||||
order: [
|
||||
{
|
||||
filial_code: 'dodge',
|
||||
delivery_date: formatDate.format(deliveryDate, 'DD.MM.YYYY'),
|
||||
room_code: '100',
|
||||
deal_time:
|
||||
formatDate.format(deliveryDate, 'DD.MM.YYYY') +
|
||||
' ' +
|
||||
selectedTimeSlot,
|
||||
robot_code: 'r2',
|
||||
status: 'B#N',
|
||||
sales_manager_code: '1',
|
||||
person_code: '12345678',
|
||||
currency_code: '860',
|
||||
owner_person_code: '1234567',
|
||||
note: value.comment,
|
||||
order_products: order_products,
|
||||
},
|
||||
],
|
||||
});
|
||||
mutate({
|
||||
order: [
|
||||
{
|
||||
filial_code: process.env.NEXT_PUBLIC_FILIAL_CODE!,
|
||||
delivery_date: `${dealTime}`,
|
||||
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: 'D',
|
||||
sales_manager_code: process.env.NEXT_PUBLIC_SALES_MANAGER_CODE!,
|
||||
person_code: user?.username,
|
||||
currency_code: '860',
|
||||
owner_person_code: user?.username,
|
||||
note: value.comment,
|
||||
order_products: order_products,
|
||||
},
|
||||
],
|
||||
});
|
||||
} else {
|
||||
toast.error(t('Xatolik yuz berdi'), {
|
||||
richColors: true,
|
||||
position: 'top-center',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Calculate total price
|
||||
const totalPrice =
|
||||
initialValues?.items.reduce(
|
||||
(sum, item) => sum + Number(item.price) * item.quantity,
|
||||
0,
|
||||
) || 0;
|
||||
const totalPrice = orderItems.reduce(
|
||||
(sum, item) => sum + Number(item.price) * item.quantity,
|
||||
0,
|
||||
);
|
||||
|
||||
const totalItems =
|
||||
initialValues?.items.reduce((sum, item) => sum + item.quantity, 0) || 0;
|
||||
const totalItems = orderItems.reduce((sum, item) => sum + item.quantity, 0);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
@@ -558,7 +639,7 @@ const RefreshOrder = () => {
|
||||
|
||||
{/* Cart Items */}
|
||||
<div className="space-y-3 mb-4 max-h-96 overflow-y-auto">
|
||||
{initialValues.items.map((item) => {
|
||||
{orderItems.map((item) => {
|
||||
const productImage = item.product.images?.[0]?.images
|
||||
? item.product.images[0].images.includes(BASE_URL)
|
||||
? item.product.images[0].images
|
||||
@@ -581,19 +662,54 @@ const RefreshOrder = () => {
|
||||
/>
|
||||
</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>
|
||||
<div className="flex items-start justify-between gap-1">
|
||||
<h4 className="font-semibold text-sm text-gray-900 truncate">
|
||||
{item.product.name}
|
||||
</h4>
|
||||
{/* O'chirish tugmasi */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => deleteItem(item.id)}
|
||||
className="flex-shrink-0 w-6 h-6 flex items-center justify-center rounded-full text-red-400 hover:text-red-600 hover:bg-red-50 transition"
|
||||
title={t("O'chirish")}
|
||||
>
|
||||
<Trash2 className="w-8 h-8" />
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-sm font-bold text-blue-600 mt-1">
|
||||
{formatPrice(
|
||||
Number(item.price) * item.quantity,
|
||||
true,
|
||||
)}
|
||||
</p>
|
||||
|
||||
{/* Quantity — input + ± tugmalar */}
|
||||
<div className="flex items-center gap-1 mt-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => updateQuantity(item.id, -1)}
|
||||
className="w-7 h-7 flex items-center justify-center rounded-full border border-gray-300 bg-white hover:bg-gray-100 transition"
|
||||
>
|
||||
<Minus className="w-3 h-3" />
|
||||
</button>
|
||||
<input
|
||||
type="text"
|
||||
inputMode="decimal"
|
||||
value={quantityInputs[item.id] ?? item.quantity}
|
||||
onChange={(e) =>
|
||||
handleQuantityInput(item.id, e.target.value)
|
||||
}
|
||||
onBlur={() => handleQuantityBlur(item.id)}
|
||||
className="w-14 h-7 text-center text-sm font-semibold border border-gray-300 rounded-md focus:outline-none focus:border-blue-500"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => updateQuantity(item.id, 1)}
|
||||
className="w-7 h-7 flex items-center justify-center rounded-full border border-gray-300 bg-white hover:bg-gray-100 transition"
|
||||
>
|
||||
<Plus className="w-3 h-3" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user