vehicles.php
12.44 KB
<?php
require_once __DIR__ . '/../bootstrap.php';
requireAuth();
if (!isAdminRole() && !hasPermission('vehicles.view')) {
http_response_code(403);
die('Access denied');
}
$canManage = isAdminRole() || hasPermission('vehicles.edit');
$pageTitle = 'Vehicles';
$msg = [];
$err = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!$canManage) {
http_response_code(403);
die('Access denied. You do not have permission to manage vehicles.');
}
$action = (string)($_POST['action'] ?? '');
if ($action === 'save_van') {
$id = (int)($_POST['id'] ?? 0);
$plate_full = trim((string)($_POST['plate_full'] ?? ''));
$plate_short = trim((string)($_POST['plate_short'] ?? ''));
$make = trim((string)($_POST['make'] ?? ''));
$model = trim((string)($_POST['model'] ?? ''));
$year = trim((string)($_POST['year'] ?? ''));
$yearVal = ($year !== '') ? (int)$year : null;
$color = trim((string)($_POST['color'] ?? ''));
$tag_color = trim((string)($_POST['tag_color'] ?? '')) ?: null;
$notes = trim((string)($_POST['notes'] ?? ''));
$is_active = isset($_POST['is_active']) ? 1 : 0;
if ($plate_full === '') $err[] = 'Vehicle registration is required.';
if ($plate_short === '') $plate_short = substr(preg_replace('/\s+/', '', $plate_full), -3);
if (!$err) {
try {
if ($id > 0) {
$stmt = $pdo->prepare('UPDATE vans SET plate_full=?, plate_short=?, make=?, model=?, year=?, color=?, tag_color=?, notes=?, is_active=? WHERE id=?');
$stmt->execute([$plate_full, $plate_short, $make, $model, $yearVal, $color, $tag_color, $notes, $is_active, $id]);
$msg[] = 'Vehicle updated.';
logActivity('vans.update', 'van', $id, "Updated vehicle: $plate_full");
} else {
$stmt = $pdo->prepare('INSERT INTO vans (plate_full, plate_short, make, model, year, color, tag_color, notes, is_active) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)');
$stmt->execute([$plate_full, $plate_short, $make, $model, $yearVal, $color, $tag_color, $notes, $is_active]);
$id = (int)$pdo->lastInsertId();
$msg[] = 'Vehicle added.';
logActivity('vans.create', 'van', $id, "Added vehicle: $plate_full");
}
} catch (PDOException $e) {
$err[] = str_contains($e->getMessage(), 'Duplicate entry') ? 'A vehicle with this registration already exists.' : ('Database error: ' . $e->getMessage());
}
}
}
if ($action === 'toggle_active') {
$id = (int)($_POST['id'] ?? 0);
if ($id > 0) {
try {
$stmt = $pdo->prepare('UPDATE vans SET is_active = NOT is_active WHERE id=?');
$stmt->execute([$id]);
$msg[] = 'Vehicle status updated.';
logActivity('vans.update', 'van', $id, 'Toggled vehicle active status');
} catch (PDOException $e) { $err[] = 'Failed to update status: ' . $e->getMessage(); }
}
}
if ($action === 'delete_van') {
$id = (int)($_POST['id'] ?? 0);
if ($id > 0) {
try {
$stmt = $pdo->prepare('DELETE FROM vans WHERE id=?');
$stmt->execute([$id]);
$msg[] = 'Vehicle deleted.';
logActivity('vans.delete', 'van', $id, 'Deleted vehicle');
} catch (PDOException $e) { $err[] = 'Failed to delete vehicle: ' . $e->getMessage(); }
}
}
}
$vans = [];
try {
$stmt = $pdo->query('SELECT * FROM vans ORDER BY is_active DESC, plate_full ASC');
$vans = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) { $err[] = 'Failed to load vehicles: ' . $e->getMessage(); }
$extraHead = '<style>
.vehicle-toolbar{display:flex;gap:10px;flex-wrap:wrap;justify-content:flex-end;align-items:center;}
.vehicle-status{display:inline-flex;align-items:center;gap:8px;}
.vehicle-dot{width:10px;height:10px;border-radius:50%;display:inline-block;}
.vehicle-dot.active{background:#2ecc71;}.vehicle-dot.inactive{background:#e74c3c;}
.vehicle-modal-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px;}
@media(max-width:720px){.vehicle-modal-grid{grid-template-columns:1fr;}.vehicle-toolbar{justify-content:flex-start;}}
</style>';
include __DIR__ . '/../partials/header.php';
?>
<div class="content-header">
<div>
<h1 class="content-title">🚐 Vehicles</h1>
<p class="content-subtitle">Vehicle records for operations. TruTrak device mapping is now under Administration → TruTrak Admin.</p>
</div>
<div class="vehicle-toolbar">
<?php if (isAdminRole() || hasPermission('trutrak.manage')): ?><a class="btn btn-secondary" href="<?= e(app_url('admin/trutrak')) ?>#vehicle-mapping">🧭 TruTrak Mapping</a><?php endif; ?>
<?php if ($canManage): ?><button class="btn btn-primary" type="button" data-open-vehicle-modal>➕ Add Vehicle</button><?php endif; ?>
</div>
</div>
<?php foreach ($msg as $m): ?><div class="alert alert-success"><?= e($m) ?></div><?php endforeach; ?>
<?php foreach ($err as $e): ?><div class="alert alert-error"><?= e($e) ?></div><?php endforeach; ?>
<div class="card">
<div class="card-header"><h3 class="card-title">Vehicle List (<?= count($vans) ?>)</h3></div>
<?php if (!$vans): ?>
<div class="text-muted" style="padding:20px;">No vehicles found.</div>
<?php else: ?>
<div class="table-wrap">
<table class="table" style="min-width:960px;">
<thead><tr><th>Status</th><th>Registration</th><th>Short</th><th>Make / Model</th><th>Year</th><th>Vehicle Colour</th><th>Tag</th><th>Notes</th><th>Actions</th></tr></thead>
<tbody>
<?php foreach ($vans as $van): ?>
<?php $tag = $van['tag_color'] ?? '#ff8c1a'; ?>
<tr style="<?= !empty($van['is_active']) ? '' : 'opacity:.55;' ?>">
<td><span class="vehicle-status"><span class="vehicle-dot <?= !empty($van['is_active']) ? 'active' : 'inactive' ?>"></span><?= !empty($van['is_active']) ? 'Active' : 'Inactive' ?></span></td>
<td><strong><?= e((string)$van['plate_full']) ?></strong></td>
<td><span class="badge" style="background:var(--accent);color:#000;"><?= e((string)$van['plate_short']) ?></span></td>
<td><?= e(trim((string)($van['make'] ?? '') . ' ' . (string)($van['model'] ?? '')) ?: '—') ?></td>
<td><?= e((string)($van['year'] ?? '—')) ?></td>
<td><?= e((string)($van['color'] ?? '—')) ?></td>
<td><span style="display:inline-block;width:18px;height:18px;border-radius:4px;background:<?= e($tag) ?>;border:1px solid var(--border);vertical-align:middle;margin-right:6px;"></span><span class="small"><?= e($tag) ?></span></td>
<td><span class="small"><?= e(mb_strimwidth((string)($van['notes'] ?? ''), 0, 60, '…')) ?></span></td>
<td>
<?php if ($canManage): ?>
<div class="d-flex gap-sm" style="flex-wrap:wrap;">
<button class="btn btn-secondary btn-xs" type="button" data-edit-vehicle='<?= e(json_encode($van, JSON_HEX_TAG|JSON_HEX_APOS|JSON_HEX_QUOT|JSON_HEX_AMP)) ?>'>Edit</button>
<form method="post" onsubmit="return confirm('Toggle active status for this vehicle?');" style="display:inline;"><input type="hidden" name="action" value="toggle_active"><input type="hidden" name="id" value="<?= (int)$van['id'] ?>"><button class="btn btn-secondary btn-xs" type="submit"><?= !empty($van['is_active']) ? 'Deactivate' : 'Activate' ?></button></form>
<form method="post" onsubmit="return confirm('Delete this vehicle? This cannot be undone.');" style="display:inline;"><input type="hidden" name="action" value="delete_van"><input type="hidden" name="id" value="<?= (int)$van['id'] ?>"><button class="btn btn-danger btn-xs" type="submit">Delete</button></form>
</div>
<?php else: ?>
<span class="text-muted small">View only</span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
<?php if ($canManage): ?>
<div class="modal-overlay" id="vehicleModal" style="display:none;">
<div class="modal modal-medium">
<div class="modal-header">
<h3 class="modal-title" id="vehicleModalTitle">Add Vehicle</h3>
<button class="modal-close" type="button" data-close-vehicle-modal>×</button>
</div>
<form method="post" id="vehicleForm">
<input type="hidden" name="action" value="save_van">
<input type="hidden" name="id" id="vehicle_id" value="">
<div class="modal-body vehicle-modal-grid">
<div><label class="field-label">Registration</label><input class="input" name="plate_full" id="plate_full" required placeholder="AB12 CDE"></div>
<div><label class="field-label">Short plate</label><input class="input" name="plate_short" id="plate_short" placeholder="Last 3, auto-filled if blank"></div>
<div><label class="field-label">Make</label><input class="input" name="make" id="make" placeholder="Ford"></div>
<div><label class="field-label">Model</label><input class="input" name="model" id="model" placeholder="Transit"></div>
<div><label class="field-label">Year</label><input class="input" name="year" id="year" type="number" min="1900" max="2100"></div>
<div><label class="field-label">Vehicle colour</label><input class="input" name="color" id="color" placeholder="White"></div>
<div><label class="field-label">Tag colour</label><input class="input" name="tag_color" id="tag_color" type="color" value="#ff8c1a"></div>
<div style="display:flex;align-items:end;"><label class="d-flex align-center gap-2"><input type="checkbox" name="is_active" id="is_active" value="1" checked> Active</label></div>
<div style="grid-column:1 / -1;"><label class="field-label">Notes</label><textarea class="input" name="notes" id="notes" rows="3"></textarea></div>
</div>
<div class="modal-footer"><button class="btn btn-secondary" type="button" data-close-vehicle-modal>Cancel</button><button class="btn btn-primary" type="submit">Save Vehicle</button></div>
</form>
</div>
</div>
<script>
(function(){
const modal = document.getElementById('vehicleModal');
const form = document.getElementById('vehicleForm');
function setVal(id, val){ const el=document.getElementById(id); if(el) el.value = val ?? ''; }
function openVehicle(data){
if(!modal || !form) return;
form.reset();
document.getElementById('vehicleModalTitle').textContent = data ? 'Edit Vehicle' : 'Add Vehicle';
setVal('vehicle_id', data?.id || '');
setVal('plate_full', data?.plate_full || '');
setVal('plate_short', data?.plate_short || '');
setVal('make', data?.make || '');
setVal('model', data?.model || '');
setVal('year', data?.year || '');
setVal('color', data?.color || '');
setVal('tag_color', data?.tag_color || '#ff8c1a');
setVal('notes', data?.notes || '');
const active=document.getElementById('is_active'); if(active) active.checked = data ? Number(data.is_active) === 1 : true;
modal.style.display = 'flex';
}
function closeVehicle(){ if(modal) modal.style.display='none'; }
document.addEventListener('click', function(ev){
if(ev.target.closest('[data-open-vehicle-modal]')) { ev.preventDefault(); openVehicle(null); }
const edit = ev.target.closest('[data-edit-vehicle]');
if(edit){ ev.preventDefault(); try{ openVehicle(JSON.parse(edit.getAttribute('data-edit-vehicle') || '{}')); }catch(e){} }
if(ev.target.closest('[data-close-vehicle-modal]')) { ev.preventDefault(); closeVehicle(); }
if(ev.target === modal) closeVehicle();
});
})();
</script>
<?php endif; ?>
<?php include __DIR__ . '/../partials/footer.php'; ?>