Добавлено обновление кэша для расходников фулфилмента в компонентах создания и отображения заказов. Реализованы новые GraphQL запросы для получения данных о расходниках. Удалены устаревшие компоненты уведомлений о непринятых поставках для упрощения интерфейса. Оптимизирована логика отображения и обновления данных о заказах.
This commit is contained in:
171
src/components/fulfillment-warehouse/supply-card.tsx
Normal file
171
src/components/fulfillment-warehouse/supply-card.tsx
Normal file
@ -0,0 +1,171 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Progress } from "@/components/ui/progress";
|
||||
import {
|
||||
Package,
|
||||
TrendingUp,
|
||||
TrendingDown,
|
||||
Calendar,
|
||||
MapPin,
|
||||
User,
|
||||
} from "lucide-react";
|
||||
import { SupplyCardProps } from "./types";
|
||||
|
||||
export function SupplyCard({
|
||||
supply,
|
||||
isExpanded,
|
||||
onToggleExpansion,
|
||||
statusConfig,
|
||||
getSupplyDeliveries,
|
||||
}: SupplyCardProps) {
|
||||
const formatCurrency = (amount: number) => {
|
||||
return new Intl.NumberFormat("ru-RU", {
|
||||
style: "currency",
|
||||
currency: "RUB",
|
||||
minimumFractionDigits: 0,
|
||||
}).format(amount);
|
||||
};
|
||||
|
||||
const formatNumber = (num: number) => {
|
||||
return new Intl.NumberFormat("ru-RU").format(num);
|
||||
};
|
||||
|
||||
const StatusIcon = statusConfig.icon;
|
||||
const isLowStock =
|
||||
supply.currentStock <= supply.minStock && supply.currentStock > 0;
|
||||
const stockPercentage =
|
||||
supply.minStock > 0 ? (supply.currentStock / supply.minStock) * 100 : 100;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Основная карточка расходника */}
|
||||
<Card
|
||||
className="glass-card p-4 hover:bg-white/15 transition-all duration-300 cursor-pointer group"
|
||||
onClick={() => onToggleExpansion(supply.id)}
|
||||
>
|
||||
{/* Заголовок карточки */}
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center space-x-2 mb-1">
|
||||
<h3 className="font-semibold text-white truncate group-hover:text-blue-300 transition-colors">
|
||||
{supply.name}
|
||||
</h3>
|
||||
</div>
|
||||
<p className="text-sm text-white/60 truncate">
|
||||
{supply.description}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2 ml-2">
|
||||
<Badge className={`${statusConfig.color} text-xs`}>
|
||||
<StatusIcon className="h-3 w-3 mr-1" />
|
||||
{statusConfig.label}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Основная информация */}
|
||||
<div className="space-y-3">
|
||||
{/* Остатки и прогресс-бар */}
|
||||
<div>
|
||||
<div className="flex items-center justify-between text-sm mb-1">
|
||||
<span className="text-white/60">Остаток</span>
|
||||
<span
|
||||
className={`font-medium ${
|
||||
isLowStock ? "text-yellow-300" : "text-white"
|
||||
}`}
|
||||
>
|
||||
{formatNumber(supply.currentStock)} /{" "}
|
||||
{formatNumber(supply.minStock)} {supply.unit}
|
||||
</span>
|
||||
</div>
|
||||
<Progress
|
||||
value={Math.min(stockPercentage, 100)}
|
||||
className="h-2 bg-white/10"
|
||||
style={{
|
||||
background: `linear-gradient(to right, ${
|
||||
stockPercentage > 50
|
||||
? "#10b981"
|
||||
: stockPercentage > 20
|
||||
? "#f59e0b"
|
||||
: "#ef4444"
|
||||
} 0%, ${
|
||||
stockPercentage > 50
|
||||
? "#10b981"
|
||||
: stockPercentage > 20
|
||||
? "#f59e0b"
|
||||
: "#ef4444"
|
||||
} ${Math.min(
|
||||
stockPercentage,
|
||||
100
|
||||
)}%, rgba(255,255,255,0.1) ${Math.min(stockPercentage, 100)}%)`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Метрики */}
|
||||
<div className="grid grid-cols-2 gap-3 text-sm">
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="p-1 bg-blue-500/20 rounded">
|
||||
<Package className="h-3 w-3 text-blue-300" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-white/60 text-xs">Цена</p>
|
||||
<p className="text-white font-medium">
|
||||
{formatCurrency(supply.price)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="p-1 bg-purple-500/20 rounded">
|
||||
<TrendingUp className="h-3 w-3 text-purple-300" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-white/60 text-xs">Стоимость</p>
|
||||
<p className="text-white font-medium">
|
||||
{formatCurrency(
|
||||
supply.totalCost || supply.price * supply.quantity
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Дополнительная информация */}
|
||||
<div className="flex items-center justify-between text-xs">
|
||||
<div className="flex items-center space-x-1">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="text-xs border-white/20 text-white/80"
|
||||
>
|
||||
{supply.category}
|
||||
</Badge>
|
||||
<Badge className="bg-blue-500/20 text-blue-300 text-xs">
|
||||
{getSupplyDeliveries(supply).length} поставок
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Поставщик и дата */}
|
||||
<div className="flex items-center justify-between text-xs text-white/60">
|
||||
<div className="flex items-center space-x-1">
|
||||
<User className="h-3 w-3" />
|
||||
<span className="truncate max-w-[120px]" title={supply.supplier}>
|
||||
{supply.supplier}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-1">
|
||||
<Calendar className="h-3 w-3" />
|
||||
<span>
|
||||
{new Date(supply.createdAt).toLocaleDateString("ru-RU")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user