Обновлен компонент CreateConsumablesSupplyPage: изменено перенаправление на страницу поставок селлера. В компоненте FulfillmentSuppliesTab заменен импорт и использование вкладки на RealSupplyOrdersTab вместо FulfillmentSuppliesSubTab для улучшения функциональности.
This commit is contained in:
@ -256,8 +256,8 @@ export function CreateConsumablesSupplyPage() {
|
||||
setProductSearchQuery("");
|
||||
setSearchQuery("");
|
||||
|
||||
// Перенаправляем на страницу поставок фулфилмента
|
||||
router.push("/fulfillment-supplies");
|
||||
// Перенаправляем на страницу поставок селлера
|
||||
router.push("/supplies");
|
||||
} else {
|
||||
toast.error(
|
||||
result.data?.createSupplyOrder?.message ||
|
||||
|
@ -3,7 +3,7 @@
|
||||
import React, { useState } from "react";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { FulfillmentGoodsTab } from "./fulfillment-goods-tab";
|
||||
import { FulfillmentSuppliesTab as FulfillmentSuppliesSubTab } from "./fulfillment-supplies-sub-tab";
|
||||
import { RealSupplyOrdersTab } from "./real-supply-orders-tab";
|
||||
import { PvzReturnsTab } from "./pvz-returns-tab";
|
||||
|
||||
export function FulfillmentSuppliesTab() {
|
||||
@ -43,7 +43,7 @@ export function FulfillmentSuppliesTab() {
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="supplies" className="mt-0 flex-1">
|
||||
<FulfillmentSuppliesSubTab />
|
||||
<RealSupplyOrdersTab />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="returns" className="mt-0 flex-1">
|
||||
|
@ -0,0 +1,376 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { StatsCard } from "../ui/stats-card";
|
||||
import { StatsGrid } from "../ui/stats-grid";
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { GET_SUPPLY_ORDERS } from "@/graphql/queries";
|
||||
import {
|
||||
Calendar,
|
||||
MapPin,
|
||||
Building2,
|
||||
TrendingUp,
|
||||
DollarSign,
|
||||
Wrench,
|
||||
Package2,
|
||||
ChevronDown,
|
||||
ChevronRight,
|
||||
} from "lucide-react";
|
||||
|
||||
interface SupplyOrderItem {
|
||||
id: string;
|
||||
quantity: number;
|
||||
price: number;
|
||||
totalPrice: number;
|
||||
product: {
|
||||
id: string;
|
||||
name: string;
|
||||
article: string;
|
||||
description?: string;
|
||||
category?: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface SupplyOrder {
|
||||
id: string;
|
||||
deliveryDate: string;
|
||||
status: string;
|
||||
totalAmount: number;
|
||||
totalItems: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
partner: {
|
||||
id: string;
|
||||
name?: string;
|
||||
fullName?: string;
|
||||
inn: string;
|
||||
address?: string;
|
||||
phones?: string[];
|
||||
emails?: string[];
|
||||
};
|
||||
organization: {
|
||||
id: string;
|
||||
name?: string;
|
||||
fullName?: string;
|
||||
type: string;
|
||||
};
|
||||
items: SupplyOrderItem[];
|
||||
}
|
||||
|
||||
export function RealSupplyOrdersTab() {
|
||||
const [expandedOrders, setExpandedOrders] = useState<Set<string>>(new Set());
|
||||
|
||||
const { data, loading, error } = useQuery(GET_SUPPLY_ORDERS);
|
||||
|
||||
const supplyOrders: SupplyOrder[] = data?.supplyOrders || [];
|
||||
|
||||
const toggleOrderExpansion = (orderId: string) => {
|
||||
const newExpanded = new Set(expandedOrders);
|
||||
if (newExpanded.has(orderId)) {
|
||||
newExpanded.delete(orderId);
|
||||
} else {
|
||||
newExpanded.add(orderId);
|
||||
}
|
||||
setExpandedOrders(newExpanded);
|
||||
};
|
||||
|
||||
const getStatusBadge = (status: string) => {
|
||||
const statusMap: Record<string, { label: string; color: string }> = {
|
||||
CREATED: {
|
||||
label: "Создан",
|
||||
color: "bg-blue-500/20 text-blue-300 border-blue-500/30",
|
||||
},
|
||||
CONFIRMED: {
|
||||
label: "Подтвержден",
|
||||
color: "bg-green-500/20 text-green-300 border-green-500/30",
|
||||
},
|
||||
IN_PROGRESS: {
|
||||
label: "В процессе",
|
||||
color: "bg-yellow-500/20 text-yellow-300 border-yellow-500/30",
|
||||
},
|
||||
DELIVERED: {
|
||||
label: "Доставлен",
|
||||
color: "bg-purple-500/20 text-purple-300 border-purple-500/30",
|
||||
},
|
||||
CANCELLED: {
|
||||
label: "Отменен",
|
||||
color: "bg-red-500/20 text-red-300 border-red-500/30",
|
||||
},
|
||||
};
|
||||
|
||||
const { label, color } = statusMap[status] || {
|
||||
label: status,
|
||||
color: "bg-gray-500/20 text-gray-300 border-gray-500/30",
|
||||
};
|
||||
|
||||
return <Badge className={`${color} border`}>{label}</Badge>;
|
||||
};
|
||||
|
||||
const formatCurrency = (amount: number) => {
|
||||
return new Intl.NumberFormat("ru-RU", {
|
||||
style: "currency",
|
||||
currency: "RUB",
|
||||
minimumFractionDigits: 0,
|
||||
}).format(amount);
|
||||
};
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
return new Date(dateString).toLocaleDateString("ru-RU", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
const formatDateTime = (dateString: string) => {
|
||||
return new Date(dateString).toLocaleString("ru-RU", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
});
|
||||
};
|
||||
|
||||
// Статистика
|
||||
const totalOrders = supplyOrders.length;
|
||||
const totalAmount = supplyOrders.reduce((sum, order) => sum + order.totalAmount, 0);
|
||||
const totalItems = supplyOrders.reduce((sum, order) => sum + order.totalItems, 0);
|
||||
const completedOrders = supplyOrders.filter(order => order.status === "DELIVERED").length;
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-2 border-white border-t-transparent"></div>
|
||||
<span className="ml-3 text-white/60">Загрузка заказов расходников...</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64">
|
||||
<div className="text-center">
|
||||
<Wrench className="h-12 w-12 text-red-400 mx-auto mb-4" />
|
||||
<p className="text-red-400 font-medium">Ошибка загрузки заказов</p>
|
||||
<p className="text-white/60 text-sm mt-2">{error.message}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Статистика заказов расходников */}
|
||||
<StatsGrid>
|
||||
<StatsCard
|
||||
title="Всего заказов"
|
||||
value={totalOrders}
|
||||
icon={Package2}
|
||||
iconColor="text-orange-400"
|
||||
iconBg="bg-orange-500/20"
|
||||
subtitle="Заказы расходников"
|
||||
/>
|
||||
|
||||
<StatsCard
|
||||
title="Общая сумма"
|
||||
value={formatCurrency(totalAmount)}
|
||||
icon={TrendingUp}
|
||||
iconColor="text-green-400"
|
||||
iconBg="bg-green-500/20"
|
||||
subtitle="Стоимость заказов"
|
||||
/>
|
||||
|
||||
<StatsCard
|
||||
title="Всего единиц"
|
||||
value={totalItems}
|
||||
icon={Wrench}
|
||||
iconColor="text-blue-400"
|
||||
iconBg="bg-blue-500/20"
|
||||
subtitle="Количество расходников"
|
||||
/>
|
||||
|
||||
<StatsCard
|
||||
title="Завершено"
|
||||
value={completedOrders}
|
||||
icon={Calendar}
|
||||
iconColor="text-purple-400"
|
||||
iconBg="bg-purple-500/20"
|
||||
subtitle="Доставленные заказы"
|
||||
/>
|
||||
</StatsGrid>
|
||||
|
||||
{/* Список заказов расходников */}
|
||||
{supplyOrders.length === 0 ? (
|
||||
<Card className="bg-white/10 backdrop-blur border-white/20 p-8">
|
||||
<div className="text-center">
|
||||
<Wrench className="h-16 w-16 text-white/20 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-semibold text-white mb-2">
|
||||
Пока нет заказов расходников
|
||||
</h3>
|
||||
<p className="text-white/60">
|
||||
Создайте первый заказ расходников через кнопку "Создать поставку"
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
) : (
|
||||
<Card className="bg-white/10 backdrop-blur border-white/20 overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr className="border-b border-white/20">
|
||||
<th className="text-left p-4 text-white font-semibold">ID</th>
|
||||
<th className="text-left p-4 text-white font-semibold">
|
||||
Поставщик
|
||||
</th>
|
||||
<th className="text-left p-4 text-white font-semibold">
|
||||
Дата поставки
|
||||
</th>
|
||||
<th className="text-left p-4 text-white font-semibold">
|
||||
Дата создания
|
||||
</th>
|
||||
<th className="text-left p-4 text-white font-semibold">
|
||||
Количество
|
||||
</th>
|
||||
<th className="text-left p-4 text-white font-semibold">
|
||||
Сумма
|
||||
</th>
|
||||
<th className="text-left p-4 text-white font-semibold">
|
||||
Статус
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{supplyOrders.map((order) => {
|
||||
const isOrderExpanded = expandedOrders.has(order.id);
|
||||
|
||||
return (
|
||||
<React.Fragment key={order.id}>
|
||||
{/* Основная строка заказа */}
|
||||
<tr
|
||||
className="border-b border-white/10 hover:bg-white/5 transition-colors cursor-pointer"
|
||||
onClick={() => toggleOrderExpansion(order.id)}
|
||||
>
|
||||
<td className="p-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
{isOrderExpanded ? (
|
||||
<ChevronDown className="h-4 w-4 text-white/60" />
|
||||
) : (
|
||||
<ChevronRight className="h-4 w-4 text-white/60" />
|
||||
)}
|
||||
<span className="text-white font-medium">
|
||||
{order.id.slice(-8)}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="p-4">
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Building2 className="h-4 w-4 text-white/40" />
|
||||
<span className="text-white font-medium">
|
||||
{order.partner.name || order.partner.fullName || "Поставщик"}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-white/60 text-sm">
|
||||
ИНН: {order.partner.inn}
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
<td className="p-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Calendar className="h-4 w-4 text-white/40" />
|
||||
<span className="text-white font-semibold">
|
||||
{formatDate(order.deliveryDate)}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="p-4">
|
||||
<span className="text-white/80">
|
||||
{formatDateTime(order.createdAt)}
|
||||
</span>
|
||||
</td>
|
||||
<td className="p-4">
|
||||
<span className="text-white font-semibold">
|
||||
{order.totalItems} шт
|
||||
</span>
|
||||
</td>
|
||||
<td className="p-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<DollarSign className="h-4 w-4 text-white/40" />
|
||||
<span className="text-green-400 font-bold">
|
||||
{formatCurrency(order.totalAmount)}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="p-4">{getStatusBadge(order.status)}</td>
|
||||
</tr>
|
||||
|
||||
{/* Развернутая информация о заказе */}
|
||||
{isOrderExpanded && (
|
||||
<tr>
|
||||
<td colSpan={7} className="p-0">
|
||||
<div className="bg-white/5 border-t border-white/10">
|
||||
<div className="p-6">
|
||||
<h4 className="text-white font-semibold mb-4">
|
||||
Состав заказа:
|
||||
</h4>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{order.items.map((item) => (
|
||||
<Card
|
||||
key={item.id}
|
||||
className="bg-white/10 backdrop-blur border-white/20 p-4"
|
||||
>
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<h5 className="text-white font-medium mb-1">
|
||||
{item.product.name}
|
||||
</h5>
|
||||
<p className="text-white/60 text-sm">
|
||||
Артикул: {item.product.article}
|
||||
</p>
|
||||
{item.product.category && (
|
||||
<Badge className="bg-purple-500/20 text-purple-300 border-purple-500/30 text-xs mt-2">
|
||||
{item.product.category.name}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="text-sm">
|
||||
<p className="text-white/60">
|
||||
Количество: {item.quantity} шт
|
||||
</p>
|
||||
<p className="text-white/60">
|
||||
Цена: {formatCurrency(item.price)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-green-400 font-semibold">
|
||||
{formatCurrency(item.totalPrice)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user