Добавлен новый компонент для отображения бизнес-процессов в интерфейсе управления. Обновлен компонент UIKitSection для интеграции нового демо и улучшения навигации. Оптимизирована логика отображения данных и улучшена читаемость кода. Исправлены текстовые метки для повышения удобства использования.

This commit is contained in:
Veronika Smirnova
2025-07-27 20:10:39 +03:00
parent f198994400
commit ec28803549
17 changed files with 4304 additions and 1205 deletions

View File

@ -18,13 +18,11 @@ import {
} from "@/components/ui/select";
import { useQuery } from "@apollo/client";
import { apolloClient } from "@/lib/apollo-client";
import { GET_MY_COUNTERPARTIES, GET_ORGANIZATION_LOGISTICS } from "@/graphql/queries";
import {
ArrowLeft,
Package,
CalendarIcon,
Building,
} from "lucide-react";
GET_MY_COUNTERPARTIES,
GET_ORGANIZATION_LOGISTICS,
} from "@/graphql/queries";
import { ArrowLeft, Package, CalendarIcon, Building } from "lucide-react";
// Компонент создания поставки товаров с новым интерфейсом
@ -47,16 +45,18 @@ export function CreateSupplyPage() {
const [goodsVolume, setGoodsVolume] = useState<number>(0);
const [cargoPlaces, setCargoPlaces] = useState<number>(0);
const [goodsPrice, setGoodsPrice] = useState<number>(0);
const [fulfillmentServicesPrice, setFulfillmentServicesPrice] = useState<number>(0);
const [fulfillmentServicesPrice, setFulfillmentServicesPrice] =
useState<number>(0);
const [logisticsPrice, setLogisticsPrice] = useState<number>(0);
const [selectedServicesCost, setSelectedServicesCost] = useState<number>(0);
const [selectedConsumablesCost, setSelectedConsumablesCost] = useState<number>(0);
const [selectedConsumablesCost, setSelectedConsumablesCost] =
useState<number>(0);
const [hasItemsInSupply, setHasItemsInSupply] = useState<boolean>(false);
// Загружаем контрагентов-фулфилментов
const { data: counterpartiesData } = useQuery(GET_MY_COUNTERPARTIES);
// Фильтруем только фулфилмент организации
// Фильтруем только фулфилмент организации
const fulfillmentOrgs = (counterpartiesData?.myCounterparties || []).filter(
(org: Organization) => org.type === "FULFILLMENT"
);
@ -87,19 +87,28 @@ export function CreateSupplyPage() {
};
// Функция для обновления информации о поставщиках (для расчета логистики)
const handleSuppliersUpdate = (suppliersData: any[]) => {
const handleSuppliersUpdate = (suppliersData: unknown[]) => {
// Находим рынок из выбранного поставщика
const selectedSupplier = suppliersData.find(supplier => supplier.selected);
const supplierMarket = selectedSupplier?.market;
console.log("Обновление поставщиков:", { selectedSupplier, supplierMarket, volume: goodsVolume });
const selectedSupplier = suppliersData.find(
(supplier: unknown) => (supplier as { selected?: boolean }).selected
);
const supplierMarket = (selectedSupplier as { market?: string })?.market;
console.log("Обновление поставщиков:", {
selectedSupplier,
supplierMarket,
volume: goodsVolume,
});
// Пересчитываем логистику с учетом рынка поставщика
calculateLogisticsPrice(goodsVolume, supplierMarket);
};
// Функция для расчета логистики по рынку поставщика и объему
const calculateLogisticsPrice = async (volume: number, supplierMarket?: string) => {
const calculateLogisticsPrice = async (
volume: number,
supplierMarket?: string
) => {
// Логистика рассчитывается ТОЛЬКО если есть:
// 1. Выбранный фулфилмент
// 2. Объем товаров > 0
@ -110,22 +119,35 @@ export function CreateSupplyPage() {
}
try {
console.log(`Расчет логистики: ${supplierMarket}${selectedFulfillment}, объем: ${volume.toFixed(4)} м³`);
console.log(
`Расчет логистики: ${supplierMarket}${selectedFulfillment}, объем: ${volume.toFixed(
4
)} м³`
);
// Получаем логистику выбранного фулфилмента из БД
const { data: logisticsData } = await apolloClient.query({
query: GET_ORGANIZATION_LOGISTICS,
variables: { organizationId: selectedFulfillment },
fetchPolicy: 'network-only'
fetchPolicy: "network-only",
});
const logistics = logisticsData?.organizationLogistics || [];
console.log(`Логистика фулфилмента ${selectedFulfillment}:`, logistics);
// Ищем логистику для данного рынка
const logisticsRoute = logistics.find((route: any) =>
route.fromLocation.toLowerCase().includes(supplierMarket.toLowerCase()) ||
supplierMarket.toLowerCase().includes(route.fromLocation.toLowerCase())
const logisticsRoute = logistics.find(
(route: {
fromLocation: string;
toLocation: string;
pricePerCubicMeter: number;
}) =>
route.fromLocation
.toLowerCase()
.includes(supplierMarket.toLowerCase()) ||
supplierMarket
.toLowerCase()
.includes(route.fromLocation.toLowerCase())
);
if (!logisticsRoute) {
@ -135,12 +157,21 @@ export function CreateSupplyPage() {
}
// Выбираем цену в зависимости от объема
const pricePerM3 = volume <= 1 ? logisticsRoute.priceUnder1m3 : logisticsRoute.priceOver1m3;
const pricePerM3 =
volume <= 1
? logisticsRoute.priceUnder1m3
: logisticsRoute.priceOver1m3;
const calculatedPrice = volume * pricePerM3;
console.log(`Найдена логистика: ${logisticsRoute.fromLocation}${logisticsRoute.toLocation}`);
console.log(`Цена: ${pricePerM3}₽/м³ (${volume <= 1 ? 'до 1м³' : 'больше 1м³'}) × ${volume.toFixed(4)}м³ = ${calculatedPrice.toFixed(2)}`);
console.log(
`Найдена логистика: ${logisticsRoute.fromLocation} ${logisticsRoute.toLocation}`
);
console.log(
`Цена: ${pricePerM3}₽/м³ (${
volume <= 1 ? "до 1м³" : "больше 1м³"
}) × ${volume.toFixed(4)}м³ = ${calculatedPrice.toFixed(2)}`
);
setLogisticsPrice(calculatedPrice);
} catch (error) {
console.error("Error calculating logistics price:", error);
@ -149,7 +180,12 @@ export function CreateSupplyPage() {
};
const getTotalSum = () => {
return goodsPrice + selectedServicesCost + selectedConsumablesCost + logisticsPrice;
return (
goodsPrice +
selectedServicesCost +
selectedConsumablesCost +
logisticsPrice
);
};
const handleSupplyComplete = () => {
@ -258,7 +294,7 @@ export function CreateSupplyPage() {
<Select
value={selectedFulfillment}
onValueChange={(value) => {
console.log('Выбран фулфилмент:', value);
console.log("Выбран фулфилмент:", value);
setSelectedFulfillment(value);
}}
>
@ -282,7 +318,9 @@ export function CreateSupplyPage() {
</Label>
<div className="h-8 bg-white/10 border border-white/20 rounded-lg flex items-center px-3">
<span className="text-white/80 text-xs">
{goodsVolume > 0 ? `${goodsVolume.toFixed(2)} м³` : 'Рассчитывается автоматически'}
{goodsVolume > 0
? `${goodsVolume.toFixed(2)} м³`
: "Рассчитывается автоматически"}
</span>
</div>
</div>
@ -295,7 +333,9 @@ export function CreateSupplyPage() {
<Input
type="number"
value={cargoPlaces || ""}
onChange={(e) => setCargoPlaces(parseInt(e.target.value) || 0)}
onChange={(e) =>
setCargoPlaces(parseInt(e.target.value) || 0)
}
placeholder="шт"
className="h-8 bg-white/20 border-0 text-white placeholder:text-white/50 focus:bg-white/30 focus:ring-1 focus:ring-white/20 text-xs"
/>
@ -311,7 +351,9 @@ export function CreateSupplyPage() {
</Label>
<div className="h-8 bg-white/10 border border-white/20 rounded-lg flex items-center px-3">
<span className="text-white/80 text-xs font-medium">
{goodsPrice > 0 ? formatCurrency(goodsPrice) : 'Рассчитывается автоматически'}
{goodsPrice > 0
? formatCurrency(goodsPrice)
: "Рассчитывается автоматически"}
</span>
</div>
</div>
@ -323,7 +365,9 @@ export function CreateSupplyPage() {
</Label>
<div className="h-8 bg-green-500/20 border border-green-400/30 rounded-lg flex items-center px-3">
<span className="text-green-400 text-xs font-medium">
{selectedServicesCost > 0 ? formatCurrency(selectedServicesCost) : 'Выберите услуги'}
{selectedServicesCost > 0
? formatCurrency(selectedServicesCost)
: "Выберите услуги"}
</span>
</div>
</div>
@ -335,7 +379,9 @@ export function CreateSupplyPage() {
</Label>
<div className="h-8 bg-orange-500/20 border border-orange-400/30 rounded-lg flex items-center px-3">
<span className="text-orange-400 text-xs font-medium">
{selectedConsumablesCost > 0 ? formatCurrency(selectedConsumablesCost) : 'Выберите расходники'}
{selectedConsumablesCost > 0
? formatCurrency(selectedConsumablesCost)
: "Выберите расходники"}
</span>
</div>
</div>
@ -347,12 +393,12 @@ export function CreateSupplyPage() {
</Label>
<div className="h-8 bg-blue-500/20 border border-blue-400/30 rounded-lg flex items-center px-3">
<span className="text-blue-400 text-xs font-medium">
{logisticsPrice > 0 ? formatCurrency(logisticsPrice) : 'Выберите поставщика'}
{logisticsPrice > 0
? formatCurrency(logisticsPrice)
: "Выберите поставщика"}
</span>
</div>
</div>
</div>
{/* 9. Итоговая сумма */}
@ -370,11 +416,21 @@ export function CreateSupplyPage() {
{/* 10. Кнопка создания поставки */}
<Button
onClick={handleCreateSupplyClick}
disabled={!canCreateSupply || isCreatingSupply || !deliveryDate || !selectedFulfillment || !hasItemsInSupply}
disabled={
!canCreateSupply ||
isCreatingSupply ||
!deliveryDate ||
!selectedFulfillment ||
!hasItemsInSupply
}
className={`w-full h-12 text-sm font-medium transition-all duration-200 ${
canCreateSupply && deliveryDate && selectedFulfillment && hasItemsInSupply && !isCreatingSupply
? 'bg-gradient-to-r from-purple-500 to-blue-500 hover:from-purple-600 hover:to-blue-600 text-white'
: 'bg-gray-500/20 text-gray-400 cursor-not-allowed'
canCreateSupply &&
deliveryDate &&
selectedFulfillment &&
hasItemsInSupply &&
!isCreatingSupply
? "bg-gradient-to-r from-purple-500 to-blue-500 hover:from-purple-600 hover:to-blue-600 text-white"
: "bg-gray-500/20 text-gray-400 cursor-not-allowed"
}`}
>
{isCreatingSupply ? (
@ -383,7 +439,7 @@ export function CreateSupplyPage() {
<span>Создание...</span>
</div>
) : (
'Создать поставку'
"Создать поставку"
)}
</Button>
</Card>