Добавлено обновление кэша для расходников фулфилмента в компонентах создания и отображения заказов. Реализованы новые GraphQL запросы для получения данных о расходниках. Удалены устаревшие компоненты уведомлений о непринятых поставках для упрощения интерфейса. Оптимизирована логика отображения и обновления данных о заказах.

This commit is contained in:
Veronika Smirnova
2025-07-29 17:45:29 +03:00
parent 7877f61d5a
commit 50438bb21f
18 changed files with 3693 additions and 191 deletions

View File

@ -28,6 +28,7 @@ import {
GET_MY_COUNTERPARTIES,
GET_ALL_PRODUCTS,
GET_SUPPLY_ORDERS,
GET_MY_SUPPLIES,
} from "@/graphql/queries";
import { CREATE_SUPPLY_ORDER } from "@/graphql/mutations";
import { OrganizationAvatar } from "@/components/market/organization-avatar";
@ -232,7 +233,10 @@ export function CreateFulfillmentConsumablesSupplyPage() {
})),
},
},
refetchQueries: [{ query: GET_SUPPLY_ORDERS }],
refetchQueries: [
{ query: GET_SUPPLY_ORDERS }, // Обновляем заказы поставок
{ query: GET_MY_SUPPLIES }, // Обновляем расходники фулфилмента
],
});
if (result.data?.createSupplyOrder?.success) {

View File

@ -36,57 +36,6 @@ import {
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;
@ -147,7 +96,7 @@ export function FulfillmentConsumablesOrdersTab() {
},
refetchQueries: [
{ query: GET_SUPPLY_ORDERS }, // Обновляем заказы поставок
{ query: GET_MY_SUPPLIES }, // Обновляем склад фулфилмента
{ query: GET_MY_SUPPLIES }, // Обновляем склад фулфилмента (расходники фф)
{ query: GET_WAREHOUSE_PRODUCTS }, // Обновляем товары склада
],
onError: (error) => {
@ -288,9 +237,6 @@ 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">

View File

@ -11,6 +11,8 @@ import { useQuery, useMutation } from "@apollo/client";
import {
GET_SUPPLY_ORDERS,
GET_PENDING_SUPPLIES_COUNT,
GET_MY_SUPPLIES,
GET_WAREHOUSE_PRODUCTS,
} from "@/graphql/queries";
import { UPDATE_SUPPLY_ORDER_STATUS } from "@/graphql/mutations";
import { useAuth } from "@/hooks/useAuth";
@ -31,56 +33,7 @@ import {
CheckCircle,
} 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 {
@ -92,6 +45,7 @@ interface SupplyOrder {
totalAmount: number;
status: string;
fulfillmentCenterId: string;
number?: number; // Порядковый номер
organization: {
id: string;
name?: string;
@ -170,7 +124,11 @@ export function FulfillmentDetailedSuppliesTab() {
// Мутация для обновления статуса заказа
const [updateSupplyOrderStatus] = useMutation(UPDATE_SUPPLY_ORDER_STATUS, {
refetchQueries: [{ query: GET_SUPPLY_ORDERS }],
refetchQueries: [
{ query: GET_SUPPLY_ORDERS }, // Обновляем заказы поставок
{ query: GET_MY_SUPPLIES }, // Обновляем склад фулфилмента (расходники фф)
{ query: GET_WAREHOUSE_PRODUCTS }, // Обновляем товары склада
],
onError: (error) => {
console.error("Error updating supply order status:", error);
toast.error("Ошибка при обновлении статуса заказа");
@ -197,7 +155,11 @@ export function FulfillmentDetailedSuppliesTab() {
}
);
// Убираем разделение на createdByUs и createdForUs, так как здесь только наши поставки
// Генерируем порядковые номера для заказов (сверху вниз от большего к меньшему)
const ordersWithNumbers = ourSupplyOrders.map((order, index) => ({
...order,
number: ourSupplyOrders.length - index, // Обратный порядок для новых заказов сверху
}));
const toggleOrderExpansion = (orderId: string) => {
const newExpanded = new Set(expandedOrders);
@ -261,9 +223,6 @@ export function FulfillmentDetailedSuppliesTab() {
return (
<div className="space-y-6">
{/* Уведомления о непринятых поставках */}
<PendingSuppliesAlert />
{/* Заголовок с кнопкой создания поставки */}
<div className="flex items-center justify-between">
<div>
@ -380,7 +339,7 @@ export function FulfillmentDetailedSuppliesTab() {
</tr>
</thead>
<tbody>
{ourSupplyOrders.map((order: SupplyOrder) => {
{ordersWithNumbers.map((order: SupplyOrder) => {
const isOrderExpanded = expandedOrders.has(order.id);
return (
@ -393,7 +352,7 @@ export function FulfillmentDetailedSuppliesTab() {
<td className="p-4">
<div className="flex items-center space-x-2">
<span className="text-white font-bold text-lg">
#{order.id.slice(-8)}
{order.number}
</span>
</div>
</td>

View File

@ -40,56 +40,7 @@ import {
} 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 {
@ -712,9 +663,6 @@ export function FulfillmentGoodsTab() {
return (
<div className="h-full flex flex-col p-2 xl:p-4">
{/* Уведомления о непринятых поставках */}
<PendingSuppliesAlert />
<Tabs
value={activeTab}
onValueChange={setActiveTab}

View File

@ -23,7 +23,12 @@ import {
Minus,
ShoppingCart,
} from "lucide-react";
import { GET_MY_COUNTERPARTIES, GET_ALL_PRODUCTS } from "@/graphql/queries";
import {
GET_MY_COUNTERPARTIES,
GET_ALL_PRODUCTS,
GET_SUPPLY_ORDERS,
GET_MY_SUPPLIES,
} from "@/graphql/queries";
import { CREATE_SUPPLY_ORDER } from "@/graphql/mutations";
import { OrganizationAvatar } from "@/components/market/organization-avatar";
import { toast } from "sonner";
@ -94,9 +99,10 @@ export function MaterialsOrderForm() {
variables: { search: null, category: null },
}
);
// Мутация для создания заказа поставки
const [createSupplyOrder, { loading: isCreatingOrder }] = useMutation(CREATE_SUPPLY_ORDER);
const [createSupplyOrder, { loading: isCreatingOrder }] =
useMutation(CREATE_SUPPLY_ORDER);
// Фильтруем только поставщиков из партнеров
const wholesalePartners = (counterpartiesData?.myCounterparties || []).filter(
@ -178,19 +184,26 @@ export function MaterialsOrderForm() {
input: {
partnerId: selectedPartner.id,
deliveryDate: deliveryDate,
items: selectedProducts.map(product => ({
items: selectedProducts.map((product) => ({
productId: product.id,
quantity: product.selectedQuantity
}))
}
}
quantity: product.selectedQuantity,
})),
},
},
refetchQueries: [
{ query: GET_SUPPLY_ORDERS }, // Обновляем заказы поставок
{ query: GET_MY_SUPPLIES }, // Обновляем расходники фулфилмента
],
});
if (result.data?.createSupplyOrder?.success) {
toast.success("Заказ поставки создан успешно!");
router.push("/fulfillment-supplies");
} else {
toast.error(result.data?.createSupplyOrder?.message || "Ошибка при создании заказа");
toast.error(
result.data?.createSupplyOrder?.message ||
"Ошибка при создании заказа"
);
}
} catch (error) {
console.error("Error creating supply order:", error);
@ -447,14 +460,20 @@ export function MaterialsOrderForm() {
</div>
</div>
{/* Кнопка создания заказа */}
<Button
{/* Кнопка создания заказа */}
<Button
onClick={handleCreateOrder}
disabled={selectedProducts.length === 0 || !deliveryDate || isCreatingOrder}
disabled={
selectedProducts.length === 0 ||
!deliveryDate ||
isCreatingOrder
}
className="w-full mt-4 bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white"
>
<ShoppingCart className="h-4 w-4 mr-2" />
{isCreatingOrder ? "Создание заказа..." : "Создать заказ поставки"}
{isCreatingOrder
? "Создание заказа..."
: "Создать заказ поставки"}
</Button>
</div>
</Card>