256 lines
8.4 KiB
TypeScript
256 lines
8.4 KiB
TypeScript
import React from "react";
|
|
import { CheckCircle, XCircle, AlertCircle, Loader2 } from "lucide-react";
|
|
import { shippingService, DeliveryScheduleItem } from "../../src/services/shipping.service";
|
|
|
|
export interface DeliveryAvailabilityStatusProps {
|
|
selectedDate: Date | null;
|
|
orderWeight: number; // Peso do pedido em toneladas
|
|
isLoading?: boolean;
|
|
}
|
|
|
|
export interface DeliveryAvailabilityResult {
|
|
available: boolean;
|
|
capacity: number;
|
|
currentLoad: number;
|
|
availableCapacity: number;
|
|
occupancy: number;
|
|
message: string;
|
|
}
|
|
|
|
const DeliveryAvailabilityStatus: React.FC<DeliveryAvailabilityStatusProps> = ({
|
|
selectedDate,
|
|
orderWeight,
|
|
isLoading = false,
|
|
}) => {
|
|
const [availability, setAvailability] = React.useState<DeliveryAvailabilityResult | null>(null);
|
|
const [checking, setChecking] = React.useState(false);
|
|
|
|
React.useEffect(() => {
|
|
const checkAvailability = async () => {
|
|
if (!selectedDate || orderWeight <= 0) {
|
|
setAvailability(null);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
setChecking(true);
|
|
|
|
// Formatar data para DD/MM/YYYY (formato usado pelo Baldinho)
|
|
const formattedDate = selectedDate.toLocaleDateString("pt-BR", {
|
|
day: "2-digit",
|
|
month: "2-digit",
|
|
year: "numeric",
|
|
});
|
|
|
|
console.log("📦 [DELIVERY_AVAILABILITY] Verificando disponibilidade:");
|
|
console.log("📦 [DELIVERY_AVAILABILITY] Data selecionada:", formattedDate);
|
|
console.log("📦 [DELIVERY_AVAILABILITY] Peso do pedido:", orderWeight, "Ton");
|
|
|
|
// Buscar dados do agendamento
|
|
const response = await shippingService.getScheduleDelivery();
|
|
|
|
if (!response || !response.deliveries || !Array.isArray(response.deliveries)) {
|
|
console.warn("📦 [DELIVERY_AVAILABILITY] Resposta inválida da API");
|
|
setAvailability({
|
|
available: false,
|
|
capacity: 0,
|
|
currentLoad: 0,
|
|
availableCapacity: 0,
|
|
occupancy: 100,
|
|
message: "Não foi possível verificar a disponibilidade",
|
|
});
|
|
return;
|
|
}
|
|
|
|
console.log("📦 [DELIVERY_AVAILABILITY] Itens recebidos:", response.deliveries.length);
|
|
|
|
// Encontrar o item correspondente à data selecionada
|
|
const deliveryItem = response.deliveries.find((item: DeliveryScheduleItem) => {
|
|
const itemDate = new Date(item.dateDelivery);
|
|
const itemFormatted = itemDate.toLocaleDateString("pt-BR", {
|
|
day: "2-digit",
|
|
month: "2-digit",
|
|
year: "numeric",
|
|
timeZone: "UTC",
|
|
});
|
|
console.log("📦 [DELIVERY_AVAILABILITY] Comparando:", itemFormatted, "com", formattedDate);
|
|
return itemFormatted === formattedDate;
|
|
});
|
|
|
|
if (!deliveryItem) {
|
|
setAvailability({
|
|
available: false,
|
|
capacity: 0,
|
|
currentLoad: 0,
|
|
availableCapacity: 0,
|
|
occupancy: 100,
|
|
message: "Data não encontrada no agendamento",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Verificar disponibilidade
|
|
const capacity = deliveryItem.deliverySize || 0;
|
|
const currentLoad = deliveryItem.saleWeigth || 0;
|
|
const availableCapacity = deliveryItem.avaliableDelivery || 0;
|
|
const isDeliveryEnabled = deliveryItem.delivery === "S";
|
|
const canFit = availableCapacity >= orderWeight;
|
|
const occupancy = capacity > 0 ? (currentLoad / capacity) * 100 : 100;
|
|
|
|
const available = isDeliveryEnabled && canFit;
|
|
|
|
let message = "";
|
|
if (!isDeliveryEnabled) {
|
|
message = "Entrega não disponível para esta data";
|
|
} else if (!canFit) {
|
|
message = `Capacidade insuficiente. Disponível: ${availableCapacity.toFixed(3)} Ton, Necessário: ${orderWeight.toFixed(3)} Ton`;
|
|
} else {
|
|
message = `Entrega disponível. Capacidade restante: ${availableCapacity.toFixed(3)} Ton`;
|
|
}
|
|
|
|
setAvailability({
|
|
available,
|
|
capacity,
|
|
currentLoad,
|
|
availableCapacity,
|
|
occupancy,
|
|
message,
|
|
});
|
|
} catch (error) {
|
|
console.error("Erro ao verificar disponibilidade:", error);
|
|
setAvailability({
|
|
available: false,
|
|
capacity: 0,
|
|
currentLoad: 0,
|
|
availableCapacity: 0,
|
|
occupancy: 100,
|
|
message: "Erro ao verificar disponibilidade de entrega",
|
|
});
|
|
} finally {
|
|
setChecking(false);
|
|
}
|
|
};
|
|
|
|
checkAvailability();
|
|
}, [selectedDate, orderWeight]);
|
|
|
|
if (!selectedDate || orderWeight <= 0) {
|
|
return null;
|
|
}
|
|
|
|
if (checking || isLoading) {
|
|
return (
|
|
<div className="mt-4 p-4 bg-slate-50 rounded-xl border border-slate-200">
|
|
<div className="flex items-center gap-3">
|
|
<Loader2 className="w-5 h-5 text-orange-500 animate-spin" />
|
|
<span className="text-sm font-medium text-slate-600">
|
|
Verificando disponibilidade de entrega...
|
|
</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!availability) {
|
|
return null;
|
|
}
|
|
|
|
const isAvailable = availability.available;
|
|
const occupancyColor =
|
|
availability.occupancy >= 100
|
|
? "bg-red-500"
|
|
: availability.occupancy >= 85
|
|
? "bg-orange-500"
|
|
: "bg-emerald-500";
|
|
|
|
return (
|
|
<div
|
|
className={`mt-4 p-5 rounded-xl border-2 transition-all duration-300 ${
|
|
isAvailable
|
|
? "bg-emerald-50 border-emerald-300 shadow-emerald-200/20"
|
|
: "bg-red-50 border-red-300 shadow-red-200/20"
|
|
}`}
|
|
>
|
|
<div className="flex items-start gap-4">
|
|
{/* Ícone */}
|
|
<div
|
|
className={`flex-shrink-0 w-12 h-12 rounded-xl flex items-center justify-center ${
|
|
isAvailable
|
|
? "bg-emerald-500/20"
|
|
: "bg-red-500/20"
|
|
}`}
|
|
>
|
|
{isAvailable ? (
|
|
<CheckCircle className="w-6 h-6 text-emerald-600" />
|
|
) : (
|
|
<XCircle className="w-6 h-6 text-red-600" />
|
|
)}
|
|
</div>
|
|
|
|
{/* Conteúdo */}
|
|
<div className="flex-1">
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<h4
|
|
className={`text-sm font-black ${
|
|
isAvailable ? "text-emerald-900" : "text-red-900"
|
|
}`}
|
|
>
|
|
{isAvailable ? "Entrega Disponível" : "Entrega Indisponível"}
|
|
</h4>
|
|
{availability.occupancy >= 85 && availability.occupancy < 100 && (
|
|
<AlertCircle className="w-4 h-4 text-orange-500" />
|
|
)}
|
|
</div>
|
|
|
|
<p
|
|
className={`text-xs font-medium mb-3 ${
|
|
isAvailable ? "text-emerald-700" : "text-red-700"
|
|
}`}
|
|
>
|
|
{availability.message}
|
|
</p>
|
|
|
|
{/* Barra de ocupação */}
|
|
<div className="space-y-2">
|
|
<div className="flex items-center justify-between text-[10px] font-bold text-slate-600">
|
|
<span>Ocupação da Capacidade</span>
|
|
<span>{availability.occupancy.toFixed(1)}%</span>
|
|
</div>
|
|
<div className="w-full h-2 bg-slate-200 rounded-full overflow-hidden shadow-inner">
|
|
<div
|
|
className={`h-full transition-all duration-1000 ${occupancyColor}`}
|
|
style={{
|
|
width: `${Math.min(availability.occupancy, 100)}%`,
|
|
}}
|
|
></div>
|
|
</div>
|
|
<div className="grid grid-cols-3 gap-2 text-[10px] font-medium text-slate-600 mt-2">
|
|
<div>
|
|
<span className="block text-slate-400 mb-0.5">Capacidade</span>
|
|
<span className="font-black">{availability.capacity.toFixed(3)} Ton</span>
|
|
</div>
|
|
<div>
|
|
<span className="block text-slate-400 mb-0.5">Carga Atual</span>
|
|
<span className="font-black">{availability.currentLoad.toFixed(3)} Ton</span>
|
|
</div>
|
|
<div>
|
|
<span className="block text-slate-400 mb-0.5">Disponível</span>
|
|
<span className={`font-black ${
|
|
availability.availableCapacity >= orderWeight
|
|
? "text-emerald-600"
|
|
: "text-red-600"
|
|
}`}>
|
|
{availability.availableCapacity.toFixed(3)} Ton
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default DeliveryAvailabilityStatus;
|
|
|