Files
sfera/src/components/fulfillment-supplies/create-fulfillment-consumables-supply-page.tsx
Bivekich 593ae16e1e a lot of
2025-07-30 18:32:52 +03:00

859 lines
40 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, useEffect } from "react";
import { useRouter } from "next/navigation";
import { useQuery, useMutation } from "@apollo/client";
import { Sidebar } from "@/components/dashboard/sidebar";
import { useSidebar } from "@/hooks/useSidebar";
import { Card } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Input } from "@/components/ui/input";
import {
ArrowLeft,
Building2,
MapPin,
Phone,
Mail,
Star,
Search,
Package,
Plus,
Minus,
ShoppingCart,
Wrench,
Box,
} from "lucide-react";
import {
GET_MY_COUNTERPARTIES,
GET_ALL_PRODUCTS,
GET_SUPPLY_ORDERS,
GET_MY_SUPPLIES,
} from "@/graphql/queries";
import { CREATE_SUPPLY_ORDER } from "@/graphql/mutations";
import { OrganizationAvatar } from "@/components/market/organization-avatar";
import { toast } from "sonner";
import Image from "next/image";
import { useAuth } from "@/hooks/useAuth";
interface FulfillmentConsumableSupplier {
id: string;
inn: string;
name?: string;
fullName?: string;
type: "FULFILLMENT" | "SELLER" | "LOGIST" | "WHOLESALE";
address?: string;
phones?: Array<{ value: string }>;
emails?: Array<{ value: string }>;
users?: Array<{ id: string; avatar?: string; managerName?: string }>;
createdAt: string;
}
interface FulfillmentConsumableProduct {
id: string;
name: string;
description?: string;
price: number;
type?: "PRODUCT" | "CONSUMABLE";
category?: { name: string };
images: string[];
mainImage?: string;
organization: {
id: string;
name: string;
};
stock?: number;
unit?: string;
}
interface SelectedFulfillmentConsumable {
id: string;
name: string;
price: number;
selectedQuantity: number;
unit?: string;
category?: string;
supplierId: string;
supplierName: string;
}
export function CreateFulfillmentConsumablesSupplyPage() {
const router = useRouter();
const { getSidebarMargin } = useSidebar();
const { user } = useAuth();
const [selectedSupplier, setSelectedSupplier] =
useState<FulfillmentConsumableSupplier | null>(null);
const [selectedLogistics, setSelectedLogistics] =
useState<FulfillmentConsumableSupplier | null>(null);
const [selectedConsumables, setSelectedConsumables] = useState<
SelectedFulfillmentConsumable[]
>([]);
const [searchQuery, setSearchQuery] = useState("");
const [productSearchQuery, setProductSearchQuery] = useState("");
const [deliveryDate, setDeliveryDate] = useState("");
const [isCreatingSupply, setIsCreatingSupply] = useState(false);
// Загружаем контрагентов-поставщиков расходников
const { data: counterpartiesData, loading: counterpartiesLoading } = useQuery(
GET_MY_COUNTERPARTIES
);
// Загружаем товары для выбранного поставщика
const { data: productsData, loading: productsLoading } = useQuery(
GET_ALL_PRODUCTS,
{
skip: !selectedSupplier,
variables: { search: productSearchQuery || null, category: null },
}
);
// Мутация для создания заказа поставки расходников
const [createSupplyOrder] = useMutation(CREATE_SUPPLY_ORDER);
// Фильтруем только поставщиков расходников (поставщиков)
const consumableSuppliers = (
counterpartiesData?.myCounterparties || []
).filter((org: FulfillmentConsumableSupplier) => org.type === "WHOLESALE");
// Фильтруем только логистические компании
const logisticsPartners = (
counterpartiesData?.myCounterparties || []
).filter((org: FulfillmentConsumableSupplier) => org.type === "LOGIST");
// Фильтруем поставщиков по поисковому запросу
const filteredSuppliers = consumableSuppliers.filter(
(supplier: FulfillmentConsumableSupplier) =>
supplier.name?.toLowerCase().includes(searchQuery.toLowerCase()) ||
supplier.fullName?.toLowerCase().includes(searchQuery.toLowerCase()) ||
supplier.inn?.toLowerCase().includes(searchQuery.toLowerCase())
);
// Фильтруем товары по выбранному поставщику
const supplierProducts = selectedSupplier
? (productsData?.allProducts || []).filter(
(product: FulfillmentConsumableProduct) =>
product.organization.id === selectedSupplier.id
)
: [];
// Отладочное логирование
React.useEffect(() => {
console.log("🛒 FULFILLMENT CONSUMABLES DEBUG:", {
selectedSupplier: selectedSupplier
? {
id: selectedSupplier.id,
name: selectedSupplier.name || selectedSupplier.fullName,
type: selectedSupplier.type,
}
: null,
productsLoading,
allProductsCount: productsData?.allProducts?.length || 0,
supplierProductsCount: supplierProducts.length,
allProducts:
productsData?.allProducts?.map((p) => ({
id: p.id,
name: p.name,
organizationId: p.organization.id,
organizationName: p.organization.name,
type: p.type || "NO_TYPE",
})) || [],
supplierProducts: supplierProducts.map((p) => ({
id: p.id,
name: p.name,
organizationId: p.organization.id,
organizationName: p.organization.name,
})),
});
}, [selectedSupplier, productsData, productsLoading, supplierProducts]);
const formatCurrency = (amount: number) => {
return new Intl.NumberFormat("ru-RU", {
style: "currency",
currency: "RUB",
minimumFractionDigits: 0,
}).format(amount);
};
const renderStars = (rating: number = 4.5) => {
return Array.from({ length: 5 }, (_, i) => (
<Star
key={i}
className={`h-3 w-3 ${
i < Math.floor(rating)
? "text-yellow-400 fill-current"
: "text-gray-400"
}`}
/>
));
};
const updateConsumableQuantity = (productId: string, quantity: number) => {
const product = supplierProducts.find(
(p: FulfillmentConsumableProduct) => p.id === productId
);
if (!product || !selectedSupplier) return;
setSelectedConsumables((prev) => {
const existing = prev.find((p) => p.id === productId);
if (quantity === 0) {
// Удаляем расходник если количество 0
return prev.filter((p) => p.id !== productId);
}
if (existing) {
// Обновляем количество существующего расходника
return prev.map((p) =>
p.id === productId ? { ...p, selectedQuantity: quantity } : p
);
} else {
// Добавляем новый расходник
return [
...prev,
{
id: product.id,
name: product.name,
price: product.price,
selectedQuantity: quantity,
unit: product.unit || "шт",
category: product.category?.name || "Расходники",
supplierId: selectedSupplier.id,
supplierName:
selectedSupplier.name || selectedSupplier.fullName || "Поставщик",
},
];
}
});
};
const getSelectedQuantity = (productId: string): number => {
const selected = selectedConsumables.find((p) => p.id === productId);
return selected ? selected.selectedQuantity : 0;
};
const getTotalAmount = () => {
return selectedConsumables.reduce(
(sum, consumable) => sum + consumable.price * consumable.selectedQuantity,
0
);
};
const getTotalItems = () => {
return selectedConsumables.reduce(
(sum, consumable) => sum + consumable.selectedQuantity,
0
);
};
const handleCreateSupply = async () => {
if (
!selectedSupplier ||
selectedConsumables.length === 0 ||
!deliveryDate
) {
toast.error("Заполните все обязательные поля");
return;
}
setIsCreatingSupply(true);
try {
const result = await createSupplyOrder({
variables: {
input: {
partnerId: selectedSupplier.id,
deliveryDate: deliveryDate,
// Для фулфилмента указываем себя как получателя (поставка на свой склад)
fulfillmentCenterId: user?.organization?.id,
logisticsPartnerId: selectedLogistics?.id,
items: selectedConsumables.map((consumable) => ({
productId: consumable.id,
quantity: consumable.selectedQuantity,
})),
},
},
refetchQueries: [
{ query: GET_SUPPLY_ORDERS }, // Обновляем заказы поставок
{ query: GET_MY_SUPPLIES }, // Обновляем расходники фулфилмента
],
});
if (result.data?.createSupplyOrder?.success) {
toast.success("Заказ поставки расходников фулфилмента создан успешно!");
// Очищаем форму
setSelectedSupplier(null);
setSelectedConsumables([]);
setDeliveryDate("");
setProductSearchQuery("");
setSearchQuery("");
// Перенаправляем на страницу поставок фулфилмента с активной вкладкой "Расходники фулфилмента"
router.push("/fulfillment-supplies?tab=detailed-supplies");
} else {
toast.error(
result.data?.createSupplyOrder?.message ||
"Ошибка при создании заказа поставки"
);
}
} catch (error) {
console.error("Error creating fulfillment consumables supply:", error);
toast.error("Ошибка при создании поставки расходников фулфилмента");
} finally {
setIsCreatingSupply(false);
}
};
return (
<div className="h-screen flex overflow-hidden">
<Sidebar />
<main
className={`flex-1 ${getSidebarMargin()} overflow-auto transition-all duration-300`}
>
<div className="min-h-full w-full flex flex-col px-3 py-2">
{/* Заголовок */}
<div className="flex items-center justify-between mb-3 flex-shrink-0">
<div>
<h1 className="text-xl font-bold text-white mb-1">
Создание поставки расходников фулфилмента
</h1>
<p className="text-white/60 text-sm">
Выберите поставщика и добавьте расходники в заказ для вашего
фулфилмент-центра
</p>
</div>
<Button
variant="ghost"
size="sm"
onClick={() => router.push("/fulfillment-supplies")}
className="text-white/60 hover:text-white hover:bg-white/10 text-sm"
>
<ArrowLeft className="h-4 w-4 mr-1" />
Назад
</Button>
</div>
{/* Основной контент с двумя блоками */}
<div className="flex-1 flex gap-3 min-h-0">
{/* Левая колонка - Поставщики и Расходники */}
<div className="flex-1 flex flex-col gap-3 min-h-0">
{/* Блок "Поставщики" */}
<Card className="bg-gradient-to-r from-white/15 via-white/10 to-white/15 backdrop-blur-xl border border-white/30 shadow-2xl flex-shrink-0 sticky top-0 z-10 rounded-xl overflow-hidden">
<div className="p-3 bg-gradient-to-r from-purple-500/10 to-pink-500/10">
<div className="flex items-center justify-between gap-4">
<h2 className="text-lg font-bold text-white flex items-center flex-shrink-0 bg-gradient-to-r from-purple-400 to-pink-400 bg-clip-text text-transparent">
<Building2 className="h-5 w-5 mr-3 text-purple-400" />
Поставщики расходников
</h2>
<div className="relative flex-1 max-w-sm">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-purple-300 h-4 w-4 z-10" />
<Input
placeholder="Найти поставщика..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="bg-white/20 backdrop-blur border-white/30 text-white placeholder-white/50 pl-10 h-8 text-sm rounded-full shadow-inner focus:ring-2 focus:ring-purple-400/50 focus:border-purple-400/50 transition-all duration-300"
/>
</div>
{selectedSupplier && (
<Button
variant="ghost"
size="sm"
onClick={() => setSelectedSupplier(null)}
className="text-white/70 hover:text-white hover:bg-white/20 text-sm h-8 px-3 flex-shrink-0 rounded-full transition-all duration-300 hover:scale-105"
>
Сбросить
</Button>
)}
</div>
</div>
<div className="px-3 pb-3 h-24 overflow-hidden">
{counterpartiesLoading ? (
<div className="text-center py-4">
<div className="animate-spin rounded-full h-6 w-6 border-2 border-purple-400 border-t-transparent mx-auto mb-2"></div>
<p className="text-white/70 text-sm font-medium">
Загружаем поставщиков...
</p>
</div>
) : filteredSuppliers.length === 0 ? (
<div className="text-center py-4">
<div className="bg-gradient-to-br from-purple-500/20 to-pink-500/20 rounded-full p-3 w-fit mx-auto mb-2">
<Building2 className="h-6 w-6 text-purple-300" />
</div>
<p className="text-white/70 text-sm font-medium">
{searchQuery
? "Поставщики не найдены"
: "Добавьте поставщиков"}
</p>
</div>
) : (
<div className="flex gap-2 h-full pt-1">
{filteredSuppliers
.slice(0, 7)
.map(
(supplier: FulfillmentConsumableSupplier, index) => (
<Card
key={supplier.id}
className={`relative cursor-pointer transition-all duration-300 border flex-shrink-0 rounded-xl overflow-hidden group hover:scale-105 hover:shadow-xl ${
selectedSupplier?.id === supplier.id
? "bg-gradient-to-br from-orange-500/30 via-orange-400/20 to-orange-500/30 border-orange-400/60 shadow-lg shadow-orange-500/25"
: "bg-gradient-to-br from-white/10 via-white/5 to-white/10 border-white/20 hover:from-white/20 hover:via-white/10 hover:to-white/20 hover:border-white/40"
}`}
style={{
width: "calc((100% - 48px) / 7)", // 48px = 6 gaps * 8px each
animationDelay: `${index * 100}ms`,
}}
onClick={() => {
console.log("🔄 ВЫБРАН ПОСТАВЩИК:", {
id: supplier.id,
name: supplier.name || supplier.fullName,
type: supplier.type,
});
setSelectedSupplier(supplier);
}}
>
<div className="flex flex-col items-center justify-center h-full p-2 space-y-1">
<div className="relative">
<OrganizationAvatar
organization={{
id: supplier.id,
name:
supplier.name ||
supplier.fullName ||
"Поставщик",
fullName: supplier.fullName,
users: (supplier.users || []).map(
(user) => ({
id: user.id,
avatar: user.avatar,
})
),
}}
size="sm"
/>
{selectedSupplier?.id === supplier.id && (
<div className="absolute -top-1 -right-1 bg-gradient-to-r from-orange-400 to-orange-500 rounded-full w-4 h-4 flex items-center justify-center shadow-lg animate-pulse">
<span className="text-white text-xs font-bold">
</span>
</div>
)}
</div>
<div className="text-center w-full space-y-0.5">
<h3 className="text-white font-semibold text-xs truncate leading-tight group-hover:text-purple-200 transition-colors duration-300">
{(
supplier.name ||
supplier.fullName ||
"Поставщик"
).slice(0, 10)}
</h3>
<div className="flex items-center justify-center space-x-1">
<span className="text-yellow-400 text-sm animate-pulse">
</span>
<span className="text-white/80 text-xs font-medium">
4.5
</span>
</div>
<div className="w-full bg-white/10 rounded-full h-1 overflow-hidden">
<div
className="bg-gradient-to-r from-purple-400 to-pink-400 h-full rounded-full animate-pulse"
style={{ width: "90%" }}
></div>
</div>
</div>
</div>
{/* Hover эффект */}
<div className="absolute inset-0 bg-gradient-to-br from-purple-500/0 to-pink-500/0 group-hover:from-purple-500/10 group-hover:to-pink-500/10 transition-all duration-300 pointer-events-none"></div>
</Card>
)
)}
{filteredSuppliers.length > 7 && (
<div
className="flex-shrink-0 flex flex-col items-center justify-center bg-gradient-to-br from-white/10 to-white/5 rounded-xl border border-white/20 text-white/70 hover:text-white transition-all duration-300 hover:scale-105"
style={{ width: "calc((100% - 48px) / 7)" }}
>
<div className="text-lg font-bold text-purple-300">
+{filteredSuppliers.length - 7}
</div>
<div className="text-xs">ещё</div>
</div>
)}
</div>
)}
</div>
</Card>
{/* Блок "Расходники" */}
<Card className="bg-white/10 backdrop-blur border-white/20 flex-1 min-h-0 flex flex-col">
<div className="p-3 border-b border-white/10 flex-shrink-0">
<div className="flex items-center justify-between mb-2">
<h2 className="text-lg font-semibold text-white flex items-center">
<Wrench className="h-4 w-4 mr-2" />
Расходники для фулфилмента
{selectedSupplier && (
<span className="text-white/60 text-xs font-normal ml-2 truncate">
- {selectedSupplier.name || selectedSupplier.fullName}
</span>
)}
</h2>
</div>
{selectedSupplier && (
<div className="relative">
<Search className="absolute left-2 top-1/2 transform -translate-y-1/2 text-white/40 h-3 w-3" />
<Input
placeholder="Поиск расходников..."
value={productSearchQuery}
onChange={(e) => setProductSearchQuery(e.target.value)}
className="bg-white/10 border-white/20 text-white placeholder-white/40 pl-7 h-8 text-sm"
/>
</div>
)}
</div>
<div className="p-3 flex-1 overflow-y-auto">
{!selectedSupplier ? (
<div className="text-center py-8">
<Wrench className="h-8 w-8 text-white/40 mx-auto mb-3" />
<p className="text-white/60 text-sm">
Выберите поставщика для просмотра расходников
</p>
</div>
) : productsLoading ? (
<div className="text-center py-8">
<div className="animate-spin rounded-full h-6 w-6 border-2 border-white border-t-transparent mx-auto mb-2"></div>
<p className="text-white/60 text-sm">Загрузка...</p>
</div>
) : supplierProducts.length === 0 ? (
<div className="text-center py-8">
<Package className="h-8 w-8 text-white/40 mx-auto mb-3" />
<p className="text-white/60 text-sm">
Нет доступных расходников
</p>
</div>
) : (
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 xl:grid-cols-7 gap-3">
{supplierProducts.map(
(product: FulfillmentConsumableProduct, index) => {
const selectedQuantity = getSelectedQuantity(
product.id
);
return (
<Card
key={product.id}
className={`relative bg-gradient-to-br from-white/10 via-white/5 to-white/10 backdrop-blur border border-white/20 p-3 rounded-xl overflow-hidden group hover:shadow-xl transition-all duration-300 ${
selectedQuantity > 0
? "ring-2 ring-green-400/50 bg-gradient-to-br from-green-500/20 via-green-400/10 to-green-500/20"
: "hover:from-white/20 hover:via-white/10 hover:to-white/20 hover:border-white/40"
}`}
style={{
animationDelay: `${index * 50}ms`,
minHeight: "200px",
width: "100%",
}}
>
<div className="space-y-2 h-full flex flex-col">
{/* Изображение товара */}
<div className="aspect-square bg-white/5 rounded-lg overflow-hidden relative flex-shrink-0">
{product.images &&
product.images.length > 0 &&
product.images[0] ? (
<Image
src={product.images[0]}
alt={product.name}
width={100}
height={100}
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-300"
/>
) : product.mainImage ? (
<Image
src={product.mainImage}
alt={product.name}
width={100}
height={100}
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-300"
/>
) : (
<div className="w-full h-full flex items-center justify-center">
<Wrench className="h-8 w-8 text-white/40" />
</div>
)}
{selectedQuantity > 0 && (
<div className="absolute top-2 right-2 bg-gradient-to-r from-green-400 to-green-500 rounded-full w-6 h-6 flex items-center justify-center shadow-lg animate-pulse">
<span className="text-white text-xs font-bold">
{selectedQuantity > 999
? "999+"
: selectedQuantity}
</span>
</div>
)}
</div>
{/* Информация о товаре */}
<div className="space-y-1 flex-grow">
<h3 className="text-white font-medium text-sm leading-tight line-clamp-2 group-hover:text-purple-200 transition-colors duration-300">
{product.name}
</h3>
{product.category && (
<Badge className="bg-purple-500/20 text-purple-300 border-purple-500/30 text-xs px-2 py-1">
{product.category.name.slice(0, 10)}
</Badge>
)}
<div className="flex items-center justify-between">
<span className="text-green-400 font-semibold text-sm">
{formatCurrency(product.price)}
</span>
{product.stock && (
<span className="text-white/60 text-xs">
{product.stock}
</span>
)}
</div>
</div>
{/* Управление количеством */}
<div className="flex flex-col items-center space-y-2 mt-auto">
<div className="flex items-center space-x-2">
<Button
variant="ghost"
size="sm"
onClick={() =>
updateConsumableQuantity(
product.id,
Math.max(0, selectedQuantity - 1)
)
}
className="h-6 w-6 p-0 text-white/60 hover:text-white hover:bg-white/20 rounded-full transition-all duration-300"
disabled={selectedQuantity === 0}
>
<Minus className="h-3 w-3" />
</Button>
<Input
type="text"
inputMode="numeric"
pattern="[0-9]*"
value={
selectedQuantity === 0
? ""
: selectedQuantity.toString()
}
onChange={(e) => {
let inputValue = e.target.value;
// Удаляем все нецифровые символы
inputValue = inputValue.replace(
/[^0-9]/g,
""
);
// Удаляем ведущие нули
inputValue = inputValue.replace(
/^0+/,
""
);
// Если строка пустая после удаления нулей, устанавливаем 0
const numericValue =
inputValue === ""
? 0
: parseInt(inputValue);
// Ограничиваем значение максимумом 99999
const clampedValue = Math.min(
numericValue,
99999
);
updateConsumableQuantity(
product.id,
clampedValue
);
}}
onBlur={(e) => {
// При потере фокуса, если поле пустое, устанавливаем 0
if (e.target.value === "") {
updateConsumableQuantity(
product.id,
0
);
}
}}
className="w-16 h-7 text-center text-sm bg-white/10 border-white/20 text-white rounded px-1 focus:ring-2 focus:ring-purple-400/50 focus:border-purple-400/50"
placeholder="0"
/>
<Button
variant="ghost"
size="sm"
onClick={() =>
updateConsumableQuantity(
product.id,
Math.min(selectedQuantity + 1, 99999)
)
}
className="h-6 w-6 p-0 text-white/60 hover:text-white hover:bg-white/20 rounded-full transition-all duration-300"
>
<Plus className="h-3 w-3" />
</Button>
</div>
{selectedQuantity > 0 && (
<div className="text-center">
<span className="text-green-400 font-bold text-sm bg-green-500/10 px-3 py-1 rounded-full">
{formatCurrency(
product.price * selectedQuantity
)}
</span>
</div>
)}
</div>
</div>
{/* Hover эффект */}
<div className="absolute inset-0 bg-gradient-to-br from-purple-500/0 to-pink-500/0 group-hover:from-purple-500/5 group-hover:to-pink-500/5 transition-all duration-300 pointer-events-none rounded-xl"></div>
</Card>
);
}
)}
</div>
)}
</div>
</Card>
</div>
{/* Правая колонка - Корзина */}
<div className="w-72 flex-shrink-0">
<Card className="bg-white/10 backdrop-blur border-white/20 p-3 sticky top-0">
<h3 className="text-white font-semibold mb-3 flex items-center text-sm">
<ShoppingCart className="h-4 w-4 mr-2" />
Корзина ({getTotalItems()} шт)
</h3>
{selectedConsumables.length === 0 ? (
<div className="text-center py-6">
<div className="bg-gradient-to-br from-purple-500/20 to-pink-500/20 rounded-full p-4 w-fit mx-auto mb-3">
<ShoppingCart className="h-8 w-8 text-purple-300" />
</div>
<p className="text-white/60 text-sm font-medium mb-2">
Корзина пуста
</p>
<p className="text-white/40 text-xs mb-3">
Добавьте расходники для создания поставки
</p>
</div>
) : (
<div className="space-y-2 mb-3 max-h-48 overflow-y-auto">
{selectedConsumables.map((consumable) => (
<div
key={consumable.id}
className="flex items-center justify-between p-2 bg-white/5 rounded-lg"
>
<div className="flex-1 min-w-0">
<p className="text-white text-xs font-medium truncate">
{consumable.name}
</p>
<p className="text-white/60 text-xs">
{formatCurrency(consumable.price)} ×{" "}
{consumable.selectedQuantity}
</p>
</div>
<div className="flex items-center space-x-2">
<span className="text-green-400 font-medium text-xs">
{formatCurrency(
consumable.price * consumable.selectedQuantity
)}
</span>
<Button
variant="ghost"
size="sm"
onClick={() =>
updateConsumableQuantity(consumable.id, 0)
}
className="h-5 w-5 p-0 text-red-400 hover:text-red-300 hover:bg-red-500/10"
>
×
</Button>
</div>
</div>
))}
</div>
)}
<div className="border-t border-white/20 pt-3">
<div className="mb-3">
<label className="text-white/60 text-xs mb-1 block">
Дата поставки:
</label>
<Input
type="date"
value={deliveryDate}
onChange={(e) => setDeliveryDate(e.target.value)}
className="bg-white/10 border-white/20 text-white h-8 text-sm"
min={new Date().toISOString().split("T")[0]}
required
/>
</div>
{/* Выбор логистики */}
<div className="mb-3">
<label className="text-white/60 text-xs mb-1 block">
Логистика (опционально):
</label>
<div className="relative">
<select
value={selectedLogistics?.id || ""}
onChange={(e) => {
const logisticsId = e.target.value;
const logistics = logisticsPartners.find(p => p.id === logisticsId);
setSelectedLogistics(logistics || null);
}}
className="w-full bg-white/10 border border-white/20 rounded-md px-3 py-2 text-white text-sm focus:outline-none focus:ring-1 focus:ring-purple-500 focus:border-transparent appearance-none"
>
<option value="" className="bg-gray-800 text-white">
Без логистики
</option>
{logisticsPartners.map((partner) => (
<option
key={partner.id}
value={partner.id}
className="bg-gray-800 text-white"
>
{partner.name || partner.fullName || partner.inn}
</option>
))}
</select>
<div className="absolute inset-y-0 right-0 flex items-center px-2 pointer-events-none">
<svg className="w-4 h-4 text-white/60" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</div>
</div>
</div>
<div className="flex items-center justify-between mb-3">
<span className="text-white font-semibold text-sm">
Итого:
</span>
<span className="text-green-400 font-bold text-lg">
{formatCurrency(getTotalAmount())}
</span>
</div>
<Button
onClick={handleCreateSupply}
disabled={
isCreatingSupply ||
!deliveryDate ||
selectedConsumables.length === 0
}
className="w-full bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white disabled:opacity-50 h-8 text-sm"
>
{isCreatingSupply ? "Создание..." : "Создать поставку"}
</Button>
</div>
</Card>
</div>
</div>
</div>
</main>
</div>
);
}