Добавлены новые кнопки и улучшения в компоненте ButtonsDemo для управления поставками и фильтрации. Обновлены мок-данные в FulfillmentGoodsTab с новыми статусами и логикой фильтрации. Изменено название вкладки в FulfillmentSuppliesTab на "Наши расходники". Улучшена структура кода и взаимодействие с пользователем.
This commit is contained in:
@ -11,7 +11,12 @@ import {
|
||||
Plus,
|
||||
Search,
|
||||
Filter,
|
||||
Loader2
|
||||
Loader2,
|
||||
Package,
|
||||
Wrench,
|
||||
RotateCcw,
|
||||
Building2,
|
||||
ShoppingCart
|
||||
} from 'lucide-react'
|
||||
|
||||
export function ButtonsDemo() {
|
||||
@ -240,6 +245,166 @@ export function ButtonsDemo() {
|
||||
</CardContent>
|
||||
</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">
|
||||
<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"
|
||||
>
|
||||
<Building2 className="h-3 w-3" />
|
||||
Поставки на ФФ
|
||||
Поставки на фулфилмент
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="marketplace"
|
||||
|
@ -30,7 +30,11 @@ import {
|
||||
ChevronRight,
|
||||
Store,
|
||||
Building2,
|
||||
Clock,
|
||||
CheckCircle,
|
||||
FileText,
|
||||
} from "lucide-react";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
|
||||
// Интерфейсы для данных
|
||||
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() {
|
||||
const [activeTab, setActiveTab] = useState("new");
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [statusFilter, setStatusFilter] = useState("all");
|
||||
const [expandedSellers, setExpandedSellers] = useState<Set<string>>(
|
||||
@ -508,7 +580,29 @@ export function FulfillmentGoodsTab() {
|
||||
: "Не выбран";
|
||||
};
|
||||
|
||||
const filteredSupplies = mockFulfillmentSupplies.filter((supply) => {
|
||||
const getFilteredSuppliesByTab = (tabName: string) => {
|
||||
let supplies = mockFulfillmentSupplies;
|
||||
|
||||
// Фильтрация по вкладке
|
||||
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 supplies.filter((supply) => {
|
||||
const matchesSearch =
|
||||
supply.supplyNumber.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
supply.seller.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
@ -522,6 +616,9 @@ export function FulfillmentGoodsTab() {
|
||||
|
||||
return matchesSearch && matchesStatus;
|
||||
});
|
||||
};
|
||||
|
||||
const filteredSupplies = getFilteredSuppliesByTab(activeTab);
|
||||
|
||||
const getTotalValue = () => {
|
||||
return filteredSupplies.reduce((sum, supply) => sum + supply.totalValue, 0);
|
||||
@ -539,7 +636,78 @@ export function FulfillmentGoodsTab() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col space-y-4 p-4">
|
||||
<div className="h-full flex flex-col p-4">
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
onValueChange={setActiveTab}
|
||||
className="h-full flex flex-col"
|
||||
>
|
||||
{/* Вкладки товаров */}
|
||||
<TabsList className="grid w-full grid-cols-3 bg-white/10 backdrop-blur border-white/10 flex-shrink-0 h-10 mb-4">
|
||||
<TabsTrigger
|
||||
value="new"
|
||||
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 flex items-center gap-2 text-sm"
|
||||
>
|
||||
<Clock className="h-4 w-4" />
|
||||
Новые
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
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>
|
||||
|
||||
<TabsContent value="new" className="flex-1 overflow-hidden">
|
||||
<TabContent tabName="new" />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="receiving" className="flex-1 overflow-hidden">
|
||||
<TabContent tabName="receiving" />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="received" className="flex-1 overflow-hidden">
|
||||
<TabContent tabName="received" />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</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">
|
||||
@ -551,7 +719,7 @@ export function FulfillmentGoodsTab() {
|
||||
<div>
|
||||
<p className="text-white/60 text-xs">Поставок</p>
|
||||
<p className="text-lg font-bold text-white">
|
||||
{filteredSupplies.length}
|
||||
{tabFilteredSupplies.length}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -565,7 +733,7 @@ export function FulfillmentGoodsTab() {
|
||||
<div>
|
||||
<p className="text-white/60 text-xs">Стоимость</p>
|
||||
<p className="text-lg font-bold text-white">
|
||||
{formatCurrency(getTotalValue())}
|
||||
{formatCurrency(getTabTotalValue())}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -579,7 +747,7 @@ export function FulfillmentGoodsTab() {
|
||||
<div>
|
||||
<p className="text-white/60 text-xs">Товаров</p>
|
||||
<p className="text-lg font-bold text-white">
|
||||
{getTotalQuantity()} ед.
|
||||
{getTabTotalQuantity()} ед.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -593,7 +761,7 @@ export function FulfillmentGoodsTab() {
|
||||
<div>
|
||||
<p className="text-white/60 text-xs">Объём</p>
|
||||
<p className="text-lg font-bold text-white">
|
||||
{getTotalVolume().toFixed(1)} м³
|
||||
{getTabTotalVolume().toFixed(1)} м³
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -637,7 +805,7 @@ export function FulfillmentGoodsTab() {
|
||||
{/* Список поставок */}
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<div className="h-full overflow-y-auto space-y-3">
|
||||
{filteredSupplies.map((supply, index) => {
|
||||
{tabFilteredSupplies.map((supply, index) => {
|
||||
const isSellerExpanded = expandedSellers.has(supply.seller.id);
|
||||
const isSupplyExpanded = expandedSupplies.has(supply.id);
|
||||
|
||||
@ -718,7 +886,9 @@ export function FulfillmentGoodsTab() {
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => toggleSellerExpansion(supply.seller.id)}
|
||||
onClick={() =>
|
||||
toggleSellerExpansion(supply.seller.id)
|
||||
}
|
||||
className="h-6 w-6 p-0 text-white/60 hover:text-white hover:bg-white/10"
|
||||
>
|
||||
{isSellerExpanded ? (
|
||||
@ -770,7 +940,7 @@ export function FulfillmentGoodsTab() {
|
||||
<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}
|
||||
{tabFilteredSupplies.length - index}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -810,7 +980,9 @@ export function FulfillmentGoodsTab() {
|
||||
|
||||
{/* Стоимость */}
|
||||
<div className="text-center">
|
||||
<p className="text-white/60 text-xs mb-1">Стоимость</p>
|
||||
<p className="text-white/60 text-xs mb-1">
|
||||
Стоимость
|
||||
</p>
|
||||
<p className="text-green-400 font-semibold text-sm">
|
||||
{formatCurrency(supply.totalValue)}
|
||||
</p>
|
||||
@ -854,13 +1026,16 @@ export function FulfillmentGoodsTab() {
|
||||
className="text-center"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<p className="text-white/60 text-xs mb-1">Логистика</p>
|
||||
<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) => (
|
||||
{logisticsPartners.map(
|
||||
(partner: Organization) => (
|
||||
<SelectItem
|
||||
key={partner.id}
|
||||
value={partner.id}
|
||||
@ -877,7 +1052,8 @@ export function FulfillmentGoodsTab() {
|
||||
</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
)
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
@ -1232,4 +1408,5 @@ export function FulfillmentGoodsTab() {
|
||||
</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"
|
||||
>
|
||||
<Building2 className="h-3 w-3" />
|
||||
Расходники (название текущего кабинета)
|
||||
Наши расходники
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="consumables"
|
||||
|
Reference in New Issue
Block a user