Files
sfera/src/components/supplies/fulfillment-supplies/real-supply-orders-tab.tsx

367 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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,
} 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="h-full flex flex-col 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">
Создайте первый заказ расходников через кнопку &quot;Создать поставку&quot;
</p>
</div>
</Card>
) : (
<Card className="bg-white/10 backdrop-blur border-white/20 overflow-hidden flex-1 flex flex-col">
<div className="overflow-auto flex-1">
<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">
<span className="text-white font-medium">
{order.id.slice(-8)}
</span>
</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>
);
}