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>Ocenicargo | Global Tracking</title>
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Leaflet Map CSS -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<!-- Icons -->
<script src="https://unpkg.com/lucide@latest"></script>
<style>
body { font-family: 'Inter', sans-serif; }
/* 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: 12px; box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1); }
.leaflet-popup-content { margin: 0; }
.leaflet-popup-tip { background: white; }
.custom-popup-clean a.leaflet-popup-close-button { display: none; }
.custom-div-icon { background: transparent; border: none; }
/* Animations */
.animate-fade-in { animation: fadeIn 0.3s ease-out; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
/* Dropdown Animation */
.group:hover .group-hover\:visible { visibility: visible; }
.group:hover .group-hover\:opacity-100 { opacity: 1; }
.group:hover .group-hover\:translate-y-0 { transform: translateY(0); }
/* Map Ticker Animation */
.ticker-line { stroke-dasharray: 10, 10; animation: dash 30s linear infinite; }
@keyframes dash { to { stroke-dashoffset: -1000; } }
/* Print Logic */
@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; }
.no-print { display: none !important; }
}
</style>
</head>
<body class="bg-gray-50 text-gray-800 min-h-screen flex flex-col">
<!-- Top Bar -->
<div class="bg-blue-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@Ocenicargo.com</span>
</div>
</div>
</div>
<!-- Main Navigation -->
<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">
<div class="flex-shrink-0 flex items-center">
<a href="index.php" class="flex items-center">
<img src="storage/app/public/photos/lRaPwLSBfTJYrbcZN4ChG4v1McRzaZhI0VLL2kaf.png"
alt="Ocenicargo"
class="h-12 w-auto">
</a>
</div>
<div class="hidden md:flex items-center gap-5">
<div class="flex gap-2">
<input type="text" id="header-search" placeholder="TRK-..." class="border rounded-lg px-3 py-1.5 text-sm w-32 md:w-48 outline-none focus:border-blue-500">
<button onclick="manualTrack('header-search')" class="bg-blue-600 text-white px-4 py-1.5 rounded-lg text-sm font-medium hover:bg-blue-700">Track</button>
</div>
</div>
<button onclick="toggleMobileMenu()" class="lg:hidden p-2 text-gray-500 hover:text-gray-900">
<i data-lucide="menu" class="w-7 h-7"></i>
</button>
</div>
</div>
</nav>
<!-- Mobile Menu -->
<div id="mobile-menu" class="hidden lg:hidden bg-white border-t border-gray-100 p-4 space-y-2 absolute w-full shadow-lg z-50 no-print">
<a href="index.php" class="block px-4 py-3 rounded-lg text-base font-medium text-blue-600 bg-blue-50">Home</a>
<a href="login.html" class="block px-4 py-3 rounded-lg text-base font-medium text-gray-700 hover:bg-gray-50">Staff Login</a>
<div class="pt-4 mt-2 border-t border-gray-100">
<input type="text" id="mobile-search" placeholder="Enter Tracking ID" class="w-full border rounded-lg px-3 py-3 mb-2 outline-none">
<button onclick="manualTrack('mobile-search')" class="block w-full text-center bg-blue-600 text-white px-4 py-3 rounded-lg font-bold hover:bg-blue-700">Track Shipment</button>
</div>
</div>
<main class="flex-grow py-8 px-4 md:px-8">
<div class="max-w-7xl mx-auto space-y-8">
<!-- 1. LOADING STATE -->
<div id="loading" class="text-center py-20 hidden">
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"></div>
<p class="mt-4 text-gray-500">Retrieving shipment details...</p>
</div>
<!-- 2. WELCOME / NO ID STATE -->
<div id="welcome-msg" class="text-center py-20 bg-white rounded-2xl shadow-sm border border-gray-200 hidden">
<div class="inline-flex bg-blue-50 p-4 rounded-full mb-4">
<i data-lucide="search" class="w-8 h-8 text-blue-600"></i>
</div>
<h2 class="text-2xl font-bold text-gray-900">Track Your Shipment</h2>
<p class="text-gray-500 mt-2 mb-6">Enter your tracking ID to see real-time updates.</p>
<div class="flex justify-center gap-2 max-w-md mx-auto">
<input type="text" id="hero-search" placeholder="Enter Tracking ID (e.g. TRK-12345)" class="border rounded-lg px-4 py-3 w-full outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-200">
<button onclick="manualTrack('hero-search')" class="bg-blue-600 text-white px-6 py-3 rounded-lg font-bold hover:bg-blue-700 transition-colors">Track</button>
</div>
</div>
<!-- 3. ERROR MESSAGE -->
<div id="error-msg" class="hidden text-center py-20 bg-white rounded-2xl shadow-sm border border-red-100">
<div class="inline-flex bg-red-50 p-3 rounded-full mb-3">
<i data-lucide="alert-circle" class="w-8 h-8 text-red-500"></i>
</div>
<h2 class="text-xl font-bold text-gray-900" id="error-title">Shipment Not Found</h2>
<p class="text-gray-500 mt-2" id="error-text">Check your tracking ID and try again.</p>
</div>
<!-- 4. MAIN CONTENT (Hidden until loaded) -->
<div id="printable-area" class="hidden space-y-8 animate-fade-in">
<!-- Breadcrumb -->
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4 no-print">
<div>
<h1 class="text-2xl md:text-3xl font-bold text-gray-900 flex items-center gap-2">
<i data-lucide="box" class="w-6 h-6 text-blue-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 class="text-gray-300">/</span> <span>Tracking</span>
</div>
</div>
<button onclick="location.reload()" class="flex items-center gap-2 bg-blue-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-blue-700 transition-colors shadow-sm">
<i data-lucide="refresh-cw" class="w-4 h-4"></i> Refresh Status
</button>
</div>
<!-- Status Card -->
<div class="bg-white rounded-2xl shadow-xl border border-gray-200 overflow-hidden">
<div class="bg-blue-600 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-2 text-blue-100">
<i data-lucide="scan-barcode" class="w-5 h-5"></i>
<span class="text-sm font-medium uppercase tracking-wider">Tracking Number</span>
</div>
<div class="flex items-center gap-3">
<span id="display-tracking-id" class="text-3xl font-bold tracking-tight">...</span>
<span class="px-2.5 py-0.5 bg-white text-blue-600 text-[10px] font-bold uppercase tracking-wider rounded-md">Verified</span>
</div>
</div>
<div class="flex flex-col items-start md:items-end gap-2">
<div class="flex items-center gap-2 bg-white/10 backdrop-blur-sm px-4 py-2 rounded-lg border border-white/20">
<i data-lucide="activity" class="w-4 h-4 text-blue-100"></i>
<span class="text-sm text-blue-100">Current Status:</span>
<span id="display-status-header" class="font-bold text-white">...</span>
</div>
<div class="flex items-center gap-2 text-xs text-blue-100/80">
<i data-lucide="calendar-clock" class="w-3 h-3"></i>
<span>Last Updated: Live from Database</span>
</div>
</div>
</div>
<!-- Info Grid -->
<div class="p-6 md:p-8 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<!-- Sender -->
<div class="bg-gray-50 p-5 rounded-xl border border-gray-100 group">
<h3 class="text-blue-600 font-bold flex items-center gap-2 mb-4 text-sm group-hover:text-blue-700">
<i data-lucide="upload-cloud" class="w-4 h-4"></i> Sender Information
</h3>
<div class="space-y-3">
<div class="flex items-start gap-3">
<i data-lucide="user" class="w-4 h-4 text-gray-400 mt-0.5"></i>
<p id="display-sender-name" class="text-sm font-medium text-gray-900">...</p>
</div>
<div class="flex items-start gap-3">
<i data-lucide="map-pin" class="w-4 h-4 text-gray-400 mt-0.5"></i>
<p id="display-sender-location" class="text-sm text-gray-600">...</p>
</div>
</div>
</div>
<!-- Receiver -->
<div class="bg-gray-50 p-5 rounded-xl border border-gray-100 hover:border-blue-200 transition-colors group">
<h3 class="text-blue-600 font-bold flex items-center gap-2 mb-4 text-sm group-hover:text-blue-700">
<i data-lucide="download-cloud" class="w-4 h-4"></i> Receiver Information
</h3>
<div class="space-y-3">
<div class="flex items-start gap-3">
<i data-lucide="user" class="w-4 h-4 text-gray-400 mt-0.5"></i>
<p id="display-receiver-name" class="text-sm font-medium text-gray-900">...</p>
</div>
<div class="flex items-start gap-3">
<i data-lucide="map-pin" class="w-4 h-4 text-gray-400 mt-0.5"></i>
<p id="display-receiver-location" class="text-sm text-gray-600">...</p>
</div>
</div>
</div>
<!-- Details -->
<div class="bg-gray-50 p-5 rounded-xl border border-gray-100 hover:border-blue-200 transition-colors group">
<h3 class="text-blue-600 font-bold flex items-center gap-2 mb-4 text-sm group-hover:text-blue-700">
<i data-lucide="clipboard-list" class="w-4 h-4"></i> Shipment Details
</h3>
<div class="space-y-3">
<div class="flex items-center gap-3">
<i data-lucide="weight" class="w-4 h-4 text-gray-400"></i>
<span class="text-sm text-gray-600">Weight: <span id="display-weight" class="font-medium text-gray-900">...</span></span>
</div>
<div class="flex items-center gap-3">
<i data-lucide="package" class="w-4 h-4 text-gray-400"></i>
<span class="text-sm text-gray-600">Type: <span id="display-type" class="font-medium text-gray-900">...</span></span>
</div>
<div class="flex items-center gap-3">
<i data-lucide="calendar-days" class="w-4 h-4 text-gray-400"></i>
<span class="text-sm text-gray-600">Shipped: <span id="display-date" class="font-medium text-gray-900">...</span></span>
</div>
</div>
</div>
<!-- Status -->
<div class="bg-gray-50 p-5 rounded-xl border border-gray-100 hover:border-blue-200 transition-colors group">
<h3 class="text-blue-600 font-bold flex items-center gap-2 mb-4 text-sm group-hover:text-blue-700">
<i data-lucide="activity" class="w-4 h-4"></i> Status Information
</h3>
<div class="space-y-3">
<div class="flex items-center gap-3">
<i data-lucide="clock" class="w-4 h-4 text-gray-400"></i>
<span class="text-sm text-gray-600">Status: <span id="display-status-badge" class="bg-blue-100 text-blue-700 px-2 py-0.5 rounded text-xs font-bold uppercase">...</span></span>
</div>
<div class="flex items-center gap-3">
<i data-lucide="map" class="w-4 h-4 text-gray-400"></i>
<span class="text-sm text-gray-600">Location: <span id="display-current-location" class="font-medium text-gray-900">...</span></span>
</div>
</div>
</div>
</div>
</div>
<!-- Visual Timeline -->
<div class="bg-white rounded-2xl shadow-xl border border-gray-200 p-6 md:p-8">
<h2 class="text-lg font-bold text-gray-900 mb-8 border-b pb-2">Shipment Progress</h2>
<div class="relative" id="visual-timeline">
<!-- Populated by JS -->
</div>
</div>
<!-- History & Map -->
<div class="bg-white rounded-2xl shadow-xl border border-gray-200 overflow-hidden grid grid-cols-1 lg:grid-cols-2 min-h-[650px]">
<!-- Left: History -->
<div class="flex flex-col h-full lg:border-r border-gray-100 order-2 lg:order-1">
<div class="p-5 border-b border-gray-100 flex items-center gap-3 bg-white sticky top-0 z-10">
<div class="p-2 bg-blue-50 rounded-lg text-blue-600">
<i data-lucide="calendar" class="w-5 h-5"></i>
</div>
<div>
<h2 class="text-lg font-bold text-gray-900">Shipment Timeline</h2>
<p class="text-xs text-gray-500">Live updates</p>
</div>
</div>
<div class="overflow-y-auto p-6 custom-scrollbar flex-1">
<div class="relative" id="history-container">
<!-- Filled by JS -->
</div>
</div>
</div>
<!-- Right: Map -->
<div class="flex flex-col h-[400px] lg:h-auto relative bg-gray-100 order-1 lg:order-2 group">
<div class="p-4 absolute top-0 left-0 right-0 z-[400] flex justify-between items-start pointer-events-none">
<div class="bg-white/90 backdrop-blur-sm p-3 rounded-xl shadow-lg border border-gray-100 pointer-events-auto inline-flex items-center gap-2">
<i data-lucide="map-pin" class="text-blue-600 w-4 h-4"></i>
<span class="text-xs font-bold text-gray-800">Live Route</span>
</div>
<a href="https://www.google.com/maps" target="_blank" class="pointer-events-auto bg-white/90 backdrop-blur-sm px-3 py-1.5 rounded-lg shadow-md text-xs font-medium text-blue-600 hover:bg-white transition-colors border border-gray-200">
Open Google Maps
</a>
</div>
<div id="map" class="absolute inset-0 z-0 bg-blue-100"></div>
</div>
</div>
<!-- Parcel Information -->
<div class="bg-white rounded-2xl shadow-xl border border-gray-200 p-6 md:p-8">
<div class="flex items-center gap-3 mb-6 border-b pb-3">
<i data-lucide="layout-grid" class="w-6 h-6 text-blue-600"></i>
<h2 class="text-lg font-bold text-gray-900">Parcel Information</h2>
</div>
<div class="grid grid-cols-1 md:grid-cols-4 gap-6">
<!-- Parcel Image Container -->
<div class="md:col-span-1 bg-gray-100 rounded-xl h-48 flex items-center justify-center relative overflow-hidden border border-gray-200" id="parcel-image-container">
<!-- Default Image State -->
<div class="text-center text-gray-400">
<i data-lucide="image" class="w-12 h-12 mx-auto mb-2 opacity-50"></i>
<p class="text-sm font-medium">No Image</p>
</div>
</div>
<div class="md:col-span-3 grid grid-cols-2 lg:grid-cols-3 gap-4">
<div class="p-4 bg-gray-50 rounded-xl border border-gray-100 space-y-1">
<i data-lucide="banknote" class="w-4 h-4 text-gray-500"></i>
<p class="text-xs text-gray-600">Duty Fees</p>
<p class="font-bold text-gray-900 text-sm">N/A</p>
</div>
<div class="p-4 bg-gray-50 rounded-xl border border-gray-100 space-y-1">
<i data-lucide="weight" class="w-4 h-4 text-gray-500"></i>
<p class="text-xs text-gray-600">Weight</p>
<p class="font-bold text-gray-900 text-sm"><span id="info-weight">...</span></p>
</div>
<div class="p-4 bg-gray-50 rounded-xl border border-gray-100 space-y-1">
<i data-lucide="calendar" class="w-4 h-4 text-gray-500"></i>
<p class="text-xs text-gray-600">Pickup Date</p>
<p class="font-bold text-gray-900 text-sm"><span id="info-pickup">...</span></p>
</div>
<div class="p-4 bg-gray-50 rounded-xl border border-gray-100 space-y-1">
<i data-lucide="clock" class="w-4 h-4 text-gray-500"></i>
<p class="text-xs text-gray-600">Expected Delivery</p>
<p class="font-bold text-gray-900 text-sm"><span id="info-delivery">...</span></p>
</div>
<div class="p-4 bg-gray-50 rounded-xl border border-gray-100 space-y-1">
<i data-lucide="wifi" class="w-4 h-4 text-gray-500"></i>
<p class="text-xs text-gray-600">Delivery Mode</p>
<p class="font-bold text-gray-900 text-sm"><span id="info-mode">...</span></p>
</div>
<div class="p-4 bg-gray-50 rounded-xl border border-gray-100 space-y-1">
<i data-lucide="activity" class="w-4 h-4 text-gray-500"></i>
<p class="text-xs text-gray-600">Tracking Status</p>
<span id="info-status" class="text-blue-700 text-xs font-bold uppercase">...</span>
</div>
</div>
<div class="md:col-span-4 flex justify-end pt-2 no-print">
<button onclick="window.print()" class="flex items-center gap-2 bg-blue-600 text-white px-5 py-3 rounded-lg font-medium hover:bg-blue-700 transition-colors shadow-md">
<i data-lucide="printer" class="w-5 h-5"></i> Print Receipt
</button>
</div>
</div>
</div>
<!-- Payment Section -->
<div id="payment-section" class="hidden bg-white rounded-2xl shadow-xl border border-red-100 overflow-hidden animate-fade-in ring-4 ring-red-50 mt-8 mb-12">
<div class="p-5 border-b border-red-100 flex flex-col md:flex-row md:items-center justify-between gap-4 bg-gradient-to-r from-red-50 to-white">
<div class="flex items-center gap-3">
<div class="p-2 bg-red-100 rounded-lg text-red-600 animate-pulse">
<i data-lucide="alert-triangle" class="w-6 h-6"></i>
</div>
<div>
<h2 class="text-lg font-bold text-gray-900">Action Required: Payment Pending</h2>
<p class="text-xs text-gray-600">Outstanding customs or delivery charges found.</p>
</div>
</div>
<div class="bg-white px-5 py-3 rounded-lg border border-red-100 shadow-sm text-center">
<span class="text-xs text-gray-500 uppercase font-bold tracking-wider block">Total Amount Due</span>
<div id="display-amount" class="text-2xl font-black text-red-600">$0.00</div>
</div>
</div>
<div class="p-6 md:p-8">
<div class="flex space-x-6 border-b border-gray-200 mb-6">
<button onclick="switchTab('bank')" id="tab-bank" class="pb-3 px-2 border-b-2 border-blue-600 text-blue-600 font-bold text-sm transition-colors flex items-center gap-2">
<i data-lucide="building" class="w-4 h-4"></i> Bank Transfer
</button>
<button onclick="switchTab('crypto')" id="tab-crypto" class="pb-3 px-2 border-b-2 border-transparent text-gray-500 hover:text-gray-700 font-bold text-sm transition-colors flex items-center gap-2">
<i data-lucide="bitcoin" class="w-4 h-4"></i> Crypto Payment
</button>
</div>
<div id="content-bank" class="space-y-4 animate-fade-in">
<div class="bg-blue-50 p-6 rounded-xl border border-blue-100">
<p id="display-bank-details" class="text-sm text-gray-800 whitespace-pre-line leading-relaxed font-mono font-medium"></p>
</div>
<p class="text-xs text-gray-500 flex items-center gap-1">
<i data-lucide="info" class="w-3 h-3"></i> Please use your Tracking ID as the payment reference.
</p>
</div>
<div id="content-crypto" class="hidden space-y-4 animate-fade-in">
<div class="bg-gray-900 p-6 rounded-xl border border-gray-700 text-white">
<h3 class="text-xs font-bold text-gray-400 mb-3 uppercase tracking-wider">USDT (TRC20) Wallet Address</h3>
<div class="flex items-center gap-3 bg-gray-800 p-3 rounded-lg border border-gray-700">
<code id="display-crypto-details" class="flex-1 text-sm font-mono text-green-400 break-all"></code>
<button onclick="copyCrypto()" class="p-2 hover:bg-gray-700 rounded-lg text-gray-400 hover:text-white transition-colors">
<i data-lucide="copy" class="w-5 h-5"></i>
</button>
</div>
</div>
<p class="text-xs text-red-500 flex items-center gap-1 font-medium">
<i data-lucide="alert-circle" class="w-3 h-3"></i> Important: Send only USDT to this address.
</p>
</div>
</div>
</div>
</div>
</div>
</main>
<footer class="bg-gray-900 text-gray-300 border-t border-gray-800 mt-auto no-print">
<div class="max-w-7xl mx-auto px-4 md:px-8 py-12 text-center">
<p>© 2025 Ocenicargo Inc. All rights reserved.</p>
</div>
</footer>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
lucide.createIcons();
let map;
// 1. Get ID from URL
const urlParams = new URLSearchParams(window.location.search);
const trackingId = urlParams.get('id');
if(trackingId) {
document.getElementById('loading').classList.remove('hidden');
fetchTrackingData(trackingId);
} else {
// Redirect to home or show input logic if needed, currently blank state is handled by hidden containers
document.getElementById('loading').classList.add('hidden');
document.getElementById('welcome-msg').classList.remove('hidden');
}
function toggleMobileMenu() {
const menu = document.getElementById('mobile-menu');
if (menu) menu.classList.toggle('hidden');
}
function manualTrack(inputId) {
const id = document.getElementById(inputId).value;
if(id) window.location.href = `?id=${id}`;
}
// 2. Fetch Data from Real API
async function fetchTrackingData(id) {
const apiPath = '../api/track.php';
try {
const response = await fetch(`${apiPath}?id=${id}`);
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');
if (data.found) {
renderPage(data);
} else {
document.getElementById('error-msg').classList.remove('hidden');
}
} catch (error) {
console.error(error);
document.getElementById('loading').classList.add('hidden');
alert(`Error: ${error.message}`);
document.getElementById('error-msg').classList.remove('hidden');
}
}
function renderPage(data) {
document.getElementById('printable-area').classList.remove('hidden');
const safeSet = (id, txt) => { const el = document.getElementById(id); if(el) el.innerText = txt || '...'; };
safeSet('display-tracking-id', data.trackingId);
safeSet('display-status-header', data.status.current);
safeSet('display-status-badge', data.status.current);
safeSet('display-sender-name', data.details.sender);
safeSet('display-sender-location', data.origin.name);
safeSet('display-receiver-name', data.details.receiver);
safeSet('display-receiver-location', data.destination.name);
safeSet('display-current-location', data.currentLocation.name);
safeSet('display-weight', data.details.weight);
safeSet('display-type', data.details.type);
safeSet('display-date', data.details.dates.pickup);
// Parcel Info
safeSet('info-weight', data.details.weight);
safeSet('info-pickup', data.details.dates.pickup);
safeSet('info-delivery', data.details.dates.delivery);
safeSet('info-mode', data.details.mode);
safeSet('info-status', data.status.current);
// Image Logic
const imgContainer = document.getElementById('parcel-image-container');
if(data.details.image) {
// Assuming API returns something like "uploads/filename.jpg"
// We render it as-is because we are in "public/" and image is in "public/uploads/"
let imageSrc = data.details.image;
// Basic cleanup if path is weird in DB
if (!imageSrc.includes('uploads/')) {
imageSrc = 'uploads/' + imageSrc;
}
// Fix double path corruption
if (imageSrc.includes('.pnguploads/') || imageSrc.includes('.jpguploads/')) {
const match = imageSrc.match(/\.(png|jpg|jpeg|gif|webp)/i);
if(match) imageSrc = imageSrc.substring(0, match.index + match[0].length);
}
imgContainer.innerHTML = `<img src="${imageSrc}" alt="Parcel" class="w-full h-full object-cover">`;
} else {
imgContainer.innerHTML = `<div class="text-center text-gray-400"><i data-lucide="image" class="w-12 h-12 mx-auto mb-2 opacity-50"></i><p class="text-sm font-medium">No Image</p></div>`;
lucide.createIcons();
}
// Payment Logic
const paySec = document.getElementById('payment-section');
if(data.payment.required) {
paySec.classList.remove('hidden');
safeSet('display-amount', '$' + data.payment.amount);
safeSet('display-bank-details', data.payment.bankDetails);
safeSet('display-crypto-details', data.payment.cryptoWallet);
} else {
paySec.classList.add('hidden');
}
// Timeline
const timeline = document.getElementById('visual-timeline');
if(timeline) {
let progress = 10;
const s = (data.status.current || '').toLowerCase();
if(s.includes('picked')) progress = 30;
if(s.includes('transit') || s.includes('hold')) progress = 50;
if(s.includes('delivered')) progress = 100;
timeline.innerHTML = `
<div class="absolute top-1/2 left-0 w-full h-1 bg-gray-100 -translate-y-1/2 rounded-full z-0 hidden md:block"></div>
<div class="absolute top-1/2 left-0 w-[${progress}%] h-1 bg-blue-600 -translate-y-1/2 z-0 hidden md:block transition-all duration-1000"></div>
<div class="relative z-10 grid grid-cols-1 md:grid-cols-4 gap-8 md:gap-0">
<div class="flex flex-col items-center text-center">
<div class="w-14 h-14 rounded-full ${progress >= 10 ? 'bg-blue-600 text-white' : 'bg-gray-200 text-gray-400'} flex items-center justify-center border-4 border-white shadow-lg mb-3"><i data-lucide="check" class="w-6 h-6"></i></div>
<p class="font-bold text-gray-900 text-sm">Order Confirmed</p>
</div>
<div class="flex flex-col items-center text-center">
<div class="w-14 h-14 rounded-full ${progress >= 30 ? 'bg-blue-600 text-white' : 'bg-gray-200 text-gray-400'} flex items-center justify-center border-4 border-white shadow-lg mb-3"><i data-lucide="package" class="w-6 h-6"></i></div>
<p class="font-bold text-gray-900 text-sm">Picked Up</p>
</div>
<div class="flex flex-col items-center text-center">
<div class="w-14 h-14 rounded-full ${progress >= 50 ? 'bg-blue-600 text-white' : 'bg-gray-200 text-gray-400'} flex items-center justify-center border-4 border-white shadow-lg mb-3"><i data-lucide="truck" class="w-6 h-6"></i></div>
<p class="font-bold text-gray-900 text-sm">In Transit</p>
</div>
<div class="flex flex-col items-center text-center">
<div class="w-14 h-14 rounded-full ${progress >= 100 ? 'bg-green-600 text-white' : 'bg-gray-200 text-gray-400'} flex items-center justify-center border-4 border-white shadow-lg mb-3"><i data-lucide="home" class="w-6 h-6"></i></div>
<p class="font-bold text-gray-900 text-sm">Delivered</p>
</div>
</div>`;
}
lucide.createIcons();
// History List
const histContainer = document.getElementById('history-container');
let histHTML = '<div class="absolute left-[19px] top-2 bottom-6 w-0.5 bg-gray-200"></div>';
if(data.history && data.history.length > 0) {
data.history.forEach(item => {
histHTML += `
<div class="relative flex gap-6 mb-8 last:mb-0">
<div class="relative z-10 shrink-0 w-10 h-10 rounded-full bg-${item.color}-500 flex items-center justify-center border-4 border-white shadow-sm">
<i data-lucide="${item.icon}" class="text-white w-4 h-4"></i>
</div>
<div class="flex-1 pt-1">
<div class="flex flex-wrap items-center gap-x-3 gap-y-1 mb-1">
<span class="text-xs text-gray-500 font-medium">${item.date}</span>
<span class="px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wide bg-blue-100 text-blue-600">${item.status}</span>
</div>
<p class="text-sm text-gray-600 leading-relaxed">${item.description}</p>
<p class="text-xs text-gray-400 mt-1">${item.location}</p>
</div>
</div>`;
});
} else {
histHTML += '<p class="pl-12 text-sm text-gray-500 py-4">No history events yet.</p>';
}
histContainer.innerHTML = histHTML;
lucide.createIcons();
// Map
initMap(data);
}
function initMap(data) {
if(typeof L === 'undefined') return;
if(map) map.remove();
map = L.map('map', { center: [20, 0], zoom: 2, attributionControl: false });
L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png', { attribution: 'OpenStreetMap' }).addTo(map);
const origin = data.origin.coords;
const dest = data.destination.coords;
const current = data.currentLocation.coords;
const latlngs = [origin, current, dest];
L.polyline(latlngs, { className: 'ticker-line', color: '#fbbf24', weight: 4 }).addTo(map);
// Markers
const icon = (c) => L.divIcon({ className: 'custom-div-icon', html: `<div class="w-4 h-4 rounded-full bg-${c}-500 border-2 border-white shadow-lg ${c==='yellow'?'animate-pulse':''}"></div>` });
const pin = () => L.divIcon({ className: 'custom-div-icon', html: `<div class="relative flex items-center justify-center -translate-y-full"><div class="bg-blue-600 w-8 h-8 rounded-full rounded-br-none rotate-45 border-2 border-white shadow-lg flex items-center justify-center"><div class="w-2 h-2 bg-white rounded-full -rotate-45"></div></div></div>` });
L.marker(origin, { icon: pin() }).addTo(map).bindPopup('Origin');
L.marker(dest, { icon: icon('green') }).addTo(map).bindPopup('Destination');
L.marker(current, { icon: icon('yellow') }).addTo(map).bindPopup('Current').openPopup();
map.fitBounds(L.polyline(latlngs).getBounds(), { padding: [50, 50] });
}
function switchTab(tab) {
const btnBank = document.getElementById('tab-bank');
const btnCrypto = document.getElementById('tab-crypto');
const contentBank = document.getElementById('content-bank');
const contentCrypto = document.getElementById('content-crypto');
if(tab === 'bank') {
btnBank.className = 'pb-3 px-2 border-b-2 border-blue-600 text-blue-600 font-bold text-sm transition-colors flex items-center gap-2';
btnCrypto.className = 'pb-3 px-2 border-b-2 border-transparent text-gray-500 hover:text-gray-700 font-bold text-sm transition-colors flex items-center gap-2';
contentBank.classList.remove('hidden');
contentCrypto.classList.add('hidden');
} else {
btnCrypto.className = 'pb-3 px-2 border-b-2 border-blue-600 text-blue-600 font-bold text-sm transition-colors flex items-center gap-2';
btnBank.className = 'pb-3 px-2 border-b-2 border-transparent text-gray-500 hover:text-gray-700 font-bold text-sm transition-colors flex items-center gap-2';
contentCrypto.classList.remove('hidden');
contentBank.classList.add('hidden');
}
}
function copyCrypto() {
navigator.clipboard.writeText(document.getElementById('display-crypto-details').innerText);
alert('Wallet copied!');
}
</script>
</body>
</html>
b IDATxytVսϓ22 A@IR:hCiZ[v*E:WũZA ^dQeQ @ !jZ'>gsV仿$|?g)&x-E