Обновлены категории товаров с "Упаковка" на "Расходники" в различных компонентах и моделях. Добавлены уведомления о непринятых поставках и обновлены соответствующие GraphQL запросы и резолверы для поддержки новых данных. Оптимизирована логика отображения и обработки данных в интерфейсе.
This commit is contained in:
@ -1,20 +1,42 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Sidebar } from "@/components/dashboard/sidebar";
|
||||
import { useSidebar } from "@/hooks/useSidebar";
|
||||
import { GET_PENDING_SUPPLIES_COUNT } from "@/graphql/queries";
|
||||
import { Building2, ShoppingCart } from "lucide-react";
|
||||
|
||||
// Импорты компонентов подразделов
|
||||
import { FulfillmentSuppliesTab } from "./fulfillment-supplies/fulfillment-supplies-tab";
|
||||
import { MarketplaceSuppliesTab } from "./marketplace-supplies/marketplace-supplies-tab";
|
||||
|
||||
// Компонент для отображения бейджа с уведомлениями
|
||||
function NotificationBadge({ count }: { count: number }) {
|
||||
if (count === 0) return null;
|
||||
|
||||
return (
|
||||
<div className="ml-1 bg-red-500 text-white text-xs font-bold rounded-full min-w-[16px] h-4 flex items-center justify-center px-1">
|
||||
{count > 99 ? "99+" : count}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function FulfillmentSuppliesDashboard() {
|
||||
const { getSidebarMargin } = useSidebar();
|
||||
const [activeTab, setActiveTab] = useState("fulfillment");
|
||||
|
||||
// Загружаем данные о непринятых поставках
|
||||
const { data: pendingData } = useQuery(GET_PENDING_SUPPLIES_COUNT, {
|
||||
pollInterval: 30000, // Обновляем каждые 30 секунд
|
||||
fetchPolicy: "cache-first",
|
||||
errorPolicy: "ignore",
|
||||
});
|
||||
|
||||
const pendingCount = pendingData?.pendingSuppliesCount?.total || 0;
|
||||
|
||||
return (
|
||||
<div className="h-screen flex overflow-hidden">
|
||||
<Sidebar />
|
||||
@ -32,13 +54,14 @@ export function FulfillmentSuppliesDashboard() {
|
||||
<TabsList className="grid w-full grid-cols-2 bg-white/5 backdrop-blur border-white/10 flex-shrink-0 h-8 xl:h-10">
|
||||
<TabsTrigger
|
||||
value="fulfillment"
|
||||
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 flex items-center gap-1 text-xs xl:text-sm"
|
||||
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 flex items-center gap-1 text-xs xl:text-sm relative"
|
||||
>
|
||||
<Building2 className="h-3 w-3" />
|
||||
<span className="hidden sm:inline">
|
||||
Поставки на фулфилмент
|
||||
</span>
|
||||
<span className="sm:hidden">Фулфилмент</span>
|
||||
<NotificationBadge count={pendingCount} />
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="marketplace"
|
||||
|
@ -7,7 +7,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
|
||||
import { useQuery, useMutation } from "@apollo/client";
|
||||
import { GET_SUPPLY_ORDERS, GET_MY_SUPPLIES } from "@/graphql/queries";
|
||||
import {
|
||||
GET_SUPPLY_ORDERS,
|
||||
GET_MY_SUPPLIES,
|
||||
GET_PENDING_SUPPLIES_COUNT,
|
||||
GET_WAREHOUSE_PRODUCTS,
|
||||
} from "@/graphql/queries";
|
||||
import { UPDATE_SUPPLY_ORDER_STATUS } from "@/graphql/mutations";
|
||||
import { useAuth } from "@/hooks/useAuth";
|
||||
import { toast } from "sonner";
|
||||
@ -27,8 +32,61 @@ import {
|
||||
Building,
|
||||
Hash,
|
||||
Store,
|
||||
Bell,
|
||||
AlertTriangle,
|
||||
} from "lucide-react";
|
||||
|
||||
// Компонент уведомлений о непринятых поставках
|
||||
function PendingSuppliesAlert() {
|
||||
const { data: pendingData } = useQuery(GET_PENDING_SUPPLIES_COUNT, {
|
||||
pollInterval: 30000, // Обновляем каждые 30 секунд
|
||||
fetchPolicy: "cache-first",
|
||||
errorPolicy: "ignore",
|
||||
});
|
||||
|
||||
const pendingCount = pendingData?.pendingSuppliesCount?.total || 0;
|
||||
const supplyOrdersCount =
|
||||
pendingData?.pendingSuppliesCount?.supplyOrders || 0;
|
||||
const incomingRequestsCount =
|
||||
pendingData?.pendingSuppliesCount?.incomingRequests || 0;
|
||||
|
||||
if (pendingCount === 0) return null;
|
||||
|
||||
return (
|
||||
<Card className="bg-gradient-to-r from-orange-500/20 to-red-500/20 backdrop-blur border-orange-400/30 p-3 mb-4">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="p-2 bg-orange-500/20 rounded-full">
|
||||
<Bell className="h-5 w-5 text-orange-300 animate-pulse" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-orange-200 font-semibold text-sm flex items-center gap-2">
|
||||
<AlertTriangle className="h-4 w-4" />
|
||||
Требует вашего внимания
|
||||
</h3>
|
||||
<div className="text-orange-100 text-xs mt-1 space-y-1">
|
||||
{supplyOrdersCount > 0 && (
|
||||
<p>
|
||||
• {supplyOrdersCount} поставок требуют вашего действия
|
||||
(подтверждение/получение)
|
||||
</p>
|
||||
)}
|
||||
{incomingRequestsCount > 0 && (
|
||||
<p>
|
||||
• {incomingRequestsCount} заявок на партнерство ожидают ответа
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<div className="bg-orange-500 text-white text-xs font-bold rounded-full w-6 h-6 flex items-center justify-center">
|
||||
{pendingCount > 99 ? "99+" : pendingCount}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
interface SupplyOrder {
|
||||
id: string;
|
||||
partnerId: string;
|
||||
@ -90,6 +148,7 @@ export function FulfillmentConsumablesOrdersTab() {
|
||||
refetchQueries: [
|
||||
{ query: GET_SUPPLY_ORDERS }, // Обновляем заказы поставок
|
||||
{ query: GET_MY_SUPPLIES }, // Обновляем склад фулфилмента
|
||||
{ query: GET_WAREHOUSE_PRODUCTS }, // Обновляем товары склада
|
||||
],
|
||||
onError: (error) => {
|
||||
console.error("Error updating supply order status:", error);
|
||||
@ -227,6 +286,9 @@ export function FulfillmentConsumablesOrdersTab() {
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
{/* Уведомления о непринятых поставках */}
|
||||
<PendingSuppliesAlert />
|
||||
|
||||
{/* Компактная статистика */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-2">
|
||||
<Card className="bg-white/10 backdrop-blur border-white/20 p-2">
|
||||
|
@ -126,7 +126,7 @@ const mockFulfillmentGoodsSupplies: FulfillmentSupply[] = [
|
||||
value: "12",
|
||||
unit: "мес",
|
||||
},
|
||||
{ id: "ffparam4", name: "Упаковка ФФ", value: "Усиленная" },
|
||||
{ id: "ffparam4", name: "Расходники ФФ", value: "Усиленная" },
|
||||
],
|
||||
},
|
||||
],
|
||||
@ -514,7 +514,7 @@ export function FulfillmentDetailedGoodsTab() {
|
||||
const isRouteExpanded = expandedRoutes.has(route.id);
|
||||
return (
|
||||
<React.Fragment key={route.id}>
|
||||
<tr
|
||||
<tr
|
||||
className="border-b border-white/10 hover:bg-white/5 transition-colors bg-blue-500/10 cursor-pointer"
|
||||
onClick={() => toggleRouteExpansion(route.id)}
|
||||
>
|
||||
@ -614,9 +614,11 @@ export function FulfillmentDetailedGoodsTab() {
|
||||
);
|
||||
return (
|
||||
<React.Fragment key={seller.id}>
|
||||
<tr
|
||||
<tr
|
||||
className="border-b border-white/10 hover:bg-white/5 transition-colors bg-green-500/10 cursor-pointer"
|
||||
onClick={() => toggleSellerExpansion(seller.id)}
|
||||
onClick={() =>
|
||||
toggleSellerExpansion(seller.id)
|
||||
}
|
||||
>
|
||||
<td className="p-4 pl-20">
|
||||
<div className="flex items-center space-x-2">
|
||||
@ -693,9 +695,13 @@ export function FulfillmentDetailedGoodsTab() {
|
||||
expandedProducts.has(product.id);
|
||||
return (
|
||||
<React.Fragment key={product.id}>
|
||||
<tr
|
||||
<tr
|
||||
className="border-b border-white/10 hover:bg-white/5 transition-colors bg-yellow-500/10 cursor-pointer"
|
||||
onClick={() => toggleProductExpansion(product.id)}
|
||||
onClick={() =>
|
||||
toggleProductExpansion(
|
||||
product.id
|
||||
)
|
||||
}
|
||||
>
|
||||
<td className="p-4 pl-28">
|
||||
<div className="flex items-center space-x-2">
|
||||
|
@ -8,7 +8,10 @@ import { StatsCard } from "../../supplies/ui/stats-card";
|
||||
import { StatsGrid } from "../../supplies/ui/stats-grid";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { GET_SUPPLY_ORDERS } from "@/graphql/queries";
|
||||
import {
|
||||
GET_SUPPLY_ORDERS,
|
||||
GET_PENDING_SUPPLIES_COUNT,
|
||||
} from "@/graphql/queries";
|
||||
import { useAuth } from "@/hooks/useAuth";
|
||||
import {
|
||||
Calendar,
|
||||
@ -20,8 +23,54 @@ import {
|
||||
Plus,
|
||||
ChevronDown,
|
||||
ChevronRight,
|
||||
Bell,
|
||||
AlertTriangle,
|
||||
} from "lucide-react";
|
||||
|
||||
// Компонент уведомлений о непринятых поставках
|
||||
function PendingSuppliesAlert() {
|
||||
const { data: pendingData } = useQuery(GET_PENDING_SUPPLIES_COUNT, {
|
||||
pollInterval: 30000, // Обновляем каждые 30 секунд
|
||||
fetchPolicy: "cache-first",
|
||||
errorPolicy: "ignore",
|
||||
});
|
||||
|
||||
const pendingCount = pendingData?.pendingSuppliesCount?.total || 0;
|
||||
const supplyOrdersCount = pendingData?.pendingSuppliesCount?.supplyOrders || 0;
|
||||
const incomingRequestsCount = pendingData?.pendingSuppliesCount?.incomingRequests || 0;
|
||||
|
||||
if (pendingCount === 0) return null;
|
||||
|
||||
return (
|
||||
<Card className="bg-gradient-to-r from-orange-500/20 to-red-500/20 backdrop-blur border-orange-400/30 p-3 mb-4">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="p-2 bg-orange-500/20 rounded-full">
|
||||
<Bell className="h-5 w-5 text-orange-300 animate-pulse" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-orange-200 font-semibold text-sm flex items-center gap-2">
|
||||
<AlertTriangle className="h-4 w-4" />
|
||||
Требует вашего внимания
|
||||
</h3>
|
||||
<div className="text-orange-100 text-xs mt-1 space-y-1">
|
||||
{supplyOrdersCount > 0 && (
|
||||
<p>• {supplyOrdersCount} поставок требуют вашего действия (подтверждение/получение)</p>
|
||||
)}
|
||||
{incomingRequestsCount > 0 && (
|
||||
<p>• {incomingRequestsCount} заявок на партнерство ожидают ответа</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<div className="bg-orange-500 text-white text-xs font-bold rounded-full w-6 h-6 flex items-center justify-center">
|
||||
{pendingCount > 99 ? "99+" : pendingCount}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
// Интерфейс для заказа
|
||||
interface SupplyOrder {
|
||||
id: string;
|
||||
@ -63,21 +112,36 @@ const formatDate = (dateString: string) => {
|
||||
// Функция для отображения статуса
|
||||
const getStatusBadge = (status: string) => {
|
||||
const statusConfig = {
|
||||
PENDING: { label: "Ожидает", color: "bg-yellow-500/20 text-yellow-300 border-yellow-500/30" },
|
||||
CONFIRMED: { label: "Подтверждён", color: "bg-blue-500/20 text-blue-300 border-blue-500/30" },
|
||||
IN_PROGRESS: { label: "В работе", color: "bg-purple-500/20 text-purple-300 border-purple-500/30" },
|
||||
SHIPPED: { label: "Отправлен", color: "bg-orange-500/20 text-orange-300 border-orange-500/30" },
|
||||
DELIVERED: { label: "Доставлен", color: "bg-green-500/20 text-green-300 border-green-500/30" },
|
||||
CANCELLED: { label: "Отменён", color: "bg-red-500/20 text-red-300 border-red-500/30" },
|
||||
PENDING: {
|
||||
label: "Ожидает",
|
||||
color: "bg-yellow-500/20 text-yellow-300 border-yellow-500/30",
|
||||
},
|
||||
CONFIRMED: {
|
||||
label: "Подтверждён",
|
||||
color: "bg-blue-500/20 text-blue-300 border-blue-500/30",
|
||||
},
|
||||
IN_PROGRESS: {
|
||||
label: "В работе",
|
||||
color: "bg-purple-500/20 text-purple-300 border-purple-500/30",
|
||||
},
|
||||
SHIPPED: {
|
||||
label: "Отправлен",
|
||||
color: "bg-orange-500/20 text-orange-300 border-orange-500/30",
|
||||
},
|
||||
DELIVERED: {
|
||||
label: "Доставлен",
|
||||
color: "bg-green-500/20 text-green-300 border-green-500/30",
|
||||
},
|
||||
CANCELLED: {
|
||||
label: "Отменён",
|
||||
color: "bg-red-500/20 text-red-300 border-red-500/30",
|
||||
},
|
||||
};
|
||||
|
||||
const config = statusConfig[status as keyof typeof statusConfig] || statusConfig.PENDING;
|
||||
const config =
|
||||
statusConfig[status as keyof typeof statusConfig] || statusConfig.PENDING;
|
||||
|
||||
return (
|
||||
<Badge className={config.color}>
|
||||
{config.label}
|
||||
</Badge>
|
||||
);
|
||||
return <Badge className={config.color}>{config.label}</Badge>;
|
||||
};
|
||||
|
||||
export function FulfillmentDetailedSuppliesTab() {
|
||||
@ -87,13 +151,13 @@ export function FulfillmentDetailedSuppliesTab() {
|
||||
|
||||
// Загружаем реальные данные заказов расходников
|
||||
const { data, loading, error } = useQuery(GET_SUPPLY_ORDERS, {
|
||||
fetchPolicy: 'cache-and-network', // Принудительно проверяем сервер
|
||||
notifyOnNetworkStatusChange: true
|
||||
fetchPolicy: "cache-and-network", // Принудительно проверяем сервер
|
||||
notifyOnNetworkStatusChange: true,
|
||||
});
|
||||
|
||||
// Получаем ID текущей организации (фулфилмент-центра)
|
||||
const currentOrganizationId = user?.organization?.id;
|
||||
|
||||
|
||||
// Фильтруем заказы созданные текущей организацией (наши расходники)
|
||||
const ourSupplyOrders: SupplyOrder[] = (data?.supplyOrders || []).filter(
|
||||
(order: SupplyOrder) => order.organizationId === currentOrganizationId
|
||||
@ -113,7 +177,9 @@ export function FulfillmentDetailedSuppliesTab() {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-2 border-white border-t-transparent"></div>
|
||||
<span className="ml-3 text-white/60">Загрузка наших расходников...</span>
|
||||
<span className="ml-3 text-white/60">
|
||||
Загрузка наших расходников...
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -123,7 +189,9 @@ export function FulfillmentDetailedSuppliesTab() {
|
||||
<div className="flex items-center justify-center h-64">
|
||||
<div className="text-center">
|
||||
<Wrench className="h-12 w-12 text-red-400 mx-auto mb-4" />
|
||||
<p className="text-red-400 font-medium">Ошибка загрузки расходников</p>
|
||||
<p className="text-red-400 font-medium">
|
||||
Ошибка загрузки расходников
|
||||
</p>
|
||||
<p className="text-white/60 text-sm mt-2">{error.message}</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -132,18 +200,21 @@ export function FulfillmentDetailedSuppliesTab() {
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Уведомления о непринятых поставках */}
|
||||
<PendingSuppliesAlert />
|
||||
|
||||
{/* Заголовок с кнопкой создания поставки */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-white mb-1">
|
||||
Наши расходники
|
||||
</h2>
|
||||
<h2 className="text-xl font-bold text-white mb-1">Наши расходники</h2>
|
||||
<p className="text-white/60 text-sm">
|
||||
Управление поставками расходников фулфилмента
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
onClick={() => router.push("/fulfillment-supplies/create-consumables")}
|
||||
onClick={() =>
|
||||
router.push("/fulfillment-supplies/create-consumables")
|
||||
}
|
||||
className="bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white shadow-lg"
|
||||
>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
@ -178,7 +249,10 @@ export function FulfillmentDetailedSuppliesTab() {
|
||||
|
||||
<StatsCard
|
||||
title="Всего единиц"
|
||||
value={ourSupplyOrders.reduce((sum: number, order: SupplyOrder) => sum + order.totalItems, 0)}
|
||||
value={ourSupplyOrders.reduce(
|
||||
(sum: number, order: SupplyOrder) => sum + order.totalItems,
|
||||
0
|
||||
)}
|
||||
icon={Wrench}
|
||||
iconColor="text-blue-400"
|
||||
iconBg="bg-blue-500/20"
|
||||
@ -208,7 +282,8 @@ export function FulfillmentDetailedSuppliesTab() {
|
||||
Пока нет заказов расходников
|
||||
</h3>
|
||||
<p className="text-white/60">
|
||||
Создайте первый заказ расходников через кнопку "Создать поставку"
|
||||
Создайте первый заказ расходников через кнопку "Создать
|
||||
поставку"
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
@ -225,8 +300,12 @@ export function FulfillmentDetailedSuppliesTab() {
|
||||
<th className="text-left p-4 text-white font-semibold">
|
||||
Дата создания
|
||||
</th>
|
||||
<th className="text-left p-4 text-white font-semibold">План</th>
|
||||
<th className="text-left p-4 text-white font-semibold">Факт</th>
|
||||
<th className="text-left p-4 text-white font-semibold">
|
||||
План
|
||||
</th>
|
||||
<th className="text-left p-4 text-white font-semibold">
|
||||
Факт
|
||||
</th>
|
||||
<th className="text-left p-4 text-white font-semibold">
|
||||
Цена расходников
|
||||
</th>
|
||||
|
@ -13,7 +13,11 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { GET_MY_EMPLOYEES, GET_MY_COUNTERPARTIES } from "@/graphql/queries";
|
||||
import {
|
||||
GET_MY_EMPLOYEES,
|
||||
GET_MY_COUNTERPARTIES,
|
||||
GET_PENDING_SUPPLIES_COUNT,
|
||||
} from "@/graphql/queries";
|
||||
import {
|
||||
Package,
|
||||
Plus,
|
||||
@ -31,9 +35,62 @@ import {
|
||||
Clock,
|
||||
CheckCircle,
|
||||
FileText,
|
||||
Bell,
|
||||
AlertTriangle,
|
||||
} from "lucide-react";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
|
||||
// Компонент уведомлений о непринятых поставках
|
||||
function PendingSuppliesAlert() {
|
||||
const { data: pendingData } = useQuery(GET_PENDING_SUPPLIES_COUNT, {
|
||||
pollInterval: 30000, // Обновляем каждые 30 секунд
|
||||
fetchPolicy: "cache-first",
|
||||
errorPolicy: "ignore",
|
||||
});
|
||||
|
||||
const pendingCount = pendingData?.pendingSuppliesCount?.total || 0;
|
||||
const supplyOrdersCount =
|
||||
pendingData?.pendingSuppliesCount?.supplyOrders || 0;
|
||||
const incomingRequestsCount =
|
||||
pendingData?.pendingSuppliesCount?.incomingRequests || 0;
|
||||
|
||||
if (pendingCount === 0) return null;
|
||||
|
||||
return (
|
||||
<Card className="bg-gradient-to-r from-orange-500/20 to-red-500/20 backdrop-blur border-orange-400/30 p-3 mb-4">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="p-2 bg-orange-500/20 rounded-full">
|
||||
<Bell className="h-5 w-5 text-orange-300 animate-pulse" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-orange-200 font-semibold text-sm flex items-center gap-2">
|
||||
<AlertTriangle className="h-4 w-4" />
|
||||
Требует вашего внимания
|
||||
</h3>
|
||||
<div className="text-orange-100 text-xs mt-1 space-y-1">
|
||||
{supplyOrdersCount > 0 && (
|
||||
<p>
|
||||
• {supplyOrdersCount} поставок требуют вашего действия
|
||||
(подтверждение/получение)
|
||||
</p>
|
||||
)}
|
||||
{incomingRequestsCount > 0 && (
|
||||
<p>
|
||||
• {incomingRequestsCount} заявок на партнерство ожидают ответа
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<div className="bg-orange-500 text-white text-xs font-bold rounded-full w-6 h-6 flex items-center justify-center">
|
||||
{pendingCount > 99 ? "99+" : pendingCount}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
// Интерфейсы для данных
|
||||
interface Employee {
|
||||
id: string;
|
||||
@ -655,6 +712,9 @@ export function FulfillmentGoodsTab() {
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col p-2 xl:p-4">
|
||||
{/* Уведомления о непринятых поставках */}
|
||||
<PendingSuppliesAlert />
|
||||
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
onValueChange={setActiveTab}
|
||||
|
@ -2,7 +2,9 @@
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useSearchParams, useRouter } from "next/navigation";
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { GET_PENDING_SUPPLIES_COUNT } from "@/graphql/queries";
|
||||
import { Package, Wrench, RotateCcw, Building2 } from "lucide-react";
|
||||
|
||||
// Импорты компонентов подкатегорий
|
||||
@ -13,11 +15,31 @@ import { FulfillmentConsumablesOrdersTab } from "./fulfillment-consumables-order
|
||||
// Новые компоненты для детального просмотра (копия из supplies модуля)
|
||||
import { FulfillmentDetailedSuppliesTab } from "./fulfillment-detailed-supplies-tab";
|
||||
|
||||
// Компонент для отображения бейджа с уведомлениями
|
||||
function NotificationBadge({ count }: { count: number }) {
|
||||
if (count === 0) return null;
|
||||
|
||||
return (
|
||||
<div className="ml-1 bg-red-500 text-white text-xs font-bold rounded-full min-w-[16px] h-4 flex items-center justify-center px-1">
|
||||
{count > 99 ? "99+" : count}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function FulfillmentSuppliesTab() {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const [activeTab, setActiveTab] = useState("goods");
|
||||
|
||||
// Загружаем данные о непринятых поставках
|
||||
const { data: pendingData } = useQuery(GET_PENDING_SUPPLIES_COUNT, {
|
||||
pollInterval: 30000, // Обновляем каждые 30 секунд
|
||||
fetchPolicy: "cache-first",
|
||||
errorPolicy: "ignore",
|
||||
});
|
||||
|
||||
const pendingCount = pendingData?.pendingSuppliesCount?.total || 0;
|
||||
|
||||
// Проверяем URL параметр при загрузке
|
||||
useEffect(() => {
|
||||
const tabParam = searchParams.get("tab");
|
||||
@ -66,12 +88,13 @@ export function FulfillmentSuppliesTab() {
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="consumables"
|
||||
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 flex items-center gap-1 text-[10px] xl:text-xs"
|
||||
className="data-[state=active]:bg-white/20 data-[state=active]:text-white text-white/70 flex items-center gap-1 text-[10px] xl:text-xs relative"
|
||||
>
|
||||
<Wrench className="h-2.5 w-2.5 xl:h-3 xl:w-3" />
|
||||
<span className="hidden md:inline">Расходники селлеров</span>
|
||||
<span className="md:hidden hidden sm:inline">Селлеры</span>
|
||||
<span className="sm:hidden">С</span>
|
||||
<NotificationBadge count={pendingCount} />
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="returns"
|
||||
|
@ -20,7 +20,7 @@ const mockSellerMaterials = [
|
||||
id: "1",
|
||||
materialName: "Пакеты полиэтиленовые 30х40",
|
||||
seller: "PackStore LLC",
|
||||
category: "Упаковка",
|
||||
category: "Расходники",
|
||||
quantity: 10000,
|
||||
expectedDate: "2024-01-14",
|
||||
status: "planned",
|
||||
@ -32,7 +32,7 @@ const mockSellerMaterials = [
|
||||
id: "2",
|
||||
materialName: "Скотч упаковочный прозрачный",
|
||||
seller: "Packaging Pro",
|
||||
category: "Упаковка",
|
||||
category: "Расходники",
|
||||
quantity: 500,
|
||||
expectedDate: "2024-01-11",
|
||||
status: "in-transit",
|
||||
|
@ -35,7 +35,7 @@ const mockSupplies: SupplyItem[] = [
|
||||
id: '2',
|
||||
name: 'Упаковочные коробки',
|
||||
type: 'materials',
|
||||
category: 'Упаковка',
|
||||
category: 'Расходники',
|
||||
quantity: 1000,
|
||||
status: 'in-transit',
|
||||
date: '2024-01-18',
|
||||
|
Reference in New Issue
Block a user