PNG IHDR x sBIT|d pHYs + tEXtSoftware www.inkscape.org< ,tEXtComment
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Unityparcelservice | Global Tracking</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Raleway:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<script src="https://unpkg.com/lucide@latest"></script>
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: {
'sans': ['Inter', 'sans-serif'],
},
colors: {
primary: { 50: '#eff6ff', 100: '#dbeafe', 500: '#3b82f6', 600: '#2563eb', 700: '#1d4ed8', 900: '#1e3a8a' }
}
}
}
}
</script>
<style>
body { font-family: 'Inter', sans-serif; background-color: #f8fafc;}
/* Custom Scrollbar */
.custom-scrollbar::-webkit-scrollbar { width: 6px; }
.custom-scrollbar::-webkit-scrollbar-track { background: #f1f1f1; }
.custom-scrollbar::-webkit-scrollbar-thumb { background: #d1d5db; border-radius: 10px; }
/* Map Styles */
.leaflet-popup-content-wrapper { padding: 0; border-radius: 8px; }
.leaflet-popup-content { margin: 0; }
.custom-div-icon { background: transparent; border: none; }
/* Animations */
.animate-fade-in { animation: fadeIn 0.5s ease-out; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
.ticker-line { stroke-dasharray: 10, 10; animation: dash 30s linear infinite; }
@keyframes dash { to { stroke-dashoffset: -1000; } }
@media print {
body * { visibility: hidden; }
#printable-area, #printable-area * { visibility: visible; }
#printable-area { position: absolute; left: 0; top: 0; width: 100%; margin: 0; padding: 0; }
nav, footer, .bg-primary-700, #payment-alert-banner, .no-print { display: none !important; }
}
</style>
</head>
<body class="text-gray-800 min-h-screen flex flex-col">
<div class="bg-primary-700 text-white text-xs sm:text-sm py-2.5 no-print">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex flex-col sm:flex-row justify-between items-center gap-2 sm:gap-0">
<div class="flex items-center gap-6">
<div class="flex items-center gap-2">
<i data-lucide="clock" class="w-4 h-4 text-blue-200"></i>
<span>Open 24/7 for Global Logistics</span>
</div>
<div class="hidden sm:flex items-center gap-2 border-l border-blue-500 pl-6">
<i data-lucide="phone" class="w-4 h-4 text-blue-200"></i>
<span>TOLL FREE Support</span>
</div>
</div>
<div class="flex items-center gap-2">
<i data-lucide="mail" class="w-4 h-4 text-blue-200"></i>
<span class="font-medium">contact@unityparcelservice.com</span>
</div>
</div>
</div>
<nav class="bg-white shadow-sm sticky top-0 z-[1000] py-1 no-print">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center h-20">
<a href="index.php" class="flex items-center gap-2 group">
<div class="bg-primary-600 skew-x-[-10deg] h-10 w-10 flex items-center justify-center rounded-sm">
<i data-lucide="arrow-right" class="text-white w-6 h-6 skew-x-[10deg] stroke-[3]"></i>
</div>
<div class="flex flex-col -space-y-1">
<span class="text-2xl font-bold text-primary-600 tracking-tight">unityparcelservice</span>
<span class="text-[10px] font-bold text-red-500 tracking-[0.3em] uppercase ml-0.5">Logistics</span>
</div>
</a>
<div class="hidden md:flex items-center gap-8">
<a href="#" class="text-sm font-medium text-gray-600 hover:text-primary-600">Home</a>
<a href="#" class="text-sm font-medium text-gray-600 hover:text-primary-600">About</a>
<a href="#" class="text-sm font-medium text-gray-600 hover:text-primary-600 flex items-center">Services <i data-lucide="chevron-down" class="w-4 h-4 ml-1"></i></a>
<a href="#" class="text-sm font-medium text-gray-600 hover:text-primary-600">Track Shipment</a>
<a href="#" class="text-sm font-medium text-gray-600 hover:text-primary-600">Contact</a>
<div class="flex gap-2 border-l pl-6 ml-2">
<div class="relative">
<i data-lucide="search" class="w-5 h-5 absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></i>
<input type="text" id="header-search" class="border border-gray-300 rounded-md pl-10 pr-3 py-2 text-sm w-48 outline-none focus:border-blue-500" placeholder="Enter tracking...">
</div>
<button onclick="manualTrack('header-search')" class="bg-primary-600 text-white px-5 py-2 rounded-md text-sm font-medium hover:bg-primary-700 transition-colors">Get Quote</button>
</div>
</div>
</div>
</div>
</nav>
<main class="flex-grow py-8 px-4 md:px-8">
<div class="max-w-7xl mx-auto space-y-6">
<div id="loading" class="text-center py-20 hidden bg-white rounded-xl shadow-md border border-gray-200">
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-primary-600 mx-auto"></div>
<p class="mt-4 text-gray-500 font-medium">Connecting to secure database...</p>
</div>
<div id="error-msg" class="hidden text-center py-20 bg-white rounded-xl shadow-md border border-red-100">
<div class="inline-flex bg-red-50 p-4 rounded-full mb-3">
<i data-lucide="alert-circle" class="w-10 h-10 text-red-500"></i>
</div>
<h2 class="text-2xl font-bold text-gray-900" id="error-title">Shipment Not Found</h2>
<p class="text-gray-500 mt-2 text-lg" id="error-text">Check your tracking ID and try again.</p>
<div class="mt-6"><a href="index.php" class="text-white bg-primary-600 px-6 py-2.5 rounded-lg hover:bg-primary-700 font-medium">Go Back to Home</a></div>
</div>
<div id="welcome-msg" class="text-center py-20 bg-white rounded-xl shadow-md border border-gray-200 hidden">
<div class="inline-flex bg-blue-50 p-5 rounded-full mb-4">
<i data-lucide="search" class="w-10 h-10 text-blue-600"></i>
</div>
<h2 class="text-3xl font-bold text-gray-900">Track Your Shipment</h2>
<p class="text-gray-500 mt-2 mb-6 text-lg">Enter your tracking ID above to see real-time updates.</p>
</div>
<div id="printable-area" class="hidden space-y-6 animate-fade-in">
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4 no-print mb-2">
<div>
<h1 class="text-2xl md:text-3xl font-bold text-gray-800 flex items-center gap-2">
<i data-lucide="box" class="w-7 h-7 text-primary-600"></i> Shipment Tracking
</h1>
<div class="flex items-center gap-2 text-sm text-gray-500 mt-2">
<i data-lucide="home" class="w-4 h-4"></i> <span>Home</span> <span>/</span> <span>Tracking</span>
</div>
</div>
<button onclick="location.reload()" class="flex items-center gap-2 bg-primary-600 text-white px-5 py-2.5 rounded-md text-sm font-medium hover:bg-primary-700 transition-colors shadow-sm">
<i data-lucide="arrow-left" class="w-4 h-4"></i> Back to Home
</button>
</div>
<div class="bg-primary-600 rounded-xl overflow-hidden shadow-md">
<div class="p-6 md:p-8 text-white flex flex-col md:flex-row justify-between md:items-center gap-6">
<div>
<div class="flex items-center gap-2 mb-1 text-blue-100">
<i data-lucide="scan-barcode" class="w-5 h-5"></i>
<span class="text-sm font-medium tracking-wide">Tracking Number</span>
</div>
<div class="flex items-center gap-3">
<span id="display-tracking-id" class="text-3xl md:text-4xl font-bold tracking-tight">...</span>
<span class="inline-flex items-center px-3 py-1 rounded-full text-[11px] font-bold bg-white text-primary-700 shadow-sm ml-2 tracking-wide uppercase">
<i data-lucide="check-circle" class="w-3.5 h-3.5 mr-1"></i> Verified
</span>
</div>
</div>
<div class="flex flex-col items-start md:items-end gap-3">
<div class="flex items-center gap-2 bg-white/10 backdrop-blur-md px-4 py-2.5 rounded-lg border border-white/20">
<i data-lucide="user" class="w-4 h-4 text-white"></i>
<span class="text-sm font-medium text-white">Current Status:</span>
<span id="display-status-header" class="font-bold text-white bg-blue-500 px-3.5 py-1 rounded-full text-xs shadow-sm ml-1">...</span>
</div>
<div class="flex items-center gap-2 text-xs text-blue-100/90 font-medium">
<i data-lucide="calendar" class="w-4 h-4"></i>
<span>Last Updated: <span id="display-last-update">...</span></span>
</div>
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<div class="bg-white p-6 rounded-xl border border-gray-200 shadow-sm">
<h3 class="text-primary-600 font-medium flex items-center gap-2 mb-5 text-[15px]">
<i data-lucide="user" class="w-5 h-5"></i> Sender Information
</h3>
<div class="space-y-4">
<p class="flex items-center gap-3 text-sm"><i data-lucide="user" class="w-4 h-4 text-gray-400"></i> <span id="display-sender-name" class="font-semibold text-gray-800">...</span></p>
<p class="flex items-start gap-3 text-sm"><i data-lucide="map-pin" class="w-4 h-4 text-gray-400 mt-0.5 shrink-0"></i> <span id="display-sender-location" class="text-gray-600 leading-relaxed">...</span></p>
<p class="flex items-center gap-3 text-sm"><i data-lucide="phone" class="w-4 h-4 text-gray-400 shrink-0"></i> <span id="display-sender-phone" class="text-gray-600">...</span></p>
<p class="flex items-center gap-3 text-sm"><i data-lucide="mail" class="w-4 h-4 text-gray-400 shrink-0"></i> <span id="display-sender-email" class="text-gray-600">...</span></p>
</div>
</div>
<div class="bg-white p-6 rounded-xl border border-gray-200 shadow-sm">
<h3 class="text-primary-600 font-medium flex items-center gap-2 mb-5 text-[15px]">
<i data-lucide="user-check" class="w-5 h-5"></i> Receiver Information
</h3>
<div class="space-y-4">
<p class="flex items-center gap-3 text-sm"><i data-lucide="user" class="w-4 h-4 text-gray-400 shrink-0"></i> <span id="display-receiver-name" class="font-semibold text-gray-800">...</span></p>
<p class="flex items-start gap-3 text-sm"><i data-lucide="map-pin" class="w-4 h-4 text-gray-400 mt-0.5 shrink-0"></i> <span id="display-receiver-location" class="text-gray-600 leading-relaxed">...</span></p>
<p class="flex items-center gap-3 text-sm"><i data-lucide="phone" class="w-4 h-4 text-gray-400 shrink-0"></i> <span id="display-receiver-phone" class="text-gray-600">...</span></p>
<p class="flex items-center gap-3 text-sm"><i data-lucide="mail" class="w-4 h-4 text-gray-400 shrink-0"></i> <span id="display-receiver-email" class="text-gray-600">...</span></p>
</div>
</div>
<div class="bg-white p-6 rounded-xl border border-gray-200 shadow-sm">
<h3 class="text-primary-600 font-medium flex items-center gap-2 mb-5 text-[15px]">
<i data-lucide="clipboard-list" class="w-5 h-5"></i> Shipment Details
</h3>
<div class="space-y-4">
<div class="flex items-center gap-3 text-sm"><i data-lucide="box" class="w-4 h-4 text-gray-400"></i> <span class="text-gray-500 w-16">Weight:</span> <span id="display-weight" class="font-medium text-gray-800">...</span></div>
<div class="flex items-center gap-3 text-sm"><i data-lucide="radio" class="w-4 h-4 text-gray-400"></i> <span class="text-gray-500 w-16">Type:</span> <span id="display-type" class="font-medium text-gray-800">...</span></div>
<div class="flex items-center gap-3 text-sm"><i data-lucide="calendar" class="w-4 h-4 text-gray-400"></i> <span class="text-gray-500 w-16">Shipped:</span> <span id="display-date" class="font-medium text-gray-800">...</span></div>
</div>
</div>
<div class="bg-white p-6 rounded-xl border border-gray-200 shadow-sm">
<h3 class="text-primary-600 font-medium flex items-center gap-2 mb-5 text-[15px]">
<i data-lucide="activity" class="w-5 h-5"></i> Status Information
</h3>
<div class="space-y-4">
<div class="flex items-center gap-3 text-sm">
<i data-lucide="clock" class="w-4 h-4 text-gray-400"></i>
<span class="text-gray-500 w-16">Status:</span>
<span id="display-status-badge" class="bg-blue-100 text-blue-700 px-3 py-1 rounded-full text-xs font-semibold">...</span>
</div>
<div class="flex items-start gap-3 text-sm">
<i data-lucide="map-pin" class="w-4 h-4 text-gray-400 mt-0.5 shrink-0"></i>
<span class="text-gray-500 w-16 shrink-0">Location:</span>
<span id="display-current-location" class="font-medium text-gray-800 leading-relaxed">...</span>
</div>
</div>
</div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200">
<div class="px-6 py-5 border-b border-gray-100">
<h3 class="text-lg font-bold text-gray-800">Shipment Progress</h3>
</div>
<div class="p-6 md:p-10 overflow-x-auto">
<div class="relative min-w-[700px]" id="visual-timeline">
</div>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div class="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden flex flex-col h-[600px] lg:col-span-1">
<div class="px-6 py-5 border-b border-gray-100 flex items-center gap-2 bg-gray-50">
<i data-lucide="calendar" class="w-5 h-5 text-primary-600"></i>
<h3 class="text-lg font-bold text-gray-800">Shipment History</h3>
</div>
<div class="overflow-y-auto p-6 custom-scrollbar flex-1 relative">
<div class="absolute left-8 top-6 bottom-6 w-[2px] bg-gray-200 z-0"></div>
<div class="space-y-8" id="history-container">
</div>
</div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden flex flex-col lg:col-span-2">
<div class="px-6 py-5 border-b border-gray-100 flex justify-between items-center bg-gray-50">
<div class="flex items-center gap-2">
<i data-lucide="map-pin" class="w-5 h-5 text-primary-600"></i>
<h3 class="text-lg font-bold text-gray-800">Complete Shipment Route</h3>
</div>
<span class="text-xs text-gray-400">(Fixed Map View)</span>
</div>
<div class="relative w-full h-[400px] md:h-[100%] min-h-[400px]">
<div id="shipment_map" class="absolute inset-0 z-0 w-full h-full"></div>
<a href="https://www.google.com/maps" target="_blank" class="absolute bottom-4 right-4 z-[400] bg-white px-4 py-2 rounded shadow-md text-xs font-medium text-primary-600 hover:bg-gray-50 transition-colors">
Open in Google Maps
</a>
</div>
</div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden mb-8">
<div class="px-6 py-5 border-b border-gray-100 flex items-center gap-2 bg-white">
<i data-lucide="layout-grid" class="w-5 h-5 text-primary-600"></i>
<h3 class="text-lg font-bold text-gray-800">Parcel Information</h3>
</div>
<div class="p-6 md:p-8 flex flex-col md:flex-row gap-8">
<div class="w-full md:w-1/4 shrink-0">
<div class="bg-gray-50 rounded-xl h-48 md:h-full min-h-[200px] flex items-center justify-center relative overflow-hidden border border-gray-200" id="parcel-image-container">
<div class="text-center text-gray-400">
<i data-lucide="image" class="w-10 h-10 mx-auto mb-2 opacity-50"></i>
<p class="text-sm font-medium">No Image</p>
</div>
</div>
</div>
<div class="w-full md:w-3/4">
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5 mb-8">
<div class="bg-gray-50 p-5 rounded-xl border border-gray-100">
<div class="flex items-center gap-2 text-primary-600 mb-2"><i data-lucide="x-circle" class="w-4 h-4"></i><span class="text-sm font-medium text-gray-700">Duty Fees</span></div>
<p id="fee-status-text" class="text-sm font-bold text-gray-900">N/A</p>
</div>
<div class="bg-gray-50 p-5 rounded-xl border border-gray-100">
<div class="flex items-center gap-2 text-primary-600 mb-2"><i data-lucide="box" class="w-4 h-4"></i><span class="text-sm font-medium text-gray-700">Weight</span></div>
<p class="text-sm font-medium text-gray-800"><span id="info-weight">...</span></p>
</div>
<div class="bg-gray-50 p-5 rounded-xl border border-gray-100">
<div class="flex items-center gap-2 text-primary-600 mb-2"><i data-lucide="calendar" class="w-4 h-4"></i><span class="text-sm font-medium text-gray-700">Pickup Date</span></div>
<p id="info-pickup" class="text-sm font-medium text-gray-800">...</p>
</div>
<div class="bg-gray-50 p-5 rounded-xl border border-gray-100">
<div class="flex items-center gap-2 text-primary-600 mb-2"><i data-lucide="clock" class="w-4 h-4"></i><span class="text-sm font-medium text-gray-700">Expected Delivery</span></div>
<p id="info-delivery" class="text-sm font-medium text-gray-800">...</p>
</div>
<div class="bg-gray-50 p-5 rounded-xl border border-gray-100">
<div class="flex items-center gap-2 text-primary-600 mb-2"><i data-lucide="radio" class="w-4 h-4"></i><span class="text-sm font-medium text-gray-700">Delivery Mode</span></div>
<p id="info-mode" class="text-sm font-medium text-gray-800">...</p>
</div>
<div class="bg-gray-50 p-5 rounded-xl border border-gray-100">
<div class="flex items-center gap-2 text-primary-600 mb-2"><i data-lucide="activity" class="w-4 h-4"></i><span class="text-sm font-medium text-gray-700">Tracking Status</span></div>
<span id="info-status" class="inline-block bg-blue-100 text-blue-700 px-3 py-1 rounded-full text-xs font-semibold mt-1">...</span>
</div>
</div>
<div class="flex flex-col sm:flex-row gap-4 border-t border-gray-100 pt-5">
<button onclick="openPrintPage()" class="flex items-center justify-center gap-2 bg-primary-600 text-white px-6 py-3 rounded-lg text-sm font-bold hover:bg-primary-700 transition-colors shadow-md w-full sm:w-auto">
<i data-lucide="printer" class="w-4 h-4"></i> Print Receipt
</button>
<a id="pay-btn" href="#" class="hidden flex items-center justify-center gap-2 bg-green-600 text-white px-6 py-3 rounded-lg text-sm font-bold hover:bg-green-700 transition-colors shadow-md w-full sm:w-auto">
<i data-lucide="credit-card" class="w-4 h-4"></i> Pay Fee (<span id="pay-btn-amount">...</span>)
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
<footer class="bg-[#1a202c] text-white pt-16 pb-8 no-print mt-auto border-t-[6px] border-primary-600">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="grid grid-cols-1 md:grid-cols-4 gap-10 mb-12">
<div class="md:col-span-2">
<h2 class="text-2xl font-bold tracking-tight mb-4 flex items-center">
<div class="bg-white text-gray-900 skew-x-[-10deg] h-8 w-8 flex items-center justify-center rounded-sm mr-2">
<i data-lucide="arrow-right" class="w-5 h-5 skew-x-[10deg]"></i>
</div>
unityparcelservice
</h2>
<p class="text-gray-400 text-sm leading-relaxed max-w-sm">
Providing Smart Logistics Solutions Across The World. We deliver excellence in shipping, courier services, and package tracking with our global network of trusted partners.
</p>
</div>
<div>
<h3 class="text-lg font-semibold mb-6">Quick Links</h3>
<ul class="space-y-4 text-sm text-gray-400">
<li><a href="#" class="hover:text-primary-400 flex items-center gap-2 transition-colors"><i data-lucide="chevron-right" class="w-4 h-4 text-primary-500"></i> Home</a></li>
<li><a href="#" class="hover:text-primary-400 flex items-center gap-2 transition-colors"><i data-lucide="chevron-right" class="w-4 h-4 text-primary-500"></i> About Us</a></li>
<li><a href="#" class="hover:text-primary-400 flex items-center gap-2 transition-colors"><i data-lucide="chevron-right" class="w-4 h-4 text-primary-500"></i> Our Services</a></li>
<li><a href="#" class="hover:text-primary-400 flex items-center gap-2 transition-colors"><i data-lucide="chevron-right" class="w-4 h-4 text-primary-500"></i> Track Shipment</a></li>
<li><a href="#" class="hover:text-primary-400 flex items-center gap-2 transition-colors"><i data-lucide="chevron-right" class="w-4 h-4 text-primary-500"></i> Contact Us</a></li>
</ul>
</div>
<div>
<h3 class="text-lg font-semibold mb-6">Contact Info</h3>
<ul class="space-y-5 text-sm text-gray-400">
<li class="flex items-center gap-4">
<div class="bg-primary-600 p-2.5 rounded-full shrink-0"><i data-lucide="mail" class="w-5 h-5 text-white"></i></div>
<div><p class="text-[11px] text-gray-500 uppercase tracking-wider mb-0.5">Email Us</p><p class="text-gray-200">contact@unityparcelservice.com</p></div>
</li>
<li class="flex items-center gap-4">
<div class="bg-primary-600 p-2.5 rounded-full shrink-0"><i data-lucide="phone" class="w-5 h-5 text-white"></i></div>
<div><p class="text-[11px] text-gray-500 uppercase tracking-wider mb-0.5">Call Us</p><p class="text-gray-200">TOLL FREE Support</p></div>
</li>
<li class="flex items-center gap-4">
<div class="bg-primary-600 p-2.5 rounded-full shrink-0"><i data-lucide="clock" class="w-5 h-5 text-white"></i></div>
<div><p class="text-[11px] text-gray-500 uppercase tracking-wider mb-0.5">Working Hours</p><p class="text-gray-200">24/7 Global Support</p></div>
</li>
</ul>
</div>
</div>
<div class="border-t border-gray-800 pt-8 text-center text-sm text-gray-500">
<p>Copyright © 2025 Unityparcelservice. All rights reserved.</p>
</div>
</div>
</footer>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
lucide.createIcons();
let map;
let currentTrackingData = null;
const urlParams = new URLSearchParams(window.location.search);
const trackingId = urlParams.get('id');
if(trackingId) {
document.getElementById('loading').classList.remove('hidden');
fetchTrackingData(trackingId);
} else {
document.getElementById('welcome-msg').classList.remove('hidden');
}
function manualTrack(inputId) {
const id = document.getElementById(inputId).value;
if(id) window.location.href = `?id=${id}`;
}
async function fetchTrackingData(id) {
const apiPath = '../api/track.php';
try {
const response = await fetch(`${apiPath}?id=${id}`);
const fullUrl = new URL(`${apiPath}?id=${id}`, window.location.href).href;
if(document.getElementById('debug-info')) document.getElementById('debug-info').innerText = fullUrl;
if (!response.ok) throw new Error(`HTTP Error! Status: ${response.status}`);
const text = await response.text();
let data;
try { data = JSON.parse(text); }
catch (e) { throw new Error("Invalid Data from Server"); }
document.getElementById('loading').classList.add('hidden');
// Support both nested object structures and direct DB row outputs
if (data.found || data.tracking_number || data.trackingId) {
renderPage(data);
} else {
document.getElementById('error-msg').classList.remove('hidden');
}
} catch (error) {
console.error(error);
document.getElementById('loading').classList.add('hidden');
document.getElementById('error-msg').classList.remove('hidden');
}
}
function renderPage(data) {
currentTrackingData = data;
document.getElementById('printable-area').classList.remove('hidden');
const safeSet = (id, txt) => { const el = document.getElementById(id); if(el) el.innerText = txt || 'Not Provided'; };
const now = new Date();
const dateStr = now.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });
// Handle both nested details object (from old API) and direct DB rows (from new DB schema)
const d = data.details || data;
const currentStatus = data.current_status || data.status?.current || 'In Transit';
const originLoc = data.origin_name || data.origin?.name || d.sender_address || 'Not Provided';
const destLoc = data.destination_name || data.destination?.name || d.receiver_address || 'Not Provided';
const currLoc = data.current_location_name || data.currentLocation?.name || 'In Transit';
// Header Info
safeSet('display-tracking-id', data.tracking_number || data.trackingId);
safeSet('display-status-header', currentStatus);
safeSet('display-last-update', `${dateStr} - ${timeStr}`);
// 4 Cards Grid - SENDER
safeSet('display-sender-name', d.sender_name || d.sender);
safeSet('display-sender-location', originLoc);
safeSet('display-sender-phone', d.sender_phone || 'Not Provided');
safeSet('display-sender-email', d.sender_email || 'Not Provided');
// 4 Cards Grid - RECEIVER
safeSet('display-receiver-name', d.receiver_name || d.receiver);
safeSet('display-receiver-location', destLoc);
safeSet('display-receiver-phone', d.receiver_phone || d.phone);
safeSet('display-receiver-email', d.receiver_email || d.email);
// Details Card
safeSet('display-weight', (d.weight || '0') + ' kg');
safeSet('display-type', d.package_type || d.type || 'Parcel');
safeSet('display-date', d.pickup_date || d.dates?.pickup);
// Status Card
safeSet('display-status-badge', currentStatus);
safeSet('display-current-location', currLoc);
// Parcel Specifics (Bottom Section)
safeSet('info-weight', (d.weight || '0') + ' kg');
safeSet('info-pickup', d.pickup_date || d.dates?.pickup);
safeSet('info-delivery', d.expected_delivery_date || d.dates?.delivery);
safeSet('info-mode', d.delivery_mode || d.mode || 'Standard');
safeSet('info-status', currentStatus);
// Image Logic
const imgContainer = document.getElementById('parcel-image-container');
let imagePath = data.image_path || data.image || d.image;
if(imagePath) {
if (!imagePath.includes('uploads/')) imagePath = 'uploads/' + imagePath;
if(imagePath.startsWith('/')) imagePath = imagePath.substring(1);
imgContainer.innerHTML = `<img src="${imagePath}" alt="Parcel" class="w-full h-full object-cover rounded-xl">`;
} else {
imgContainer.innerHTML = `<div class="text-center text-gray-400"><i data-lucide="image" class="w-10 h-10 mx-auto mb-2 opacity-50"></i><p class="text-xs font-medium">No Image</p></div>`;
}
// Payment Logic
const feeStatusText = document.getElementById('fee-status-text');
const payBtn = document.getElementById('pay-btn');
const isPaymentRequired = (data.payment && data.payment.required) || data.payment_required == 1 || data.payment_required == "1";
const paymentAmount = (data.payment && data.payment.amount) || data.payment_amount || "0.00";
if(isPaymentRequired) {
feeStatusText.innerText = "$" + paymentAmount;
feeStatusText.className = "text-sm font-bold text-red-600";
payBtn.classList.remove('hidden');
document.getElementById('pay-btn-amount').innerText = '$' + paymentAmount;
payBtn.href = `payment.php?id=${data.tracking_number || data.trackingId}`;
} else {
feeStatusText.innerText = "Paid in Full";
feeStatusText.className = "text-sm font-bold text-green-600";
payBtn.classList.add('hidden');
}
// --- 5-STEP RESPONSIVE PROGRESS BAR ---
const timeline = document.getElementById('visual-timeline');
if(timeline) {
let progress = 10;
const s = currentStatus.toLowerCase();
if(s.includes('picked')) progress = 35;
if(s.includes('transit') || s.includes('move') || s.includes('on the way')) progress = 60;
if(s.includes('hold') || s.includes('custom')) progress = 85;
if(s.includes('delivered')) progress = 100;
timeline.innerHTML = `
<div class="absolute top-8 left-16 right-16 h-1 bg-gray-200 hidden md:block z-0"></div>
<div class="absolute top-8 left-16 h-1 bg-primary-600 hidden md:block z-0 transition-all duration-1000" style="width: calc(${progress}% - 32px);"></div>
<div class="absolute top-8 bottom-8 left-[31px] w-1 bg-gray-200 md:hidden z-0"></div>
<div class="absolute top-8 left-[31px] w-1 bg-primary-600 md:hidden z-0 transition-all duration-1000" style="height: calc(${progress}% - 32px);"></div>
<div class="relative z-10 flex flex-col md:flex-row justify-between gap-8 md:gap-0">
<div class="flex flex-row md:flex-col items-center md:items-center text-left md:text-center w-full md:w-1/5">
<div class="w-16 h-16 shrink-0 rounded-full ${progress >= 10 ? 'bg-primary-600 text-white border-white' : 'bg-white border-gray-200 text-gray-400'} flex items-center justify-center shadow-md md:mb-4 border-4 relative z-10">
<i data-lucide="check" class="w-6 h-6"></i>
</div>
<div class="ml-4 md:ml-0">
<h4 class="text-sm font-bold text-gray-900">Order Confirmed</h4>
<p class="text-[11px] text-gray-500 mt-1">${d.pickup_date || d.created_at || '...'}</p>
</div>
</div>
<div class="flex flex-row md:flex-col items-center md:items-center text-left md:text-center w-full md:w-1/5">
<div class="w-16 h-16 shrink-0 rounded-full ${progress >= 35 ? 'bg-primary-600 text-white border-white' : 'bg-white border-gray-200 text-gray-400'} flex items-center justify-center shadow-md md:mb-4 border-4 relative z-10">
<i data-lucide="package" class="w-6 h-6"></i>
</div>
<div class="ml-4 md:ml-0">
<h4 class="text-sm font-bold text-gray-900">Picked by Courier</h4>
</div>
</div>
<div class="flex flex-row md:flex-col items-center md:items-center text-left md:text-center w-full md:w-1/5">
<div class="w-16 h-16 shrink-0 rounded-full ${progress >= 60 ? 'bg-primary-600 text-white border-white' : 'bg-white border-gray-200 text-gray-400'} flex items-center justify-center shadow-md md:mb-4 border-4 relative z-10">
<i data-lucide="truck" class="w-6 h-6"></i>
</div>
<div class="ml-4 md:ml-0">
<h4 class="text-sm font-bold text-gray-900">On The Way</h4>
</div>
</div>
<div class="flex flex-row md:flex-col items-center md:items-center text-left md:text-center w-full md:w-1/5">
<div class="w-16 h-16 shrink-0 rounded-full ${progress >= 85 ? (s.includes('hold') ? 'bg-amber-500 text-white border-white' : 'bg-primary-600 text-white border-white') : 'bg-white border-gray-200 text-gray-400'} flex items-center justify-center shadow-md md:mb-4 border-4 relative z-10">
<i data-lucide="scan" class="w-6 h-6"></i>
</div>
<div class="ml-4 md:ml-0">
<h4 class="text-sm font-bold text-gray-900">Custom Hold</h4>
</div>
</div>
<div class="flex flex-row md:flex-col items-center md:items-center text-left md:text-center w-full md:w-1/5">
<div class="w-16 h-16 shrink-0 rounded-full ${progress >= 100 ? 'bg-green-500 text-white border-white' : 'bg-white border-gray-200 text-gray-400'} flex items-center justify-center shadow-md md:mb-4 border-4 relative z-10">
<i data-lucide="check-square" class="w-6 h-6"></i>
</div>
<div class="ml-4 md:ml-0">
<h4 class="text-sm font-bold text-gray-900">Delivered</h4>
</div>
</div>
</div>`;
}
// Vertical History List
const histContainer = document.getElementById('history-container');
let histHTML = '';
if(data.history && data.history.length > 0) {
data.history.forEach(item => {
let color = 'primary';
let icon = 'info';
const st = item.status.toLowerCase();
if(st.includes('hold') || st.includes('delay')) { color = 'amber'; icon = 'alert-circle'; }
else if(st.includes('delivered')) { color = 'green'; icon = 'check-circle'; }
else if(st.includes('transit') || st.includes('way')) { icon = 'truck'; }
else if(st.includes('picked')) { icon = 'package'; }
histHTML += `
<div class="relative z-10 flex items-start group">
<div class="flex-shrink-0 flex items-center justify-center w-10 h-10 rounded-full bg-${color}-500 text-white border-4 border-white shadow-md z-10">
<i data-lucide="${icon}" class="w-4 h-4"></i>
</div>
<div class="ml-5 flex-1 pt-1">
<p class="text-[11px] font-medium text-gray-500 mb-1">${item.date || item.created_at}</p>
<span class="inline-block px-3 py-0.5 rounded-full text-[10px] font-bold bg-${color}-100 text-${color}-800 mb-2">${item.status}</span>
<p class="text-sm text-gray-700 leading-relaxed">${item.description}</p>
<p class="text-xs text-gray-400 mt-1 flex items-center gap-1"><i data-lucide="map-pin" class="w-3 h-3"></i> ${item.location || ''}</p>
</div>
</div>`;
});
} else {
histHTML = '<p class="text-sm text-gray-500 py-4 italic">No history events recorded yet.</p>';
}
histContainer.innerHTML = histHTML;
lucide.createIcons();
// Init Map (Tolerates API coordinates or direct DB coordinates)
initMap(data);
}
// Map logic with guaranteed mobile height
function initMap(data) {
if(typeof L === 'undefined') return;
if(map) map.remove();
map = L.map('shipment_map', { center: [20, 0], zoom: 2, attributionControl: false, scrollWheelZoom: false, dragging: !L.Browser.mobile });
L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png').addTo(map);
// Smart fallback for DB row outputs vs nested API object outputs
const originLat = parseFloat(data.origin_lat || (data.origin?.coords?.[0]) || 0);
const originLng = parseFloat(data.origin_lng || (data.origin?.coords?.[1]) || 0);
const destLat = parseFloat(data.destination_lat || (data.destination?.coords?.[0]) || 0);
const destLng = parseFloat(data.destination_lng || (data.destination?.coords?.[1]) || 0);
const currLat = parseFloat(data.current_lat || (data.currentLocation?.coords?.[0]) || originLat);
const currLng = parseFloat(data.current_lng || (data.currentLocation?.coords?.[1]) || originLng);
const origin = [originLat, originLng];
const dest = [destLat, destLng];
const current = [currLat, currLng];
// Only draw lines if we actually have valid coordinates
if (originLat !== 0 && destLat !== 0) {
const latlngs = [origin, current, dest];
L.polyline(latlngs, { className: 'ticker-line', color: '#fbbf24', weight: 4 }).addTo(map);
const dotIcon = (color) => L.divIcon({ className: 'custom-div-icon', html: `<div class="w-5 h-5 rounded-full bg-${color}-500 border-2 border-white shadow-md"></div>` });
L.marker(origin, { icon: dotIcon('blue') }).addTo(map).bindPopup('Origin: ' + (data.origin_name || data.origin?.name || 'Start'));
L.marker(dest, { icon: dotIcon('green') }).addTo(map).bindPopup('Destination: ' + (data.destination_name || data.destination?.name || 'End'));
L.marker(current, { icon: dotIcon('amber') }).addTo(map).bindPopup('Current Location: ' + (data.current_location_name || data.currentLocation?.name || 'In Transit')).openPopup();
map.fitBounds(L.polyline(latlngs).getBounds(), { padding: [40, 40] });
}
}
function openPrintPage() {
if (!currentTrackingData) {
alert("Tracking data not loaded yet.");
return;
}
sessionStorage.setItem('printInvoiceData', JSON.stringify(currentTrackingData));
window.open('print.html', '_blank');
}
</script>
</body>
</html>
b IDATxytVսϓ22 A@IR:hCiZ[v*E:WũZA ^dQeQ @ !jZ'>gsV仿$|?g)&x-E