order create

This commit is contained in:
Samandar Turgunboyev
2026-01-23 19:29:04 +05:00
parent a5e100ed90
commit c2a2c2b09b
11 changed files with 187 additions and 60 deletions

BIN
public/logos/product.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

BIN
src/assets/product.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

View File

@@ -5,25 +5,71 @@ import { AxiosResponse } from 'axios';
interface CartItem {
id: string;
cart_item: {
id: string;
product_name: string;
product_id: string;
product_image: string;
id: number;
product: {
id: number;
images: {
id: number;
image: string;
}[];
liked: boolean;
meansurement: null | string;
inventory_id: null | string;
product_id: string;
code: string;
name: string;
short_name: string;
weight_netto: null | string;
weight_brutto: null | string;
litr: null | string;
box_type_code: null | string;
box_quant: null | string;
groups: number[];
state: 'A' | 'P';
barcodes: string | null;
article_code: null | string;
marketing_group_code: null | string;
inventory_kinds: { id: number; name: string }[];
sector_codes: { id: number; code: string }[];
prices: {
id: number;
price: string;
price_type: {
id: number;
name: string;
code: string;
};
}[];
};
quantity: number;
product_price: number;
}[];
}
export interface OrderCreateBody {
items: {
product_id: number;
quantity: number;
order: {
filial_code: string;
delivery_date: string;
room_code: string;
robot_code: string;
deal_time: string;
status: string;
sales_manager_code: string;
person_code: string;
currency_code: string;
owner_person_code: string;
note: string;
order_products: [
{
inventory_kind: string;
product_code: string;
on_balance: string;
order_quant: number;
price_type_code: string;
product_price: string;
warehouse_code: string;
},
];
}[];
comment: string;
long: number;
lat: number;
date: string;
time: string;
}
export const cart_api = {

View File

@@ -21,6 +21,7 @@ import { useTranslations } from 'next-intl';
import Image from 'next/image';
import { useEffect, useRef, useState } from 'react';
import { toast } from 'sonner';
import ProductBanner from '@/assets/product.png';
const CartPage = () => {
const { cart_id } = useCartId();
@@ -102,7 +103,10 @@ const CartPage = () => {
);
const subtotal = cartItems.reduce(
(sum, item) => sum + item.product_price * Number(item.quantity),
(sum, item) =>
sum +
Math.max(...item.product.prices.map((e) => Number(e.price))) *
Number(item.quantity),
0,
);
@@ -145,7 +149,9 @@ const CartPage = () => {
<Button
variant="destructive"
size="icon"
onClick={() => deleteCartItem({ cart_item_id: item.id })}
onClick={() =>
deleteCartItem({ cart_item_id: String(item.id) })
}
className="absolute right-2 w-7 h-7 top-2 cursor-pointer"
>
<Trash className="size-4" />
@@ -153,8 +159,12 @@ const CartPage = () => {
<div className="w-24 h-40 bg-gray-100 rounded-lg flex-shrink-0 overflow-hidden">
<Image
src={BASE_URL + item.product_image}
alt={item.product_name}
src={
item.product.images.length > 0
? BASE_URL + item.product.images[0].image
: ProductBanner
}
alt={item.product.name}
width={500}
height={500}
className="object-cover"
@@ -164,11 +174,16 @@ const CartPage = () => {
<div className="flex-1">
<h3 className="font-semibold text-lg mb-1">
{item.product_name}
{item.product.name}
</h3>
<div className="flex items-center gap-2 mb-3 max-lg:flex-col max-lg:items-start max-lg:gap-1">
<span className="text-blue-600 font-bold text-xl">
{formatPrice(item.product_price, true)}
{formatPrice(
Math.max(
...item.product.prices.map((e) => Number(e.price)),
),
true,
)}
</span>
</div>
@@ -176,7 +191,7 @@ const CartPage = () => {
<button
onClick={() =>
handleQuantityChange(
item.id,
String(item.id),
Number(quantities[item.id]) - 1,
)
}
@@ -197,7 +212,7 @@ const CartPage = () => {
// Debounce bilan update
const valNum = Number(val);
if (!isNaN(valNum))
handleQuantityChange(item.id, valNum);
handleQuantityChange(String(item.id), valNum);
}}
type="text"
className="w-16 text-center"
@@ -206,7 +221,7 @@ const CartPage = () => {
<button
onClick={() =>
handleQuantityChange(
item.id,
String(item.id),
Number(quantities[item.id]) + 1,
)
}

View File

@@ -95,10 +95,19 @@ const OrderPage = () => {
const { mutate, isPending } = useMutation({
mutationFn: (body: OrderCreateBody) => cart_api.createOrder(body),
onSuccess: () => {
setOrderSuccess(true);
setCart(cart_id);
queryClinet.refetchQueries({ queryKey: ['cart_items'] });
onSuccess: (res) => {
const message = JSON.parse(res.data.response);
if (message.successes.length > 0) {
setOrderSuccess(true);
setCart(cart_id);
queryClinet.refetchQueries({ queryKey: ['cart_items'] });
} else {
toast.error('Xatolik yuz berdi', {
richColors: true,
position: 'top-center',
});
}
},
onError: (error: AxiosError) => {
(
@@ -123,7 +132,10 @@ const OrderPage = () => {
const [selectedTimeSlot, setSelectedTimeSlot] = useState<string>('');
const subtotal = cartItems?.reduce(
(sum, item) => sum + item.product_price * item.quantity,
(sum, item) =>
sum +
Math.max(...item.product.prices.map((e) => Number(e.price))) *
item.quantity,
0,
);
@@ -247,18 +259,45 @@ const OrderPage = () => {
return;
}
const items = cartItems.map((item) => ({
product_id: Number(item.product_id),
quantity: item.quantity,
}));
const order_products: {
inventory_kind: sting;
product_code: sting;
on_balance: sting;
order_quant: number;
price_type_code: sting;
product_price: sting;
warehouse_code: sting;
}[] = [];
cartItems.forEach((e) => {
order_products.push({
inventory_kind: 'G',
product_code: e.product.code,
on_balance: 'Y',
order_quant: e.quantity,
price_type_code: e.product.prices[0].price_type.code,
product_price: e.product.prices[0].price,
warehouse_code: 'wh1',
});
});
mutate({
comment: value.comment,
items: items,
long: Number(value.long),
lat: Number(value.lat),
date: formatDate.format(deliveryDate, 'YYYY-MM-DD'),
time: selectedTimeSlot,
order: [
{
filial_code: 'dodge',
delivery_date: formatDate.format(deliveryDate, 'DD.MM.YYYY'),
room_code: '100',
deal_time: formatDate.format(deliveryDate, 'DD.MM.YYYY'),
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,
},
],
});
}

View File

@@ -91,7 +91,7 @@ const Product = () => {
{product &&
!isLoading &&
product.results
.filter((product) => product.is_active)
.filter((product) => product.state === 'A')
.map((item) => (
<ProductCard key={item.id} product={item} error={isError} />
))}

View File

@@ -10,17 +10,34 @@ export interface ProductList {
export interface ProductListResult {
id: number;
name: string;
image: string;
price: number;
description: string;
images: { id: number; image: string }[];
liked: boolean;
unity: {
id: string;
name: string;
};
min_quantity: number;
is_active: boolean;
meansurement: null | string;
inventory_id: null | string;
product_id: string;
code: string;
name: string;
short_name: string;
weight_netto: null | string;
weight_brutto: null | string;
litr: null | string;
box_type_code: null | string;
box_quant: null | string;
groups: number[];
state: 'A' | 'P';
barcodes: string;
article_code: null | string;
marketing_group_code: null | string;
inventory_kinds: { id: number; name: string }[];
sector_codes: { id: number; code: string }[];
prices: {
id: number;
price: string;
price_type: {
id: number;
name: string;
};
}[];
}
export interface ProductDetail {

View File

@@ -105,7 +105,7 @@ export function CategoryCarousel({ category }: { category: CategoryResult }) {
{product &&
!isLoading &&
product.results
.filter((product) => product.is_active)
.filter((product) => product.state === 'A')
.map((product) => (
<CarouselItem
key={product.id}

View File

@@ -19,6 +19,7 @@ import { useTranslations } from 'next-intl';
import Image from 'next/image';
import { MouseEvent, useEffect, useRef, useState } from 'react';
import { toast } from 'sonner';
import LogosProduct from '@/assets/product.png';
export function ProductCard({
product,
@@ -89,7 +90,7 @@ export function ProductCard({
useEffect(() => {
const item = cartItems?.data?.cart_item?.find(
(item) => Number(item.product_id) === product.id,
(item) => Number(item.product.id) === product.id,
);
setQuantity(item ? item.quantity : 0);
@@ -120,7 +121,7 @@ export function ProductCard({
if (newQty > 1) {
const cartItemId = cartItems?.data?.cart_item.find(
(item) => Number(item.product_id) === product.id,
(item) => Number(item.product.id) === product.id,
)?.id;
if (cartItemId) {
updateCartItem({
@@ -207,12 +208,14 @@ export function ProductCard({
<Image
fill
src={
product?.image?.includes(BASE_URL)
? product.image
: BASE_URL + product.image
product.images.length > 0
? product?.images[0].image?.includes(BASE_URL)
? product.images[0].image
: BASE_URL + product.images[0].image
: LogosProduct
}
alt={product.name}
className="object-contain"
className="object-cover"
/>
</div>
</div>
@@ -230,9 +233,13 @@ export function ProductCard({
</h3>
<div>
<span className="text-lg sm:text-xl font-bold text-green-600">
{formatPrice(product.price, true)}
</span>
{product.prices.length > 0 && (
<span className="text-lg sm:text-xl font-bold text-green-600">
{formatPrice(
Math.max(...product.prices.map((p) => Number(p.price))),
)}
</span>
)}
{/* {product. && (
<div className="text-xs sm:text-sm text-slate-400 line-through">

View File

@@ -51,7 +51,7 @@ const Footer = () => {
</h3>
<ul className="space-y-2 text-sm">
{category?.slice(0, 6)?.map((link) => (
<Fragment key={link.name}>
<Fragment key={link.id}>
<li
key={link.id}
className="text-white hover:text-gray-300 transition-colors cursor-pointer"

View File

@@ -19,6 +19,7 @@ import Image from 'next/image';
import { useState } from 'react';
import 'swiper/css';
import { banner_api } from '../lib/api';
import CategoryImage from '@/assets/water-bottle.png';
const Welcome = () => {
const [api, setApi] = useState<CarouselApi>();
@@ -129,7 +130,9 @@ const Welcome = () => {
<Link href={`/category/${banner.id}`}>
<div className="flex flex-col gap-1 items-center justify-start bg-white p-3 rounded-lg shadow-md cursor-pointer space-x-3">
<Image
src={BASE_URL + banner.image}
src={
banner.image ? BASE_URL + banner.image : CategoryImage
}
alt={banner.name}
width={500}
height={500}