Добавлены новые кнопки и улучшения в компоненте ButtonsDemo для управления поставками и фильтрации. Обновлены мок-данные в FulfillmentGoodsTab с новыми статусами и логикой фильтрации. Изменено название вкладки в FulfillmentSuppliesTab на "Наши расходники". Улучшена структура кода и взаимодействие с пользователем.
This commit is contained in:
@ -11,7 +11,12 @@ import {
|
|||||||
Plus,
|
Plus,
|
||||||
Search,
|
Search,
|
||||||
Filter,
|
Filter,
|
||||||
Loader2
|
Loader2,
|
||||||
|
Package,
|
||||||
|
Wrench,
|
||||||
|
RotateCcw,
|
||||||
|
Building2,
|
||||||
|
ShoppingCart
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
|
|
||||||
export function ButtonsDemo() {
|
export function ButtonsDemo() {
|
||||||
@ -240,6 +245,166 @@ export function ButtonsDemo() {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
{/* Кнопки из Fulfillment Supplies */}
|
||||||
|
<Card className="glass-card border-white/10">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-white">Кнопки из страницы Поставки Фулфилмента</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-6">
|
||||||
|
{/* Основные кнопки создания */}
|
||||||
|
<div>
|
||||||
|
<h4 className="text-white/90 text-sm font-medium mb-3">Кнопки создания</h4>
|
||||||
|
<div className="flex flex-wrap gap-3">
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
className="bg-gradient-to-r from-blue-500 to-cyan-500 hover:from-blue-600 hover:to-cyan-600 text-white text-xs"
|
||||||
|
>
|
||||||
|
<Plus className="h-3 w-3 mr-1" />
|
||||||
|
Создать
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
className="bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white text-xs"
|
||||||
|
>
|
||||||
|
<Plus className="h-3 w-3 mr-1" />
|
||||||
|
Создать
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
className="bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white text-sm px-6 h-[60px] whitespace-nowrap"
|
||||||
|
>
|
||||||
|
<Plus className="h-4 w-4 mr-2" />
|
||||||
|
Создать поставку
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Кнопки заказа */}
|
||||||
|
<div>
|
||||||
|
<h4 className="text-white/90 text-sm font-medium mb-3">Кнопки заказа</h4>
|
||||||
|
<div className="flex flex-wrap gap-3">
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
className="bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white text-sm px-6 h-[60px] whitespace-nowrap"
|
||||||
|
>
|
||||||
|
<Plus className="h-4 w-4 mr-2" />
|
||||||
|
Заказать
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
className="bg-gradient-to-r from-orange-500 to-red-500 hover:from-orange-600 hover:to-red-600 text-white text-sm px-6 h-[60px] whitespace-nowrap"
|
||||||
|
>
|
||||||
|
<Plus className="h-4 w-4 mr-2" />
|
||||||
|
Заказать материалы
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
className="bg-gradient-to-r from-blue-500 to-cyan-500 hover:from-blue-600 hover:to-cyan-600 text-white text-sm px-6 h-[60px] whitespace-nowrap"
|
||||||
|
>
|
||||||
|
<Plus className="h-4 w-4 mr-2" />
|
||||||
|
Запланировать сбор
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Кнопки фильтров */}
|
||||||
|
<div>
|
||||||
|
<h4 className="text-white/90 text-sm font-medium mb-3">Кнопки фильтров (активные и неактивные)</h4>
|
||||||
|
<div className="flex flex-wrap gap-1">
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
size="sm"
|
||||||
|
className="h-7 px-2 text-xs bg-white/20 text-white"
|
||||||
|
>
|
||||||
|
Все
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="h-7 px-2 text-xs text-white/70 hover:bg-white/10 flex items-center gap-1"
|
||||||
|
>
|
||||||
|
<Package className="h-3 w-3" />
|
||||||
|
Товары
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="h-7 px-2 text-xs text-white/70 hover:bg-white/10 flex items-center gap-1"
|
||||||
|
>
|
||||||
|
<Wrench className="h-3 w-3" />
|
||||||
|
Расходники
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="h-7 px-2 text-xs text-white/70 hover:bg-white/10 flex items-center gap-1"
|
||||||
|
>
|
||||||
|
<RotateCcw className="h-3 w-3" />
|
||||||
|
Возвраты
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Кнопки маркетплейсов */}
|
||||||
|
<div>
|
||||||
|
<h4 className="text-white/90 text-sm font-medium mb-3">Кнопки маркетплейсов</h4>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
size="sm"
|
||||||
|
className="bg-white/20 text-white flex items-center gap-1"
|
||||||
|
>
|
||||||
|
Все
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="text-white/70 hover:bg-white/10 flex items-center gap-1"
|
||||||
|
>
|
||||||
|
<div className="w-3 h-3 bg-purple-400 rounded-full"></div>
|
||||||
|
Wildberries
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="text-white/70 hover:bg-white/10 flex items-center gap-1"
|
||||||
|
>
|
||||||
|
<div className="w-3 h-3 bg-blue-400 rounded-full"></div>
|
||||||
|
Ozon
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Навигационная кнопка */}
|
||||||
|
<div>
|
||||||
|
<h4 className="text-white/90 text-sm font-medium mb-3">Навигация</h4>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="text-white/60 hover:text-white hover:bg-white/10"
|
||||||
|
>
|
||||||
|
К поставкам
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Специализированные кнопки по маркетплейсам */}
|
||||||
|
<div>
|
||||||
|
<h4 className="text-white/90 text-sm font-medium mb-3">Специализированные кнопки</h4>
|
||||||
|
<div className="flex flex-wrap gap-3">
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
className="bg-gradient-to-r from-blue-500 to-indigo-500 hover:from-blue-600 hover:to-indigo-600 text-white text-sm px-6 h-[60px] whitespace-nowrap"
|
||||||
|
>
|
||||||
|
<Plus className="h-4 w-4 mr-2" />
|
||||||
|
Создать поставку (Ozon)
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
{/* Примеры использования */}
|
{/* Примеры использования */}
|
||||||
<Card className="glass-card border-white/10">
|
<Card className="glass-card border-white/10">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
|
@ -35,7 +35,7 @@ export function FulfillmentSuppliesDashboard() {
|
|||||||
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 flex items-center gap-1 text-sm"
|
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 flex items-center gap-1 text-sm"
|
||||||
>
|
>
|
||||||
<Building2 className="h-3 w-3" />
|
<Building2 className="h-3 w-3" />
|
||||||
Поставки на ФФ
|
Поставки на фулфилмент
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
value="marketplace"
|
value="marketplace"
|
||||||
|
@ -30,7 +30,11 @@ import {
|
|||||||
ChevronRight,
|
ChevronRight,
|
||||||
Store,
|
Store,
|
||||||
Building2,
|
Building2,
|
||||||
|
Clock,
|
||||||
|
CheckCircle,
|
||||||
|
FileText,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
|
|
||||||
// Интерфейсы для данных
|
// Интерфейсы для данных
|
||||||
interface Employee {
|
interface Employee {
|
||||||
@ -320,9 +324,77 @@ const mockFulfillmentSupplies: Supply[] = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
// Добавляем больше тестовых данных для демонстрации вкладок
|
||||||
|
{
|
||||||
|
id: "4",
|
||||||
|
supplyNumber: "ФФ-2024-004",
|
||||||
|
supplyDate: "2024-01-20",
|
||||||
|
seller: {
|
||||||
|
id: "seller4",
|
||||||
|
name: "Gaming Store",
|
||||||
|
storeName: "ГеймингМир",
|
||||||
|
managerName: "Игоров Игорь Игоревич",
|
||||||
|
phone: "+7 (495) 777-88-99",
|
||||||
|
email: "info@gamingworld.ru",
|
||||||
|
inn: "7707778899",
|
||||||
|
},
|
||||||
|
itemsQuantity: 120,
|
||||||
|
cargoPlaces: 4,
|
||||||
|
volume: 10.3,
|
||||||
|
responsibleEmployeeId: "emp1",
|
||||||
|
logisticsPartnerId: "log1",
|
||||||
|
status: "planned", // Новые
|
||||||
|
totalValue: 1800000,
|
||||||
|
routes: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "5",
|
||||||
|
supplyNumber: "ФФ-2024-005",
|
||||||
|
supplyDate: "2024-01-18",
|
||||||
|
seller: {
|
||||||
|
id: "seller5",
|
||||||
|
name: "Fashion Store",
|
||||||
|
storeName: "МодныйСтиль",
|
||||||
|
managerName: "Стильнов Стиль Стильнович",
|
||||||
|
phone: "+7 (495) 666-77-88",
|
||||||
|
email: "info@fashionstore.ru",
|
||||||
|
inn: "7706667788",
|
||||||
|
},
|
||||||
|
itemsQuantity: 85,
|
||||||
|
cargoPlaces: 2,
|
||||||
|
volume: 6.5,
|
||||||
|
responsibleEmployeeId: "emp2",
|
||||||
|
logisticsPartnerId: "log2",
|
||||||
|
status: "in-processing", // Принято
|
||||||
|
totalValue: 1200000,
|
||||||
|
routes: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "6",
|
||||||
|
supplyNumber: "ФФ-2024-006",
|
||||||
|
supplyDate: "2024-01-22",
|
||||||
|
seller: {
|
||||||
|
id: "seller6",
|
||||||
|
name: "Sports Store",
|
||||||
|
storeName: "СпортМастер",
|
||||||
|
managerName: "Спортов Спорт Спортович",
|
||||||
|
phone: "+7 (495) 555-66-77",
|
||||||
|
email: "info@sportsstore.ru",
|
||||||
|
inn: "7705556677",
|
||||||
|
},
|
||||||
|
itemsQuantity: 95,
|
||||||
|
cargoPlaces: 3,
|
||||||
|
volume: 8.7,
|
||||||
|
responsibleEmployeeId: "emp1",
|
||||||
|
logisticsPartnerId: "log1",
|
||||||
|
status: "planned", // Новые
|
||||||
|
totalValue: 1500000,
|
||||||
|
routes: [],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export function FulfillmentGoodsTab() {
|
export function FulfillmentGoodsTab() {
|
||||||
|
const [activeTab, setActiveTab] = useState("new");
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
const [statusFilter, setStatusFilter] = useState("all");
|
const [statusFilter, setStatusFilter] = useState("all");
|
||||||
const [expandedSellers, setExpandedSellers] = useState<Set<string>>(
|
const [expandedSellers, setExpandedSellers] = useState<Set<string>>(
|
||||||
@ -508,20 +580,45 @@ export function FulfillmentGoodsTab() {
|
|||||||
: "Не выбран";
|
: "Не выбран";
|
||||||
};
|
};
|
||||||
|
|
||||||
const filteredSupplies = mockFulfillmentSupplies.filter((supply) => {
|
const getFilteredSuppliesByTab = (tabName: string) => {
|
||||||
const matchesSearch =
|
let supplies = mockFulfillmentSupplies;
|
||||||
supply.supplyNumber.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
||||||
supply.seller.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
||||||
supply.seller.storeName
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(searchTerm.toLowerCase()) ||
|
|
||||||
supply.seller.inn.includes(searchTerm);
|
|
||||||
|
|
||||||
const matchesStatus =
|
// Фильтрация по вкладке
|
||||||
statusFilter === "all" || supply.status === statusFilter;
|
switch (tabName) {
|
||||||
|
case "new":
|
||||||
|
supplies = supplies.filter((supply) => supply.status === "planned");
|
||||||
|
break;
|
||||||
|
case "receiving":
|
||||||
|
supplies = supplies.filter((supply) => supply.status === "in-transit");
|
||||||
|
break;
|
||||||
|
case "received":
|
||||||
|
supplies = supplies.filter(
|
||||||
|
(supply) =>
|
||||||
|
supply.status === "delivered" || supply.status === "in-processing"
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return matchesSearch && matchesStatus;
|
// Дополнительная фильтрация по поиску и статусу
|
||||||
});
|
return supplies.filter((supply) => {
|
||||||
|
const matchesSearch =
|
||||||
|
supply.supplyNumber.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
|
supply.seller.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
|
supply.seller.storeName
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(searchTerm.toLowerCase()) ||
|
||||||
|
supply.seller.inn.includes(searchTerm);
|
||||||
|
|
||||||
|
const matchesStatus =
|
||||||
|
statusFilter === "all" || supply.status === statusFilter;
|
||||||
|
|
||||||
|
return matchesSearch && matchesStatus;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const filteredSupplies = getFilteredSuppliesByTab(activeTab);
|
||||||
|
|
||||||
const getTotalValue = () => {
|
const getTotalValue = () => {
|
||||||
return filteredSupplies.reduce((sum, supply) => sum + supply.totalValue, 0);
|
return filteredSupplies.reduce((sum, supply) => sum + supply.totalValue, 0);
|
||||||
@ -539,697 +636,777 @@ export function FulfillmentGoodsTab() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full flex flex-col space-y-4 p-4">
|
<div className="h-full flex flex-col p-4">
|
||||||
{/* Статистика с кнопкой */}
|
<Tabs
|
||||||
<div className="flex items-center justify-between gap-4">
|
value={activeTab}
|
||||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3 flex-1">
|
onValueChange={setActiveTab}
|
||||||
<Card className="glass-card p-3 h-[60px]">
|
className="h-full flex flex-col"
|
||||||
<div className="flex items-center space-x-2 h-full">
|
>
|
||||||
<div className="p-1.5 bg-blue-500/20 rounded">
|
{/* Вкладки товаров */}
|
||||||
<Package className="h-3 w-3 text-blue-400" />
|
<TabsList className="grid w-full grid-cols-3 bg-white/10 backdrop-blur border-white/10 flex-shrink-0 h-10 mb-4">
|
||||||
</div>
|
<TabsTrigger
|
||||||
<div>
|
value="new"
|
||||||
<p className="text-white/60 text-xs">Поставок</p>
|
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 flex items-center gap-2 text-sm"
|
||||||
<p className="text-lg font-bold text-white">
|
>
|
||||||
{filteredSupplies.length}
|
<Clock className="h-4 w-4" />
|
||||||
</p>
|
Новые
|
||||||
</div>
|
</TabsTrigger>
|
||||||
</div>
|
<TabsTrigger
|
||||||
</Card>
|
value="receiving"
|
||||||
|
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 flex items-center gap-2 text-sm"
|
||||||
|
>
|
||||||
|
<FileText className="h-4 w-4" />
|
||||||
|
Приёмка
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger
|
||||||
|
value="received"
|
||||||
|
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 flex items-center gap-2 text-sm"
|
||||||
|
>
|
||||||
|
<CheckCircle className="h-4 w-4" />
|
||||||
|
Принято
|
||||||
|
</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
|
||||||
<Card className="glass-card p-3 h-[60px]">
|
<TabsContent value="new" className="flex-1 overflow-hidden">
|
||||||
<div className="flex items-center space-x-2 h-full">
|
<TabContent tabName="new" />
|
||||||
<div className="p-1.5 bg-green-500/20 rounded">
|
</TabsContent>
|
||||||
<TrendingUp className="h-3 w-3 text-green-400" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="text-white/60 text-xs">Стоимость</p>
|
|
||||||
<p className="text-lg font-bold text-white">
|
|
||||||
{formatCurrency(getTotalValue())}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card className="glass-card p-3 h-[60px]">
|
<TabsContent value="receiving" className="flex-1 overflow-hidden">
|
||||||
<div className="flex items-center space-x-2 h-full">
|
<TabContent tabName="receiving" />
|
||||||
<div className="p-1.5 bg-purple-500/20 rounded">
|
</TabsContent>
|
||||||
<Package2 className="h-3 w-3 text-purple-400" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="text-white/60 text-xs">Товаров</p>
|
|
||||||
<p className="text-lg font-bold text-white">
|
|
||||||
{getTotalQuantity()} ед.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card className="glass-card p-3 h-[60px]">
|
<TabsContent value="received" className="flex-1 overflow-hidden">
|
||||||
<div className="flex items-center space-x-2 h-full">
|
<TabContent tabName="received" />
|
||||||
<div className="p-1.5 bg-orange-500/20 rounded">
|
</TabsContent>
|
||||||
<Boxes className="h-3 w-3 text-orange-400" />
|
</Tabs>
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="text-white/60 text-xs">Объём</p>
|
|
||||||
<p className="text-lg font-bold text-white">
|
|
||||||
{getTotalVolume().toFixed(1)} м³
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
className="bg-gradient-to-r from-blue-500 to-purple-500 hover:from-blue-600 hover:to-purple-600 text-white text-sm px-6 h-[60px] whitespace-nowrap"
|
|
||||||
>
|
|
||||||
<Plus className="h-4 w-4 mr-2" />
|
|
||||||
Создать поставку
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Фильтры */}
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<div className="relative flex-1 max-w-md">
|
|
||||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-white/40" />
|
|
||||||
<Input
|
|
||||||
placeholder="Поиск по номеру, магазину, ИНН..."
|
|
||||||
value={searchTerm}
|
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
|
||||||
className="glass-input pl-10 text-white placeholder:text-white/40"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<select
|
|
||||||
value={statusFilter}
|
|
||||||
onChange={(e) => setStatusFilter(e.target.value)}
|
|
||||||
className="glass-input text-white text-sm px-3 py-2 rounded-lg bg-white/5 border border-white/10"
|
|
||||||
>
|
|
||||||
<option value="all">Все статусы</option>
|
|
||||||
<option value="planned">Запланировано</option>
|
|
||||||
<option value="in-transit">В пути</option>
|
|
||||||
<option value="delivered">Доставлено</option>
|
|
||||||
<option value="in-processing">Обрабатывается</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Список поставок */}
|
|
||||||
<div className="flex-1 overflow-hidden">
|
|
||||||
<div className="h-full overflow-y-auto space-y-3">
|
|
||||||
{filteredSupplies.map((supply, index) => {
|
|
||||||
const isSellerExpanded = expandedSellers.has(supply.seller.id);
|
|
||||||
const isSupplyExpanded = expandedSupplies.has(supply.id);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card
|
|
||||||
key={supply.id}
|
|
||||||
className="glass-card p-4 hover:bg-white/10 transition-colors"
|
|
||||||
>
|
|
||||||
<div className="space-y-3">
|
|
||||||
{/* Компактный блок с названием магазина */}
|
|
||||||
<div className="flex items-center justify-between bg-white/5 rounded-lg p-2">
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<div className="p-1.5 bg-green-500/20 rounded">
|
|
||||||
<Store className="h-3 w-3 text-green-400" />
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<span className="text-white font-medium text-sm">
|
|
||||||
{supply.seller.storeName}
|
|
||||||
</span>
|
|
||||||
<span className="text-white/60 text-xs"> • </span>
|
|
||||||
<span className="text-white/80 text-xs">
|
|
||||||
{supply.seller.managerName}
|
|
||||||
</span>
|
|
||||||
<span className="text-white/60 text-xs"> • </span>
|
|
||||||
<span className="text-white/80 text-xs">
|
|
||||||
{supply.seller.phone}
|
|
||||||
</span>
|
|
||||||
<span className="text-white/60 text-xs"> • </span>
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<a
|
|
||||||
href={`https://t.me/${supply.seller.phone.replace(
|
|
||||||
/[^\d]/g,
|
|
||||||
""
|
|
||||||
)}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="p-1 bg-blue-500/20 rounded hover:bg-blue-500/30 transition-colors"
|
|
||||||
title="Написать в Telegram"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="h-3 w-3 text-blue-400"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="currentColor"
|
|
||||||
>
|
|
||||||
<path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z" />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href={`https://wa.me/${supply.seller.phone.replace(
|
|
||||||
/[^\d]/g,
|
|
||||||
""
|
|
||||||
)}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="p-1 bg-green-500/20 rounded hover:bg-green-500/30 transition-colors"
|
|
||||||
title="Написать в WhatsApp"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="h-3 w-3 text-green-400"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="currentColor"
|
|
||||||
>
|
|
||||||
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893A11.821 11.821 0 0020.885 3.488" />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<span className="text-white/60 text-xs">
|
|
||||||
Номер поставки
|
|
||||||
</span>
|
|
||||||
<span className="text-primary font-semibold text-sm">
|
|
||||||
{supply.supplyNumber}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => toggleSellerExpansion(supply.seller.id)}
|
|
||||||
className="h-6 w-6 p-0 text-white/60 hover:text-white hover:bg-white/10"
|
|
||||||
>
|
|
||||||
{isSellerExpanded ? (
|
|
||||||
<ChevronDown className="h-3 w-3" />
|
|
||||||
) : (
|
|
||||||
<ChevronRight className="h-3 w-3" />
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Скрытая детальная информация о магазине */}
|
|
||||||
{isSellerExpanded && (
|
|
||||||
<div className="bg-white/5 rounded-lg p-3 space-y-2">
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
|
||||||
<div>
|
|
||||||
<p className="text-white/60 text-xs">
|
|
||||||
Юридическое название
|
|
||||||
</p>
|
|
||||||
<p className="text-white text-sm">
|
|
||||||
{supply.seller.name}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="text-white/60 text-xs">ИНН</p>
|
|
||||||
<p className="text-white text-sm font-mono">
|
|
||||||
{supply.seller.inn}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="text-white/60 text-xs">Email</p>
|
|
||||||
<p className="text-white text-sm">
|
|
||||||
{supply.seller.email}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Единый блок со всеми параметрами в одной строке */}
|
|
||||||
<div
|
|
||||||
className="bg-white/5 rounded-lg p-4 cursor-pointer hover:bg-white/10 transition-colors"
|
|
||||||
onClick={() => toggleSupplyExpansion(supply.id)}
|
|
||||||
>
|
|
||||||
<div className="grid grid-cols-2 lg:grid-cols-9 gap-4 items-center">
|
|
||||||
{/* Порядковый номер */}
|
|
||||||
<div className="text-center">
|
|
||||||
<p className="text-white/60 text-xs mb-1">№</p>
|
|
||||||
<div className="flex items-center justify-center">
|
|
||||||
<div className="bg-blue-500/20 rounded-full w-8 h-8 flex items-center justify-center">
|
|
||||||
<span className="text-blue-300 font-bold text-sm">
|
|
||||||
{filteredSupplies.length - index}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Дата поставки */}
|
|
||||||
<div className="text-center">
|
|
||||||
<p className="text-white/60 text-xs mb-1">Дата</p>
|
|
||||||
<p className="text-white font-semibold text-sm">
|
|
||||||
{formatDate(supply.supplyDate)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Количество товаров */}
|
|
||||||
<div className="text-center">
|
|
||||||
<p className="text-white/60 text-xs mb-1">Товаров</p>
|
|
||||||
<p className="text-white font-semibold text-sm">
|
|
||||||
{supply.itemsQuantity}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Количество мест */}
|
|
||||||
<div className="text-center">
|
|
||||||
<p className="text-white/60 text-xs mb-1">Мест</p>
|
|
||||||
<p className="text-white font-semibold text-sm">
|
|
||||||
{supply.cargoPlaces}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Объём */}
|
|
||||||
<div className="text-center">
|
|
||||||
<p className="text-white/60 text-xs mb-1">Объём</p>
|
|
||||||
<p className="text-white font-semibold text-sm">
|
|
||||||
{supply.volume} м³
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Стоимость */}
|
|
||||||
<div className="text-center">
|
|
||||||
<p className="text-white/60 text-xs mb-1">Стоимость</p>
|
|
||||||
<p className="text-green-400 font-semibold text-sm">
|
|
||||||
{formatCurrency(supply.totalValue)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Ответственный сотрудник */}
|
|
||||||
<div
|
|
||||||
className="text-center"
|
|
||||||
onClick={(e) => e.stopPropagation()}
|
|
||||||
>
|
|
||||||
<p className="text-white/60 text-xs mb-1">
|
|
||||||
Ответственный
|
|
||||||
</p>
|
|
||||||
<Select defaultValue="">
|
|
||||||
<SelectTrigger className="h-6 w-full glass-input bg-white/10 border-white/20 text-white hover:bg-white/15 focus:bg-white/15 focus:ring-1 focus:ring-purple-400/50 text-xs px-2">
|
|
||||||
<SelectValue placeholder="не выбрано" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent className="bg-gray-900/95 backdrop-blur border-white/20 text-white">
|
|
||||||
{employees.map((employee) => (
|
|
||||||
<SelectItem
|
|
||||||
key={employee.id}
|
|
||||||
value={employee.id}
|
|
||||||
className="text-white hover:bg-white/10 focus:bg-white/10 text-xs"
|
|
||||||
>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<span className="font-medium">
|
|
||||||
{employee.firstName} {employee.lastName}
|
|
||||||
</span>
|
|
||||||
<span className="text-xs text-white/60">
|
|
||||||
{employee.position}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Логистический партнер */}
|
|
||||||
<div
|
|
||||||
className="text-center"
|
|
||||||
onClick={(e) => e.stopPropagation()}
|
|
||||||
>
|
|
||||||
<p className="text-white/60 text-xs mb-1">Логистика</p>
|
|
||||||
<Select defaultValue="">
|
|
||||||
<SelectTrigger className="h-6 w-full glass-input bg-white/10 border-white/20 text-white hover:bg-white/15 focus:bg-white/15 focus:ring-1 focus:ring-orange-400/50 text-xs px-2">
|
|
||||||
<SelectValue placeholder="не выбрано" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent className="bg-gray-900/95 backdrop-blur border-white/20 text-white">
|
|
||||||
{logisticsPartners.map((partner: Organization) => (
|
|
||||||
<SelectItem
|
|
||||||
key={partner.id}
|
|
||||||
value={partner.id}
|
|
||||||
className="text-white hover:bg-white/10 focus:bg-white/10 text-xs"
|
|
||||||
>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<span className="font-medium">
|
|
||||||
{partner.name ||
|
|
||||||
partner.fullName ||
|
|
||||||
"Без названия"}
|
|
||||||
</span>
|
|
||||||
<span className="text-xs text-white/60">
|
|
||||||
Логистический партнер
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Статус */}
|
|
||||||
<div className="text-center">
|
|
||||||
<p className="text-white/60 text-xs mb-1">Статус</p>
|
|
||||||
<div className="flex justify-center">
|
|
||||||
{getStatusBadge(supply.status)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Второй уровень - Маршруты */}
|
|
||||||
{isSupplyExpanded &&
|
|
||||||
supply.routes &&
|
|
||||||
supply.routes.length > 0 && (
|
|
||||||
<div className="mt-4 pt-4 border-t border-white/10">
|
|
||||||
<div className="mb-3">
|
|
||||||
<h4 className="text-white font-medium text-sm flex items-center gap-2">
|
|
||||||
<Truck className="h-4 w-4 text-orange-400" />
|
|
||||||
Маршруты ({supply.routes.length})
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
{supply.routes.map((route) => {
|
|
||||||
const isRouteExpanded = expandedRoutes.has(
|
|
||||||
route.id
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={route.id}
|
|
||||||
className="bg-white/5 rounded-lg p-3 border border-white/10"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="grid grid-cols-1 lg:grid-cols-8 gap-3 items-center cursor-pointer hover:bg-white/5 transition-colors rounded-lg p-1"
|
|
||||||
onClick={() =>
|
|
||||||
toggleRouteExpansion(route.id)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{/* Название маршрута */}
|
|
||||||
<div className="lg:col-span-2">
|
|
||||||
<p className="text-white/60 text-xs mb-1">
|
|
||||||
Маршрут
|
|
||||||
</p>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<p className="text-white font-medium text-sm">
|
|
||||||
{route.routeName}
|
|
||||||
</p>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
className="h-4 w-4 p-0 text-white/60 hover:text-white hover:bg-white/10"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
toggleRouteExpansion(route.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{isRouteExpanded ? (
|
|
||||||
<ChevronDown className="h-3 w-3" />
|
|
||||||
) : (
|
|
||||||
<ChevronRight className="h-3 w-3" />
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Откуда */}
|
|
||||||
<div>
|
|
||||||
<p className="text-white/60 text-xs mb-1">
|
|
||||||
Откуда
|
|
||||||
</p>
|
|
||||||
<p className="text-white text-xs">
|
|
||||||
{route.fromAddress}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Куда */}
|
|
||||||
<div>
|
|
||||||
<p className="text-white/60 text-xs mb-1">
|
|
||||||
Куда
|
|
||||||
</p>
|
|
||||||
<p className="text-white text-xs">
|
|
||||||
{route.toAddress}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Расстояние */}
|
|
||||||
<div className="text-center">
|
|
||||||
<p className="text-white/60 text-xs mb-1">
|
|
||||||
Расстояние
|
|
||||||
</p>
|
|
||||||
<p className="text-white font-semibold text-sm">
|
|
||||||
{route.distance} км
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Время в пути */}
|
|
||||||
<div className="text-center">
|
|
||||||
<p className="text-white/60 text-xs mb-1">
|
|
||||||
Время
|
|
||||||
</p>
|
|
||||||
<p className="text-white font-semibold text-sm">
|
|
||||||
{route.estimatedTime}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Транспорт */}
|
|
||||||
<div className="text-center">
|
|
||||||
<p className="text-white/60 text-xs mb-1">
|
|
||||||
Транспорт
|
|
||||||
</p>
|
|
||||||
<p className="text-white text-xs">
|
|
||||||
{route.transportType}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Стоимость и статус */}
|
|
||||||
<div className="text-center">
|
|
||||||
<p className="text-white/60 text-xs mb-1">
|
|
||||||
Стоимость
|
|
||||||
</p>
|
|
||||||
<p className="text-green-400 font-semibold text-sm mb-1">
|
|
||||||
{formatCurrency(route.cost)}
|
|
||||||
</p>
|
|
||||||
{getRouteStatusBadge(route.status)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Третий уровень - Поставщики/Оптовики */}
|
|
||||||
{isRouteExpanded &&
|
|
||||||
route.suppliers &&
|
|
||||||
route.suppliers.length > 0 && (
|
|
||||||
<div className="mt-4 pt-4 border-t border-white/10">
|
|
||||||
<div className="mb-3">
|
|
||||||
<h5 className="text-white font-medium text-sm flex items-center gap-2">
|
|
||||||
<Building2 className="h-4 w-4 text-purple-400" />
|
|
||||||
Поставщики/Оптовики (
|
|
||||||
{route.suppliers.length})
|
|
||||||
</h5>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
{route.suppliers.map((supplier) => {
|
|
||||||
const isSupplierExpanded =
|
|
||||||
expandedSuppliers.has(
|
|
||||||
supplier.id
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={supplier.id}
|
|
||||||
className="bg-white/5 rounded-lg p-3 border border-white/5"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="grid grid-cols-1 lg:grid-cols-7 gap-3 items-center cursor-pointer hover:bg-white/5 transition-colors rounded-lg p-1"
|
|
||||||
onClick={() =>
|
|
||||||
toggleSupplierExpansion(
|
|
||||||
supplier.id
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{/* Название поставщика */}
|
|
||||||
<div className="lg:col-span-2">
|
|
||||||
<p className="text-white/60 text-xs mb-1">
|
|
||||||
Поставщик
|
|
||||||
</p>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<p className="text-white font-medium text-sm">
|
|
||||||
{supplier.name}
|
|
||||||
</p>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
className="h-4 w-4 p-0 text-white/60 hover:text-white hover:bg-white/10"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
toggleSupplierExpansion(
|
|
||||||
supplier.id
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{isSupplierExpanded ? (
|
|
||||||
<ChevronDown className="h-3 w-3" />
|
|
||||||
) : (
|
|
||||||
<ChevronRight className="h-3 w-3" />
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* ИНН */}
|
|
||||||
<div>
|
|
||||||
<p className="text-white/60 text-xs mb-1">
|
|
||||||
ИНН
|
|
||||||
</p>
|
|
||||||
<p className="text-white text-xs font-mono">
|
|
||||||
{supplier.inn}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Тип */}
|
|
||||||
<div className="text-center">
|
|
||||||
<p className="text-white/60 text-xs mb-1">
|
|
||||||
Тип
|
|
||||||
</p>
|
|
||||||
<Badge
|
|
||||||
variant="outline"
|
|
||||||
className={`text-xs ${
|
|
||||||
supplier.type ===
|
|
||||||
"WHOLESALE"
|
|
||||||
? "text-blue-300 border-blue-400/30 bg-blue-500/10"
|
|
||||||
: "text-orange-300 border-orange-400/30 bg-orange-500/10"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{supplier.type ===
|
|
||||||
"WHOLESALE"
|
|
||||||
? "Оптовик"
|
|
||||||
: "Поставщик"}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Менеджер */}
|
|
||||||
<div>
|
|
||||||
<p className="text-white/60 text-xs mb-1">
|
|
||||||
Менеджер
|
|
||||||
</p>
|
|
||||||
<p className="text-white text-xs">
|
|
||||||
{supplier.managerName}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Стоимость */}
|
|
||||||
<div className="text-center">
|
|
||||||
<p className="text-white/60 text-xs mb-1">
|
|
||||||
Стоимость
|
|
||||||
</p>
|
|
||||||
<p className="text-green-400 font-semibold text-sm">
|
|
||||||
{formatCurrency(
|
|
||||||
supplier.totalValue
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Статус */}
|
|
||||||
<div className="text-center">
|
|
||||||
<p className="text-white/60 text-xs mb-1">
|
|
||||||
Статус
|
|
||||||
</p>
|
|
||||||
{getSupplierStatusBadge(
|
|
||||||
supplier.status
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Детальная информация о поставщике */}
|
|
||||||
{isSupplierExpanded && (
|
|
||||||
<div className="mt-3 pt-3 border-t border-white/5">
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
|
|
||||||
<div>
|
|
||||||
<p className="text-white/60 text-xs mb-1">
|
|
||||||
Полное название
|
|
||||||
</p>
|
|
||||||
<p className="text-white text-sm">
|
|
||||||
{supplier.fullName ||
|
|
||||||
supplier.name}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="text-white/60 text-xs mb-1">
|
|
||||||
Телефон
|
|
||||||
</p>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<p className="text-white text-sm">
|
|
||||||
{supplier.phone}
|
|
||||||
</p>
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<a
|
|
||||||
href={`https://t.me/${supplier.phone.replace(
|
|
||||||
/[^\d]/g,
|
|
||||||
""
|
|
||||||
)}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="p-1 bg-blue-500/20 rounded hover:bg-blue-500/30 transition-colors"
|
|
||||||
title="Написать в Telegram"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="h-3 w-3 text-blue-400"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="currentColor"
|
|
||||||
>
|
|
||||||
<path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z" />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href={`https://wa.me/${supplier.phone.replace(
|
|
||||||
/[^\d]/g,
|
|
||||||
""
|
|
||||||
)}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="p-1 bg-green-500/20 rounded hover:bg-green-500/30 transition-colors"
|
|
||||||
title="Написать в WhatsApp"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="h-3 w-3 text-green-400"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="currentColor"
|
|
||||||
>
|
|
||||||
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893A11.821 11.821 0 0020.885 3.488" />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="text-white/60 text-xs mb-1">
|
|
||||||
Email
|
|
||||||
</p>
|
|
||||||
<p className="text-white text-sm">
|
|
||||||
{supplier.email}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mt-3">
|
|
||||||
<p className="text-white/60 text-xs mb-1">
|
|
||||||
Адрес
|
|
||||||
</p>
|
|
||||||
<p className="text-white text-sm">
|
|
||||||
{supplier.address}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
function TabContent({ tabName }: { tabName: string }) {
|
||||||
|
const tabFilteredSupplies = getFilteredSuppliesByTab(tabName);
|
||||||
|
|
||||||
|
const getTabTotalValue = () => {
|
||||||
|
return tabFilteredSupplies.reduce(
|
||||||
|
(sum, supply) => sum + supply.totalValue,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTabTotalQuantity = () => {
|
||||||
|
return tabFilteredSupplies.reduce(
|
||||||
|
(sum, supply) => sum + supply.itemsQuantity,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTabTotalVolume = () => {
|
||||||
|
return tabFilteredSupplies.reduce(
|
||||||
|
(sum, supply) => sum + supply.volume,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full flex flex-col space-y-4">
|
||||||
|
{/* Статистика с кнопкой */}
|
||||||
|
<div className="flex items-center justify-between gap-4">
|
||||||
|
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3 flex-1">
|
||||||
|
<Card className="glass-card p-3 h-[60px]">
|
||||||
|
<div className="flex items-center space-x-2 h-full">
|
||||||
|
<div className="p-1.5 bg-blue-500/20 rounded">
|
||||||
|
<Package className="h-3 w-3 text-blue-400" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-white/60 text-xs">Поставок</p>
|
||||||
|
<p className="text-lg font-bold text-white">
|
||||||
|
{tabFilteredSupplies.length}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card className="glass-card p-3 h-[60px]">
|
||||||
|
<div className="flex items-center space-x-2 h-full">
|
||||||
|
<div className="p-1.5 bg-green-500/20 rounded">
|
||||||
|
<TrendingUp className="h-3 w-3 text-green-400" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-white/60 text-xs">Стоимость</p>
|
||||||
|
<p className="text-lg font-bold text-white">
|
||||||
|
{formatCurrency(getTabTotalValue())}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card className="glass-card p-3 h-[60px]">
|
||||||
|
<div className="flex items-center space-x-2 h-full">
|
||||||
|
<div className="p-1.5 bg-purple-500/20 rounded">
|
||||||
|
<Package2 className="h-3 w-3 text-purple-400" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-white/60 text-xs">Товаров</p>
|
||||||
|
<p className="text-lg font-bold text-white">
|
||||||
|
{getTabTotalQuantity()} ед.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card className="glass-card p-3 h-[60px]">
|
||||||
|
<div className="flex items-center space-x-2 h-full">
|
||||||
|
<div className="p-1.5 bg-orange-500/20 rounded">
|
||||||
|
<Boxes className="h-3 w-3 text-orange-400" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-white/60 text-xs">Объём</p>
|
||||||
|
<p className="text-lg font-bold text-white">
|
||||||
|
{getTabTotalVolume().toFixed(1)} м³
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
className="bg-gradient-to-r from-blue-500 to-purple-500 hover:from-blue-600 hover:to-purple-600 text-white text-sm px-6 h-[60px] whitespace-nowrap"
|
||||||
|
>
|
||||||
|
<Plus className="h-4 w-4 mr-2" />
|
||||||
|
Создать поставку
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Фильтры */}
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="relative flex-1 max-w-md">
|
||||||
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-white/40" />
|
||||||
|
<Input
|
||||||
|
placeholder="Поиск по номеру, магазину, ИНН..."
|
||||||
|
value={searchTerm}
|
||||||
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
|
className="glass-input pl-10 text-white placeholder:text-white/40"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<select
|
||||||
|
value={statusFilter}
|
||||||
|
onChange={(e) => setStatusFilter(e.target.value)}
|
||||||
|
className="glass-input text-white text-sm px-3 py-2 rounded-lg bg-white/5 border border-white/10"
|
||||||
|
>
|
||||||
|
<option value="all">Все статусы</option>
|
||||||
|
<option value="planned">Запланировано</option>
|
||||||
|
<option value="in-transit">В пути</option>
|
||||||
|
<option value="delivered">Доставлено</option>
|
||||||
|
<option value="in-processing">Обрабатывается</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Список поставок */}
|
||||||
|
<div className="flex-1 overflow-hidden">
|
||||||
|
<div className="h-full overflow-y-auto space-y-3">
|
||||||
|
{tabFilteredSupplies.map((supply, index) => {
|
||||||
|
const isSellerExpanded = expandedSellers.has(supply.seller.id);
|
||||||
|
const isSupplyExpanded = expandedSupplies.has(supply.id);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
key={supply.id}
|
||||||
|
className="glass-card p-4 hover:bg-white/10 transition-colors"
|
||||||
|
>
|
||||||
|
<div className="space-y-3">
|
||||||
|
{/* Компактный блок с названием магазина */}
|
||||||
|
<div className="flex items-center justify-between bg-white/5 rounded-lg p-2">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="p-1.5 bg-green-500/20 rounded">
|
||||||
|
<Store className="h-3 w-3 text-green-400" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-white font-medium text-sm">
|
||||||
|
{supply.seller.storeName}
|
||||||
|
</span>
|
||||||
|
<span className="text-white/60 text-xs"> • </span>
|
||||||
|
<span className="text-white/80 text-xs">
|
||||||
|
{supply.seller.managerName}
|
||||||
|
</span>
|
||||||
|
<span className="text-white/60 text-xs"> • </span>
|
||||||
|
<span className="text-white/80 text-xs">
|
||||||
|
{supply.seller.phone}
|
||||||
|
</span>
|
||||||
|
<span className="text-white/60 text-xs"> • </span>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<a
|
||||||
|
href={`https://t.me/${supply.seller.phone.replace(
|
||||||
|
/[^\d]/g,
|
||||||
|
""
|
||||||
|
)}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="p-1 bg-blue-500/20 rounded hover:bg-blue-500/30 transition-colors"
|
||||||
|
title="Написать в Telegram"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
className="h-3 w-3 text-blue-400"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href={`https://wa.me/${supply.seller.phone.replace(
|
||||||
|
/[^\d]/g,
|
||||||
|
""
|
||||||
|
)}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="p-1 bg-green-500/20 rounded hover:bg-green-500/30 transition-colors"
|
||||||
|
title="Написать в WhatsApp"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
className="h-3 w-3 text-green-400"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893A11.821 11.821 0 0020.885 3.488" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-white/60 text-xs">
|
||||||
|
Номер поставки
|
||||||
|
</span>
|
||||||
|
<span className="text-primary font-semibold text-sm">
|
||||||
|
{supply.supplyNumber}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() =>
|
||||||
|
toggleSellerExpansion(supply.seller.id)
|
||||||
|
}
|
||||||
|
className="h-6 w-6 p-0 text-white/60 hover:text-white hover:bg-white/10"
|
||||||
|
>
|
||||||
|
{isSellerExpanded ? (
|
||||||
|
<ChevronDown className="h-3 w-3" />
|
||||||
|
) : (
|
||||||
|
<ChevronRight className="h-3 w-3" />
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Скрытая детальная информация о магазине */}
|
||||||
|
{isSellerExpanded && (
|
||||||
|
<div className="bg-white/5 rounded-lg p-3 space-y-2">
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<p className="text-white/60 text-xs">
|
||||||
|
Юридическое название
|
||||||
|
</p>
|
||||||
|
<p className="text-white text-sm">
|
||||||
|
{supply.seller.name}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-white/60 text-xs">ИНН</p>
|
||||||
|
<p className="text-white text-sm font-mono">
|
||||||
|
{supply.seller.inn}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-white/60 text-xs">Email</p>
|
||||||
|
<p className="text-white text-sm">
|
||||||
|
{supply.seller.email}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Единый блок со всеми параметрами в одной строке */}
|
||||||
|
<div
|
||||||
|
className="bg-white/5 rounded-lg p-4 cursor-pointer hover:bg-white/10 transition-colors"
|
||||||
|
onClick={() => toggleSupplyExpansion(supply.id)}
|
||||||
|
>
|
||||||
|
<div className="grid grid-cols-2 lg:grid-cols-9 gap-4 items-center">
|
||||||
|
{/* Порядковый номер */}
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="text-white/60 text-xs mb-1">№</p>
|
||||||
|
<div className="flex items-center justify-center">
|
||||||
|
<div className="bg-blue-500/20 rounded-full w-8 h-8 flex items-center justify-center">
|
||||||
|
<span className="text-blue-300 font-bold text-sm">
|
||||||
|
{tabFilteredSupplies.length - index}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Дата поставки */}
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="text-white/60 text-xs mb-1">Дата</p>
|
||||||
|
<p className="text-white font-semibold text-sm">
|
||||||
|
{formatDate(supply.supplyDate)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Количество товаров */}
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="text-white/60 text-xs mb-1">Товаров</p>
|
||||||
|
<p className="text-white font-semibold text-sm">
|
||||||
|
{supply.itemsQuantity}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Количество мест */}
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="text-white/60 text-xs mb-1">Мест</p>
|
||||||
|
<p className="text-white font-semibold text-sm">
|
||||||
|
{supply.cargoPlaces}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Объём */}
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="text-white/60 text-xs mb-1">Объём</p>
|
||||||
|
<p className="text-white font-semibold text-sm">
|
||||||
|
{supply.volume} м³
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Стоимость */}
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="text-white/60 text-xs mb-1">
|
||||||
|
Стоимость
|
||||||
|
</p>
|
||||||
|
<p className="text-green-400 font-semibold text-sm">
|
||||||
|
{formatCurrency(supply.totalValue)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Ответственный сотрудник */}
|
||||||
|
<div
|
||||||
|
className="text-center"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<p className="text-white/60 text-xs mb-1">
|
||||||
|
Ответственный
|
||||||
|
</p>
|
||||||
|
<Select defaultValue="">
|
||||||
|
<SelectTrigger className="h-6 w-full glass-input bg-white/10 border-white/20 text-white hover:bg-white/15 focus:bg-white/15 focus:ring-1 focus:ring-purple-400/50 text-xs px-2">
|
||||||
|
<SelectValue placeholder="не выбрано" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent className="bg-gray-900/95 backdrop-blur border-white/20 text-white">
|
||||||
|
{employees.map((employee) => (
|
||||||
|
<SelectItem
|
||||||
|
key={employee.id}
|
||||||
|
value={employee.id}
|
||||||
|
className="text-white hover:bg-white/10 focus:bg-white/10 text-xs"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="font-medium">
|
||||||
|
{employee.firstName} {employee.lastName}
|
||||||
|
</span>
|
||||||
|
<span className="text-xs text-white/60">
|
||||||
|
{employee.position}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Логистический партнер */}
|
||||||
|
<div
|
||||||
|
className="text-center"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<p className="text-white/60 text-xs mb-1">
|
||||||
|
Логистика
|
||||||
|
</p>
|
||||||
|
<Select defaultValue="">
|
||||||
|
<SelectTrigger className="h-6 w-full glass-input bg-white/10 border-white/20 text-white hover:bg-white/15 focus:bg-white/15 focus:ring-1 focus:ring-orange-400/50 text-xs px-2">
|
||||||
|
<SelectValue placeholder="не выбрано" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent className="bg-gray-900/95 backdrop-blur border-white/20 text-white">
|
||||||
|
{logisticsPartners.map(
|
||||||
|
(partner: Organization) => (
|
||||||
|
<SelectItem
|
||||||
|
key={partner.id}
|
||||||
|
value={partner.id}
|
||||||
|
className="text-white hover:bg-white/10 focus:bg-white/10 text-xs"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="font-medium">
|
||||||
|
{partner.name ||
|
||||||
|
partner.fullName ||
|
||||||
|
"Без названия"}
|
||||||
|
</span>
|
||||||
|
<span className="text-xs text-white/60">
|
||||||
|
Логистический партнер
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</SelectItem>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Статус */}
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="text-white/60 text-xs mb-1">Статус</p>
|
||||||
|
<div className="flex justify-center">
|
||||||
|
{getStatusBadge(supply.status)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Второй уровень - Маршруты */}
|
||||||
|
{isSupplyExpanded &&
|
||||||
|
supply.routes &&
|
||||||
|
supply.routes.length > 0 && (
|
||||||
|
<div className="mt-4 pt-4 border-t border-white/10">
|
||||||
|
<div className="mb-3">
|
||||||
|
<h4 className="text-white font-medium text-sm flex items-center gap-2">
|
||||||
|
<Truck className="h-4 w-4 text-orange-400" />
|
||||||
|
Маршруты ({supply.routes.length})
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
{supply.routes.map((route) => {
|
||||||
|
const isRouteExpanded = expandedRoutes.has(
|
||||||
|
route.id
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={route.id}
|
||||||
|
className="bg-white/5 rounded-lg p-3 border border-white/10"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="grid grid-cols-1 lg:grid-cols-8 gap-3 items-center cursor-pointer hover:bg-white/5 transition-colors rounded-lg p-1"
|
||||||
|
onClick={() =>
|
||||||
|
toggleRouteExpansion(route.id)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{/* Название маршрута */}
|
||||||
|
<div className="lg:col-span-2">
|
||||||
|
<p className="text-white/60 text-xs mb-1">
|
||||||
|
Маршрут
|
||||||
|
</p>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<p className="text-white font-medium text-sm">
|
||||||
|
{route.routeName}
|
||||||
|
</p>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="h-4 w-4 p-0 text-white/60 hover:text-white hover:bg-white/10"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
toggleRouteExpansion(route.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isRouteExpanded ? (
|
||||||
|
<ChevronDown className="h-3 w-3" />
|
||||||
|
) : (
|
||||||
|
<ChevronRight className="h-3 w-3" />
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Откуда */}
|
||||||
|
<div>
|
||||||
|
<p className="text-white/60 text-xs mb-1">
|
||||||
|
Откуда
|
||||||
|
</p>
|
||||||
|
<p className="text-white text-xs">
|
||||||
|
{route.fromAddress}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Куда */}
|
||||||
|
<div>
|
||||||
|
<p className="text-white/60 text-xs mb-1">
|
||||||
|
Куда
|
||||||
|
</p>
|
||||||
|
<p className="text-white text-xs">
|
||||||
|
{route.toAddress}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Расстояние */}
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="text-white/60 text-xs mb-1">
|
||||||
|
Расстояние
|
||||||
|
</p>
|
||||||
|
<p className="text-white font-semibold text-sm">
|
||||||
|
{route.distance} км
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Время в пути */}
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="text-white/60 text-xs mb-1">
|
||||||
|
Время
|
||||||
|
</p>
|
||||||
|
<p className="text-white font-semibold text-sm">
|
||||||
|
{route.estimatedTime}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Транспорт */}
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="text-white/60 text-xs mb-1">
|
||||||
|
Транспорт
|
||||||
|
</p>
|
||||||
|
<p className="text-white text-xs">
|
||||||
|
{route.transportType}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Стоимость и статус */}
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="text-white/60 text-xs mb-1">
|
||||||
|
Стоимость
|
||||||
|
</p>
|
||||||
|
<p className="text-green-400 font-semibold text-sm mb-1">
|
||||||
|
{formatCurrency(route.cost)}
|
||||||
|
</p>
|
||||||
|
{getRouteStatusBadge(route.status)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Третий уровень - Поставщики/Оптовики */}
|
||||||
|
{isRouteExpanded &&
|
||||||
|
route.suppliers &&
|
||||||
|
route.suppliers.length > 0 && (
|
||||||
|
<div className="mt-4 pt-4 border-t border-white/10">
|
||||||
|
<div className="mb-3">
|
||||||
|
<h5 className="text-white font-medium text-sm flex items-center gap-2">
|
||||||
|
<Building2 className="h-4 w-4 text-purple-400" />
|
||||||
|
Поставщики/Оптовики (
|
||||||
|
{route.suppliers.length})
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
{route.suppliers.map((supplier) => {
|
||||||
|
const isSupplierExpanded =
|
||||||
|
expandedSuppliers.has(
|
||||||
|
supplier.id
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={supplier.id}
|
||||||
|
className="bg-white/5 rounded-lg p-3 border border-white/5"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="grid grid-cols-1 lg:grid-cols-7 gap-3 items-center cursor-pointer hover:bg-white/5 transition-colors rounded-lg p-1"
|
||||||
|
onClick={() =>
|
||||||
|
toggleSupplierExpansion(
|
||||||
|
supplier.id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{/* Название поставщика */}
|
||||||
|
<div className="lg:col-span-2">
|
||||||
|
<p className="text-white/60 text-xs mb-1">
|
||||||
|
Поставщик
|
||||||
|
</p>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<p className="text-white font-medium text-sm">
|
||||||
|
{supplier.name}
|
||||||
|
</p>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="h-4 w-4 p-0 text-white/60 hover:text-white hover:bg-white/10"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
toggleSupplierExpansion(
|
||||||
|
supplier.id
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isSupplierExpanded ? (
|
||||||
|
<ChevronDown className="h-3 w-3" />
|
||||||
|
) : (
|
||||||
|
<ChevronRight className="h-3 w-3" />
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* ИНН */}
|
||||||
|
<div>
|
||||||
|
<p className="text-white/60 text-xs mb-1">
|
||||||
|
ИНН
|
||||||
|
</p>
|
||||||
|
<p className="text-white text-xs font-mono">
|
||||||
|
{supplier.inn}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Тип */}
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="text-white/60 text-xs mb-1">
|
||||||
|
Тип
|
||||||
|
</p>
|
||||||
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className={`text-xs ${
|
||||||
|
supplier.type ===
|
||||||
|
"WHOLESALE"
|
||||||
|
? "text-blue-300 border-blue-400/30 bg-blue-500/10"
|
||||||
|
: "text-orange-300 border-orange-400/30 bg-orange-500/10"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{supplier.type ===
|
||||||
|
"WHOLESALE"
|
||||||
|
? "Оптовик"
|
||||||
|
: "Поставщик"}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Менеджер */}
|
||||||
|
<div>
|
||||||
|
<p className="text-white/60 text-xs mb-1">
|
||||||
|
Менеджер
|
||||||
|
</p>
|
||||||
|
<p className="text-white text-xs">
|
||||||
|
{supplier.managerName}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Стоимость */}
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="text-white/60 text-xs mb-1">
|
||||||
|
Стоимость
|
||||||
|
</p>
|
||||||
|
<p className="text-green-400 font-semibold text-sm">
|
||||||
|
{formatCurrency(
|
||||||
|
supplier.totalValue
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Статус */}
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="text-white/60 text-xs mb-1">
|
||||||
|
Статус
|
||||||
|
</p>
|
||||||
|
{getSupplierStatusBadge(
|
||||||
|
supplier.status
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Детальная информация о поставщике */}
|
||||||
|
{isSupplierExpanded && (
|
||||||
|
<div className="mt-3 pt-3 border-t border-white/5">
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
|
||||||
|
<div>
|
||||||
|
<p className="text-white/60 text-xs mb-1">
|
||||||
|
Полное название
|
||||||
|
</p>
|
||||||
|
<p className="text-white text-sm">
|
||||||
|
{supplier.fullName ||
|
||||||
|
supplier.name}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-white/60 text-xs mb-1">
|
||||||
|
Телефон
|
||||||
|
</p>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<p className="text-white text-sm">
|
||||||
|
{supplier.phone}
|
||||||
|
</p>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<a
|
||||||
|
href={`https://t.me/${supplier.phone.replace(
|
||||||
|
/[^\d]/g,
|
||||||
|
""
|
||||||
|
)}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="p-1 bg-blue-500/20 rounded hover:bg-blue-500/30 transition-colors"
|
||||||
|
title="Написать в Telegram"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
className="h-3 w-3 text-blue-400"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href={`https://wa.me/${supplier.phone.replace(
|
||||||
|
/[^\d]/g,
|
||||||
|
""
|
||||||
|
)}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="p-1 bg-green-500/20 rounded hover:bg-green-500/30 transition-colors"
|
||||||
|
title="Написать в WhatsApp"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
className="h-3 w-3 text-green-400"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893A11.821 11.821 0 0020.885 3.488" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-white/60 text-xs mb-1">
|
||||||
|
Email
|
||||||
|
</p>
|
||||||
|
<p className="text-white text-sm">
|
||||||
|
{supplier.email}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-3">
|
||||||
|
<p className="text-white/60 text-xs mb-1">
|
||||||
|
Адрес
|
||||||
|
</p>
|
||||||
|
<p className="text-white text-sm">
|
||||||
|
{supplier.address}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ export function FulfillmentSuppliesTab() {
|
|||||||
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 flex items-center gap-1 text-xs"
|
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 flex items-center gap-1 text-xs"
|
||||||
>
|
>
|
||||||
<Building2 className="h-3 w-3" />
|
<Building2 className="h-3 w-3" />
|
||||||
Расходники (название текущего кабинета)
|
Наши расходники
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
value="consumables"
|
value="consumables"
|
||||||
|
Reference in New Issue
Block a user