378 lines
18 KiB
PHP
378 lines
18 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/includes/layout.php'; require_role('admin');
|
|
|
|
$userId = (int)($_GET['id'] ?? 0);
|
|
$isAjax = isset($_GET['ajax']) && $_GET['ajax'] === '1';
|
|
|
|
if ($userId <= 0) {
|
|
if ($isAjax) {
|
|
echo json_encode(['success' => false, 'message' => 'Invalid ID']);
|
|
exit;
|
|
}
|
|
header('Location: admin_truck_owners.php');
|
|
exit;
|
|
}
|
|
|
|
$errors = [];
|
|
$flash = null;
|
|
|
|
// Fetch Truck Owner Profile
|
|
$stmt = db()->prepare("
|
|
SELECT u.id, u.email, u.full_name, u.status, u.role,
|
|
p.phone, p.address_line, p.country_id, p.city_id,
|
|
p.truck_type, p.load_capacity, p.plate_no, p.bank_account, p.bank_name, p.bank_branch,
|
|
p.id_card_path, p.truck_pic_path, p.registration_path
|
|
FROM users u
|
|
LEFT JOIN truck_owner_profiles p ON u.id = p.user_id
|
|
WHERE u.id = ? AND u.role = 'truck_owner'
|
|
");
|
|
$stmt->execute([$userId]);
|
|
$owner = $stmt->fetch();
|
|
|
|
if (!$owner) {
|
|
if ($isAjax) {
|
|
echo json_encode(['success' => false, 'message' => 'Owner not found']);
|
|
exit;
|
|
}
|
|
header('Location: admin_truck_owners.php');
|
|
exit;
|
|
}
|
|
|
|
$countries = db()->query("SELECT id, name_en, name_ar FROM countries ORDER BY name_en ASC")->fetchAll();
|
|
$cities = db()->query("SELECT id, country_id, name_en, name_ar FROM cities ORDER BY name_en ASC")->fetchAll();
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') { validate_csrf_token();
|
|
$fullName = trim($_POST['full_name'] ?? '');
|
|
$email = trim($_POST['email'] ?? '');
|
|
$phone = trim($_POST['phone'] ?? '');
|
|
$countryId = (int)($_POST['country_id'] ?? 0);
|
|
$cityId = (int)($_POST['city_id'] ?? 0);
|
|
$addressLine = trim($_POST['address_line'] ?? '');
|
|
|
|
$truckType = trim($_POST['truck_type'] ?? '');
|
|
$loadCapacity = trim($_POST['load_capacity'] ?? '');
|
|
$plateNo = trim($_POST['plate_no'] ?? '');
|
|
$status = trim($_POST['status'] ?? '');
|
|
$password = $_POST['password'] ?? '';
|
|
|
|
$bankAccount = trim($_POST['bank_account'] ?? '');
|
|
$bankName = trim($_POST['bank_name'] ?? '');
|
|
$bankBranch = trim($_POST['bank_branch'] ?? '');
|
|
|
|
if ($fullName === '') $errors[] = 'Full name is required.';
|
|
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) $errors[] = 'Valid email is required.';
|
|
if ($phone === '') $errors[] = 'Phone number is required.';
|
|
if (!in_array($status, ['pending', 'active', 'rejected'], true)) $errors[] = 'Invalid status.';
|
|
|
|
if ($truckType === '' || $loadCapacity === '' || $plateNo === '') {
|
|
$errors[] = 'Truck type, load capacity, and plate number are required.';
|
|
} elseif (!is_numeric($loadCapacity) || (float)$loadCapacity <= 0) {
|
|
$errors[] = 'Load capacity must be a positive number.';
|
|
}
|
|
|
|
if ($countryId <= 0 || $cityId <= 0) {
|
|
$errors[] = 'Please select country and city.';
|
|
} else {
|
|
$cityCheck = db()->prepare("SELECT COUNT(*) FROM cities WHERE id = ? AND country_id = ?");
|
|
$cityCheck->execute([$cityId, $countryId]);
|
|
if ((int)$cityCheck->fetchColumn() === 0) {
|
|
$errors[] = 'Selected city does not belong to selected country.';
|
|
}
|
|
}
|
|
|
|
if (!$errors) {
|
|
try {
|
|
db()->beginTransaction();
|
|
|
|
$stmtUser = db()->prepare("UPDATE users SET full_name = ?, email = ?, status = ? WHERE id = ? AND role = 'truck_owner'");
|
|
$stmtUser->execute([$fullName, $email, $status, $userId]);
|
|
|
|
if ($password !== '') {
|
|
$stmtPass = db()->prepare("UPDATE users SET password = ? WHERE id = ? AND role = 'truck_owner'");
|
|
$stmtPass->execute([password_hash($password, PASSWORD_DEFAULT), $userId]);
|
|
}
|
|
|
|
$stmtProfile = db()->prepare("
|
|
UPDATE truck_owner_profiles
|
|
SET phone = ?, address_line = ?, country_id = ?, city_id = ?,
|
|
truck_type = ?, load_capacity = ?, plate_no = ?,
|
|
bank_account = ?, bank_name = ?, bank_branch = ?
|
|
WHERE user_id = ?
|
|
");
|
|
$stmtProfile->execute([$phone, $addressLine, $countryId, $cityId, $truckType, $loadCapacity, $plateNo, $bankAccount, $bankName, $bankBranch, $userId]);
|
|
|
|
db()->commit();
|
|
$flash = 'Truck Owner profile updated successfully.';
|
|
|
|
// If AJAX, return success immediately
|
|
if ($isAjax) {
|
|
header('Content-Type: application/json');
|
|
echo json_encode(['success' => true, 'message' => $flash]);
|
|
exit;
|
|
}
|
|
|
|
// Refresh data for non-ajax
|
|
$owner['full_name'] = $fullName;
|
|
$owner['email'] = $email;
|
|
$owner['status'] = $status;
|
|
$owner['phone'] = $phone;
|
|
$owner['address_line'] = $addressLine;
|
|
$owner['country_id'] = $countryId;
|
|
$owner['city_id'] = $cityId;
|
|
$owner['truck_type'] = $truckType;
|
|
$owner['load_capacity'] = $loadCapacity;
|
|
$owner['plate_no'] = $plateNo;
|
|
$owner['bank_account'] = $bankAccount;
|
|
$owner['bank_name'] = $bankName;
|
|
$owner['bank_branch'] = $bankBranch;
|
|
|
|
} catch (Throwable $e) {
|
|
db()->rollBack();
|
|
if (stripos($e->getMessage(), 'Duplicate entry') !== false) {
|
|
$errors[] = 'This email is already in use by another account.';
|
|
} else {
|
|
$errors[] = 'Failed to update truck owner profile. Please try again.';
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($isAjax && $errors) {
|
|
header('Content-Type: application/json');
|
|
echo json_encode(['success' => false, 'message' => implode('<br>', $errors)]);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
$idCards = json_decode($owner['id_card_path'] ?? '[]', true) ?: [];
|
|
$regs = json_decode($owner['registration_path'] ?? '[]', true) ?: [];
|
|
$pic = $owner['truck_pic_path'];
|
|
|
|
// -- OUTPUT START --
|
|
if (!$isAjax):
|
|
render_header('Edit Truck Owner', 'admin', true);
|
|
?>
|
|
|
|
<div class="row g-0">
|
|
<div class="col-md-2 bg-white border-end min-vh-100">
|
|
<?php render_admin_sidebar('truck_owners'); ?>
|
|
</div>
|
|
<div class="col-md-10 p-4">
|
|
<div class="page-intro d-flex flex-column flex-md-row justify-content-between align-items-md-center mb-4">
|
|
<div>
|
|
<a href="admin_truck_owners.php" class="text-decoration-none small text-muted mb-2 d-inline-block">← Back to Truck Owners</a>
|
|
<h1 class="section-title mb-1">Edit Truck Owner</h1>
|
|
<p class="muted mb-0">Update profile information for <?= e($owner['full_name']) ?>.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if ($flash): ?>
|
|
<div class="alert alert-success" data-auto-dismiss="true"><?= e($flash) ?></div>
|
|
<?php endif; ?>
|
|
<?php if ($errors): ?>
|
|
<div class="alert alert-warning"><?= e(implode('<br>', $errors)) ?></div>
|
|
<?php endif; ?>
|
|
|
|
<div class="panel p-4">
|
|
<?php endif; // End non-ajax wrapper ?>
|
|
|
|
<!-- The Form (Shared) -->
|
|
<form method="post" action="admin_truck_owner_edit.php?id=<?= $userId ?> <?= csrf_field() ?><?= $isAjax ? '&ajax=1' : '' ?>" class="ajax-form">
|
|
<?php if ($isAjax): ?>
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Edit Truck Owner: <?= e($owner['full_name']) ?></h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div id="form-errors" class="alert alert-danger d-none"></div>
|
|
<?php endif; ?>
|
|
|
|
<h5 class="mb-3 <?= $isAjax ? 'd-none' : '' ?>">Personal Details</h5>
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-md-6">
|
|
<label class="form-label" for="full_name">Full Name</label>
|
|
<input type="text" name="full_name" id="full_name" class="form-control" value="<?= e((string)$owner['full_name']) ?>" required>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label" for="email">Email</label>
|
|
<input type="email" name="email" id="email" class="form-control" value="<?= e((string)$owner['email']) ?>" required>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="phone">Phone</label>
|
|
<input type="text" name="phone" id="phone" class="form-control" value="<?= e((string)$owner['phone']) ?>" required>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="password">Password <small class="text-muted">(leave blank)</small></label>
|
|
<input type="password" name="password" id="password" class="form-control" autocomplete="new-password">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="status">Account Status</label>
|
|
<select name="status" id="status" class="form-select" required>
|
|
<option value="pending" <?= $owner['status'] === 'pending' ? 'selected' : '' ?>>Pending</option>
|
|
<option value="active" <?= $owner['status'] === 'active' ? 'selected' : '' ?>>Active</option>
|
|
<option value="rejected" <?= $owner['status'] === 'rejected' ? 'selected' : '' ?>>Rejected</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<h5 class="mb-3">Location</h5>
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="country_id">Country</label>
|
|
<select name="country_id" id="country_id" class="form-select" onchange="syncCities()" required>
|
|
<option value="">Select country</option>
|
|
<?php foreach ($countries as $country): ?>
|
|
<option value="<?= e((string)$country['id']) ?>" <?= (string)$owner['country_id'] === (string)$country['id'] ? 'selected' : '' ?>>
|
|
<?= e($lang === 'ar' && !empty($country['name_ar']) ? $country['name_ar'] : $country['name_en']) ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="city_id">City</label>
|
|
<select name="city_id" id="city_id" class="form-select" required data-selected="<?= e((string)$owner['city_id']) ?>">
|
|
<option value="">Select city</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="address_line">Address Line</label>
|
|
<input type="text" name="address_line" id="address_line" class="form-control" value="<?= e((string)$owner['address_line']) ?>" required>
|
|
</div>
|
|
</div>
|
|
|
|
<h5 class="mb-3">Truck Details</h5>
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="truck_type">Truck Type</label>
|
|
<input type="text" name="truck_type" id="truck_type" class="form-control" value="<?= e((string)$owner['truck_type']) ?>" required>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="load_capacity">Load Capacity (tons)</label>
|
|
<input type="number" step="0.01" min="0.1" name="load_capacity" id="load_capacity" class="form-control" value="<?= e((string)$owner['load_capacity']) ?>" required>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="plate_no">Plate Number</label>
|
|
<input type="text" name="plate_no" id="plate_no" class="form-control" value="<?= e((string)$owner['plate_no']) ?>" required>
|
|
</div>
|
|
</div>
|
|
|
|
<h5 class="mb-3 mt-4 border-top pt-3">Bank Details</h5>
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="bank_account">Bank Account / IBAN</label>
|
|
<input type="text" name="bank_account" id="bank_account" class="form-control" value="<?= e((string)($owner['bank_account'] ?? '')) ?>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="bank_name">Bank Name</label>
|
|
<input type="text" name="bank_name" id="bank_name" class="form-control" value="<?= e((string)($owner['bank_name'] ?? '')) ?>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="bank_branch">Bank Branch</label>
|
|
<input type="text" name="bank_branch" id="bank_branch" class="form-control" value="<?= e((string)($owner['bank_branch'] ?? '')) ?>">
|
|
</div>
|
|
</div>
|
|
|
|
<?php if (!$isAjax): // Hide heavy images in modal edit to keep it clean, or keep them? User said "big and long". Let's keep them but maybe minimized. Actually let's keep them, they are important. ?>
|
|
<h5 class="mb-3 mt-4 border-top pt-3">Uploaded Documents</h5>
|
|
<div class="row g-4 mb-4">
|
|
<div class="col-md-4">
|
|
<h6 class="text-muted mb-2">ID Card</h6>
|
|
<?php if ($idCards): ?>
|
|
<div class="d-flex flex-wrap gap-2">
|
|
<?php foreach ($idCards as $path): ?>
|
|
<a href="<?= e('/' . $path) ?>" target="_blank" class="d-block border rounded overflow-hidden" style="width: 100px; height: 100px;">
|
|
<img src="<?= e('/' . $path) ?>" alt="ID Card" class="w-100 h-100 object-fit-cover" style="object-fit: cover;">
|
|
</a>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php else: ?>
|
|
<p class="small text-muted">No ID card uploaded.</p>
|
|
<?php endif; ?>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<h6 class="text-muted mb-2">Truck Picture</h6>
|
|
<?php if ($pic): ?>
|
|
<a href="<?= e('/' . $pic) ?>" target="_blank" class="d-block border rounded overflow-hidden" style="width: 100px; height: 100px;">
|
|
<img src="<?= e('/' . $pic) ?>" alt="Truck Picture" class="w-100 h-100 object-fit-cover" style="object-fit: cover;">
|
|
</a>
|
|
<?php else: ?>
|
|
<p class="small text-muted">No picture uploaded.</p>
|
|
<?php endif; ?>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<h6 class="text-muted mb-2">Registration</h6>
|
|
<?php if ($regs): ?>
|
|
<div class="d-flex flex-wrap gap-2">
|
|
<?php foreach ($regs as $path): ?>
|
|
<a href="<?= e('/' . $path) ?>" target="_blank" class="d-block border rounded overflow-hidden" style="width: 100px; height: 100px;">
|
|
<img src="<?= e('/' . $path) ?>" alt="Registration" class="w-100 h-100 object-fit-cover" style="object-fit: cover;">
|
|
</a>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php else: ?>
|
|
<p class="small text-muted">No registration uploaded.</p>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($isAjax): ?>
|
|
</div> <!-- modal-body -->
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-primary">Save Changes</button>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="mt-4">
|
|
<button type="submit" class="btn btn-primary">Save Changes</button>
|
|
<a href="admin_truck_owners.php" class="btn btn-outline-dark ms-2">Cancel</a>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
</form>
|
|
|
|
<?php if (!$isAjax): ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<script>
|
|
(function() {
|
|
const allCities = <?= json_encode($cities, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
|
const countrySelect = document.querySelector('#country_id'); // Use querySelector to scope? No, IDs are unique.
|
|
const citySelect = document.querySelector('#city_id');
|
|
const lang = '<?= $lang ?>';
|
|
|
|
window.syncCities = function() { // Expose globally for onchange
|
|
if (!countrySelect || !citySelect) return;
|
|
|
|
const countryId = countrySelect.value;
|
|
const selectedValue = citySelect.dataset.selected || '';
|
|
citySelect.innerHTML = '<option value="">Select city</option>';
|
|
|
|
allCities.forEach((city) => {
|
|
if (String(city.country_id) !== String(countryId)) {
|
|
return;
|
|
}
|
|
const option = document.createElement('option');
|
|
option.value = city.id;
|
|
option.textContent = lang === 'ar' && city.name_ar ? city.name_ar : (city.name_en || city.name_ar);
|
|
if (String(city.id) === String(selectedValue)) {
|
|
option.selected = true;
|
|
}
|
|
citySelect.appendChild(option);
|
|
});
|
|
citySelect.dataset.selected = '';
|
|
};
|
|
|
|
// Initialize
|
|
if (countrySelect && citySelect) {
|
|
syncCities();
|
|
// Re-bind change event if needed, but onchange in HTML handles it.
|
|
}
|
|
})();
|
|
</script>
|
|
|
|
<?php if (!$isAjax) render_footer(); ?>
|