PNG IHDR x sBIT|d pHYs + tEXtSoftware www.inkscape.org< ,tEXtComment
<?php
// Enable Error Reporting
ini_set('display_errors', 1);
error_reporting(E_ALL);
session_start();
if (!isset($_SESSION['admin_logged_in'])) {
header("Location: login.php");
exit;
}
require_once '../config/database.php';
$db = (new Database())->getConnection();
// --- 1. Handle Actions ---
if (isset($_GET['action']) && isset($_GET['id'])) {
$id = $_GET['id'];
// Fetch current location for history log continuity
$locStmt = $db->prepare("SELECT current_location_name FROM shipments WHERE id = :id");
$locStmt->execute([':id' => $id]);
$currentLocation = $locStmt->fetchColumn() ?: 'Processing Center';
if ($_GET['action'] == 'approve') {
// Approve
$stmt = $db->prepare("UPDATE shipments SET payment_required = 0, current_status = 'Payment Received', status_details = 'Payment verified successfully. Shipment processing.' WHERE id = :id");
$stmt->execute([':id' => $id]);
// Log
$hist = $db->prepare("INSERT INTO shipment_history (shipment_id, status, description, location, icon, color, date) VALUES (:id, 'Payment Received', 'Payment verified by admin.', :loc, 'check', 'green', NOW())");
$hist->execute([':id' => $id, ':loc' => $currentLocation]);
header("Location: payments.php?msg=approved");
exit;
}
if ($_GET['action'] == 'reject') {
// Reject
$stmt = $db->prepare("UPDATE shipments SET payment_proof = NULL, current_status = 'Payment Declined', status_details = 'Invalid payment proof submitted. Please try again.' WHERE id = :id");
$stmt->execute([':id' => $id]);
// Log
$hist = $db->prepare("INSERT INTO shipment_history (shipment_id, status, description, location, icon, color, date) VALUES (:id, 'Payment Declined', 'Proof rejected. Please re-upload.', :loc, 'alert-circle', 'red', NOW())");
$hist->execute([':id' => $id, ':loc' => $currentLocation]);
header("Location: payments.php?msg=rejected");
exit;
}
}
// --- 2. Fetch Data ---
$query = "SELECT * FROM shipments WHERE payment_proof IS NOT NULL AND payment_required = 1 ORDER BY created_at DESC";
$payments = $db->query($query)->fetchAll(PDO::FETCH_ASSOC);
$historyQuery = "SELECT * FROM shipments WHERE payment_proof IS NOT NULL AND payment_required = 0 ORDER BY created_at DESC LIMIT 20";
$approved_payments = $db->query($historyQuery)->fetchAll(PDO::FETCH_ASSOC);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Verify Payments | Flyvoswift Admin</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<script src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
<style>
[x-cloak] { display: none !important; }
.no-scrollbar::-webkit-scrollbar { display: none; }
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
</style>
</head>
<body class="bg-gray-100 flex h-screen overflow-hidden" x-data="{ sidebarOpen: false }">
<!-- Mobile Sidebar Overlay -->
<div x-show="sidebarOpen"
@click="sidebarOpen = false"
x-transition:enter="transition-opacity ease-linear duration-300"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition-opacity ease-linear duration-300"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
class="fixed inset-0 bg-gray-900/80 z-40 md:hidden"
x-cloak>
</div>
<!-- Sidebar -->
<aside :class="sidebarOpen ? 'translate-x-0' : '-translate-x-full'"
class="fixed inset-y-0 left-0 z-50 w-64 bg-gray-900 text-white transition-transform duration-300 ease-in-out md:translate-x-0 md:static md:flex md:flex-col shrink-0 shadow-xl md:shadow-none">
<div class="h-16 flex items-center justify-between px-6 border-b border-gray-800 bg-gray-900">
<span class="text-xl font-bold tracking-tight">Flyvoswift<span class="text-blue-500">Admin</span></span>
<button @click="sidebarOpen = false" class="md:hidden text-gray-400 hover:text-white transition-colors">
<i data-lucide="x" class="w-6 h-6"></i>
</button>
</div>
<nav class="flex-1 p-4 space-y-2 overflow-y-auto">
<a href="index.php" class="flex items-center gap-3 px-4 py-3 text-gray-400 hover:bg-gray-800 hover:text-white rounded-lg transition-colors">
<i data-lucide="layout-dashboard" class="w-5 h-5"></i> Dashboard
</a>
<a href="shipments.php" class="flex items-center gap-3 px-4 py-3 text-gray-400 hover:bg-gray-800 hover:text-white rounded-lg transition-colors">
<i data-lucide="package" class="w-5 h-5"></i> Shipments
</a>
<a href="payments.php" class="flex items-center gap-3 px-4 py-3 bg-blue-600 text-white rounded-lg shadow-md transition-colors">
<i data-lucide="credit-card" class="w-5 h-5"></i> Payments
<?php if(count($payments) > 0): ?>
<span class="bg-red-500 text-white text-[10px] font-bold px-2 py-0.5 rounded-full ml-auto"><?php echo count($payments); ?></span>
<?php endif; ?>
</a>
<a href="settings.php" class="flex items-center gap-3 px-4 py-3 text-gray-400 hover:bg-gray-800 hover:text-white rounded-lg transition-colors">
<i data-lucide="settings" class="w-5 h-5"></i> Settings
</a>
</nav>
<div class="p-4 border-t border-gray-800 bg-gray-900">
<a href="logout.php" class="flex items-center gap-2 text-gray-400 hover:text-red-400 text-sm transition-colors">
<i data-lucide="log-out" class="w-4 h-4"></i> Logout
</a>
</div>
</aside>
<!-- Main Content -->
<main class="flex-1 flex flex-col h-full overflow-hidden bg-gray-50 relative w-full">
<!-- Mobile Header -->
<header class="flex items-center justify-between px-4 h-16 bg-white border-b border-gray-200 md:hidden shrink-0 z-30 sticky top-0">
<div class="flex items-center gap-3">
<button @click="sidebarOpen = true" class="text-gray-500 hover:text-gray-900 p-1">
<i data-lucide="menu" class="w-6 h-6"></i>
</button>
<span class="font-bold text-lg text-gray-900">Payments</span>
</div>
<div class="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center text-blue-600">
<i data-lucide="user" class="w-4 h-4"></i>
</div>
</header>
<!-- Scrollable Content -->
<div class="flex-1 overflow-y-auto p-4 md:p-8 scroll-smooth">
<div class="mb-8 hidden md:block">
<h1 class="text-2xl font-bold text-gray-900">Payment Verification</h1>
<p class="text-sm text-gray-500 mt-1">Review uploaded proofs and approve shipments.</p>
</div>
<!-- Alerts -->
<?php if(isset($_GET['msg'])): ?>
<div class="mb-6 p-4 rounded-lg flex items-center gap-2 shadow-sm <?php echo $_GET['msg'] == 'approved' ? 'bg-green-50 border border-green-200 text-green-700' : 'bg-red-50 border border-red-200 text-red-700'; ?>">
<i data-lucide="<?php echo $_GET['msg'] == 'approved' ? 'check-circle' : 'alert-circle'; ?>" class="w-5 h-5"></i>
<?php echo $_GET['msg'] == 'approved' ? 'Payment approved successfully.' : 'Payment rejected.'; ?>
</div>
<?php endif; ?>
<!-- PENDING PAYMENTS GRID -->
<div class="mb-8">
<h2 class="text-lg font-semibold text-gray-700 mb-4 flex items-center gap-2">
<i data-lucide="clock" class="w-5 h-5 text-yellow-500"></i> Pending Review
</h2>
<?php if (count($payments) > 0): ?>
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
<?php foreach($payments as $row): ?>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden flex flex-col transition-all hover:shadow-md">
<!-- Card Header -->
<div class="p-4 border-b border-gray-100 bg-gray-50 flex justify-between items-center">
<span class="font-mono font-bold text-blue-600 bg-blue-100 px-2 py-1 rounded text-sm"><?php echo htmlspecialchars($row['tracking_number']); ?></span>
<span class="text-xs text-gray-500"><?php echo date('M d, H:i', strtotime($row['created_at'])); ?></span>
</div>
<!-- Card Body -->
<div class="p-5 flex-1">
<div class="mb-4">
<p class="text-xs text-gray-500 uppercase font-bold mb-1">Amount Declared</p>
<p class="text-2xl font-bold text-gray-900">$<?php echo number_format($row['payment_amount'], 2); ?></p>
</div>
<div class="mb-2">
<p class="text-xs text-gray-500 uppercase font-bold mb-2">Proof of Payment</p>
<a href="../public/<?php echo htmlspecialchars($row['payment_proof']); ?>" target="_blank" class="group relative block w-full h-32 bg-gray-100 rounded-lg overflow-hidden border border-gray-200 hover:border-blue-400 transition-colors">
<?php
$ext = pathinfo($row['payment_proof'], PATHINFO_EXTENSION);
if(in_array(strtolower($ext), ['jpg', 'jpeg', 'png', 'gif', 'webp'])): ?>
<img src="../public/<?php echo htmlspecialchars($row['payment_proof']); ?>" class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500">
<?php else: ?>
<div class="w-full h-full flex items-center justify-center text-gray-400 flex-col">
<i data-lucide="file-text" class="w-10 h-10 mb-2"></i>
<span class="text-xs font-bold uppercase"><?php echo $ext; ?> File</span>
</div>
<?php endif; ?>
<div class="absolute inset-0 bg-black/50 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity">
<span class="text-white text-sm font-bold flex items-center gap-2"><i data-lucide="eye" class="w-4 h-4"></i> View Proof</span>
</div>
</a>
</div>
</div>
<!-- Card Actions -->
<div class="p-4 border-t border-gray-100 bg-gray-50 flex gap-3">
<a href="payments.php?action=reject&id=<?php echo $row['id']; ?>" onclick="return confirm('Reject this payment?')" class="flex-1 py-2 rounded-lg border border-red-200 text-red-600 hover:bg-red-50 text-center font-bold text-sm transition-colors">
Reject
</a>
<a href="payments.php?action=approve&id=<?php echo $row['id']; ?>" onclick="return confirm('Approve this payment?')" class="flex-1 py-2 rounded-lg bg-green-600 text-white hover:bg-green-700 text-center font-bold text-sm shadow-sm transition-colors">
Approve
</a>
</div>
</div>
<?php endforeach; ?>
</div>
<?php else: ?>
<div class="text-center py-16 bg-white rounded-xl border border-dashed border-gray-300">
<div class="inline-flex bg-gray-100 p-4 rounded-full mb-4 text-gray-400">
<i data-lucide="check-circle" class="w-8 h-8"></i>
</div>
<h3 class="text-lg font-medium text-gray-900">All Caught Up!</h3>
<p class="text-gray-500 mt-1 text-sm">No pending payments to verify.</p>
</div>
<?php endif; ?>
</div>
<!-- APPROVED PAYMENTS TABLE -->
<div class="mt-8">
<h2 class="text-lg font-semibold text-gray-700 mb-4 flex items-center gap-2">
<i data-lucide="history" class="w-5 h-5 text-blue-500"></i> Approved History
</h2>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
<div class="overflow-x-auto">
<table class="w-full text-left text-sm min-w-[600px]">
<thead class="bg-gray-50 text-gray-500 text-xs uppercase border-b border-gray-200">
<tr>
<th class="px-6 py-3 font-medium">Tracking</th>
<th class="px-6 py-3 font-medium">Amount</th>
<th class="px-6 py-3 font-medium">Date Processed</th>
<th class="px-6 py-3 font-medium">Proof</th>
<th class="px-6 py-3 font-medium text-right">Status</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200">
<?php if (count($approved_payments) > 0): ?>
<?php foreach($approved_payments as $ap): ?>
<tr class="hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 font-mono text-blue-600 font-bold"><?php echo htmlspecialchars($ap['tracking_number']); ?></td>
<td class="px-6 py-4 font-medium text-gray-900">$<?php echo number_format($ap['payment_amount'], 2); ?></td>
<td class="px-6 py-4 text-gray-500"><?php echo date('M d, Y', strtotime($ap['created_at'])); ?></td>
<td class="px-6 py-4">
<a href="../public/<?php echo htmlspecialchars($ap['payment_proof']); ?>" target="_blank" class="text-blue-600 hover:text-blue-800 hover:underline flex items-center gap-1 text-xs font-medium">
<i data-lucide="file-check" class="w-3 h-3"></i> View Receipt
</a>
</td>
<td class="px-6 py-4 text-right">
<span class="bg-green-100 text-green-700 px-2 py-1 rounded text-xs font-bold uppercase border border-green-200">Approved</span>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="5" class="px-6 py-10 text-center text-gray-500">No approved payments found yet.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</main>
<script>lucide.createIcons();</script>
</body>
</html>
b IDATxytVսϓ22 A@IR:hCiZ[v*E:WũZA ^dQeQ @ !jZ'>gsV仿$|?g)&x-E