order create
This commit is contained in:
BIN
public/logos/product.png
Normal file
BIN
public/logos/product.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
BIN
src/assets/product.png
Normal file
BIN
src/assets/product.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
@@ -5,25 +5,71 @@ import { AxiosResponse } from 'axios';
|
||||
interface CartItem {
|
||||
id: string;
|
||||
cart_item: {
|
||||
id: string;
|
||||
product_name: string;
|
||||
id: number;
|
||||
product: {
|
||||
id: number;
|
||||
images: {
|
||||
id: number;
|
||||
image: string;
|
||||
}[];
|
||||
liked: boolean;
|
||||
meansurement: null | string;
|
||||
inventory_id: null | string;
|
||||
product_id: string;
|
||||
product_image: 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 = {
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -95,10 +95,19 @@ const OrderPage = () => {
|
||||
|
||||
const { mutate, isPending } = useMutation({
|
||||
mutationFn: (body: OrderCreateBody) => cart_api.createOrder(body),
|
||||
onSuccess: () => {
|
||||
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,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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} />
|
||||
))}
|
||||
|
||||
@@ -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;
|
||||
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;
|
||||
};
|
||||
min_quantity: number;
|
||||
is_active: boolean;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface ProductDetail {
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
{product.prices.length > 0 && (
|
||||
<span className="text-lg sm:text-xl font-bold text-green-600">
|
||||
{formatPrice(product.price, true)}
|
||||
{formatPrice(
|
||||
Math.max(...product.prices.map((p) => Number(p.price))),
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{/* {product. && (
|
||||
<div className="text-xs sm:text-sm text-slate-400 line-through">
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user