order update
This commit is contained in:
BIN
public/shablon.jpg
Normal file
BIN
public/shablon.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
@@ -1,5 +1,5 @@
|
|||||||
export interface User {
|
export interface User {
|
||||||
id: string;
|
id: number;
|
||||||
first_name: string;
|
first_name: string;
|
||||||
last_name: string;
|
last_name: string;
|
||||||
username: string;
|
username: string;
|
||||||
@@ -15,28 +15,30 @@ export interface Product {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Item {
|
export interface Item {
|
||||||
id: string;
|
id: number;
|
||||||
quantity: number;
|
quantity: number;
|
||||||
price: number;
|
price: number;
|
||||||
product: Product;
|
product: {
|
||||||
|
id: number;
|
||||||
|
name_uz: string;
|
||||||
|
name_ru: string;
|
||||||
|
price: number;
|
||||||
|
code: string;
|
||||||
|
unity: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Order {
|
export interface Order {
|
||||||
id: string;
|
id: number;
|
||||||
order_number: number;
|
|
||||||
status: string;
|
status: string;
|
||||||
total_price: number;
|
total_price: number;
|
||||||
user: User;
|
|
||||||
payment_type: string;
|
|
||||||
delivery_type: string;
|
|
||||||
delivery_price: number;
|
delivery_price: number;
|
||||||
contact_number: string;
|
|
||||||
comment: string;
|
comment: string;
|
||||||
name: string;
|
user: User;
|
||||||
items: Item[];
|
items: Item[];
|
||||||
created_at: string;
|
created_at: string;
|
||||||
long: number | null;
|
long: number;
|
||||||
lat: number | null;
|
lat: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OrdersResponse {
|
export interface OrdersResponse {
|
||||||
|
|||||||
@@ -55,8 +55,7 @@ const OrderDelete = ({
|
|||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Buyurtmani o'chirish</DialogTitle>
|
<DialogTitle>Buyurtmani o'chirish</DialogTitle>
|
||||||
<DialogDescription className="text-md font-semibold">
|
<DialogDescription className="text-md font-semibold">
|
||||||
Siz rostan ham {orderDelete?.order_number} sonli buyurtmani
|
Siz rostan ham {orderDelete?.id} sonli buyurtmani o'chirmoqchimisiz?
|
||||||
o'chirmoqchimisiz?
|
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
|
|||||||
@@ -1,25 +1,17 @@
|
|||||||
import type { Order } from "@/features/order/lib/type";
|
import type { Order } from "@/features/order/lib/type";
|
||||||
import formatPhone from "@/shared/lib/formatPhone";
|
import formatDate from "@/shared/lib/formatDate";
|
||||||
|
import formatPrice from "@/shared/lib/formatPrice";
|
||||||
import { Map, Placemark, YMaps } from "@pbe/react-yandex-maps";
|
import { Map, Placemark, YMaps } from "@pbe/react-yandex-maps";
|
||||||
import {
|
import {
|
||||||
Calendar,
|
Calendar,
|
||||||
CreditCard,
|
|
||||||
MapIcon,
|
MapIcon,
|
||||||
MessageSquare,
|
MessageSquare,
|
||||||
Package,
|
Package,
|
||||||
Phone,
|
|
||||||
Truck,
|
|
||||||
User,
|
User,
|
||||||
X,
|
X,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { type Dispatch, type SetStateAction } from "react";
|
import { type Dispatch, type SetStateAction } from "react";
|
||||||
|
|
||||||
const deliveryTypeLabel: Record<string, string> = {
|
|
||||||
YANDEX_GO: "Yandex Go",
|
|
||||||
DELIVERY_COURIES: "Kuryer orqali yetkazish",
|
|
||||||
PICKUP: "O‘zi olib ketish",
|
|
||||||
};
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
detail: boolean;
|
detail: boolean;
|
||||||
setDetail: Dispatch<SetStateAction<boolean>>;
|
setDetail: Dispatch<SetStateAction<boolean>>;
|
||||||
@@ -29,20 +21,6 @@ interface Props {
|
|||||||
const OrderDetail = ({ detail, setDetail, order }: Props) => {
|
const OrderDetail = ({ detail, setDetail, order }: Props) => {
|
||||||
if (!detail || !order) return null;
|
if (!detail || !order) return null;
|
||||||
|
|
||||||
const formatDate = (dateString: string) => {
|
|
||||||
return new Date(dateString).toLocaleString("uz-UZ", {
|
|
||||||
year: "numeric",
|
|
||||||
month: "long",
|
|
||||||
day: "numeric",
|
|
||||||
hour: "2-digit",
|
|
||||||
minute: "2-digit",
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const formatPrice = (price: number) => {
|
|
||||||
return new Intl.NumberFormat("uz-UZ").format(price) + " so'm";
|
|
||||||
};
|
|
||||||
|
|
||||||
const getStatusColor = (status: string) => {
|
const getStatusColor = (status: string) => {
|
||||||
const colors: Record<string, string> = {
|
const colors: Record<string, string> = {
|
||||||
pending: "bg-yellow-100 text-yellow-800",
|
pending: "bg-yellow-100 text-yellow-800",
|
||||||
@@ -65,9 +43,6 @@ const OrderDetail = ({ detail, setDetail, order }: Props) => {
|
|||||||
<div className="fixed right-0 top-0 h-full w-full max-w-2xl bg-white shadow-xl z-50 overflow-y-auto">
|
<div className="fixed right-0 top-0 h-full w-full max-w-2xl bg-white shadow-xl z-50 overflow-y-auto">
|
||||||
<div className="sticky top-0 bg-white border-b px-6 py-4 flex items-center justify-between">
|
<div className="sticky top-0 bg-white border-b px-6 py-4 flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl font-bold">
|
|
||||||
Buyurtma #{order.order_number}
|
|
||||||
</h2>
|
|
||||||
<p className="text-sm text-gray-500">ID: {order.id}</p>
|
<p className="text-sm text-gray-500">ID: {order.id}</p>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
@@ -88,7 +63,7 @@ const OrderDetail = ({ detail, setDetail, order }: Props) => {
|
|||||||
</span>
|
</span>
|
||||||
<div className="flex items-center text-sm text-gray-500">
|
<div className="flex items-center text-sm text-gray-500">
|
||||||
<Calendar className="w-4 h-4 mr-1" />
|
<Calendar className="w-4 h-4 mr-1" />
|
||||||
{formatDate(order.created_at)}
|
{formatDate.format(order.created_at, "DD-MM-YYYY")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -99,12 +74,6 @@ const OrderDetail = ({ detail, setDetail, order }: Props) => {
|
|||||||
{formatPrice(order.total_price)}
|
{formatPrice(order.total_price)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<p className="text-gray-500">Yetkazish narxi</p>
|
|
||||||
<p className="text-xl font-semibold text-gray-700">
|
|
||||||
{formatPrice(order.delivery_price)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -116,36 +85,11 @@ const OrderDetail = ({ detail, setDetail, order }: Props) => {
|
|||||||
<div className="space-y-2 text-sm">
|
<div className="space-y-2 text-sm">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="text-gray-500">Ism:</span>
|
<span className="text-gray-500">Ism:</span>
|
||||||
<span className="font-medium">{order.name}</span>
|
<span className="font-medium">{order.user.username}</span>
|
||||||
</div>
|
|
||||||
<div className="flex justify-between items-center">
|
|
||||||
<span className="text-gray-500">Telefon:</span>
|
|
||||||
<a
|
|
||||||
href={`tel:${order.contact_number}`}
|
|
||||||
className="font-medium text-blue-600 hover:underline flex items-center"
|
|
||||||
>
|
|
||||||
<Phone className="w-4 h-4 mr-1" />
|
|
||||||
{formatPhone(order.contact_number)}
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="border rounded-lg p-4">
|
|
||||||
<h3 className="font-semibold text-lg mb-3 flex items-center">
|
|
||||||
<Truck className="w-5 h-5 mr-2" />
|
|
||||||
Yetkazish ma'lumotlari
|
|
||||||
</h3>
|
|
||||||
<div className="space-y-2 text-sm">
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<span className="text-gray-500">Turi:</span>
|
|
||||||
<span className="font-medium capitalize">
|
|
||||||
{deliveryTypeLabel[order.delivery_type] ??
|
|
||||||
order.delivery_type}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="border rounded-lg p-4">
|
<div className="border rounded-lg p-4">
|
||||||
{order.lat && order.long && (
|
{order.lat && order.long && (
|
||||||
<div className="border rounded-lg p-4 space-y-2">
|
<div className="border rounded-lg p-4 space-y-2">
|
||||||
@@ -172,16 +116,6 @@ const OrderDetail = ({ detail, setDetail, order }: Props) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="border rounded-lg p-4">
|
|
||||||
<h3 className="font-semibold text-lg mb-3 flex items-center">
|
|
||||||
<CreditCard className="w-5 h-5 mr-2" />
|
|
||||||
To'lov turi
|
|
||||||
</h3>
|
|
||||||
<p className="text-sm font-medium capitalize">
|
|
||||||
{order.payment_type === "CASH" ? "Naxt" : "Karta orqali"}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{order.comment && (
|
{order.comment && (
|
||||||
<div className="border rounded-lg p-4">
|
<div className="border rounded-lg p-4">
|
||||||
<h3 className="font-semibold text-lg mb-3 flex items-center">
|
<h3 className="font-semibold text-lg mb-3 flex items-center">
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import { order_api } from "@/features/order/lib/api";
|
import { order_api } from "@/features/order/lib/api";
|
||||||
import type { Order } from "@/features/order/lib/type";
|
import type { Order } from "@/features/order/lib/type";
|
||||||
import formatPhone from "@/shared/lib/formatPhone";
|
|
||||||
import formatPrice from "@/shared/lib/formatPrice";
|
import formatPrice from "@/shared/lib/formatPrice";
|
||||||
import { Button } from "@/shared/ui/button";
|
import { Button } from "@/shared/ui/button";
|
||||||
import {
|
import {
|
||||||
@@ -35,12 +34,6 @@ interface Props {
|
|||||||
setOrderDetail: Dispatch<SetStateAction<Order | null>>;
|
setOrderDetail: Dispatch<SetStateAction<Order | null>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const deliveryTypeLabel: Record<string, string> = {
|
|
||||||
YANDEX_GO: "Yandex Go",
|
|
||||||
DELIVERY_COURIES: "Kuryer orqali yetkazish",
|
|
||||||
PICKUP: "O‘zi olib ketish",
|
|
||||||
};
|
|
||||||
|
|
||||||
type OrderStatus =
|
type OrderStatus =
|
||||||
| "NEW"
|
| "NEW"
|
||||||
// "PROCESSING" |
|
// "PROCESSING" |
|
||||||
@@ -113,11 +106,7 @@ const OrderTable = ({
|
|||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead>#</TableHead>
|
<TableHead>#</TableHead>
|
||||||
<TableHead>Order №</TableHead>
|
|
||||||
<TableHead>Foydalanuvchi</TableHead>
|
<TableHead>Foydalanuvchi</TableHead>
|
||||||
<TableHead>Kontakt</TableHead>
|
|
||||||
<TableHead>Toʻlov turi</TableHead>
|
|
||||||
<TableHead>Yetkazib berish</TableHead>
|
|
||||||
<TableHead>Umumiy narx</TableHead>
|
<TableHead>Umumiy narx</TableHead>
|
||||||
<TableHead>Izoh</TableHead>
|
<TableHead>Izoh</TableHead>
|
||||||
<TableHead>Holat</TableHead>
|
<TableHead>Holat</TableHead>
|
||||||
@@ -129,18 +118,7 @@ const OrderTable = ({
|
|||||||
{orders.map((order, index) => (
|
{orders.map((order, index) => (
|
||||||
<TableRow key={order.id}>
|
<TableRow key={order.id}>
|
||||||
<TableCell>{index + 1}</TableCell>
|
<TableCell>{index + 1}</TableCell>
|
||||||
<TableCell>{order.order_number}</TableCell>
|
<TableCell>{order.user.username}</TableCell>
|
||||||
<TableCell>
|
|
||||||
{order.user.first_name} {order.user.last_name} (
|
|
||||||
{order.user.username})
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>{formatPhone(order.contact_number)}</TableCell>
|
|
||||||
<TableCell>
|
|
||||||
{order.payment_type === "CASH" ? "Naxt" : "Karta orqali"}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>
|
|
||||||
{deliveryTypeLabel[order.delivery_type] ?? order.delivery_type}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>{formatPrice(order.total_price, true)}</TableCell>
|
<TableCell>{formatPrice(order.total_price, true)}</TableCell>
|
||||||
<TableCell>{order.comment || "-"}</TableCell>
|
<TableCell>{order.comment || "-"}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ export const createPlanFormData = z.object({
|
|||||||
name_ru: z.string().min(1, "Majburiy maydon"),
|
name_ru: z.string().min(1, "Majburiy maydon"),
|
||||||
description_uz: z.string().min(1, "Majburiy maydon"),
|
description_uz: z.string().min(1, "Majburiy maydon"),
|
||||||
description_ru: z.string().min(1, "Majburiy maydon"),
|
description_ru: z.string().min(1, "Majburiy maydon"),
|
||||||
category_id: z.string().uuid("Kategoriya noto‘g‘ri"),
|
category_id: z.string(),
|
||||||
unity_id: z.string().uuid("Birlik noto‘g‘ri"),
|
unity_id: z.string(),
|
||||||
price: z.number().positive("Narx noto‘g‘ri"),
|
price: z.number().positive("Narx noto‘g‘ri"),
|
||||||
quantity_left: z.number().min(0),
|
quantity_left: z.number().min(0),
|
||||||
min_quantity: z.number().min(0),
|
min_quantity: z.number().min(0),
|
||||||
|
|||||||
51
src/features/plans/ui/ExcelUpload.tsx
Normal file
51
src/features/plans/ui/ExcelUpload.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Button } from "@/shared/ui/button";
|
||||||
|
import { useRef } from "react";
|
||||||
|
|
||||||
|
export default function ExcelUpload() {
|
||||||
|
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
|
const handleButtonClick = () => {
|
||||||
|
fileInputRef.current?.click();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const file = e.target.files?.[0];
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
// Excel format tekshiruvi
|
||||||
|
const allowedTypes = [
|
||||||
|
"application/vnd.ms-excel",
|
||||||
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!allowedTypes.includes(file.type)) {
|
||||||
|
alert("Iltimos, faqat Excel (.xls, .xlsx) fayl yuklang");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 👉 shu yerda backendga yuborasiz
|
||||||
|
// uploadExcel(file)
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
onClick={handleButtonClick}
|
||||||
|
className="h-12 bg-blue-500 text-white hover:bg-blue-600"
|
||||||
|
variant="secondary"
|
||||||
|
>
|
||||||
|
Excel yuklash
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<input
|
||||||
|
ref={fileInputRef}
|
||||||
|
type="file"
|
||||||
|
accept=".xls,.xlsx"
|
||||||
|
className="hidden"
|
||||||
|
onChange={handleFileChange}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { Product } from "@/features/plans/lib/data";
|
import type { Product } from "@/features/plans/lib/data";
|
||||||
import AddedPlan from "@/features/plans/ui/AddedPlan";
|
import AddedPlan from "@/features/plans/ui/AddedPlan";
|
||||||
|
import ExcelUpload from "@/features/plans/ui/ExcelUpload";
|
||||||
import { Button } from "@/shared/ui/button";
|
import { Button } from "@/shared/ui/button";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -9,7 +10,7 @@ import {
|
|||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/shared/ui/dialog";
|
} from "@/shared/ui/dialog";
|
||||||
import { Input } from "@/shared/ui/input";
|
import { Input } from "@/shared/ui/input";
|
||||||
import { Plus } from "lucide-react";
|
import { AlertCircle, Plus } from "lucide-react";
|
||||||
import type { Dispatch, SetStateAction } from "react";
|
import type { Dispatch, SetStateAction } from "react";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -38,6 +39,30 @@ const FilterPlans = ({
|
|||||||
value={searchUser}
|
value={searchUser}
|
||||||
onChange={(e) => setSearchUser(e.target.value)}
|
onChange={(e) => setSearchUser(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button
|
||||||
|
className="h-12 bg-blue-500 text-white hover:bg-blue-600 cursor-pointer"
|
||||||
|
variant={"secondary"}
|
||||||
|
>
|
||||||
|
Excel uchun shablon
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader className="text-xl font-medium">
|
||||||
|
Excel uchun kerakli shablon
|
||||||
|
</DialogHeader>
|
||||||
|
<img src="/shablon.jpg" className="h-full w-full" />
|
||||||
|
<div className="flex justify-center items-start gap-2">
|
||||||
|
<AlertCircle className="text-red-500 size-12" />
|
||||||
|
<p className="text-xl font-medium text-red-500">
|
||||||
|
Excel shu ko'rinishda bo'lishi kerak aks holda mahsulot qo'shishda
|
||||||
|
xatolik yuz berishi mumkin.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
<ExcelUpload />
|
||||||
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
|
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
Reference in New Issue
Block a user