| Server IP : 72.60.21.38 / Your IP : 216.73.216.25 Web Server : LiteSpeed System : Linux uk-fast-web1372.main-hosting.eu 4.18.0-553.121.1.lve.el8.x86_64 #1 SMP Thu Apr 30 16:40:41 UTC 2026 x86_64 User : u390967363 ( 390967363) PHP Version : 8.2.30 Disable Function : system, exec, shell_exec, passthru, mysql_list_dbs, ini_alter, dl, symlink, link, chgrp, leak, popen, apache_child_terminate, virtual, mb_send_mail MySQL : OFF | cURL : ON | WGET : ON | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /home/u390967363/domains/aibenproperties.com/public_html/app/ |
Upload File : |
<?php
require_once 'includes/db.php';
require_once 'includes/functions.php';
$role = $_SESSION['user_role'] ?? 'guest';
$role_norm = strtolower(str_replace([' ', '-'], '_', (string)$role));
// Allow broader executive roles
if (!in_array($role_norm, ['chairman_ceo','chairman','ceo','executive','executives','super_admin','super_admins'])) {
include 'includes/header.php';
echo "<div class='container p-4'><div class='alert alert-danger'>Access denied.</div></div>";
include 'includes/footer.php';
exit;
}
$notice = $_GET['notice'] ?? null;
$type = $_GET['type'] ?? 'success';
$companyId = getCurrentCompanyId();
$isSuperAdminRole = in_array($role_norm, ['super_admin','super_admins'], true);
$isImpersonatingCompany = $isSuperAdminRole && !empty($_SESSION['impersonate_company_id']);
// Demo seed handler
if (isset($_GET['create_demo']) && $_GET['create_demo'] === '1') {
try {
$pdo->beginTransaction();
// Create or find demo client
$clientId = null;
$emailDemo = 'demo.client@aiben.local';
$st = $pdo->prepare("SELECT id FROM users WHERE email = ? LIMIT 1");
try { $st->execute([$emailDemo]); $clientId = (int)$st->fetchColumn(); } catch (Exception $e) { $clientId = null; }
if (!$clientId) {
$cols = ['name','role','email','created_at'];
$vals = ['Demo Client','client',$emailDemo,date('Y-m-d H:i:s')];
if (function_exists('tableHasColumn') && tableHasColumn('users','password')) { $cols[]='password'; $vals[]=password_hash('demo123', PASSWORD_DEFAULT); }
if (function_exists('tableHasColumn') && tableHasColumn('users','username')) { $cols[]='username'; $vals[]='demo.client'; }
if (function_exists('tableHasColumn') && tableHasColumn('users','phone')) { $cols[]='phone'; $vals[]='0000000000'; }
if (function_exists('tableHasColumn') && tableHasColumn('users','status')) { $cols[]='status'; $vals[]='active'; }
if (function_exists('tableHasColumn') && tableHasColumn('users','is_active')) { $cols[]='is_active'; $vals[]=1; }
if (function_exists('tableHasColumn') && tableHasColumn('users','updated_at')) { $cols[]='updated_at'; $vals[] = date('Y-m-d H:i:s'); }
if (function_exists('tableHasColumn') && tableHasColumn('users','created_by')) { $cols[]='created_by'; $vals[]=(int)($_SESSION['user_id'] ?? 0); }
if (function_exists('tableHasColumn') && tableHasColumn('users','company_id') && $companyId) { $cols[]='company_id'; $vals[]=$companyId; }
$sql = "INSERT INTO users (".implode(',', $cols).") VALUES (".implode(',', array_fill(0,count($cols),'?')).")";
$stmt = $pdo->prepare($sql);
$stmt->execute($vals);
$clientId = (int)$pdo->lastInsertId();
}
// Create or find demo property
$propertyId = null;
$st2 = $pdo->prepare("SELECT id FROM properties WHERE title = ? LIMIT 1");
try { $st2->execute(['Demo Plot A']); $propertyId = (int)$st2->fetchColumn(); } catch (Exception $e) { $propertyId = null; }
if (!$propertyId) {
$cols = ['title','price','location','created_at'];
$vals = ['Demo Plot A',1500000,'Demo Estate',date('Y-m-d H:i:s')];
if (function_exists('tableHasColumn') && tableHasColumn('properties','status')) { $cols[]='status'; $vals[]='available'; }
if (function_exists('tableHasColumn') && tableHasColumn('properties','available_units')) { $cols[]='available_units'; $vals[]=1; }
if (function_exists('tableHasColumn') && tableHasColumn('properties','code')) { $cols[]='code'; $vals[]='DEMO-PLOT-A'; }
if (function_exists('tableHasColumn') && tableHasColumn('properties','updated_at')) { $cols[]='updated_at'; $vals[] = date('Y-m-d H:i:s'); }
if (function_exists('tableHasColumn') && tableHasColumn('properties','created_by')) { $cols[]='created_by'; $vals[]=(int)($_SESSION['user_id'] ?? 0); }
if (function_exists('tableHasColumn') && tableHasColumn('properties','company_id') && $companyId) { $cols[]='company_id'; $vals[]=$companyId; }
$sql2 = "INSERT INTO properties (".implode(',', $cols).") VALUES (".implode(',', array_fill(0,count($cols),'?')).")";
$stmt2 = $pdo->prepare($sql2);
$stmt2->execute($vals);
$propertyId = (int)$pdo->lastInsertId();
}
// Create demo allocation
$colsA = ['user_id','property_id','status','created_at'];
$valsA = [$clientId,$propertyId,'pending_executive_approval',date('Y-m-d H:i:s')];
$priceVal = 1500000;
if (function_exists('tableHasColumn') && tableHasColumn('allocations','total_price')) { $colsA[]='total_price'; $valsA[]=$priceVal; }
elseif (function_exists('tableHasColumn') && tableHasColumn('allocations','amount')) { $colsA[]='amount'; $valsA[]=$priceVal; }
elseif (function_exists('tableHasColumn') && tableHasColumn('allocations','price')) { $colsA[]='price'; $valsA[]=$priceVal; }
elseif (function_exists('tableHasColumn') && tableHasColumn('allocations','final_price')) { $colsA[]='final_price'; $valsA[]=$priceVal; }
elseif (function_exists('tableHasColumn') && tableHasColumn('allocations','sale_price')) { $colsA[]='sale_price'; $valsA[]=$priceVal; }
if (function_exists('tableHasColumn') && tableHasColumn('allocations','reference')) { $colsA[]='reference'; $valsA[]='DEMO-ALLO-'.substr(uniqid('', true), -6); }
if (function_exists('tableHasColumn') && tableHasColumn('allocations','updated_at')) { $colsA[]='updated_at'; $valsA[] = date('Y-m-d H:i:s'); }
if (function_exists('tableHasColumn') && tableHasColumn('allocations','created_by')) { $colsA[]='created_by'; $valsA[]=(int)($_SESSION['user_id'] ?? 0); }
if (function_exists('tableHasColumn') && tableHasColumn('allocations','company_id') && $companyId) { $colsA[]='company_id'; $valsA[]=$companyId; }
$sqlA = "INSERT INTO allocations (".implode(',', $colsA).") VALUES (".implode(',', array_fill(0,count($colsA),'?')).")";
$stmtA = $pdo->prepare($sqlA);
$stmtA->execute($valsA);
$allocId = (int)$pdo->lastInsertId();
$pdo->commit();
header("Location: executive-allocations.php?notice=" . urlencode("Demo allocation created (#$allocId).") . "&type=success");
exit;
} catch (Exception $e) {
try { $pdo->rollBack(); } catch (Exception $e2) {}
$msg = substr((string)$e->getMessage(), 0, 180);
header("Location: executive-allocations.php?notice=" . urlencode("Failed to create demo allocation: ".$msg) . "&type=danger");
exit;
}
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['exec_action'])) {
$allocId = isset($_POST['allocation_id']) && ctype_digit($_POST['allocation_id']) ? (int)$_POST['allocation_id'] : 0;
$decision = $_POST['decision'] ?? '';
$comment = trim($_POST['comment'] ?? '');
if ($allocId <= 0 || !in_array($decision, ['approve','reject'])) {
header("Location: executive-allocations.php?notice=" . urlencode("Invalid request") . "&type=danger");
exit;
}
if ($comment === '') {
header("Location: executive-allocations.php?notice=" . urlencode("Comment is required") . "&type=danger");
exit;
}
try {
$params = [];
$sql = "UPDATE allocations SET status = ? ";
$newStatus = $decision === 'approve' ? 'executive_approved' : 'rejected';
$params[] = $newStatus;
if (function_exists('tableHasColumn') && tableHasColumn('allocations','exec_comment')) {
$sql .= ", exec_comment = ? ";
$params[] = $comment;
}
if (function_exists('tableHasColumn') && tableHasColumn('allocations','exec_decided_by')) {
$sql .= ", exec_decided_by = ? ";
$params[] = (int)($_SESSION['user_id'] ?? 0);
}
if (function_exists('tableHasColumn') && tableHasColumn('allocations','exec_decided_at')) {
$sql .= ", exec_decided_at = NOW() ";
}
if (function_exists('tableHasColumn') && tableHasColumn('allocations','locked')) {
$sql .= ", locked = 1 ";
}
$sql .= " WHERE id = ? ";
$params[] = $allocId;
if ((!$isSuperAdminRole || $isImpersonatingCompany) && $companyId && function_exists('tableHasColumn') && tableHasColumn('allocations','company_id')) {
$sql .= " AND company_id = ? ";
$params[] = $companyId;
}
$st = $pdo->prepare($sql);
$st->execute($params);
if (function_exists('logActivity') && isset($_SESSION['user_id'])) {
$details = ['allocation_id'=>$allocId,'decision'=>$decision,'comment'=>$comment];
if ($companyId) { $details['company_id'] = $companyId; }
logActivity($_SESSION['user_id'], 'EXEC_ALLOCATION_DECISION', json_encode($details));
}
header("Location: executive-allocations.php?notice=" . urlencode("Allocation ".($decision==='approve'?'executive approved':'rejected')) . "&type=success");
exit;
} catch (Exception $e) {
header("Location: executive-allocations.php?notice=" . urlencode("Action failed") . "&type=danger");
exit;
}
}
// Fetch pending for executive decision
$rows = [];
$showPending = (($_GET['show'] ?? '') === 'pending');
try {
$hasRef = function_exists('tableHasColumn') && tableHasColumn('allocations', 'reference');
$hasRefNo = function_exists('tableHasColumn') && tableHasColumn('allocations', 'ref_no');
$refExpr = $hasRef ? 'a.reference' : ($hasRefNo ? 'a.ref_no' : "CONCAT('ALC-', a.id)");
$createdExpr = 'NULL';
if (function_exists('tableHasColumn')) {
if (tableHasColumn('allocations', 'created_at')) { $createdExpr = 'a.created_at'; }
elseif (tableHasColumn('allocations', 'date_created')) { $createdExpr = 'a.date_created'; }
elseif (tableHasColumn('allocations', 'created_on')) { $createdExpr = 'a.created_on'; }
elseif (tableHasColumn('allocations', 'created')) { $createdExpr = 'a.created'; }
}
$priceCandidates = [];
if (function_exists('tableHasColumn')) {
foreach (['total_price','amount','price','final_price','sale_price'] as $pc) {
if (tableHasColumn('allocations', $pc)) { $priceCandidates[] = 'a.' . $pc; }
}
}
$totalExpr = !empty($priceCandidates) ? ('COALESCE(' . implode(', ', $priceCandidates) . ')') : '0';
$userNameExpr = (function_exists('tableHasColumn') && tableHasColumn('users', 'name')) ? 'u.name' : ((function_exists('tableHasColumn') && tableHasColumn('users', 'fullname')) ? 'u.fullname' : "CONCAT('User#', u.id)");
$propertyTitleExpr = (function_exists('tableHasColumn') && tableHasColumn('properties', 'title')) ? 'p.title' : ((function_exists('tableHasColumn') && tableHasColumn('properties', 'name')) ? 'p.name' : "CONCAT('Property#', p.id)");
$baseSelect = "SELECT a.id, {$refExpr} AS reference, {$userNameExpr} AS client, {$propertyTitleExpr} AS property,
{$totalExpr} AS total_price, a.status, {$createdExpr} AS created_at
FROM allocations a
LEFT JOIN users u ON a.user_id = u.id
LEFT JOIN properties p ON a.property_id = p.id
WHERE 1=1";
$sql = $baseSelect;
$params = [];
if ($showPending) {
$sql .= " AND a.status IN ('pending','admin_approved','approved','pending_executive_approval','pending_chairman_approval','plot_assigned_pending_chairman_approval','executive_review','awaiting_chairman_approval','pending_head_admin_review','draft_prepared','returned_for_correction')";
}
if ((!$isSuperAdminRole || $isImpersonatingCompany) && $companyId && function_exists('tableHasColumn') && tableHasColumn('allocations','company_id')) {
$sql .= " AND (a.company_id = ? OR a.company_id IS NULL OR a.company_id = 0) ";
$params[] = $companyId;
}
$sql .= " ORDER BY a.created_at DESC LIMIT 100";
$st = $pdo->prepare($sql);
$st->execute($params);
$rows = $st->fetchAll(PDO::FETCH_ASSOC);
if (empty($rows) && $showPending) {
$sql2 = $baseSelect;
$params2 = [];
if ((!$isSuperAdminRole || $isImpersonatingCompany) && $companyId && function_exists('tableHasColumn') && tableHasColumn('allocations','company_id')) {
$sql2 .= " AND (a.company_id = ? OR a.company_id IS NULL OR a.company_id = 0) ";
$params2[] = $companyId;
}
$sql2 .= " ORDER BY a.created_at DESC LIMIT 50";
$st2 = $pdo->prepare($sql2);
$st2->execute($params2);
$rows = $st2->fetchAll(PDO::FETCH_ASSOC);
$notice = $notice ?: "No items pending executive decision. Showing recent allocations.";
$type = 'warning';
}
} catch (Exception $e) {}
// Fallback to payment queue if still empty
$payRows = [];
if (empty($rows)) {
try {
$sqlp = "SELECT p.id, p.amount, p.status, p.created_at, p.transaction_id
FROM payments p
WHERE p.status IN ('pending_confirmation','verified')";
$paramsp = [];
if ((!$isSuperAdminRole || $isImpersonatingCompany) && $companyId && function_exists('tableHasColumn') && tableHasColumn('payments','company_id')) {
$sqlp .= " AND p.company_id = ? ";
$paramsp[] = $companyId;
}
$sqlp .= " ORDER BY p.created_at DESC LIMIT 50";
$stp = $pdo->prepare($sqlp);
$stp->execute($paramsp);
$payRows = $stp->fetchAll(PDO::FETCH_ASSOC);
if (!empty($payRows)) {
$notice = $notice ?: "No allocations found. Showing payment queue.";
$type = 'warning';
}
} catch (Exception $e) {}
}
// Try to surface latest demo allocation if no rows
$demo = null;
if (empty($rows)) {
try {
$demoRefExpr = $hasRef ? 'a.reference' : ($hasRefNo ? 'a.ref_no' : "CONCAT('ALC-', a.id)");
$sqld = $baseSelect . " AND (
({$demoRefExpr} LIKE 'DEMO-ALLO-%') OR
(u.email = 'demo.client@aiben.local') OR
({$propertyTitleExpr} = 'Demo Plot A') OR
(p.code = 'DEMO-PLOT-A')
)";
$paramsd = [];
if ((!$isSuperAdminRole || $isImpersonatingCompany) && $companyId && function_exists('tableHasColumn') && tableHasColumn('allocations','company_id')) {
$sqld .= " AND a.company_id = ? ";
$paramsd[] = $companyId;
}
$sqld .= " ORDER BY a.created_at DESC LIMIT 1";
$std = $pdo->prepare($sqld);
$std->execute($paramsd);
$demo = $std->fetch(PDO::FETCH_ASSOC);
if ($demo) {
$notice = $notice ?: "Showing latest demo allocation to test letter generation.";
$type = 'info';
} else {
$sqla = $baseSelect;
$paramsa = [];
if ((!$isSuperAdminRole || $isImpersonatingCompany) && $companyId && function_exists('tableHasColumn') && tableHasColumn('allocations','company_id')) {
$sqla .= " AND a.company_id = ? ";
$paramsa[] = $companyId;
}
$sqla .= " ORDER BY a.created_at DESC LIMIT 1";
$sta = $pdo->prepare($sqla);
$sta->execute($paramsa);
$demo = $sta->fetch(PDO::FETCH_ASSOC) ?: null;
if ($demo) {
$notice = $notice ?: "No pending items. Showing latest allocation for testing.";
$type = 'info';
}
}
} catch (Exception $e) {}
}
$isChairmanQueue = in_array($role_norm, ['chairman_ceo','chairman','ceo','super_admin','super_admins'], true);
$pageTitle = $isChairmanQueue ? 'Chairman Allocation Queue' : 'Executive Allocation Queue';
$pageSubtitle = $isChairmanQueue ? 'Final verification and release-ready allocations waiting for chairman action.' : 'Review pending allocations and make executive approval decisions.';
$queueCount = count($rows);
$latestQueueDate = !empty($rows[0]['created_at']) ? date('M d, Y', strtotime((string)$rows[0]['created_at'])) : '—';
$totalQueueValue = 0.0;
foreach ($rows as $queueRow) {
$totalQueueValue += (float)($queueRow['total_price'] ?? 0);
}
include 'includes/header.php';
?>
<style>
.exec-queue-page{background:#f6f8fc}
.exec-queue-page .hero-card,.exec-queue-page .summary-card,.exec-queue-page .queue-card{border:0;border-radius:18px;box-shadow:0 10px 30px rgba(15,23,42,.08)}
.exec-queue-page .hero-card{background:linear-gradient(135deg,#0f172a 0%,#1e3a8a 100%);color:#fff}
.exec-queue-page .hero-card h2{color:#fff}
.exec-queue-page .summary-label{font-size:.78rem;color:#6b7280;text-transform:uppercase;letter-spacing:.04em}
.exec-queue-page .summary-value{font-size:1.8rem;font-weight:800;color:#111827;line-height:1.1}
.exec-queue-page .hero-card .summary-label,.exec-queue-page .hero-card .summary-value{color:#fff}
.exec-queue-page .hero-muted{color:rgba(255,255,255,.8)}
.exec-queue-page .status-pill{display:inline-flex;align-items:center;gap:.4rem;border-radius:999px;padding:.4rem .75rem;font-size:.78rem;font-weight:600}
.exec-queue-page .status-pill.pending{background:#fff7ed;color:#c2410c}
.exec-queue-page .queue-actions{display:flex;flex-wrap:wrap;gap:.5rem}
.exec-queue-page .empty-box{padding:3rem 1.5rem;text-align:center}
.exec-queue-page .execq-table-scroll{display:block;width:100%;max-width:100%;overflow:auto !important;overflow-x:auto !important;overflow-y:hidden;-webkit-overflow-scrolling:touch;touch-action:pan-x pan-y;cursor:grab}
.exec-queue-page .execq-table-scroll.execq-grabbing{cursor:grabbing}
.exec-queue-page .table-responsive.execq-table-scroll{overflow:auto !important;overflow-x:auto !important;overflow-y:hidden}
.exec-queue-page .execq-table{width:max-content;min-width:980px}
.exec-queue-page .execq-table th,.exec-queue-page .execq-table td{white-space:nowrap !important;vertical-align:middle}
.exec-queue-page .execq-table td.execq-primary{white-space:normal !important;min-width:220px;max-width:360px}
.exec-queue-page .queue-hint{display:none}
@media (max-width: 991.98px){
.exec-queue-page .queue-hint{display:block}
}
@media (max-width: 1199.98px){
.exec-queue-page .execq-table{width:100%;min-width:0;table-layout:fixed}
.exec-queue-page .execq-table th,.exec-queue-page .execq-table td{white-space:normal !important}
.exec-queue-page .execq-table td{word-break:break-word}
.exec-queue-page .execq-table td.execq-primary{min-width:0;max-width:none}
}
@media (max-width:576px){
.exec-queue-page .queue-actions{gap:.35rem}
.exec-queue-page .queue-actions .btn{padding:.25rem .45rem}
}
</style>
<div class="container-fluid py-4 exec-queue-page">
<div class="hero-card card mb-4">
<div class="card-body p-4">
<div class="d-flex flex-wrap justify-content-between gap-3 align-items-start">
<div>
<div class="summary-label mb-2"><?= $isChairmanQueue ? 'Chairman Verification Desk' : 'Executive Approval Desk' ?></div>
<h2 class="mb-2"><?= htmlspecialchars($pageTitle) ?></h2>
<p class="hero-muted mb-0"><?= htmlspecialchars($pageSubtitle) ?></p>
</div>
<div class="queue-actions">
<a href="executive-dashboard.php" class="btn btn-light btn-sm">Back to Dashboard</a>
<?php if ($showPending): ?>
<a href="executive-allocations.php" class="btn btn-outline-light btn-sm">Show All</a>
<?php else: ?>
<a href="executive-allocations.php?show=pending" class="btn btn-outline-light btn-sm">Pending Only</a>
<?php endif; ?>
<a href="allocation-letters.php" class="btn btn-outline-light btn-sm">Allocation Letters</a>
<?php if ($isChairmanQueue): ?>
<a href="chairman-approval.php" class="btn btn-warning btn-sm">Open Verification Desk</a>
<?php endif; ?>
</div>
</div>
</div>
</div>
<?php if ($notice): ?>
<div class="alert alert-<?= htmlspecialchars($type) ?>"><?= htmlspecialchars($notice) ?></div>
<?php endif; ?>
<div class="row g-3 mb-4">
<div class="col-md-4">
<div class="summary-card card h-100">
<div class="card-body">
<div class="summary-label">Items In Queue</div>
<div class="summary-value"><?= number_format($queueCount) ?></div>
<div class="text-muted small mt-2"><?= $showPending ? 'Showing only pending approval items' : 'Showing current approval queue' ?></div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="summary-card card h-100">
<div class="card-body">
<div class="summary-label">Queue Value</div>
<div class="summary-value"><?= formatCurrency($totalQueueValue) ?></div>
<div class="text-muted small mt-2">Total value of visible allocation requests</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="summary-card card h-100">
<div class="card-body">
<div class="summary-label">Latest Submission</div>
<div class="summary-value" style="font-size:1.3rem;"><?= htmlspecialchars($latestQueueDate) ?></div>
<div class="text-muted small mt-2">Newest request currently in this queue</div>
</div>
</div>
</div>
</div>
<div class="queue-card card shadow-sm">
<div class="card-header bg-white border-0 d-flex justify-content-between align-items-center py-3">
<div>
<h5 class="mb-1"><?= $isChairmanQueue ? 'Verification Queue' : 'Approval Queue' ?></h5>
<div class="text-muted small">Review allocation details, preview letter, then approve or reject.</div>
</div>
<span class="status-pill pending"><i class="fa-regular fa-clock"></i><?= $showPending ? 'Pending Only' : 'Queue View' ?></span>
</div>
<div class="card-body p-0">
<div class="queue-hint px-3 py-2 small text-muted border-bottom">Swipe left/right to see all columns.</div>
<div class="table-responsive execq-table-scroll">
<table class="table table-hover mb-0 execq-table">
<thead class="table-light">
<tr>
<th>Reference</th>
<th>Client</th>
<th>Property</th>
<th class="text-end">Total</th>
<th>Status</th>
<th class="d-none d-md-table-cell">Created</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<?php if (empty($rows)): ?>
<tr>
<td colspan="7" class="empty-box">
<div class="mb-2 fs-5 fw-semibold">No allocation is waiting for review</div>
<div class="text-muted mb-3">New approval items will appear here once admin sends them to the executive/chairman queue.</div>
<div class="d-inline-flex flex-wrap justify-content-center gap-2">
<a href="executive-dashboard.php" class="btn btn-outline-secondary btn-sm">Back to Dashboard</a>
<a href="allocation-letters.php?view=executive" class="btn btn-outline-primary btn-sm">Open Allocation Letters</a>
<?php if (!$showPending): ?>
<a href="executive-allocations.php?show=pending" class="btn btn-outline-warning btn-sm">Pending Only</a>
<?php endif; ?>
</div>
</td>
</tr>
<?php else: foreach ($rows as $r): ?>
<tr>
<td>
<div class="fw-semibold"><?= htmlspecialchars($r['reference'] ?: ('ALC-' . (int)$r['id'])) ?></div>
<div class="text-muted small">#<?= (int)$r['id'] ?></div>
<div class="text-muted small d-md-none"><?= !empty($r['created_at']) ? date('M d, Y h:i A', strtotime((string)$r['created_at'])) : '—' ?></div>
</td>
<td class="execq-primary"><?= htmlspecialchars($r['client'] ?? '—') ?></td>
<td class="execq-primary"><?= htmlspecialchars($r['property'] ?? '—') ?></td>
<td class="text-end">₦<?= number_format((float)$r['total_price'],2) ?></td>
<td><span class="badge bg-warning text-dark"><?= htmlspecialchars($r['status']) ?></span></td>
<td class="d-none d-md-table-cell"><?= !empty($r['created_at']) ? date('M d, Y h:i A', strtotime((string)$r['created_at'])) : '—' ?></td>
<td>
<div class="queue-actions">
<a href="allocation-details.php?id=<?= (int)$r['id'] ?>" class="btn btn-outline-secondary btn-sm">Details</a>
<a href="allocation-letters.php?view=executive&action=preview&allocation_id=<?= (int)$r['id'] ?>" class="btn btn-outline-primary btn-sm" target="_blank">Preview Letter</a>
<?php if ($isChairmanQueue): ?>
<a href="chairman-approval.php?alloc_id=<?= (int)$r['id'] ?>" class="btn btn-dark btn-sm">Verify</a>
<?php endif; ?>
<button class="btn btn-success btn-sm" data-bs-toggle="modal" data-bs-target="#execModal" data-id="<?= (int)$r['id'] ?>" data-decision="approve">Approve</button>
<button class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#execModal" data-id="<?= (int)$r['id'] ?>" data-decision="reject">Reject</button>
</div>
</td>
</tr>
<?php endforeach; endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Comment Modal -->
<div class="modal fade" id="execModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<form method="post" class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Confirm Decision</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<input type="hidden" name="exec_action" value="1">
<input type="hidden" name="allocation_id" id="allocId">
<input type="hidden" name="decision" id="allocDecision">
<div class="mb-3">
<label class="form-label">Comment (required)</label>
<textarea name="comment" class="form-control" required rows="3" placeholder="Provide reason"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-dark">Confirm</button>
</div>
</form>
</div>
</div>
<script>
document.getElementById('execModal').addEventListener('show.bs.modal', function (ev) {
const btn = ev.relatedTarget;
const id = btn.getAttribute('data-id');
const decision = btn.getAttribute('data-decision');
document.getElementById('allocId').value = id;
document.getElementById('allocDecision').value = decision;
});
document.querySelector('#execModal form').addEventListener('submit', async function(e){
const decision = document.getElementById('allocDecision').value;
const title = decision === 'approve' ? 'Approve Allocation' : 'Reject Allocation';
const message = decision === 'approve' ? 'Are you sure you want to approve this allocation?' : 'Are you sure you want to reject this allocation?';
const variant = decision === 'approve' ? 'success' : 'danger';
const ok = window.showConfirm ? await window.showConfirm({ title, message, confirmText: 'Yes, proceed', variant }) : window.confirm('Are you sure?');
if (!ok) { e.preventDefault(); return false; }
});
// Auto session timeout (15min)
(function(){
let t; const logoutAfter=15*60*1000; const reset=()=>{clearTimeout(t);t=setTimeout(()=>location.href='logout.php?reason=idle',logoutAfter);};
['click','mousemove','keydown','scroll','touchstart'].forEach(ev=>window.addEventListener(ev,reset,{passive:true}));
reset();
})();
// Drag-to-scroll fallback (desktop) for the queue table
(function(){
var el = document.querySelector('.execq-table-scroll');
if (!el) return;
var isDown = false;
var startX = 0;
var scrollLeft = 0;
el.addEventListener('mousedown', function(e){
if (e.button !== 0) return;
if (e.target && e.target.closest('a,button,input,select,textarea,label,.modal,.dropdown-menu')) return;
isDown = true;
el.classList.add('execq-grabbing');
startX = e.pageX;
scrollLeft = el.scrollLeft;
});
document.addEventListener('mouseup', function(){
if (!isDown) return;
isDown = false;
el.classList.remove('execq-grabbing');
});
document.addEventListener('mousemove', function(e){
if (!isDown) return;
var walk = (e.pageX - startX);
el.scrollLeft = scrollLeft - walk;
});
})();
</script>
<?php include 'includes/footer.php'; ?>