425 lines
20 KiB
PHP
425 lines
20 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/includes/layout.php';
|
|
ensure_schema();
|
|
|
|
$errors = [];
|
|
$saved = false;
|
|
$saved_role = '';
|
|
$values = [
|
|
'role' => $_GET['role'] ?? 'shipper',
|
|
'full_name' => '',
|
|
'email' => '',
|
|
'phone' => '',
|
|
'country_id' => '',
|
|
'city_id' => '',
|
|
'address_line' => '',
|
|
'company_name' => '',
|
|
'truck_type' => '',
|
|
'load_capacity' => '',
|
|
'plate_no' => '',
|
|
'bank_account' => '',
|
|
'bank_name' => '',
|
|
'bank_branch' => '',
|
|
];
|
|
|
|
$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') {
|
|
$role = $_POST['role'] ?? 'shipper';
|
|
$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'] ?? '');
|
|
$companyName = trim($_POST['company_name'] ?? '');
|
|
$passwordRaw = (string)($_POST['password'] ?? '');
|
|
|
|
$values = [
|
|
'role' => $role,
|
|
'full_name' => $fullName,
|
|
'email' => $email,
|
|
'phone' => $phone,
|
|
'country_id' => $countryId > 0 ? (string)$countryId : '',
|
|
'city_id' => $cityId > 0 ? (string)$cityId : '',
|
|
'address_line' => $addressLine,
|
|
'company_name' => $companyName,
|
|
'truck_type' => trim($_POST['truck_type'] ?? ''),
|
|
'load_capacity' => trim($_POST['load_capacity'] ?? ''),
|
|
'plate_no' => trim($_POST['plate_no'] ?? ''),
|
|
];
|
|
|
|
if (!in_array($role, ['shipper', 'truck_owner'], true)) {
|
|
$errors[] = 'Invalid role selected.';
|
|
}
|
|
if ($fullName === '') {
|
|
$errors[] = 'Full name is required.';
|
|
}
|
|
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
|
$errors[] = 'Please provide a valid email address.';
|
|
}
|
|
if ($phone === '') {
|
|
$errors[] = 'Phone number is required.';
|
|
}
|
|
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 ($addressLine === '') {
|
|
$errors[] = 'Address is required.';
|
|
}
|
|
if (strlen($passwordRaw) < 6) {
|
|
$errors[] = 'Password must be at least 6 characters.';
|
|
}
|
|
if ($role === 'shipper' && $companyName === '') {
|
|
$errors[] = 'Company name is required for shipper registration.';
|
|
}
|
|
|
|
if (!$errors) {
|
|
$password = password_hash($passwordRaw, PASSWORD_DEFAULT);
|
|
$pdo = db();
|
|
|
|
try {
|
|
$pdo->beginTransaction();
|
|
|
|
$status = ($role === 'truck_owner') ? 'pending' : 'active';
|
|
$stmt = $pdo->prepare("INSERT INTO users (email, password, full_name, role, status) VALUES (?, ?, ?, ?, ?)");
|
|
$stmt->execute([$email, $password, $fullName, $role, $status]);
|
|
$userId = (int)$pdo->lastInsertId();
|
|
|
|
if ($role === 'shipper') {
|
|
$shipperStmt = $pdo->prepare(
|
|
"INSERT INTO shipper_profiles (user_id, company_name, phone, country_id, city_id, address_line)
|
|
VALUES (?, ?, ?, ?, ?, ?)"
|
|
);
|
|
$shipperStmt->execute([$userId, $companyName, $phone, $countryId, $cityId, $addressLine]);
|
|
} else {
|
|
$truckType = trim($_POST['truck_type'] ?? '');
|
|
$loadCapacity = trim($_POST['load_capacity'] ?? '');
|
|
$plateNo = trim($_POST['plate_no'] ?? '');
|
|
|
|
if ($truckType === '' || $loadCapacity === '' || $plateNo === '') {
|
|
$errors[] = 'Please complete truck details.';
|
|
} elseif (!is_numeric($loadCapacity) || (float)$loadCapacity <= 0) {
|
|
$errors[] = 'Load capacity must be numeric and greater than zero.';
|
|
}
|
|
|
|
$uploadDir = __DIR__ . '/uploads/profiles/' . $userId . '/';
|
|
if (!is_dir($uploadDir)) {
|
|
mkdir($uploadDir, 0775, true);
|
|
}
|
|
|
|
$allowed = ['image/jpeg' => 'jpg', 'image/png' => 'png', 'image/webp' => 'webp'];
|
|
$maxSize = 8 * 1024 * 1024;
|
|
$saveImage = static function (string $tmpName, int $size, string $prefix) use ($uploadDir, $allowed, $maxSize): ?string {
|
|
if ($size <= 0 || $size > $maxSize) {
|
|
return null;
|
|
}
|
|
$mime = mime_content_type($tmpName) ?: '';
|
|
if (!isset($allowed[$mime])) {
|
|
return null;
|
|
}
|
|
$filename = uniqid($prefix, true) . '.' . $allowed[$mime];
|
|
$target = $uploadDir . $filename;
|
|
if (!move_uploaded_file($tmpName, $target)) {
|
|
return null;
|
|
}
|
|
return 'uploads/profiles/' . basename($uploadDir) . '/' . $filename;
|
|
};
|
|
|
|
$idCardPaths = [];
|
|
if (is_uploaded_file($_FILES['id_card_front']['tmp_name'] ?? '')) {
|
|
$path = $saveImage($_FILES['id_card_front']['tmp_name'], (int)$_FILES['id_card_front']['size'], 'id_front_');
|
|
if ($path) $idCardPaths[] = $path;
|
|
}
|
|
if (is_uploaded_file($_FILES['id_card_back']['tmp_name'] ?? '')) {
|
|
$path = $saveImage($_FILES['id_card_back']['tmp_name'], (int)$_FILES['id_card_back']['size'], 'id_back_');
|
|
if ($path) $idCardPaths[] = $path;
|
|
}
|
|
|
|
$regPaths = [];
|
|
if (is_uploaded_file($_FILES['truck_reg_front']['tmp_name'] ?? '')) {
|
|
$path = $saveImage($_FILES['truck_reg_front']['tmp_name'], (int)$_FILES['truck_reg_front']['size'], 'reg_front_');
|
|
if ($path) $regPaths[] = $path;
|
|
}
|
|
if (is_uploaded_file($_FILES['truck_reg_back']['tmp_name'] ?? '')) {
|
|
$path = $saveImage($_FILES['truck_reg_back']['tmp_name'], (int)$_FILES['truck_reg_back']['size'], 'reg_back_');
|
|
if ($path) $regPaths[] = $path;
|
|
}
|
|
|
|
$truckPic = null;
|
|
$truckTmp = $_FILES['truck_picture']['tmp_name'] ?? '';
|
|
if (is_uploaded_file($truckTmp)) {
|
|
$truckSize = (int)($_FILES['truck_picture']['size'] ?? 0);
|
|
$truckPic = $saveImage($truckTmp, $truckSize, 'truck_');
|
|
}
|
|
|
|
if (count($idCardPaths) < 2 || count($regPaths) < 2 || !$truckPic) {
|
|
$errors[] = 'Please upload all required truck-owner images (ID front/back, registration front/back, truck photo).';
|
|
}
|
|
|
|
if (!$errors) {
|
|
$ownerStmt = $pdo->prepare(
|
|
"INSERT INTO truck_owner_profiles (user_id, phone, country_id, city_id, address_line, truck_type, load_capacity, plate_no, bank_account, bank_name, bank_branch, id_card_path, truck_pic_path, registration_path)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
);
|
|
$ownerStmt->execute([
|
|
$userId,
|
|
$phone,
|
|
$countryId,
|
|
$cityId,
|
|
$addressLine,
|
|
$truckType,
|
|
$loadCapacity,
|
|
$plateNo,
|
|
$bankAccount,
|
|
$bankName,
|
|
$bankBranch,
|
|
json_encode($idCardPaths, JSON_UNESCAPED_SLASHES),
|
|
$truckPic,
|
|
json_encode($regPaths, JSON_UNESCAPED_SLASHES),
|
|
]);
|
|
}
|
|
}
|
|
|
|
if ($errors) {
|
|
$pdo->rollBack();
|
|
} else {
|
|
$pdo->commit();
|
|
|
|
// Send Welcome Notification
|
|
$user = [
|
|
'id' => $userId,
|
|
'email' => $email,
|
|
'full_name' => $fullName,
|
|
'role' => $role,
|
|
'phone' => $phone
|
|
];
|
|
|
|
try {
|
|
require_once __DIR__ . '/includes/NotificationService.php';
|
|
NotificationService::send('welcome_message', $user, [], $lang);
|
|
} catch (Throwable $e) {
|
|
error_log('Failed to send welcome notification: ' . $e->getMessage());
|
|
}
|
|
|
|
$saved = true;
|
|
$saved_role = $role;
|
|
$values = [
|
|
'role' => $_GET['role'] ?? 'shipper',
|
|
'full_name' => '',
|
|
'email' => '',
|
|
'phone' => '',
|
|
'country_id' => '',
|
|
'city_id' => '',
|
|
'address_line' => '',
|
|
'company_name' => '',
|
|
'truck_type' => '',
|
|
'load_capacity' => '',
|
|
'plate_no' => '',
|
|
'bank_account' => '',
|
|
'bank_name' => '',
|
|
'bank_branch' => '',
|
|
];
|
|
}
|
|
} catch (Throwable $e) {
|
|
if ($pdo->inTransaction()) {
|
|
$pdo->rollBack();
|
|
}
|
|
if (stripos($e->getMessage(), 'Duplicate entry') !== false) {
|
|
$errors[] = 'This email is already registered.';
|
|
} else {
|
|
$errors[] = 'Registration failed. Please try again.';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
render_header('Shipper & Truck Owner Registration');
|
|
?>
|
|
|
|
<div class="page-intro">
|
|
<h1 class="section-title mb-1"><?= e(t('reg_title')) ?></h1>
|
|
<p class="muted mb-0"><?= e(t('reg_subtitle')) ?></p>
|
|
</div>
|
|
|
|
<div class="panel p-4">
|
|
<?php if ($saved): ?>
|
|
<?php if ($saved_role === 'truck_owner'): ?>
|
|
<div class="alert alert-success"><?= e(t('reg_success_pending')) ?></div>
|
|
<?php else: ?>
|
|
<div class="alert alert-success"><?= e(t('reg_success')) ?></div>
|
|
<?php endif; ?>
|
|
<?php endif; ?>
|
|
<?php if ($errors): ?>
|
|
<div class="alert alert-warning"><?= e(implode(' ', $errors)) ?></div>
|
|
<?php endif; ?>
|
|
|
|
<form method="post" enctype="multipart/form-data" id="regForm" novalidate>
|
|
<div class="row g-3">
|
|
<div class="col-md-3">
|
|
<label class="form-label" for="role"><?= e(t('role')) ?></label>
|
|
<select name="role" id="role" class="form-select" onchange="toggleFields()" required>
|
|
<option value="shipper" <?= $values['role'] === 'shipper' ? 'selected' : '' ?>><?= e(t('shipper')) ?></option>
|
|
<option value="truck_owner" <?= $values['role'] === 'truck_owner' ? 'selected' : '' ?>><?= e(t('truck_owner')) ?></option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label" for="full_name"><?= e(t('full_name')) ?></label>
|
|
<input type="text" name="full_name" id="full_name" class="form-control" value="<?= e($values['full_name']) ?>" required>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label" for="email"><?= e(t('email')) ?></label>
|
|
<input type="email" name="email" id="email" class="form-control" value="<?= e($values['email']) ?>" required>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label" for="password"><?= e(t('password')) ?></label>
|
|
<input type="password" name="password" id="password" class="form-control" minlength="6" required>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label" for="phone"><?= e(t('phone')) ?></label>
|
|
<input type="text" name="phone" id="phone" class="form-control" value="<?= e($values['phone']) ?>" required>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label" for="country_id"><?= e(t('country')) ?></label>
|
|
<select name="country_id" id="country_id" class="form-select" onchange="syncCities()" required>
|
|
<option value=""><?= e(t('select_country')) ?></option>
|
|
<?php foreach ($countries as $country): ?>
|
|
<option value="<?= e((string)$country['id']) ?>" <?= $values['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-3">
|
|
<label class="form-label" for="city_id"><?= e(t('city')) ?></label>
|
|
<select name="city_id" id="city_id" class="form-select" required data-selected="<?= e($values['city_id']) ?>">
|
|
<option value=""><?= e(t('select_city')) ?></option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label" for="address_line"><?= e(t('address')) ?></label>
|
|
<input type="text" name="address_line" id="address_line" class="form-control" value="<?= e($values['address_line']) ?>" required>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="shipperFields" class="mt-4">
|
|
<h2 class="h5 mb-3"><?= e(t('shipper_details')) ?></h2>
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label" for="company_name"><?= e(t('company_name')) ?></label>
|
|
<input type="text" name="company_name" id="company_name" class="form-control" value="<?= e($values['company_name']) ?>">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="truckFields" class="mt-4" style="display:none;">
|
|
<h2 class="h5 mb-3"><?= e(t('truck_details')) ?></h2>
|
|
<div class="row g-3">
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="truck_type"><?= e(t('truck_type')) ?></label>
|
|
<input type="text" name="truck_type" id="truck_type" class="form-control" value="<?= e($values['truck_type']) ?>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="load_capacity"><?= e(t('load_capacity')) ?></label>
|
|
<input type="number" name="load_capacity" id="load_capacity" class="form-control" step="0.01" min="0.1" value="<?= e($values['load_capacity']) ?>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="plate_no"><?= e(t('plate_no')) ?></label>
|
|
<input type="text" name="plate_no" id="plate_no" class="form-control" value="<?= e($values['plate_no']) ?>">
|
|
</div>
|
|
|
|
<div class="col-md-4 mt-3">
|
|
<label class="form-label" for="bank_account"><?= e(t('bank_account')) ?></label>
|
|
<input type="text" name="bank_account" id="bank_account" class="form-control" value="<?= e($values['bank_account']) ?>">
|
|
</div>
|
|
<div class="col-md-4 mt-3">
|
|
<label class="form-label" for="bank_name"><?= e(t('bank_name')) ?></label>
|
|
<input type="text" name="bank_name" id="bank_name" class="form-control" value="<?= e($values['bank_name']) ?>">
|
|
</div>
|
|
<div class="col-md-4 mt-3">
|
|
<label class="form-label" for="bank_branch"><?= e(t('bank_branch')) ?></label>
|
|
<input type="text" name="bank_branch" id="bank_branch" class="form-control" value="<?= e($values['bank_branch']) ?>">
|
|
</div>
|
|
|
|
<div class="col-md-6 mt-3">
|
|
<label class="form-label" for="id_card_front"><?= e(t('id_card_front')) ?></label>
|
|
<input type="file" name="id_card_front" id="id_card_front" class="form-control" accept="image/png,image/jpeg,image/webp">
|
|
</div>
|
|
<div class="col-md-6 mt-3">
|
|
<label class="form-label" for="id_card_back"><?= e(t('id_card_back')) ?></label>
|
|
<input type="file" name="id_card_back" id="id_card_back" class="form-control" accept="image/png,image/jpeg,image/webp">
|
|
</div>
|
|
<div class="col-md-6 mt-3">
|
|
<label class="form-label" for="truck_reg_front"><?= e(t('truck_reg_front')) ?></label>
|
|
<input type="file" name="truck_reg_front" id="truck_reg_front" class="form-control" accept="image/png,image/jpeg,image/webp">
|
|
</div>
|
|
<div class="col-md-6 mt-3">
|
|
<label class="form-label" for="truck_reg_back"><?= e(t('truck_reg_back')) ?></label>
|
|
<input type="file" name="truck_reg_back" id="truck_reg_back" class="form-control" accept="image/png,image/jpeg,image/webp">
|
|
</div>
|
|
<div class="col-md-12 mt-3">
|
|
<label class="form-label" for="truck_picture"><?= e(t('truck_picture')) ?></label>
|
|
<input type="file" name="truck_picture" id="truck_picture" class="form-control" accept="image/png,image/jpeg,image/webp">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-4 d-flex gap-2">
|
|
<button type="submit" class="btn btn-primary"><?= e(t('create_account')) ?></button>
|
|
<a class="btn btn-outline-dark" href="<?= e(url_with_lang('index.php')) ?>"><?= e(t('back_to_home')) ?></a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<script>
|
|
const allCities = <?= json_encode($cities, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
|
|
|
function syncCities() {
|
|
const countryId = document.getElementById('country_id').value;
|
|
const citySelect = document.getElementById('city_id');
|
|
const selectedValue = citySelect.dataset.selected || '';
|
|
citySelect.innerHTML = '<option value=""><?= e(t('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_en)' : '(city.name_en || city.name_ar)' ?>;
|
|
if (String(city.id) === String(selectedValue)) {
|
|
option.selected = true;
|
|
}
|
|
citySelect.appendChild(option);
|
|
});
|
|
citySelect.dataset.selected = '';
|
|
}
|
|
|
|
function toggleFields() {
|
|
const role = document.getElementById('role').value;
|
|
const truckFields = document.getElementById('truckFields');
|
|
const shipperFields = document.getElementById('shipperFields');
|
|
const companyInput = document.getElementById('company_name');
|
|
const isOwner = role === 'truck_owner';
|
|
shipperFields.style.display = isOwner ? 'none' : 'block';
|
|
truckFields.style.display = isOwner ? 'block' : 'none';
|
|
|
|
companyInput.required = !isOwner;
|
|
truckFields.querySelectorAll('input[type="text"], input[type="number"]').forEach((input) => {
|
|
input.required = isOwner;
|
|
});
|
|
}
|
|
syncCities();
|
|
toggleFields();
|
|
</script>
|
|
|
|
<?php render_footer(); ?>
|