password edit

This commit is contained in:
Flatlogic Bot 2026-03-08 05:41:10 +00:00
parent 6485678cb7
commit c17c11d23a
17 changed files with 1397 additions and 263 deletions

View File

@ -31,6 +31,20 @@ try {
$shipments = [];
}
$stats = [
'total_shipments' => 0,
'active_shipments' => 0,
'total_shippers' => 0,
'total_truck_owners' => 0,
];
try {
$stats['total_shipments'] = (int)db()->query("SELECT COUNT(*) FROM shipments")->fetchColumn();
$stats['active_shipments'] = (int)db()->query("SELECT COUNT(*) FROM shipments WHERE status != 'delivered'")->fetchColumn();
$stats['total_shippers'] = (int)db()->query("SELECT COUNT(*) FROM users WHERE role = 'shipper'")->fetchColumn();
$stats['total_truck_owners'] = (int)db()->query("SELECT COUNT(*) FROM users WHERE role = 'truck_owner'")->fetchColumn();
} catch (Throwable $e) {}
$flash = get_flash();
render_header(t('admin_dashboard'), 'admin');
@ -41,74 +55,165 @@ render_header(t('admin_dashboard'), 'admin');
<?php render_admin_sidebar('dashboard'); ?>
</div>
<div class="col-lg-9">
<div class="page-intro">
<div class="page-intro mb-4">
<h1 class="section-title mb-1"><?= e(t('admin_dashboard')) ?></h1>
<p class="muted mb-0">Control shipment status, manage locations, and onboard new users from one place.</p>
<p class="muted mb-0">Overview of your platform's performance and recent activity.</p>
</div>
<div class="panel p-4 mb-4">
<h2 class="h5 mb-3">Quick actions</h2>
<div class="d-flex flex-wrap gap-2">
<a href="<?= e(url_with_lang('admin_countries.php')) ?>" class="btn btn-outline-dark">Manage Countries</a>
<a href="<?= e(url_with_lang('admin_cities.php')) ?>" class="btn btn-outline-dark">Manage Cities</a>
<a href="<?= e(url_with_lang('register.php')) ?>" class="btn btn-primary">Register New User</a>
</div>
</div>
<div class="panel p-4">
<div class="d-flex justify-content-between align-items-center mb-2">
<h2 class="section-title h4 mb-0"><?= e(t('admin_dashboard')) ?></h2>
<span class="small text-muted"><?= e(count($shipments)) ?> total</span>
</div>
<?php if ($flash): ?>
<div class="alert alert-success" data-auto-dismiss="true"><?= e($flash['message']) ?></div>
<?php endif; ?>
<?php if ($errors): ?>
<div class="alert alert-warning"><?= e(implode(' ', $errors)) ?></div>
<?php endif; ?>
<?php if (!$shipments): ?>
<p class="muted mb-0"><?= e(t('no_shipments')) ?></p>
<?php else: ?>
<div class="table-responsive">
<table class="table align-middle mb-0">
<thead>
<tr>
<th><?= e(t('shipper_company')) ?></th>
<th><?= e(t('origin')) ?></th>
<th><?= e(t('destination')) ?></th>
<th><?= e(t('payment_method')) ?></th>
<th><?= e(t('status')) ?></th>
<th><?= e(t('update_status')) ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($shipments as $row): ?>
<tr>
<td><?= e($row['shipper_company']) ?></td>
<td><?= e($row['origin_city']) ?></td>
<td><?= e($row['destination_city']) ?></td>
<td><?= e($row['payment_method'] === 'bank_transfer' ? t('payment_bank') : t('payment_thawani')) ?></td>
<td><span class="badge <?= e($row['status']) ?>"><?= e(status_label($row['status'])) ?></span></td>
<td>
<form class="d-flex gap-2" method="post">
<input type="hidden" name="action" value="update_status">
<input type="hidden" name="shipment_id" value="<?= e($row['id']) ?>">
<select class="form-select form-select-sm" name="status">
<option value="posted" <?= $row['status'] === 'posted' ? 'selected' : '' ?>><?= e(t('status_posted')) ?></option>
<option value="offered" <?= $row['status'] === 'offered' ? 'selected' : '' ?>><?= e(t('status_offered')) ?></option>
<option value="confirmed" <?= $row['status'] === 'confirmed' ? 'selected' : '' ?>><?= e(t('status_confirmed')) ?></option>
<option value="in_transit" <?= $row['status'] === 'in_transit' ? 'selected' : '' ?>><?= e(t('status_in_transit')) ?></option>
<option value="delivered" <?= $row['status'] === 'delivered' ? 'selected' : '' ?>><?= e(t('status_delivered')) ?></option>
</select>
<button class="btn btn-sm btn-primary" type="submit"><?= e(t('save')) ?></button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<!-- Stats Row -->
<div class="row g-3 mb-4">
<div class="col-md-3">
<div class="panel p-4 text-center h-100 shadow-sm border-0 rounded-4 position-relative overflow-hidden" style="background: linear-gradient(135deg, #ffffff, #f8f9fa);">
<div class="position-absolute opacity-10" style="inset-inline-end: 10px; top: 15px;"><i class="bi bi-box-seam" style="font-size: 3.5rem;"></i></div>
<div class="text-primary mb-2 position-relative"><i class="bi bi-box-seam fs-2"></i></div>
<h3 class="h2 mb-0 fw-bold position-relative"><?= $stats['total_shipments'] ?></h3>
<p class="text-muted small text-uppercase mb-0 fw-bold position-relative" style="letter-spacing: 0.5px;">Total Shipments</p>
</div>
<?php endif; ?>
</div>
<div class="col-md-3">
<div class="panel p-4 text-center h-100 shadow-sm border-0 rounded-4 position-relative overflow-hidden" style="background: linear-gradient(135deg, #ffffff, #f8f9fa);">
<div class="position-absolute opacity-10" style="inset-inline-end: 10px; top: 15px;"><i class="bi bi-truck" style="font-size: 3.5rem;"></i></div>
<div class="text-warning mb-2 position-relative"><i class="bi bi-truck fs-2"></i></div>
<h3 class="h2 mb-0 fw-bold position-relative"><?= $stats['active_shipments'] ?></h3>
<p class="text-muted small text-uppercase mb-0 fw-bold position-relative" style="letter-spacing: 0.5px;">Active Shipments</p>
</div>
</div>
<div class="col-md-3">
<div class="panel p-4 text-center h-100 shadow-sm border-0 rounded-4 position-relative overflow-hidden" style="background: linear-gradient(135deg, #ffffff, #f8f9fa);">
<div class="position-absolute opacity-10" style="inset-inline-end: 10px; top: 15px;"><i class="bi bi-people" style="font-size: 3.5rem;"></i></div>
<div class="text-success mb-2 position-relative"><i class="bi bi-people fs-2"></i></div>
<h3 class="h2 mb-0 fw-bold position-relative"><?= $stats['total_shippers'] ?></h3>
<p class="text-muted small text-uppercase mb-0 fw-bold position-relative" style="letter-spacing: 0.5px;">Total Shippers</p>
</div>
</div>
<div class="col-md-3">
<div class="panel p-4 text-center h-100 shadow-sm border-0 rounded-4 position-relative overflow-hidden" style="background: linear-gradient(135deg, #ffffff, #f8f9fa);">
<div class="position-absolute opacity-10" style="inset-inline-end: 10px; top: 15px;"><i class="bi bi-person-vcard" style="font-size: 3.5rem;"></i></div>
<div class="text-info mb-2 position-relative"><i class="bi bi-person-vcard fs-2"></i></div>
<h3 class="h2 mb-0 fw-bold position-relative"><?= $stats['total_truck_owners'] ?></h3>
<p class="text-muted small text-uppercase mb-0 fw-bold position-relative" style="letter-spacing: 0.5px;">Truck Owners</p>
</div>
</div>
</div>
<div class="row g-4">
<!-- Main Content: Shipments -->
<div class="col-lg-8">
<div class="panel p-4 shadow-sm border-0 h-100 rounded-4 d-flex flex-column">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2 class="h5 mb-0 fw-bold"><i class="bi bi-clock-history text-muted me-2"></i>Recent Shipments</h2>
<span class="badge bg-light text-dark border"><?= e(count($shipments)) ?> shown</span>
</div>
<?php if ($flash): ?>
<div class="alert alert-success" data-auto-dismiss="true"><?= e($flash['message']) ?></div>
<?php endif; ?>
<?php if ($errors): ?>
<div class="alert alert-warning"><?= e(implode(' ', $errors)) ?></div>
<?php endif; ?>
<?php if (!$shipments): ?>
<div class="text-center p-5 text-muted flex-grow-1 d-flex flex-column justify-content-center">
<i class="bi bi-inbox fs-1 mb-3 d-block opacity-50"></i>
<p class="mb-0"><?= e(t('no_shipments')) ?></p>
</div>
<?php else: ?>
<div class="table-responsive flex-grow-1">
<table class="table align-middle mb-0">
<thead>
<tr>
<th class="text-uppercase small text-muted border-top-0">Shipment</th>
<th class="text-uppercase small text-muted border-top-0">Route</th>
<th class="text-uppercase small text-muted border-top-0"><?= e(t('status')) ?></th>
<th class="text-uppercase small text-muted border-top-0 text-end">Action</th>
</tr>
</thead>
<tbody>
<?php foreach ($shipments as $row): ?>
<tr>
<td>
<div class="fw-bold"><?= e($row['shipper_company']) ?></div>
<small class="text-muted"><?= e($row['payment_method'] === 'bank_transfer' ? t('payment_bank') : t('payment_thawani')) ?></small>
</td>
<td>
<div class="d-flex align-items-center gap-2">
<span class="fw-medium"><?= e($row['origin_city']) ?></span>
<i class="bi bi-arrow-right text-muted small"></i>
<span class="fw-medium"><?= e($row['destination_city']) ?></span>
</div>
</td>
<td><span class="badge <?= e($row['status']) ?> rounded-pill px-3 py-2"><?= e(status_label($row['status'])) ?></span></td>
<td class="text-end">
<form class="d-flex gap-2 justify-content-end" method="post">
<input type="hidden" name="action" value="update_status">
<input type="hidden" name="shipment_id" value="<?= e($row['id']) ?>">
<select class="form-select form-select-sm" name="status" style="width: auto;">
<option value="posted" <?= $row['status'] === 'posted' ? 'selected' : '' ?>><?= e(t('status_posted')) ?></option>
<option value="offered" <?= $row['status'] === 'offered' ? 'selected' : '' ?>><?= e(t('status_offered')) ?></option>
<option value="confirmed" <?= $row['status'] === 'confirmed' ? 'selected' : '' ?>><?= e(t('status_confirmed')) ?></option>
<option value="in_transit" <?= $row['status'] === 'in_transit' ? 'selected' : '' ?>><?= e(t('status_in_transit')) ?></option>
<option value="delivered" <?= $row['status'] === 'delivered' ? 'selected' : '' ?>><?= e(t('status_delivered')) ?></option>
</select>
<button class="btn btn-sm btn-primary px-3 rounded-pill" type="submit"><i class="bi bi-check2"></i> Save</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</div>
<!-- Sidebar: Quick Links List -->
<div class="col-lg-4">
<div class="panel p-4 shadow-sm border-0 h-100 rounded-4" style="background-color: #fafbfc;">
<h2 class="h5 mb-4 fw-bold"><i class="bi bi-lightning-charge text-warning me-2"></i>Quick Links</h2>
<div class="list-group list-group-flush bg-transparent">
<a href="<?= e(url_with_lang('admin_countries.php')) ?>" class="list-group-item list-group-item-action bg-transparent border-bottom d-flex align-items-center py-3 px-0">
<div class="bg-white rounded p-3 shadow-sm me-3 text-primary d-flex align-items-center justify-content-center" style="width: 48px; height: 48px;"><i class="bi bi-globe2 fs-5"></i></div>
<div>
<h6 class="mb-1 fw-bold">Manage Countries</h6>
<small class="text-muted d-block line-height-sm">Add or remove supported countries</small>
</div>
<i class="bi bi-chevron-right ms-auto text-muted small"></i>
</a>
<a href="<?= e(url_with_lang('admin_cities.php')) ?>" class="list-group-item list-group-item-action bg-transparent border-bottom d-flex align-items-center py-3 px-0">
<div class="bg-white rounded p-3 shadow-sm me-3 text-primary d-flex align-items-center justify-content-center" style="width: 48px; height: 48px;"><i class="bi bi-pin-map fs-5"></i></div>
<div>
<h6 class="mb-1 fw-bold">Manage Cities</h6>
<small class="text-muted d-block line-height-sm">Configure cities for routing</small>
</div>
<i class="bi bi-chevron-right ms-auto text-muted small"></i>
</a>
<a href="<?= e(url_with_lang('register.php')) ?>" class="list-group-item list-group-item-action bg-transparent border-bottom d-flex align-items-center py-3 px-0">
<div class="bg-white rounded p-3 shadow-sm me-3 text-success d-flex align-items-center justify-content-center" style="width: 48px; height: 48px;"><i class="bi bi-person-plus fs-5"></i></div>
<div>
<h6 class="mb-1 fw-bold">Register User</h6>
<small class="text-muted d-block line-height-sm">Manually onboard a new user</small>
</div>
<i class="bi bi-chevron-right ms-auto text-muted small"></i>
</a>
<a href="<?= e(url_with_lang('admin_company_profile.php')) ?>" class="list-group-item list-group-item-action bg-transparent border-bottom d-flex align-items-center py-3 px-0">
<div class="bg-white rounded p-3 shadow-sm me-3 text-secondary d-flex align-items-center justify-content-center" style="width: 48px; height: 48px;"><i class="bi bi-buildings fs-5"></i></div>
<div>
<h6 class="mb-1 fw-bold">Company Profile</h6>
<small class="text-muted d-block line-height-sm">Update platform branding</small>
</div>
<i class="bi bi-chevron-right ms-auto text-muted small"></i>
</a>
<a href="<?= e(url_with_lang('admin_landing_pages.php')) ?>" class="list-group-item list-group-item-action bg-transparent d-flex align-items-center py-3 px-0">
<div class="bg-white rounded p-3 shadow-sm me-3 text-info d-flex align-items-center justify-content-center" style="width: 48px; height: 48px;"><i class="bi bi-layout-text-window-reverse fs-5"></i></div>
<div>
<h6 class="mb-1 fw-bold">Landing Pages</h6>
<small class="text-muted d-block line-height-sm">Edit homepage content and sections</small>
</div>
<i class="bi bi-chevron-right ms-auto text-muted small"></i>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<?php render_footer(); ?>
<?php render_footer(); ?>

159
admin_shipment_edit.php Normal file
View File

@ -0,0 +1,159 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/includes/layout.php';
$errors = [];
$flash = null;
$id = (int)($_GET['id'] ?? 0);
if (!$id) {
header('Location: admin_shipments.php'); exit;
}
$stmt = db()->prepare("SELECT * FROM shipments WHERE id = ?");
$stmt->execute([$id]);
$shipment = $stmt->fetch();
if (!$shipment) {
header('Location: admin_shipments.php'); exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$shipper_name = trim($_POST['shipper_name'] ?? '');
$shipper_company = trim($_POST['shipper_company'] ?? '');
$origin_city = trim($_POST['origin_city'] ?? '');
$destination_city = trim($_POST['destination_city'] ?? '');
$cargo_description = trim($_POST['cargo_description'] ?? '');
$weight_tons = (float)($_POST['weight_tons'] ?? 0);
$pickup_date = trim($_POST['pickup_date'] ?? '');
$delivery_date = trim($_POST['delivery_date'] ?? '');
$payment_method = trim($_POST['payment_method'] ?? 'thawani');
$status = trim($_POST['status'] ?? 'posted');
if ($shipper_name === '') $errors[] = "Shipper name is required.";
if ($origin_city === '') $errors[] = "Origin city is required.";
if ($destination_city === '') $errors[] = "Destination city is required.";
if (empty($errors)) {
$updateSql = "
UPDATE shipments
SET shipper_name = ?, shipper_company = ?, origin_city = ?, destination_city = ?,
cargo_description = ?, weight_tons = ?, pickup_date = ?, delivery_date = ?,
payment_method = ?, status = ?
WHERE id = ?
";
db()->prepare($updateSql)->execute([
$shipper_name, $shipper_company, $origin_city, $destination_city,
$cargo_description, $weight_tons, $pickup_date, $delivery_date,
$payment_method, $status, $id
]);
$flash = "Shipment updated successfully.";
// Refresh
$stmt->execute([$id]);
$shipment = $stmt->fetch();
}
}
render_header('Edit Shipment', 'admin');
?>
<div class="row g-4">
<div class="col-lg-3">
<?php render_admin_sidebar('shipments'); ?>
</div>
<div class="col-lg-9">
<div class="d-flex align-items-center gap-3 mb-4">
<a href="admin_shipments.php" class="btn btn-light border text-secondary" title="Back">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-left" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8z"/>
</svg>
</a>
<div>
<h1 class="section-title mb-1">Edit Shipment #<?= e((string)$shipment['id']) ?></h1>
<p class="muted mb-0">Update shipment details and status.</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-danger">
<ul class="mb-0">
<?php foreach ($errors as $error): ?>
<li><?= e($error) ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<div class="panel p-4">
<form method="post">
<div class="row g-3">
<div class="col-md-6">
<label class="form-label">Shipper Name</label>
<input type="text" name="shipper_name" class="form-control" value="<?= e($shipment['shipper_name']) ?>" required>
</div>
<div class="col-md-6">
<label class="form-label">Shipper Company</label>
<input type="text" name="shipper_company" class="form-control" value="<?= e((string)$shipment['shipper_company']) ?>">
</div>
<div class="col-md-6">
<label class="form-label">Origin City</label>
<input type="text" name="origin_city" class="form-control" value="<?= e($shipment['origin_city']) ?>" required>
</div>
<div class="col-md-6">
<label class="form-label">Destination City</label>
<input type="text" name="destination_city" class="form-control" value="<?= e($shipment['destination_city']) ?>" required>
</div>
<div class="col-md-12">
<label class="form-label">Cargo Description</label>
<input type="text" name="cargo_description" class="form-control" value="<?= e((string)$shipment['cargo_description']) ?>">
</div>
<div class="col-md-4">
<label class="form-label">Weight (Tons)</label>
<input type="number" step="0.1" name="weight_tons" class="form-control" value="<?= e((string)$shipment['weight_tons']) ?>">
</div>
<div class="col-md-4">
<label class="form-label">Pickup Date</label>
<input type="date" name="pickup_date" class="form-control" value="<?= e((string)$shipment['pickup_date']) ?>">
</div>
<div class="col-md-4">
<label class="form-label">Delivery Date</label>
<input type="date" name="delivery_date" class="form-control" value="<?= e((string)$shipment['delivery_date']) ?>">
</div>
<div class="col-md-6">
<label class="form-label">Payment Method</label>
<select name="payment_method" class="form-select">
<option value="thawani" <?= $shipment['payment_method'] === 'thawani' ? 'selected' : '' ?>>Thawani</option>
<option value="bank_transfer" <?= $shipment['payment_method'] === 'bank_transfer' ? 'selected' : '' ?>>Bank Transfer</option>
</select>
</div>
<div class="col-md-6">
<label class="form-label">Status</label>
<select name="status" class="form-select">
<option value="posted" <?= $shipment['status'] === 'posted' ? 'selected' : '' ?>>Posted</option>
<option value="offered" <?= $shipment['status'] === 'offered' ? 'selected' : '' ?>>Offered</option>
<option value="confirmed" <?= $shipment['status'] === 'confirmed' ? 'selected' : '' ?>>Confirmed</option>
<option value="in_transit" <?= $shipment['status'] === 'in_transit' ? 'selected' : '' ?>>In Transit</option>
<option value="delivered" <?= $shipment['status'] === 'delivered' ? 'selected' : '' ?>>Delivered</option>
</select>
</div>
</div>
<div class="mt-4 pt-3 border-top d-flex justify-content-end">
<button type="submit" class="btn btn-primary">Save Changes</button>
</div>
</form>
</div>
</div>
</div>
<?php render_footer(); ?>

218
admin_shipments.php Normal file
View File

@ -0,0 +1,218 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/includes/layout.php';
$errors = [];
$flash = null;
// Handle action (Delete)
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'], $_POST['shipment_id'])) {
$shipmentId = (int)$_POST['shipment_id'];
$action = $_POST['action'];
if ($action === 'delete') {
db()->prepare("DELETE FROM shipments WHERE id = ?")->execute([$shipmentId]);
$flash = 'Shipment deleted successfully.';
}
}
// Search, Filter, Sort and Pagination parameters
$q = trim($_GET['q'] ?? '');
$status = trim($_GET['status'] ?? '');
$sort = trim($_GET['sort'] ?? 'newest');
$page = max(1, (int)($_GET['page'] ?? 1));
$limit = 10;
$offset = ($page - 1) * $limit;
$whereClause = "1=1";
$params = [];
if ($q !== '') {
$whereClause .= " AND (shipper_name LIKE ? OR shipper_company LIKE ? OR origin_city LIKE ? OR destination_city LIKE ? OR cargo_description LIKE ?)";
$likeQ = "%$q%";
$params = array_merge($params, array_fill(0, 5, $likeQ));
}
if ($status !== '' && in_array($status, ['posted', 'offered', 'confirmed', 'in_transit', 'delivered'])) {
$whereClause .= " AND status = ?";
$params[] = $status;
}
// Sorting logic
$orderBy = match ($sort) {
'oldest' => 'created_at ASC',
'pickup_asc' => 'pickup_date ASC',
'pickup_desc' => 'pickup_date DESC',
default => 'created_at DESC', // newest
};
// Total count
$countSql = "SELECT COUNT(*) FROM shipments WHERE $whereClause";
$stmt = db()->prepare($countSql);
$stmt->execute($params);
$total = (int)$stmt->fetchColumn();
$totalPages = (int)ceil($total / $limit);
// Fetch shipments
$sql = "
SELECT *
FROM shipments
WHERE $whereClause
ORDER BY $orderBy
LIMIT $limit OFFSET $offset
";
$stmt = db()->prepare($sql);
$stmt->execute($params);
$shipments = $stmt->fetchAll();
render_header('Manage Shipments', 'admin');
?>
<div class="row g-4">
<div class="col-lg-3">
<?php render_admin_sidebar('shipments'); ?>
</div>
<div class="col-lg-9">
<div class="page-intro d-flex flex-column flex-md-row justify-content-between align-items-md-center mb-4">
<div>
<h1 class="section-title mb-1">Shipments</h1>
<p class="muted mb-0">Manage all shipments across the platform.</p>
</div>
</div>
<?php if ($flash): ?>
<div class="alert alert-success" data-auto-dismiss="true"><?= e($flash) ?></div>
<?php endif; ?>
<div class="panel p-4 mb-4">
<form method="get" class="row g-3">
<div class="col-md-5">
<label class="form-label small text-muted">Search</label>
<input type="text" name="q" class="form-control" placeholder="Search shipments..." value="<?= e($q) ?>">
</div>
<div class="col-md-3">
<label class="form-label small text-muted">Status</label>
<select name="status" class="form-select">
<option value="">All Statuses</option>
<option value="posted" <?= $status === 'posted' ? 'selected' : '' ?>>Posted</option>
<option value="offered" <?= $status === 'offered' ? 'selected' : '' ?>>Offered</option>
<option value="confirmed" <?= $status === 'confirmed' ? 'selected' : '' ?>>Confirmed</option>
<option value="in_transit" <?= $status === 'in_transit' ? 'selected' : '' ?>>In Transit</option>
<option value="delivered" <?= $status === 'delivered' ? 'selected' : '' ?>>Delivered</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label small text-muted">Sort By</label>
<select name="sort" class="form-select">
<option value="newest" <?= $sort === 'newest' ? 'selected' : '' ?>>Newest First</option>
<option value="oldest" <?= $sort === 'oldest' ? 'selected' : '' ?>>Oldest First</option>
<option value="pickup_asc" <?= $sort === 'pickup_asc' ? 'selected' : '' ?>>Pickup Date (Soonest)</option>
<option value="pickup_desc" <?= $sort === 'pickup_desc' ? 'selected' : '' ?>>Pickup Date (Latest)</option>
</select>
</div>
<div class="col-md-1 d-flex align-items-end">
<button type="submit" class="btn btn-primary w-100">Filter</button>
</div>
</form>
</div>
<div class="panel p-0">
<?php if (!$shipments && ($q || $status)): ?>
<div class="p-4"><p class="muted mb-0">No shipments found matching your criteria.</p></div>
<?php elseif (!$shipments): ?>
<div class="p-4"><p class="muted mb-0">No shipments found on the platform yet.</p></div>
<?php else: ?>
<div class="table-responsive">
<table class="table mb-0 align-middle table-hover">
<thead class="table-light">
<tr>
<th class="ps-4">ID</th>
<th>Shipper</th>
<th>Route</th>
<th>Dates</th>
<th>Status</th>
<th class="text-end pe-4">Action</th>
</tr>
</thead>
<tbody>
<?php foreach ($shipments as $shipment): ?>
<tr>
<td class="ps-4"><?= e((string)$shipment['id']) ?></td>
<td>
<div class="fw-bold text-dark"><?= e($shipment['shipper_name']) ?></div>
<div class="text-muted small"><?= e((string)$shipment['shipper_company']) ?></div>
</td>
<td>
<div><span class="text-muted small">From:</span> <?= e($shipment['origin_city']) ?></div>
<div><span class="text-muted small">To:</span> <?= e($shipment['destination_city']) ?></div>
</td>
<td>
<div class="small"><span class="text-muted">Pick:</span> <?= e($shipment['pickup_date']) ?></div>
<div class="small"><span class="text-muted">Drop:</span> <?= e($shipment['delivery_date']) ?></div>
</td>
<td>
<?php
$statusClass = 'bg-secondary-subtle text-secondary';
if ($shipment['status'] === 'posted') $statusClass = 'bg-primary-subtle text-primary';
elseif ($shipment['status'] === 'offered') $statusClass = 'bg-info-subtle text-info';
elseif ($shipment['status'] === 'confirmed') $statusClass = 'bg-success-subtle text-success';
elseif ($shipment['status'] === 'in_transit') $statusClass = 'bg-warning-subtle text-warning';
elseif ($shipment['status'] === 'delivered') $statusClass = 'bg-dark-subtle text-dark';
?>
<span class="badge <?= $statusClass ?>"><?= e(ucfirst(str_replace('_', ' ', $shipment['status']))) ?></span>
</td>
<td class="text-end pe-4">
<div class="d-inline-flex gap-1 align-items-center">
<a href="shipment_detail.php?id=<?= e((string)$shipment['id']) ?>" class="btn btn-sm btn-light border text-primary" title="View Shipment">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16">
<path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/>
<path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
</svg>
</a>
<a href="admin_shipment_edit.php?id=<?= e((string)$shipment['id']) ?>" class="btn btn-sm btn-light border text-primary" title="Edit Shipment">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" class="bi bi-pencil" viewBox="0 0 16 16">
<path d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325"/>
</svg>
</a>
<form method="post" class="d-inline m-0 p-0">
<input type="hidden" name="shipment_id" value="<?= e((string)$shipment['id']) ?>">
<button type="submit" name="action" value="delete" class="btn btn-sm btn-danger" onclick="return confirm('Delete this shipment?');" title="Delete">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" class="bi bi-trash" viewBox="0 0 16 16">
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0z"/>
<path d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4zM2.5 3h11V2h-11z"/>
</svg>
</button>
</form>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php if ($totalPages > 1): ?>
<div class="px-4 py-3 border-top d-flex justify-content-between align-items-center">
<span class="text-muted small">Showing <?= count($shipments) ?> of <?= $total ?> shipments</span>
<ul class="pagination pagination-sm mb-0">
<li class="page-item <?= $page <= 1 ? 'disabled' : '' ?>">
<a class="page-link" href="?q=<?= urlencode($q) ?>&status=<?= urlencode($status) ?>&sort=<?= urlencode($sort) ?>&page=<?= $page - 1 ?>">Previous</a>
</li>
<?php for ($i = 1; $i <= $totalPages; $i++): ?>
<li class="page-item <?= $i === $page ? 'active' : '' ?>">
<a class="page-link" href="?q=<?= urlencode($q) ?>&status=<?= urlencode($status) ?>&sort=<?= urlencode($sort) ?>&page=<?= $i ?>"><?= $i ?></a>
</li>
<?php endfor; ?>
<li class="page-item <?= $page >= $totalPages ? 'disabled' : '' ?>">
<a class="page-link" href="?q=<?= urlencode($q) ?>&status=<?= urlencode($status) ?>&sort=<?= urlencode($sort) ?>&page=<?= $page + 1 ?>">Next</a>
</li>
</ul>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
</div>
<?php render_footer(); ?>

View File

@ -40,6 +40,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$addressLine = trim($_POST['address_line'] ?? '');
$companyName = trim($_POST['company_name'] ?? '');
$status = trim($_POST['status'] ?? '');
$password = $_POST['password'] ?? '';
if ($fullName === '') $errors[] = 'Full name is required.';
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) $errors[] = 'Valid email is required.';
@ -64,6 +65,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$stmtUser = db()->prepare("UPDATE users SET full_name = ?, email = ?, status = ? WHERE id = ? AND role = 'shipper'");
$stmtUser->execute([$fullName, $email, $status, $userId]);
if ($password !== '') {
$stmtPass = db()->prepare("UPDATE users SET password = ? WHERE id = ? AND role = 'shipper'");
$stmtPass->execute([password_hash($password, PASSWORD_DEFAULT), $userId]);
}
$stmtProfile = db()->prepare("
UPDATE shipper_profiles
SET company_name = ?, phone = ?, address_line = ?, country_id = ?, city_id = ?
@ -157,11 +163,17 @@ render_header('Edit Shipper', 'admin');
</select>
</div>
<div class="col-md-8">
<div class="col-md-6">
<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)$shipper['address_line']) ?>" required>
</div>
<div class="col-md-4">
<div class="col-md-3">
<label class="form-label" for="password">Password <small class="text-muted">(leave blank to keep)</small></label>
<input type="password" name="password" id="password" class="form-control" autocomplete="new-password">
</div>
<div class="col-md-3">
<label class="form-label" for="status">Account Status</label>
<select name="status" id="status" class="form-select" required>
<option value="pending" <?= $shipper['status'] === 'pending' ? 'selected' : '' ?>>Pending</option>

View File

@ -26,6 +26,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'], $_POST['use
// Search and Pagination parameters
$q = trim($_GET['q'] ?? '');
$status = trim($_GET['status'] ?? '');
$page = max(1, (int)($_GET['page'] ?? 1));
$limit = 10;
$offset = ($page - 1) * $limit;
@ -36,7 +37,12 @@ $params = [];
if ($q !== '') {
$whereClause .= " AND (u.full_name LIKE ? OR u.email LIKE ? OR p.company_name LIKE ?)";
$likeQ = "%$q%";
$params = [$likeQ, $likeQ, $likeQ];
$params = array_merge($params, [$likeQ, $likeQ, $likeQ]);
}
if ($status !== '' && in_array($status, ['active', 'pending', 'rejected'])) {
$whereClause .= " AND u.status = ?";
$params[] = $status;
}
// Total count
@ -82,23 +88,36 @@ render_header('Manage Shippers', 'admin');
<h1 class="section-title mb-1">Shippers</h1>
<p class="muted mb-0">Manage registered shippers.</p>
</div>
<form method="get" class="d-flex mt-3 mt-md-0 gap-2">
<input type="text" name="q" class="form-control form-control-sm" placeholder="Search name, email, company..." value="<?= e($q) ?>">
<button type="submit" class="btn btn-sm btn-primary">Search</button>
<?php if ($q): ?>
<a href="?q=" class="btn btn-sm btn-outline-secondary">Clear</a>
<?php endif; ?>
</form>
</div>
<?php if ($flash): ?>
<div class="alert alert-success" data-auto-dismiss="true"><?= e($flash) ?></div>
<?php endif; ?>
<div class="panel p-4 mb-4">
<form method="get" class="row g-3">
<div class="col-md-8">
<label class="form-label small text-muted">Search</label>
<input type="text" name="q" class="form-control" placeholder="Search name, email, company..." value="<?= e($q) ?>">
</div>
<div class="col-md-3">
<label class="form-label small text-muted">Status</label>
<select name="status" class="form-select">
<option value="">All Statuses</option>
<option value="active" <?= $status === 'active' ? 'selected' : '' ?>>Active</option>
<option value="pending" <?= $status === 'pending' ? 'selected' : '' ?>>Pending</option>
<option value="rejected" <?= $status === 'rejected' ? 'selected' : '' ?>>Rejected</option>
</select>
</div>
<div class="col-md-1 d-flex align-items-end">
<button type="submit" class="btn btn-primary w-100">Filter</button>
</div>
</form>
</div>
<div class="panel p-0">
<?php if (!$shippers && $q): ?>
<div class="p-4"><p class="muted mb-0">No shippers found matching your search.</p></div>
<?php if (!$shippers && ($q || $status)): ?>
<div class="p-4"><p class="muted mb-0">No shippers found matching your criteria.</p></div>
<?php elseif (!$shippers): ?>
<div class="p-4"><p class="muted mb-0">No shippers registered yet.</p></div>
<?php else: ?>
@ -181,15 +200,15 @@ render_header('Manage Shippers', 'admin');
<span class="text-muted small">Showing <?= count($shippers) ?> of <?= $total ?> shippers</span>
<ul class="pagination pagination-sm mb-0">
<li class="page-item <?= $page <= 1 ? 'disabled' : '' ?>">
<a class="page-link" href="?q=<?= urlencode($q) ?>&page=<?= $page - 1 ?>">Previous</a>
<a class="page-link" href="?q=<?= urlencode($q) ?>&status=<?= urlencode($status) ?>&page=<?= $page - 1 ?>">Previous</a>
</li>
<?php for ($i = 1; $i <= $totalPages; $i++): ?>
<li class="page-item <?= $i === $page ? 'active' : '' ?>">
<a class="page-link" href="?q=<?= urlencode($q) ?>&page=<?= $i ?>"><?= $i ?></a>
<a class="page-link" href="?q=<?= urlencode($q) ?>&status=<?= urlencode($status) ?>&page=<?= $i ?>"><?= $i ?></a>
</li>
<?php endfor; ?>
<li class="page-item <?= $page >= $totalPages ? 'disabled' : '' ?>">
<a class="page-link" href="?q=<?= urlencode($q) ?>&page=<?= $page + 1 ?>">Next</a>
<a class="page-link" href="?q=<?= urlencode($q) ?>&status=<?= urlencode($status) ?>&page=<?= $page + 1 ?>">Next</a>
</li>
</ul>
</div>

View File

@ -45,6 +45,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$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'] ?? '');
@ -76,6 +78,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$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
@ -154,11 +161,15 @@ render_header('Edit Truck Owner', 'admin');
<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-6">
<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-6">
<div class="col-md-4">
<label class="form-label" for="password">Password <small class="text-muted">(leave blank to keep)</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>

View File

@ -25,6 +25,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'], $_POST['use
// Search and Pagination parameters
$q = trim($_GET['q'] ?? '');
$status = trim($_GET['status'] ?? '');
$page = max(1, (int)($_GET['page'] ?? 1));
$limit = 10;
$offset = ($page - 1) * $limit;
@ -35,7 +36,12 @@ $params = [];
if ($q !== '') {
$whereClause .= " AND (u.full_name LIKE ? OR u.email LIKE ? OR p.plate_no LIKE ? OR p.truck_type LIKE ?)";
$likeQ = "%$q%";
$params = [$likeQ, $likeQ, $likeQ, $likeQ];
$params = array_merge($params, [$likeQ, $likeQ, $likeQ, $likeQ]);
}
if ($status !== '' && in_array($status, ['active', 'pending', 'rejected'])) {
$whereClause .= " AND u.status = ?";
$params[] = $status;
}
// Total count
@ -82,23 +88,36 @@ render_header('Manage Truck Owners', 'admin');
<h1 class="section-title mb-1">Truck Owners</h1>
<p class="muted mb-0">Review registrations and approve truck owners.</p>
</div>
<form method="get" class="d-flex mt-3 mt-md-0 gap-2">
<input type="text" name="q" class="form-control form-control-sm" placeholder="Search name, email, plate..." value="<?= e($q) ?>">
<button type="submit" class="btn btn-sm btn-primary">Search</button>
<?php if ($q): ?>
<a href="?q=" class="btn btn-sm btn-outline-secondary">Clear</a>
<?php endif; ?>
</form>
</div>
<?php if ($flash): ?>
<div class="alert alert-success" data-auto-dismiss="true"><?= e($flash) ?></div>
<?php endif; ?>
<div class="panel p-4 mb-4">
<form method="get" class="row g-3">
<div class="col-md-8">
<label class="form-label small text-muted">Search</label>
<input type="text" name="q" class="form-control" placeholder="Search name, email, plate..." value="<?= e($q) ?>">
</div>
<div class="col-md-3">
<label class="form-label small text-muted">Status</label>
<select name="status" class="form-select">
<option value="">All Statuses</option>
<option value="active" <?= $status === 'active' ? 'selected' : '' ?>>Active</option>
<option value="pending" <?= $status === 'pending' ? 'selected' : '' ?>>Pending</option>
<option value="rejected" <?= $status === 'rejected' ? 'selected' : '' ?>>Rejected</option>
</select>
</div>
<div class="col-md-1 d-flex align-items-end">
<button type="submit" class="btn btn-primary w-100">Filter</button>
</div>
</form>
</div>
<div class="panel p-0">
<?php if (!$owners && $q): ?>
<div class="p-4"><p class="muted mb-0">No truck owners found matching your search.</p></div>
<?php if (!$owners && ($q || $status)): ?>
<div class="p-4"><p class="muted mb-0">No truck owners found matching your criteria.</p></div>
<?php elseif (!$owners): ?>
<div class="p-4"><p class="muted mb-0">No truck owners registered yet.</p></div>
<?php else: ?>
@ -189,15 +208,15 @@ render_header('Manage Truck Owners', 'admin');
<span class="text-muted small">Showing <?= count($owners) ?> of <?= $total ?> truck owners</span>
<ul class="pagination pagination-sm mb-0">
<li class="page-item <?= $page <= 1 ? 'disabled' : '' ?>">
<a class="page-link" href="?q=<?= urlencode($q) ?>&page=<?= $page - 1 ?>">Previous</a>
<a class="page-link" href="?q=<?= urlencode($q) ?>&status=<?= urlencode($status) ?>&page=<?= $page - 1 ?>">Previous</a>
</li>
<?php for ($i = 1; $i <= $totalPages; $i++): ?>
<li class="page-item <?= $i === $page ? 'active' : '' ?>">
<a class="page-link" href="?q=<?= urlencode($q) ?>&page=<?= $i ?>"><?= $i ?></a>
<a class="page-link" href="?q=<?= urlencode($q) ?>&status=<?= urlencode($status) ?>&page=<?= $i ?>"><?= $i ?></a>
</li>
<?php endfor; ?>
<li class="page-item <?= $page >= $totalPages ? 'disabled' : '' ?>">
<a class="page-link" href="?q=<?= urlencode($q) ?>&page=<?= $page + 1 ?>">Next</a>
<a class="page-link" href="?q=<?= urlencode($q) ?>&status=<?= urlencode($status) ?>&page=<?= $page + 1 ?>">Next</a>
</li>
</ul>
</div>
@ -258,4 +277,4 @@ $pic = $owner['truck_pic_path'];
</div>
<?php endforeach; ?>
<?php render_footer(); ?>
<?php render_footer(); ?>

View File

@ -31,7 +31,11 @@ function render_header(string $title, string $active = ''): void
<meta property="og:image" content="<?= e($projectImageUrl) ?>" />
<meta property="twitter:image" content="<?= e($projectImageUrl) ?>" />
<?php endif; ?>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<?php if ($dir === "rtl"): ?>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.min.css">
<?php else: ?>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<?php endif; ?>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="/assets/css/custom.css?v=<?= time() ?>">
</head>
@ -168,23 +172,23 @@ function render_footer(): void
<?php endif; ?>
</div>
<div class="col-md-2 offset-md-2">
<h6 class="fw-bold mb-3">Company</h6>
<h6 class="fw-bold mb-3"><?= e(t('company')) ?></h6>
<ul class="list-unstyled text-muted small">
<li class="mb-2"><a href="#" class="text-decoration-none text-muted">About Us</a></li>
<li class="mb-2"><a href="#" class="text-decoration-none text-muted">Careers</a></li>
<li class="mb-2"><a href="#" class="text-decoration-none text-muted">Contact</a></li>
<li class="mb-2"><a href="#" class="text-decoration-none text-muted"><?= e(t('about_us')) ?></a></li>
<li class="mb-2"><a href="#" class="text-decoration-none text-muted"><?= e(t('careers')) ?></a></li>
<li class="mb-2"><a href="#" class="text-decoration-none text-muted"><?= e(t('contact')) ?></a></li>
</ul>
</div>
<div class="col-md-2">
<h6 class="fw-bold mb-3">Resources</h6>
<h6 class="fw-bold mb-3"><?= e(t('resources')) ?></h6>
<ul class="list-unstyled text-muted small">
<li class="mb-2"><a href="<?= e(url_with_lang('faq.php')) ?>" class="text-decoration-none text-muted">Help Center / FAQ</a></li>
<li class="mb-2"><a href="<?= e(url_with_lang('terms.php')) ?>" class="text-decoration-none text-muted"><?= $lang === 'ar' ? 'شروط الخدمة' : 'Terms of Service' ?></a></li>
<li class="mb-2"><a href="<?= e(url_with_lang('privacy.php')) ?>" class="text-decoration-none text-muted"><?= $lang === 'ar' ? 'سياسة الخصوصية' : 'Privacy Policy' ?></a></li>
<li class="mb-2"><a href="<?= e(url_with_lang('faq.php')) ?>" class="text-decoration-none text-muted"><?= e(t('help_center')) ?></a></li>
<li class="mb-2"><a href="<?= e(url_with_lang('terms.php')) ?>" class="text-decoration-none text-muted"><?= e(t('terms_of_service')) ?></a></li>
<li class="mb-2"><a href="<?= e(url_with_lang('privacy.php')) ?>" class="text-decoration-none text-muted"><?= e(t('privacy_policy')) ?></a></li>
</ul>
</div>
<div class="col-md-2">
<h6 class="fw-bold mb-3">Language</h6>
<h6 class="fw-bold mb-3"><?= e(t('language')) ?></h6>
<div class="d-flex gap-2">
<a href="<?= e(current_url_with_lang('en')) ?>" class="text-decoration-none <?= $lang === 'en' ? 'text-primary fw-bold' : 'text-muted' ?>">EN</a>
<span class="text-muted">|</span>
@ -194,7 +198,7 @@ function render_footer(): void
</div>
<div class="d-flex flex-column flex-md-row justify-content-between align-items-center pt-4 border-top small text-muted">
<span>&copy; <?= date('Y') ?> <?= e($appName) ?>. All rights reserved.</span>
<span>&copy; <?= date('Y') ?> <?= e($appName) ?>. <?= e(t('all_rights_reserved')) ?></span>
<span class="mt-2 mt-md-0"><?= e(t('footer_note')) ?></span>
</div>
</div>
@ -214,71 +218,74 @@ function render_admin_sidebar(string $active = 'dashboard'): void
$pagesActive = in_array($active, ['faqs', 'landing_pages']);
?>
<aside class="admin-sidebar panel p-4 shadow-sm border-0">
<h2 class="h5 fw-bold mb-4"><i class="bi bi-shield-lock me-2 text-primary"></i>Admin Panel</h2>
<h2 class="h5 fw-bold mb-4"><i class="bi bi-shield-lock me-2 text-primary"></i><?= e(t('nav_admin')) ?></h2>
<nav class="nav flex-column gap-2">
<a class="admin-nav-link <?= $active === 'shipments' ? 'active' : '' ?>" href="<?= e(url_with_lang('admin_shipments.php')) ?>">
<i class="bi bi-box2-fill me-2"></i><?= e(t('shipments') ?: 'Shipments') ?>
</a>
<a class="admin-nav-link <?= $active === 'dashboard' ? 'active' : '' ?>" href="<?= e(url_with_lang('admin_dashboard.php')) ?>">
<i class="bi bi-speedometer2 me-2"></i>Dashboard
<i class="bi bi-speedometer2 me-2"></i><?= e(t('dashboard')) ?>
</a>
<a class="nav-link fw-bold text-muted text-uppercase mt-2 d-flex justify-content-between align-items-center p-2 rounded" style="cursor: pointer; font-size: 0.85rem;" data-bs-toggle="collapse" data-bs-target="#collapseSettings" aria-expanded="<?= $settingsActive ? 'true' : 'false' ?>">
<span><i class="bi bi-gear-fill me-2"></i>Settings</span>
<span><i class="bi bi-gear-fill me-2"></i><?= e(t('settings')) ?></span>
<i class="bi bi-chevron-down small"></i>
</a>
<div class="collapse <?= $settingsActive ? 'show' : '' ?>" id="collapseSettings">
<div class="nav flex-column gap-1 ms-3 border-start ps-2 border-2">
<a class="admin-nav-link <?= $active === 'company_profile' ? 'active' : '' ?>" href="<?= e(url_with_lang('admin_company_profile.php')) ?>">
<i class="bi bi-building me-2"></i>Company Setting
<i class="bi bi-building me-2"></i><?= e(t('company_setting')) ?>
</a>
<a class="admin-nav-link <?= $active === 'integrations' ? 'active' : '' ?>" href="<?= e(url_with_lang('admin_integrations.php')) ?>">
<i class="bi bi-plug me-2"></i>Integrations
<i class="bi bi-plug me-2"></i><?= e(t('integrations')) ?>
</a>
</div>
</div>
<a class="nav-link fw-bold text-muted text-uppercase mt-2 d-flex justify-content-between align-items-center p-2 rounded" style="cursor: pointer; font-size: 0.85rem;" data-bs-toggle="collapse" data-bs-target="#collapseLocations" aria-expanded="<?= $locationsActive ? 'true' : 'false' ?>">
<span><i class="bi bi-geo-alt-fill me-2"></i>Locations</span>
<span><i class="bi bi-geo-alt-fill me-2"></i><?= e(t('locations')) ?></span>
<i class="bi bi-chevron-down small"></i>
</a>
<div class="collapse <?= $locationsActive ? 'show' : '' ?>" id="collapseLocations">
<div class="nav flex-column gap-1 ms-3 border-start ps-2 border-2">
<a class="admin-nav-link <?= $active === 'countries' ? 'active' : '' ?>" href="<?= e(url_with_lang('admin_countries.php')) ?>">
<i class="bi bi-globe-americas me-2"></i>Countries
<i class="bi bi-globe-americas me-2"></i><?= e(t('countries')) ?>
</a>
<a class="admin-nav-link <?= $active === 'cities' ? 'active' : '' ?>" href="<?= e(url_with_lang('admin_cities.php')) ?>">
<i class="bi bi-pin-map me-2"></i>Cities
<i class="bi bi-pin-map me-2"></i><?= e(t('cities')) ?>
</a>
</div>
</div>
<a class="nav-link fw-bold text-muted text-uppercase mt-2 d-flex justify-content-between align-items-center p-2 rounded" style="cursor: pointer; font-size: 0.85rem;" data-bs-toggle="collapse" data-bs-target="#collapseUsers" aria-expanded="<?= $usersActive ? 'true' : 'false' ?>">
<span><i class="bi bi-people-fill me-2"></i>Users</span>
<span><i class="bi bi-people-fill me-2"></i><?= e(t('users')) ?></span>
<i class="bi bi-chevron-down small"></i>
</a>
<div class="collapse <?= $usersActive ? 'show' : '' ?>" id="collapseUsers">
<div class="nav flex-column gap-1 ms-3 border-start ps-2 border-2">
<a class="admin-nav-link <?= $active === 'shippers' ? 'active' : '' ?>" href="<?= e(url_with_lang('admin_shippers.php')) ?>">
<i class="bi bi-box-seam me-2"></i>Shippers
<i class="bi bi-box-seam me-2"></i><?= e(t('shippers')) ?>
</a>
<a class="admin-nav-link <?= $active === 'truck_owners' ? 'active' : '' ?>" href="<?= e(url_with_lang('admin_truck_owners.php')) ?>">
<i class="bi bi-truck me-2"></i>Truck Owners
<i class="bi bi-truck me-2"></i><?= e(t('truck_owners')) ?>
</a>
<a class="admin-nav-link <?= $active === 'register' ? 'active' : '' ?>" href="<?= e(url_with_lang('register.php')) ?>">
<i class="bi bi-person-plus me-2"></i>User Registration
<i class="bi bi-person-plus me-2"></i><?= e(t('user_registration')) ?>
</a>
</div>
</div>
<a class="nav-link fw-bold text-muted text-uppercase mt-2 d-flex justify-content-between align-items-center p-2 rounded" style="cursor: pointer; font-size: 0.85rem;" data-bs-toggle="collapse" data-bs-target="#collapsePages" aria-expanded="<?= $pagesActive ? 'true' : 'false' ?>">
<span><i class="bi bi-file-earmark-text-fill me-2"></i>Pages</span>
<span><i class="bi bi-file-earmark-text-fill me-2"></i><?= e(t('pages')) ?></span>
<i class="bi bi-chevron-down small"></i>
</a>
<div class="collapse <?= $pagesActive ? 'show' : '' ?>" id="collapsePages">
<div class="nav flex-column gap-1 ms-3 border-start ps-2 border-2">
<a class="admin-nav-link <?= $active === 'faqs' ? 'active' : '' ?>" href="<?= e(url_with_lang('admin_faqs.php')) ?>">
<i class="bi bi-question-square me-2"></i>FAQs
<i class="bi bi-question-square me-2"></i><?= e(t('faqs')) ?>
</a>
<a class="admin-nav-link <?= $active === 'landing_pages' ? 'active' : '' ?>" href="<?= e(url_with_lang('admin_landing_pages.php')) ?>">
<i class="bi bi-layout-text-window me-2"></i>Landing Pages
<i class="bi bi-layout-text-window me-2"></i><?= e(t('landing_pages')) ?>
</a>
</div>
</div>

View File

@ -167,7 +167,7 @@ try {
</div>
<div class="ms-3">
<h5 class="fw-bold mb-1"><?= e(t('step_confirm')) ?></h5>
<p class="text-muted mb-0">Secure booking and track the delivery until completion.</p>
<p class="text-muted mb-0"><?= e(t('step_confirm_desc')) ?></p>
</div>
</div>
</div>
@ -219,17 +219,17 @@ try {
<?php elseif ($sec['section_type'] === 'faq'): ?>
<section class="mb-5 text-center">
<div class="panel p-5 border-0 shadow-sm rounded-4" style="background-color: #f8f9fa;">
<h2 class="display-6 fw-bold mb-3"><?= e($sec['title'] ?: 'Have Questions?') ?></h2>
<p class="text-muted fs-5 mb-4 mx-auto" style="max-width: 600px;"><?= e($sec['subtitle'] ?: 'Check out our Frequently Asked Questions to learn more about how our platform works.') ?></p>
<h2 class="display-6 fw-bold mb-3"><?= e($sec['title'] ?: t('faq_title')) ?></h2>
<p class="text-muted fs-5 mb-4 mx-auto" style="max-width: 600px;"><?= e($sec['subtitle'] ?: t('faq_subtitle')) ?></p>
<a href="<?= e(url_with_lang('faq.php')) ?>" class="btn btn-primary btn-lg px-4 rounded-pill shadow-sm">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-question-circle me-2" viewBox="0 0 16 16"><path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"/><path d="M5.255 5.786a.237.237 0 0 0 .241.247h.825c.138 0 .248-.113.266-.25.09-.656.54-1.134 1.342-1.134.686 0 1.314.343 1.314 1.168 0 .635-.374.927-.965 1.371-.673.489-1.206 1.06-1.168 1.987l.003.217a.25.25 0 0 0 .25.246h.811a.25.25 0 0 0 .25-.25v-.105c0-.718.273-.927 1.01-1.486.609-.463 1.244-.977 1.244-2.056 0-1.511-1.276-2.241-2.673-2.241-1.267 0-2.655.59-2.75 2.286zm1.557 5.763c0 .533.425.927 1.01.927.609 0 1.028-.394 1.028-.927 0-.552-.42-.94-1.029-.94-.584 0-1.009.388-1.009.94z"/></svg>View FAQ
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-question-circle me-2" viewBox="0 0 16 16"><path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"/><path d="M5.255 5.786a.237.237 0 0 0 .241.247h.825c.138 0 .248-.113.266-.25.09-.656.54-1.134 1.342-1.134.686 0 1.314.343 1.314 1.168 0 .635-.374.927-.965 1.371-.673.489-1.206 1.06-1.168 1.987l.003.217a.25.25 0 0 0 .25.246h.811a.25.25 0 0 0 .25-.25v-.105c0-.718.273-.927 1.01-1.486.609-.463 1.244-.977 1.244-2.056 0-1.511-1.276-2.241-2.673-2.241-1.267 0-2.655.59-2.75 2.286zm1.557 5.763c0 .533.425.927 1.01.927.609 0 1.028-.394 1.028-.927 0-.552-.42-.94-1.029-.94-.584 0-1.009.388-1.009.94z"/></svg><?= e(t('view_faq')) ?>
</a>
</div>
</section>
<?php elseif ($sec['section_type'] === 'motivation'): ?>
<div class="motivation-box mb-5" <?php if ($sec['image_path']) { echo 'style="background-image: url(' . e($sec['image_path']) . '); background-size: cover; background-position: center;"'; } ?>>
<h3 class="display-6 fw-bold mb-3"><?= e($sec['title'] ?: 'Ready to transform your logistics?') ?></h3>
<p class="fs-5 text-white-50 mb-4 mx-auto" style="max-width: 600px;"><?= e($sec['subtitle'] ?: 'Join our platform today to find reliable trucks or secure the best shipments in the market.') ?></p>
<h3 class="display-6 fw-bold mb-3"><?= e($sec['title'] ?: t('motivation_title')) ?></h3>
<p class="fs-5 text-white-50 mb-4 mx-auto" style="max-width: 600px;"><?= e($sec['subtitle'] ?: t('motivation_subtitle')) ?></p>
<div class="d-flex flex-wrap justify-content-center gap-3">
<a class="btn btn-light btn-lg px-4 text-primary fw-bold" href="<?= e(url_with_lang('register.php', ['role' => 'shipper'])) ?>"><?= e(t('register_shipper')) ?></a>
<a class="btn btn-outline-light btn-lg px-4 fw-bold" href="<?= e(url_with_lang('register.php', ['role' => 'truck_owner'])) ?>"><?= e(t('register_owner')) ?></a>

View File

@ -97,30 +97,30 @@ render_header('Login / Reset Password', 'login');
<div class="card shadow-sm border-0 rounded-4 mb-4" id="loginFormCard">
<div class="card-body p-4 p-md-5">
<div class="text-center mb-4">
<h2 class="fw-bold text-dark">Welcome Back</h2>
<p class="text-muted">Sign in to your account to continue</p>
<h2 class="fw-bold text-dark"><?= e(t('login_title')) ?></h2>
<p class="text-muted"><?= e(t('login_subtitle')) ?></p>
</div>
<form method="post" action="">
<input type="hidden" name="action" value="login">
<div class="mb-3">
<label for="email" class="form-label fw-bold">Email address</label>
<input type="email" class="form-control form-control-lg bg-light border-0 rounded-3" id="email" name="email" required autofocus placeholder="name@example.com">
<label for="email" class="form-label fw-bold"><?= e(t('email_address')) ?></label>
<input type="email" class="form-control form-control-lg bg-light border-0 rounded-3" id="email" name="email" required autofocus placeholder="<?= e(t('email_placeholder')) ?>">
</div>
<div class="mb-4">
<div class="d-flex justify-content-between align-items-center mb-1">
<label for="password" class="form-label fw-bold mb-0">Password</label>
<a href="#" class="small text-decoration-none text-primary" onclick="document.getElementById('loginFormCard').classList.add('d-none'); document.getElementById('resetFormCard').classList.remove('d-none'); return false;">Forgot password?</a>
<label for="password" class="form-label fw-bold mb-0"><?= e(t('password')) ?></label>
<a href="#" class="small text-decoration-none text-primary" onclick="document.getElementById('loginFormCard').classList.add('d-none'); document.getElementById('resetFormCard').classList.remove('d-none'); return false;"><?= e(t('forgot_password')) ?></a>
</div>
<input type="password" class="form-control form-control-lg bg-light border-0 rounded-3" id="password" name="password" required placeholder="Enter your password">
<input type="password" class="form-control form-control-lg bg-light border-0 rounded-3" id="password" name="password" required placeholder="<?= e(t('password_placeholder')) ?>">
</div>
<button type="submit" class="btn btn-primary btn-lg w-100 rounded-pill fw-bold shadow-sm">Sign In</button>
<button type="submit" class="btn btn-primary btn-lg w-100 rounded-pill fw-bold shadow-sm"><?= e(t('sign_in')) ?></button>
<div class="text-center mt-4">
<p class="text-muted small mb-0">Don't have an account? <a href="<?= e(url_with_lang('register.php')) ?>" class="text-decoration-none text-primary fw-bold">Register now</a></p>
<p class="text-muted small mb-0"><?= e(t('dont_have_account')) ?> <a href="<?= e(url_with_lang('register.php')) ?>" class="text-decoration-none text-primary fw-bold"><?= e(t('register_now')) ?></a></p>
</div>
</form>
</div>
@ -129,22 +129,22 @@ render_header('Login / Reset Password', 'login');
<div class="card shadow-sm border-0 rounded-4 mb-4 d-none" id="resetFormCard">
<div class="card-body p-4 p-md-5">
<div class="text-center mb-4">
<h2 class="fw-bold text-dark">Reset Password</h2>
<p class="text-muted">Enter your email and we'll send you a link to reset your password</p>
<h2 class="fw-bold text-dark"><?= e(t('reset_password_title')) ?></h2>
<p class="text-muted"><?= e(t('reset_password_subtitle')) ?></p>
</div>
<form method="post" action="">
<input type="hidden" name="action" value="reset_password">
<div class="mb-4">
<label for="reset_email" class="form-label fw-bold">Email address</label>
<input type="email" class="form-control form-control-lg bg-light border-0 rounded-3" id="reset_email" name="reset_email" required placeholder="name@example.com">
<label for="reset_email" class="form-label fw-bold"><?= e(t('email_address')) ?></label>
<input type="email" class="form-control form-control-lg bg-light border-0 rounded-3" id="reset_email" name="reset_email" required placeholder="<?= e(t('email_placeholder')) ?>">
</div>
<button type="submit" class="btn btn-primary btn-lg w-100 rounded-pill fw-bold shadow-sm mb-3">Send Reset Link</button>
<button type="submit" class="btn btn-primary btn-lg w-100 rounded-pill fw-bold shadow-sm mb-3"><?= e(t('send_reset_link')) ?></button>
<div class="text-center">
<a href="#" class="text-decoration-none text-muted small fw-bold" onclick="document.getElementById('resetFormCard').classList.add('d-none'); document.getElementById('loginFormCard').classList.remove('d-none'); return false;"><i class="bi bi-arrow-left me-1"></i>Back to login</a>
<a href="#" class="text-decoration-none text-muted small fw-bold" onclick="document.getElementById('resetFormCard').classList.add('d-none'); document.getElementById('loginFormCard').classList.remove('d-none'); return false;"><i class="bi bi-arrow-left me-1"></i><?= e(t('back_to_login')) ?></a>
</div>
</form>
</div>

369
patch_app_translations.php Normal file
View File

@ -0,0 +1,369 @@
<?php
$content = file_get_contents('includes/app.php');
$translations_en = [
'app_name' => 'CargoLink',
'nav_home' => 'Overview',
'nav_shipper' => 'Shipper Desk',
'nav_owner' => 'Truck Owner Desk',
'nav_admin' => 'Admin Panel',
'hero_title' => 'Move cargo faster with verified trucks.',
'hero_subtitle' => 'Post shipments, collect offers, and pay via Thawani or bank transfer. Built for local and nearby cross-border moves.',
'hero_tagline' => 'Multilingual Logistics Marketplace',
'register_shipper' => 'Register as Shipper',
'register_owner' => 'Register as Truck Owner',
'cta_shipper' => 'Post a shipment',
'cta_owner' => 'Find loads',
'cta_admin' => 'Open admin',
'stats_shipments' => 'Shipments posted',
'stats_offers' => 'Active offers',
'stats_confirmed' => 'Confirmed trips',
'section_workflow' => 'How it works',
'recent_shipments' => 'Recent shipments',
'step_post' => 'Shipper posts cargo details and preferred payment.',
'step_offer' => 'Truck owners respond with their best rate.',
'step_confirm' => 'Admin confirms booking and status.',
'step_confirm_desc' => 'Secure booking and track the delivery until completion.',
'shipper_dashboard' => 'Shipper Dashboard',
'new_shipment' => 'Create shipment',
'shipper_name' => 'Shipper name',
'shipper_company' => 'Company',
'origin' => 'Origin city',
'destination' => 'Destination city',
'cargo' => 'Cargo description',
'cargo_placeholder' => 'e.g. 20 Pallets of Electronics',
'weight' => 'Weight (tons)',
'pickup_date' => 'Pickup date',
'delivery_date' => 'Delivery date',
'payment_method' => 'Payment method',
'payment_thawani' => 'Thawani online payment',
'payment_bank' => 'Bank transfer',
'submit_shipment' => 'Submit shipment',
'shipments_list' => 'Your latest shipments',
'status' => 'Status',
'offer' => 'Best offer',
'actions' => 'Actions',
'view' => 'View',
'owner_dashboard' => 'Truck Owner Dashboard',
'available_shipments' => 'Available shipments',
'offer_price' => 'Offer price',
'offer_owner' => 'Truck owner name',
'submit_offer' => 'Send offer',
'admin_dashboard' => 'Admin Dashboard',
'update_status' => 'Update status',
'save' => 'Save',
'shipment_detail' => 'Shipment detail',
'created_at' => 'Created',
'best_offer' => 'Best offer',
'assign_owner' => 'Assigned owner',
'no_shipments' => 'No shipments yet. Create the first one to get started.',
'no_offers' => 'No offers yet.',
'success_shipment' => 'Shipment posted successfully.',
'success_offer' => 'Offer submitted to the shipper.',
'success_status' => 'Status updated.',
'error_required' => 'Please fill in all required fields.',
'error_invalid' => 'Please enter valid values.',
'status_posted' => 'Posted',
'status_offered' => 'Offered',
'status_confirmed' => 'Confirmed',
'status_in_transit' => 'In transit',
'status_delivered' => 'Delivered',
'footer_note' => 'This is the initial MVP slice. Payments are not yet connected.',
'marketing_title_1' => 'For Shippers',
'marketing_desc_1' => 'Find the right truck for your cargo quickly and securely. Post your load and get offers instantly.',
'marketing_title_2' => 'For Truck Owners',
'marketing_desc_2' => 'Maximize your earnings and eliminate empty miles. Browse available shipments and offer your rate.',
'motivation_phrase' => 'Empowering the logistics of tomorrow.',
'why_choose_us' => 'Why Choose CargoLink?',
'feature_1_title' => 'Fast Matching',
'feature_1_desc' => 'Connect with available trucks or shipments in minutes.',
'feature_2_title' => 'Secure Payments',
'feature_2_desc' => 'Your transactions are protected with security.',
'feature_3_title' => 'Verified Users',
'feature_3_desc' => 'We verify all truck owners to ensure peace of mind.',
'view_faq' => 'View FAQ',
'faq_title' => 'Have Questions?',
'faq_subtitle' => 'Check out our Frequently Asked Questions to learn more about how our platform works.',
'motivation_title' => 'Ready to transform your logistics?',
'motivation_subtitle' => 'Join our platform today to find reliable trucks or secure the best shipments in the market.',
'company' => 'Company',
'about_us' => 'About Us',
'careers' => 'Careers',
'contact' => 'Contact',
'resources' => 'Resources',
'help_center' => 'Help Center / FAQ',
'terms_of_service' => 'Terms of Service',
'privacy_policy' => 'Privacy Policy',
'language' => 'Language',
'all_rights_reserved' => 'All rights reserved.',
'dashboard' => 'Dashboard',
'settings' => 'Settings',
'company_setting' => 'Company Setting',
'integrations' => 'Integrations',
'locations' => 'Locations',
'countries' => 'Countries',
'cities' => 'Cities',
'users' => 'Users',
'shippers' => 'Shippers',
'truck_owners' => 'Truck Owners',
'user_registration' => 'User Registration',
'pages' => 'Pages',
'faqs' => 'FAQs',
'landing_pages' => 'Landing Pages',
'login_title' => 'Welcome Back',
'login_subtitle' => 'Sign in to your account to continue',
'email_address' => 'Email address',
'email_placeholder' => 'name@example.com',
'password' => 'Password',
'forgot_password' => 'Forgot password?',
'password_placeholder' => 'Enter your password',
'sign_in' => 'Sign In',
'dont_have_account' => 'Don\'t have an account?',
'register_now' => 'Register now',
'reset_password_title' => 'Reset Password',
'reset_password_subtitle' => 'Enter your email and we\'ll send you a link to reset your password',
'send_reset_link' => 'Send Reset Link',
'back_to_login' => 'Back to login',
'invalid_image' => 'Invalid image format. Please upload JPG, PNG, GIF, or WEBP.',
'upload_failed' => 'Failed to save uploaded file.',
'profile_updated' => 'Profile updated successfully.',
'my_profile' => 'My Profile',
'profile_picture' => 'Profile Picture',
'change_picture' => 'Change Picture',
'picture_hint' => 'JPG, PNG, or GIF up to 5MB',
'full_name' => 'Full Name',
'email_hint' => 'Email address cannot be changed.',
'account_role' => 'Account Role',
'save_changes' => 'Save Changes',
'reg_title' => 'Create your logistics account',
'reg_subtitle' => 'Shippers and truck owners can self-register with full profile details.',
'reg_success_pending' => 'Registration completed successfully. Your account is pending admin approval.',
'reg_success' => 'Registration completed successfully.',
'role' => 'Role',
'shipper' => 'Shipper',
'truck_owner' => 'Truck Owner',
'email' => 'Email',
'phone' => 'Phone',
'country' => 'Country',
'select_country' => 'Select country',
'city' => 'City',
'select_city' => 'Select city',
'address' => 'Address',
'shipper_details' => 'Shipper details',
'company_name' => 'Company name',
'truck_details' => 'Truck owner details',
'truck_type' => 'Truck type',
'load_capacity' => 'Load capacity (tons)',
'plate_no' => 'Plate number',
'bank_account' => 'Bank Account / IBAN',
'bank_name' => 'Bank Name',
'bank_branch' => 'Bank Branch',
'id_card_front' => 'ID card (Front Face)',
'id_card_back' => 'ID card (Back Face)',
'truck_reg_front' => 'Truck Registration (Front Face)',
'truck_reg_back' => 'Truck Registration (Back Face)',
'truck_picture' => 'Clear Truck Photo (showing plate number)',
'create_account' => 'Create account',
'back_to_admin' => 'Back to admin',
'welcome_back' => 'Welcome to your dashboard. Manage your cargo shipments here.',
'total_shipments_posted' => 'Total Shipments',
'active_shipments' => 'Active Shipments',
'delivered_shipments' => 'Delivered Shipments',
'route_label' => 'Route',
'total_label' => 'total',
'welcome_back_owner' => 'Find loads and submit your best rate.',
'total_offers' => 'Total Offers',
'won_shipments' => 'Won Shipments',
];
$translations_ar = [
'app_name' => 'CargoLink',
'nav_home' => 'نظرة عامة',
'nav_shipper' => 'لوحة الشاحن',
'nav_owner' => 'لوحة مالك الشاحنة',
'nav_admin' => 'لوحة الإدارة',
'hero_title' => 'انقل شحنتك بسرعة مع شاحنات موثوقة.',
'hero_subtitle' => 'أنشئ شحنة، استلم عروضاً، وادفع عبر ثواني أو التحويل البنكي.',
'hero_tagline' => 'منصة لوجستية متعددة اللغات',
'register_shipper' => 'التسجيل كشاحن',
'register_owner' => 'التسجيل كمالك شاحنة',
'cta_shipper' => 'إنشاء شحنة',
'cta_owner' => 'البحث عن الشحنات',
'cta_admin' => 'الدخول للإدارة',
'stats_shipments' => 'الشحنات المنشورة',
'stats_offers' => 'العروض الحالية',
'stats_confirmed' => 'الرحلات المؤكدة',
'section_workflow' => 'طريقة العمل',
'recent_shipments' => 'أحدث الشحنات',
'step_post' => 'يقوم الشاحن بإدخال تفاصيل الشحنة وطريقة الدفع.',
'step_offer' => 'يرسل أصحاب الشاحنات أفضل عروضهم.',
'step_confirm' => 'تؤكد الإدارة الحجز وتحدث الحالة.',
'step_confirm_desc' => 'حجز آمن وتتبع التسليم حتى الانتهاء.',
'shipper_dashboard' => 'لوحة الشاحن',
'new_shipment' => 'إنشاء شحنة',
'shipper_name' => 'اسم الشاحن',
'shipper_company' => 'الشركة',
'origin' => 'مدينة الانطلاق',
'destination' => 'مدينة الوصول',
'cargo' => 'وصف الحمولة',
'cargo_placeholder' => 'مثال: 20 منصة إلكترونيات',
'weight' => 'الوزن (طن)',
'pickup_date' => 'تاريخ الاستلام',
'delivery_date' => 'تاريخ التسليم',
'payment_method' => 'طريقة الدفع',
'payment_thawani' => 'الدفع الإلكتروني عبر ثواني',
'payment_bank' => 'تحويل بنكي',
'submit_shipment' => 'إرسال الشحنة',
'shipments_list' => 'أحدث الشحنات',
'status' => 'الحالة',
'offer' => 'أفضل عرض',
'actions' => 'إجراءات',
'view' => 'عرض',
'owner_dashboard' => 'لوحة مالك الشاحنة',
'available_shipments' => 'الشحنات المتاحة',
'offer_price' => 'سعر العرض',
'offer_owner' => 'اسم مالك الشاحنة',
'submit_offer' => 'إرسال العرض',
'admin_dashboard' => 'لوحة الإدارة',
'update_status' => 'تحديث الحالة',
'save' => 'حفظ',
'shipment_detail' => 'تفاصيل الشحنة',
'created_at' => 'تم الإنشاء',
'best_offer' => 'أفضل عرض',
'assign_owner' => 'المالك المعتمد',
'no_shipments' => 'لا توجد شحنات بعد. ابدأ بإنشاء أول شحنة.',
'no_offers' => 'لا توجد عروض بعد.',
'success_shipment' => 'تم نشر الشحنة بنجاح.',
'success_offer' => 'تم إرسال العرض إلى الشاحن.',
'success_status' => 'تم تحديث الحالة.',
'error_required' => 'يرجى تعبئة جميع الحقول المطلوبة.',
'error_invalid' => 'يرجى إدخال قيم صحيحة.',
'status_posted' => 'منشورة',
'status_offered' => 'بعرض',
'status_confirmed' => 'مؤكدة',
'status_in_transit' => 'قيد النقل',
'status_delivered' => 'تم التسليم',
'footer_note' => 'هذه هي النسخة الأولية. الدفع غير متصل بعد.',
'marketing_title_1' => 'للشاحنين',
'marketing_desc_1' => 'ابحث عن الشاحنة المناسبة لحمولتك بسرعة وأمان.',
'marketing_title_2' => 'لأصحاب الشاحنات',
'marketing_desc_2' => 'عظّم أرباحك وتجنب العودة فارغاً.',
'motivation_phrase' => 'تمكين الخدمات اللوجستية للمستقبل.',
'why_choose_us' => 'لماذا تختار كارجو لينك؟',
'feature_1_title' => 'مطابقة سريعة',
'feature_1_desc' => 'تواصل مع الشاحنات المتاحة في دقائق.',
'feature_2_title' => 'مدفوعات آمنة',
'feature_2_desc' => 'معاملاتك محمية بأعلى معايير الأمان.',
'feature_3_title' => 'مستخدمون موثوقون',
'feature_3_desc' => 'نقوم بالتحقق من جميع أصحاب الشاحنات لضمان راحتك.',
'view_faq' => 'عرض الأسئلة الشائعة',
'faq_title' => 'لديك أسئلة؟',
'faq_subtitle' => 'اطلع على الأسئلة الشائعة لمعرفة المزيد حول كيفية عمل منصتنا.',
'motivation_title' => 'هل أنت مستعد لتحويل خدماتك اللوجستية؟',
'motivation_subtitle' => 'انضم إلى منصتنا اليوم للعثور على شاحنات موثوقة أو تأمين أفضل الشحنات في السوق.',
'company' => 'الشركة',
'about_us' => 'معلومات عنا',
'careers' => 'الوظائف',
'contact' => 'اتصل بنا',
'resources' => 'الموارد',
'help_center' => 'مركز المساعدة / الأسئلة الشائعة',
'terms_of_service' => 'شروط الخدمة',
'privacy_policy' => 'سياسة الخصوصية',
'language' => 'اللغة',
'all_rights_reserved' => 'جميع الحقوق محفوظة.',
'dashboard' => 'لوحة القيادة',
'settings' => 'الإعدادات',
'company_setting' => 'إعدادات الشركة',
'integrations' => 'التكاملات',
'locations' => 'المواقع',
'countries' => 'البلدان',
'cities' => 'المدن',
'users' => 'المستخدمون',
'shippers' => 'الشاحنون',
'truck_owners' => 'أصحاب الشاحنات',
'user_registration' => 'تسجيل المستخدم',
'pages' => 'الصفحات',
'faqs' => 'الأسئلة الشائعة',
'landing_pages' => 'صفحات الهبوط',
'login_title' => 'مرحبًا بعودتك',
'login_subtitle' => 'قم بتسجيل الدخول إلى حسابك للمتابعة',
'email_address' => 'البريد الإلكتروني',
'email_placeholder' => 'name@example.com',
'password' => 'كلمة المرور',
'forgot_password' => 'هل نسيت كلمة المرور؟',
'password_placeholder' => 'أدخل كلمة المرور',
'sign_in' => 'تسجيل الدخول',
'dont_have_account' => 'ليس لديك حساب؟',
'register_now' => 'سجل الآن',
'reset_password_title' => 'إعادة تعيين كلمة المرور',
'reset_password_subtitle' => 'أدخل بريدك الإلكتروني وسنرسل لك رابطًا لإعادة تعيين كلمة المرور',
'send_reset_link' => 'إرسال رابط إعادة التعيين',
'back_to_login' => 'العودة لتسجيل الدخول',
'invalid_image' => 'صيغة صورة غير صالحة. يرجى تحميل JPG أو PNG أو GIF أو WEBP.',
'upload_failed' => 'فشل في حفظ الملف المحمل.',
'profile_updated' => 'تم تحديث الملف الشخصي بنجاح.',
'my_profile' => 'ملفي الشخصي',
'profile_picture' => 'صورة الملف الشخصي',
'change_picture' => 'تغيير الصورة',
'picture_hint' => 'JPG، PNG، أو GIF حتى 5 ميغابايت',
'full_name' => 'الاسم الكامل',
'email_hint' => 'لا يمكن تغيير عنوان البريد الإلكتروني.',
'account_role' => 'دور الحساب',
'save_changes' => 'حفظ التغييرات',
'reg_title' => 'أنشئ حسابك اللوجستي',
'reg_subtitle' => 'يمكن للشاحنين وأصحاب الشاحنات التسجيل الذاتي ببيانات الملف الشخصي الكاملة.',
'reg_success_pending' => 'اكتمل التسجيل بنجاح. حسابك في انتظار موافقة الإدارة.',
'reg_success' => 'اكتمل التسجيل بنجاح.',
'role' => 'الدور',
'shipper' => 'شاحن',
'truck_owner' => 'مالك شاحنة',
'email' => 'البريد الإلكتروني',
'phone' => 'الهاتف',
'country' => 'البلد',
'select_country' => 'اختر البلد',
'city' => 'المدينة',
'select_city' => 'اختر المدينة',
'address' => 'العنوان',
'shipper_details' => 'تفاصيل الشاحن',
'company_name' => 'اسم الشركة',
'truck_details' => 'تفاصيل مالك الشاحنة',
'truck_type' => 'نوع الشاحنة',
'load_capacity' => 'سعة الحمولة (طن)',
'plate_no' => 'رقم اللوحة',
'bank_account' => 'الحساب البنكي / الآيبان',
'bank_name' => 'اسم البنك',
'bank_branch' => 'فرع البنك',
'id_card_front' => 'البطاقة الشخصية (الوجه الأمامي)',
'id_card_back' => 'البطاقة الشخصية (الوجه الخلفي)',
'truck_reg_front' => 'تسجيل الشاحنة (الوجه الأمامي)',
'truck_reg_back' => 'تسجيل الشاحنة (الوجه الخلفي)',
'truck_picture' => 'صورة واضحة للشاحنة (تظهر رقم اللوحة)',
'create_account' => 'إنشاء حساب',
'back_to_admin' => 'العودة للإدارة',
'welcome_back' => 'مرحبًا بك في لوحة القيادة الخاصة بك. قم بإدارة شحناتك هنا.',
'total_shipments_posted' => 'إجمالي الشحنات',
'active_shipments' => 'الشحنات النشطة',
'delivered_shipments' => 'الشحنات المسلمة',
'route_label' => 'المسار',
'total_label' => 'المجموع',
'welcome_back_owner' => 'ابحث عن الأحمال وقدم أفضل سعر لديك.',
'total_offers' => 'إجمالي العروض',
'won_shipments' => 'الشحنات الفائزة',
];
$trans_str_en = var_export($translations_en, true);
$trans_str_ar = var_export($translations_ar, true);
$new_translations = '$translations = [
"en" => ' . $trans_str_en . ',
"ar" => ' . $trans_str_ar . '
];';
// Find where $translations array starts and ends.
// In the current file it starts at '$translations = [' and ends before 'function t('
$pattern = '/\$translations\s*=\s*\[.*?(?=\nfunction t\(\))/s';
$content = preg_replace($pattern, $new_translations . "\n\n", $content);
file_put_contents('includes/app.php', $content);
echo "Updated translations in app.php\n";
?>

23
patch_lines.php Normal file
View File

@ -0,0 +1,23 @@
<?php
$lines = file('includes/app.php');
$start = -1;
$end = -1;
foreach ($lines as $i => $line) {
if (strpos($line, '$translations = [') === 0) {
$start = $i;
}
if (strpos($line, 'function t(') === 0) {
$end = $i;
break;
}
}
if ($start !== -1 && $end !== -1) {
$new_content = array_slice($lines, 0, $start);
$new_content[] = file_get_contents('new_trans.txt');
$new_content = array_merge($new_content, array_slice($lines, $end));
file_put_contents('includes/app.php', implode("", $new_content));
echo "Replaced lines $start to $end\n";
} else {
echo "Could not find start/end\n";
}
?>

View File

@ -39,7 +39,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'updat
finfo_close($fileInfo);
if (!in_array($mimeType, $allowedTypes)) {
$errors[] = "Invalid image format. Please upload JPG, PNG, GIF, or WEBP.";
$errors[] = t('invalid_image');
} else {
$uploadDir = __DIR__ . '/uploads/profiles/';
if (!is_dir($uploadDir)) {
@ -53,7 +53,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'updat
if (move_uploaded_file($_FILES['profile_picture']['tmp_name'], $destination)) {
$profilePicPath = '/uploads/profiles/' . $filename;
} else {
$errors[] = "Failed to save uploaded file.";
$errors[] = t('upload_failed');
}
}
}
@ -66,7 +66,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'updat
':pic' => $profilePicPath,
':id' => $userId
]);
set_flash('success', "Profile updated successfully.");
set_flash('success', t('profile_updated'));
header("Location: " . url_with_lang('profile.php'));
exit;
} catch (Throwable $e) {
@ -75,7 +75,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'updat
}
}
render_header('My Profile', 'profile');
render_header(t('my_profile'), 'profile');
$flash = get_flash();
?>
@ -84,7 +84,7 @@ $flash = get_flash();
<div class="panel p-4 p-md-5">
<div class="d-flex align-items-center mb-4">
<i class="bi bi-person-badge fs-2 text-primary me-3"></i>
<h2 class="section-title mb-0">My Profile</h2>
<h2 class="section-title mb-0"><?= e(t('my_profile')) ?></h2>
</div>
<?php if ($flash): ?>
@ -109,39 +109,39 @@ $flash = get_flash();
<div class="text-center mb-4">
<div class="position-relative d-inline-block">
<?php if (!empty($user['profile_picture'])): ?>
<img src="<?= e($user['profile_picture']) ?>" alt="Profile Picture" class="rounded-circle shadow-sm object-fit-cover" style="width: 120px; height: 120px;">
<img src="<?= e($user['profile_picture']) ?>" alt="<?= e(t('profile_picture')) ?>" class="rounded-circle shadow-sm object-fit-cover" style="width: 120px; height: 120px;">
<?php else: ?>
<div class="rounded-circle bg-light d-flex align-items-center justify-content-center shadow-sm" style="width: 120px; height: 120px;">
<i class="bi bi-person text-secondary" style="font-size: 4rem;"></i>
</div>
<?php endif; ?>
<label for="profile_picture" class="position-absolute bottom-0 end-0 bg-primary text-white rounded-circle p-2 cursor-pointer shadow" style="cursor: pointer;" title="Change Picture">
<label for="profile_picture" class="position-absolute bottom-0 end-0 bg-primary text-white rounded-circle p-2 cursor-pointer shadow" style="cursor: pointer;" title="<?= e(t('change_picture')) ?>">
<i class="bi bi-camera-fill"></i>
</label>
</div>
<input type="file" id="profile_picture" name="profile_picture" class="d-none" accept="image/jpeg,image/png,image/gif,image/webp" onchange="previewImage(this)">
<div class="small text-muted mt-2">JPG, PNG, or GIF up to 5MB</div>
<div class="small text-muted mt-2"><?= e(t('picture_hint')) ?></div>
</div>
<div class="mb-3">
<label class="form-label fw-bold">Full Name</label>
<label class="form-label fw-bold"><?= e(t('full_name')) ?></label>
<input type="text" class="form-control form-control-lg" name="full_name" value="<?= e($user['full_name'] ?? '') ?>" required>
</div>
<div class="mb-3">
<label class="form-label fw-bold">Email Address</label>
<label class="form-label fw-bold"><?= e(t('email_address')) ?></label>
<input type="email" class="form-control form-control-lg bg-light" value="<?= e($user['email'] ?? '') ?>" readonly disabled>
<div class="form-text">Email address cannot be changed.</div>
<div class="form-text"><?= e(t('email_hint')) ?></div>
</div>
<div class="mb-4">
<label class="form-label fw-bold">Account Role</label>
<label class="form-label fw-bold"><?= e(t('account_role')) ?></label>
<input type="text" class="form-control form-control-lg bg-light text-capitalize" value="<?= e($user['role'] ?? '') ?>" readonly disabled>
</div>
<button type="submit" class="btn btn-primary btn-lg w-100 shadow-sm rounded-pill fw-bold">
Save Changes
<?= e(t('save_changes')) ?>
</button>
</form>
</div>

View File

@ -230,16 +230,16 @@ render_header('Shipper & Truck Owner Registration');
?>
<div class="page-intro">
<h1 class="section-title mb-1">Create your logistics account</h1>
<p class="muted mb-0">Shippers and truck owners can self-register with full profile details.</p>
<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">Registration completed successfully. Your account is pending admin approval.</div>
<div class="alert alert-success"><?= e(t('reg_success_pending')) ?></div>
<?php else: ?>
<div class="alert alert-success">Registration completed successfully.</div>
<div class="alert alert-success"><?= e(t('reg_success')) ?></div>
<?php endif; ?>
<?php endif; ?>
<?php if ($errors): ?>
@ -249,32 +249,32 @@ render_header('Shipper & Truck Owner Registration');
<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">Role</label>
<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' : '' ?>>Shipper</option>
<option value="truck_owner" <?= $values['role'] === 'truck_owner' ? 'selected' : '' ?>>Truck Owner</option>
<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">Full name</label>
<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">Email</label>
<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">Password</label>
<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">Phone</label>
<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">Country</label>
<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="">Select country</option>
<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']) ?>
@ -283,82 +283,82 @@ render_header('Shipper & Truck Owner Registration');
</select>
</div>
<div class="col-md-3">
<label class="form-label" for="city_id">City</label>
<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="">Select city</option>
<option value=""><?= e(t('select_city')) ?></option>
</select>
</div>
<div class="col-md-3">
<label class="form-label" for="address_line">Address</label>
<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">Shipper details</h2>
<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">Company name</label>
<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">Truck owner details</h2>
<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">Truck type</label>
<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">Load capacity (tons)</label>
<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">Plate number</label>
<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">Bank Account / IBAN</label>
<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">Bank Name</label>
<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">Bank Branch</label>
<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">ID card (Front Face)</label>
<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">ID card (Back Face)</label>
<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">Truck Registration (Front Face)</label>
<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">Truck Registration (Back Face)</label>
<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">Clear Truck Photo (showing plate number)</label>
<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">Create account</button>
<a class="btn btn-outline-dark" href="<?= e(url_with_lang('admin_dashboard.php')) ?>">Back to admin</a>
<button type="submit" class="btn btn-primary"><?= e(t('create_account')) ?></button>
<a class="btn btn-outline-dark" href="<?= e(url_with_lang('admin_dashboard.php')) ?>"><?= e(t('back_to_admin')) ?></a>
</div>
</form>
</div>
@ -370,7 +370,7 @@ function syncCities() {
const countryId = document.getElementById('country_id').value;
const citySelect = document.getElementById('city_id');
const selectedValue = citySelect.dataset.selected || '';
citySelect.innerHTML = '<option value="">Select city</option>';
citySelect.innerHTML = '<option value=""><?= e(t('select_city')) ?></option>';
allCities.forEach((city) => {
if (String(city.country_id) !== String(countryId)) {

View File

@ -39,6 +39,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'submi
}
$flash = get_flash();
$isAdmin = ($_SESSION['user_role'] ?? '') === 'admin';
render_header(t('shipment_detail'));
?>
@ -46,6 +47,13 @@ render_header(t('shipment_detail'));
<?php if (!$shipment): ?>
<div class="panel p-4">
<p class="muted mb-0"><?= e(t('no_shipments')) ?></p>
<div class="mt-3">
<?php if ($isAdmin): ?>
<a href="<?= e(url_with_lang('admin_shipments.php')) ?>" class="btn btn-outline-primary">Back to Shipments</a>
<?php else: ?>
<a href="<?= e(url_with_lang('index.php')) ?>" class="btn btn-outline-primary"><?= e(t('nav_home')) ?></a>
<?php endif; ?>
</div>
</div>
<?php else: ?>
<div class="row g-4">
@ -100,8 +108,17 @@ render_header(t('shipment_detail'));
<div class="fw-semibold"><?= $shipment['offer_price'] ? e($shipment['offer_price'] . ' / ' . ($shipment['offer_owner'] ?? '')) : e(t('no_offers')) ?></div>
</div>
</div>
<div class="mt-4">
<a class="btn btn-outline-dark" href="<?= e(url_with_lang('truck_owner_dashboard.php')) ?>"><?= e(t('nav_owner')) ?></a>
<div class="mt-4 d-flex gap-2">
<?php if ($isAdmin): ?>
<a class="btn btn-outline-dark" href="<?= e(url_with_lang('admin_shipments.php')) ?>">
<i class="bi bi-arrow-left me-2"></i>Back to Shipments
</a>
<a class="btn btn-primary" href="<?= e(url_with_lang('admin_shipment_edit.php', ['id' => $shipmentId])) ?>">
<i class="bi bi-pencil me-2"></i>Edit Shipment
</a>
<?php else: ?>
<a class="btn btn-outline-dark" href="<?= e(url_with_lang('truck_owner_dashboard.php')) ?>"><?= e(t('nav_owner')) ?></a>
<?php endif; ?>
</div>
</div>
</div>
@ -131,4 +148,4 @@ render_header(t('shipment_detail'));
</div>
<?php endif; ?>
<?php render_footer(); ?>
<?php render_footer(); ?>

View File

@ -7,6 +7,21 @@ ensure_schema();
$errors = [];
// Try to pre-fill from profile if logged in
$prefillName = $_SESSION['shipper_name'] ?? '';
$prefillCompany = '';
if (isset($_SESSION['user_id'])) {
try {
$stmt = db()->prepare("SELECT u.full_name, p.company_name FROM users u LEFT JOIN shipper_profiles p ON u.id = p.user_id WHERE u.id = ?");
$stmt->execute([$_SESSION['user_id']]);
$profile = $stmt->fetch();
if ($profile) {
$prefillName = $profile['full_name'];
$prefillCompany = $profile['company_name'] ?? '';
}
} catch (Throwable $e) {}
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'create_shipment') {
$shipperName = trim($_POST['shipper_name'] ?? '');
$shipperCompany = trim($_POST['shipper_company'] ?? '');
@ -41,6 +56,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'creat
':payment_method' => $payment,
]);
$_SESSION['shipper_name'] = $shipperName;
$_SESSION['shipper_company_session'] = $shipperCompany; // for rudimentary filtering
set_flash('success', t('success_shipment'));
header('Location: ' . url_with_lang('shipper_dashboard.php'));
exit;
@ -48,22 +64,79 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'creat
}
$shipments = [];
$filterCompany = $_SESSION['shipper_company_session'] ?? $prefillCompany;
try {
$stmt = db()->query("SELECT * FROM shipments ORDER BY created_at DESC LIMIT 20");
if ($filterCompany) {
$stmt = db()->prepare("SELECT * FROM shipments WHERE shipper_company = ? ORDER BY created_at DESC LIMIT 20");
$stmt->execute([$filterCompany]);
} else {
$stmt = db()->query("SELECT * FROM shipments ORDER BY created_at DESC LIMIT 20");
}
$shipments = $stmt->fetchAll();
} catch (Throwable $e) {
$shipments = [];
}
render_header(t('shipper_dashboard'), 'shipper');
$stats = [
'total' => 0,
'active' => 0,
'delivered' => 0,
];
try {
if ($filterCompany) {
$stats['total'] = (int)db()->prepare("SELECT COUNT(*) FROM shipments WHERE shipper_company = ?")->execute([$filterCompany]) ? (int)db()->query("SELECT COUNT(*) FROM shipments WHERE shipper_company = " . db()->quote($filterCompany))->fetchColumn() : 0;
$stats['active'] = (int)db()->query("SELECT COUNT(*) FROM shipments WHERE shipper_company = " . db()->quote($filterCompany) . " AND status != 'delivered'")->fetchColumn();
$stats['delivered'] = (int)db()->query("SELECT COUNT(*) FROM shipments WHERE shipper_company = " . db()->quote($filterCompany) . " AND status = 'delivered'")->fetchColumn();
} else {
$stats['total'] = (int)db()->query("SELECT COUNT(*) FROM shipments")->fetchColumn();
$stats['active'] = (int)db()->query("SELECT COUNT(*) FROM shipments WHERE status != 'delivered'")->fetchColumn();
$stats['delivered'] = (int)db()->query("SELECT COUNT(*) FROM shipments WHERE status = 'delivered'")->fetchColumn();
}
} catch (Throwable $e) {}
render_header(t('shipper_dashboard'), 'shipper');
$flash = get_flash();
?>
<div class="page-intro mb-4">
<h1 class="section-title mb-1"><?= e(t('shipper_dashboard')) ?></h1>
<p class="muted mb-0"><?= e(t('welcome_back') ?? 'Welcome to your dashboard. Manage your cargo shipments here.') ?></p>
</div>
<!-- Stats Row -->
<div class="row g-3 mb-4">
<div class="col-md-4">
<div class="panel p-4 text-center h-100 shadow-sm border-0 rounded-4 position-relative overflow-hidden" style="background: linear-gradient(135deg, #ffffff, #f8f9fa);">
<div class="position-absolute opacity-10" style="inset-inline-end: 10px; top: 15px;"><i class="bi bi-box-seam" style="font-size: 3.5rem;"></i></div>
<div class="text-primary mb-2 position-relative"><i class="bi bi-box-seam fs-2"></i></div>
<h3 class="h2 mb-0 fw-bold position-relative"><?= $stats['total'] ?></h3>
<p class="text-muted small text-uppercase mb-0 fw-bold position-relative" style="letter-spacing: 0.5px;"><?= e(t('total_shipments_posted')) ?></p>
</div>
</div>
<div class="col-md-4">
<div class="panel p-4 text-center h-100 shadow-sm border-0 rounded-4 position-relative overflow-hidden" style="background: linear-gradient(135deg, #ffffff, #f8f9fa);">
<div class="position-absolute opacity-10" style="inset-inline-end: 10px; top: 15px;"><i class="bi bi-truck" style="font-size: 3.5rem;"></i></div>
<div class="text-warning mb-2 position-relative"><i class="bi bi-truck fs-2"></i></div>
<h3 class="h2 mb-0 fw-bold position-relative"><?= $stats['active'] ?></h3>
<p class="text-muted small text-uppercase mb-0 fw-bold position-relative" style="letter-spacing: 0.5px;"><?= e(t('active_shipments')) ?></p>
</div>
</div>
<div class="col-md-4">
<div class="panel p-4 text-center h-100 shadow-sm border-0 rounded-4 position-relative overflow-hidden" style="background: linear-gradient(135deg, #ffffff, #f8f9fa);">
<div class="position-absolute opacity-10" style="inset-inline-end: 10px; top: 15px;"><i class="bi bi-check-circle" style="font-size: 3.5rem;"></i></div>
<div class="text-success mb-2 position-relative"><i class="bi bi-check-circle fs-2"></i></div>
<h3 class="h2 mb-0 fw-bold position-relative"><?= $stats['delivered'] ?></h3>
<p class="text-muted small text-uppercase mb-0 fw-bold position-relative" style="letter-spacing: 0.5px;"><?= e(t('delivered_shipments')) ?></p>
</div>
</div>
</div>
<div class="row g-4">
<div class="col-lg-5">
<div class="panel p-3">
<h2 class="section-title"><?= e(t('new_shipment')) ?></h2>
<div class="panel p-4 shadow-sm border-0 rounded-4 h-100">
<h2 class="h5 fw-bold mb-4"><i class="bi bi-plus-circle-fill text-primary me-2"></i><?= e(t('new_shipment')) ?></h2>
<?php if ($flash): ?>
<div class="alert alert-success" data-auto-dismiss="true"><?= e($flash['message']) ?></div>
<?php endif; ?>
@ -73,81 +146,102 @@ $flash = get_flash();
<form method="post">
<input type="hidden" name="action" value="create_shipment">
<div class="mb-3">
<label class="form-label"><?= e(t('shipper_name')) ?></label>
<input class="form-control" name="shipper_name" value="<?= e($_SESSION['shipper_name'] ?? '') ?>" required>
<label class="form-label text-muted small fw-bold"><?= e(t('shipper_name')) ?></label>
<input class="form-control" name="shipper_name" value="<?= e($prefillName) ?>" required>
</div>
<div class="mb-3">
<label class="form-label"><?= e(t('shipper_company')) ?></label>
<input class="form-control" name="shipper_company" required>
<label class="form-label text-muted small fw-bold"><?= e(t('shipper_company')) ?></label>
<input class="form-control" name="shipper_company" value="<?= e($prefillCompany) ?>" required>
</div>
<div class="mb-3">
<label class="form-label"><?= e(t('origin')) ?></label>
<input class="form-control" name="origin_city" required>
</div>
<div class="mb-3">
<label class="form-label"><?= e(t('destination')) ?></label>
<input class="form-control" name="destination_city" required>
</div>
<div class="mb-3">
<label class="form-label"><?= e(t('cargo')) ?></label>
<input class="form-control" name="cargo_description" required>
</div>
<div class="row g-3">
<div class="row g-3 mb-3">
<div class="col-md-6">
<label class="form-label"><?= e(t('weight')) ?></label>
<label class="form-label text-muted small fw-bold"><?= e(t('origin')) ?></label>
<input class="form-control" name="origin_city" required>
</div>
<div class="col-md-6">
<label class="form-label text-muted small fw-bold"><?= e(t('destination')) ?></label>
<input class="form-control" name="destination_city" required>
</div>
</div>
<div class="mb-3">
<label class="form-label text-muted small fw-bold"><?= e(t('cargo')) ?></label>
<input class="form-control" name="cargo_description" placeholder="<?= e(t('cargo_placeholder') ?? 'e.g. 20 Pallets of Electronics') ?>" required>
</div>
<div class="row g-3 mb-3">
<div class="col-md-6">
<label class="form-label text-muted small fw-bold"><?= e(t('weight')) ?></label>
<input class="form-control" name="weight_tons" type="number" step="0.01" min="0.1" required>
</div>
<div class="col-md-6">
<label class="form-label"><?= e(t('payment_method')) ?></label>
<label class="form-label text-muted small fw-bold"><?= e(t('payment_method')) ?></label>
<select class="form-select" name="payment_method">
<option value="thawani"><?= e(t('payment_thawani')) ?></option>
<option value="bank_transfer"><?= e(t('payment_bank')) ?></option>
</select>
</div>
</div>
<div class="row g-3 mt-1">
<div class="row g-3 mb-4">
<div class="col-md-6">
<label class="form-label"><?= e(t('pickup_date')) ?></label>
<label class="form-label text-muted small fw-bold"><?= e(t('pickup_date')) ?></label>
<input class="form-control" name="pickup_date" type="date" required>
</div>
<div class="col-md-6">
<label class="form-label"><?= e(t('delivery_date')) ?></label>
<label class="form-label text-muted small fw-bold"><?= e(t('delivery_date')) ?></label>
<input class="form-control" name="delivery_date" type="date" required>
</div>
</div>
<button class="btn btn-primary w-100 mt-4" type="submit"><?= e(t('submit_shipment')) ?></button>
<button class="btn btn-primary w-100 py-2 fw-bold rounded-pill shadow-sm" type="submit"><?= e(t('submit_shipment')) ?></button>
</form>
</div>
</div>
<div class="col-lg-7">
<div class="panel p-3">
<div class="d-flex justify-content-between align-items-center mb-2">
<h2 class="section-title mb-0"><?= e(t('shipments_list')) ?></h2>
<span class="small text-muted"><?= e(count($shipments)) ?> total</span>
<div class="panel p-4 shadow-sm border-0 rounded-4 h-100 d-flex flex-column">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2 class="h5 mb-0 fw-bold"><i class="bi bi-list-ul text-muted me-2"></i><?= e(t('shipments_list')) ?></h2>
<span class="badge bg-light text-dark border"><?= e(count($shipments)) ?> <?= e(t('total_label') ?? 'total') ?></span>
</div>
<?php if (!$shipments): ?>
<p class="muted mb-0"><?= e(t('no_shipments')) ?></p>
<div class="text-center p-5 text-muted flex-grow-1 d-flex flex-column justify-content-center">
<i class="bi bi-inbox fs-1 mb-3 d-block opacity-50"></i>
<p class="mb-0"><?= e(t('no_shipments')) ?></p>
</div>
<?php else: ?>
<div class="table-responsive">
<div class="table-responsive flex-grow-1">
<table class="table align-middle mb-0">
<thead>
<tr>
<th><?= e(t('origin')) ?></th>
<th><?= e(t('destination')) ?></th>
<th><?= e(t('status')) ?></th>
<th><?= e(t('offer')) ?></th>
<th><?= e(t('actions')) ?></th>
<th class="text-uppercase small text-muted border-top-0"><?= e(t('route_label') ?? 'Route') ?></th>
<th class="text-uppercase small text-muted border-top-0"><?= e(t('status')) ?></th>
<th class="text-uppercase small text-muted border-top-0"><?= e(t('offer')) ?></th>
<th class="text-uppercase small text-muted border-top-0 text-end"><?= e(t('actions')) ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($shipments as $row): ?>
<tr>
<td><?= e($row['origin_city']) ?></td>
<td><?= e($row['destination_city']) ?></td>
<td><span class="badge-status <?= e($row['status']) ?>"><?= e(status_label($row['status'])) ?></span></td>
<td><?= $row['offer_price'] ? e($row['offer_price'] . ' / ' . ($row['offer_owner'] ?? '')) : e(t('no_offers')) ?></td>
<td>
<a class="btn btn-sm btn-outline-dark" href="<?= e(url_with_lang('shipment_detail.php', ['id' => $row['id']])) ?>">
<div class="d-flex align-items-center gap-2">
<span class="fw-medium"><?= e($row['origin_city']) ?></span>
<i class="bi bi-arrow-right text-muted small"></i>
<span class="fw-medium"><?= e($row['destination_city']) ?></span>
</div>
<small class="text-muted"><?= e(date('M d', strtotime($row['pickup_date']))) ?></small>
</td>
<td><span class="badge <?= e($row['status']) ?> rounded-pill px-3 py-2"><?= e(status_label($row['status'])) ?></span></td>
<td>
<?php if ($row['offer_price']): ?>
<div class="fw-bold text-success">$<?= e($row['offer_price']) ?></div>
<small class="text-muted"><?= e($row['offer_owner']) ?></small>
<?php else: ?>
<span class="text-muted small"><?= e(t('no_offers')) ?></span>
<?php endif; ?>
</td>
<td class="text-end">
<a class="btn btn-sm btn-outline-primary rounded-pill px-3" href="<?= e(url_with_lang('shipment_detail.php', ['id' => $row['id']])) ?>">
<?= e(t('view')) ?>
</a>
</td>
@ -161,4 +255,4 @@ $flash = get_flash();
</div>
</div>
<?php render_footer(); ?>
<?php render_footer(); ?>

View File

@ -7,6 +7,16 @@ ensure_schema();
$errors = [];
// Try to prefill owner name from profile if logged in
$prefillOwnerName = '';
if (isset($_SESSION['user_id'])) {
try {
$stmt = db()->prepare("SELECT full_name FROM users WHERE id = ?");
$stmt->execute([$_SESSION['user_id']]);
$prefillOwnerName = $stmt->fetchColumn() ?: '';
} catch (Throwable $e) {}
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'submit_offer') {
$shipmentId = (int) ($_POST['shipment_id'] ?? 0);
$offerOwner = trim($_POST['offer_owner'] ?? '');
@ -29,6 +39,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'submi
':id' => $shipmentId,
]);
if ($stmt->rowCount() > 0) {
$_SESSION['last_offer_owner'] = $offerOwner; // Save for next time
set_flash('success', t('success_offer'));
header('Location: ' . url_with_lang('truck_owner_dashboard.php'));
exit;
@ -38,6 +49,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'submi
}
}
$ownerName = $_SESSION['last_offer_owner'] ?? $prefillOwnerName;
$shipments = [];
try {
$stmt = db()->query("SELECT * FROM shipments WHERE status IN ('posted','offered') ORDER BY created_at DESC LIMIT 20");
@ -46,57 +59,125 @@ try {
$shipments = [];
}
render_header(t('owner_dashboard'), 'owner');
$stats = [
'available' => count($shipments),
'my_offers' => 0,
'won' => 0,
];
try {
if ($ownerName) {
$stats['my_offers'] = (int)db()->query("SELECT COUNT(*) FROM shipments WHERE offer_owner = " . db()->quote($ownerName))->fetchColumn();
$stats['won'] = (int)db()->query("SELECT COUNT(*) FROM shipments WHERE offer_owner = " . db()->quote($ownerName) . " AND status IN ('confirmed','in_transit','delivered')")->fetchColumn();
}
} catch (Throwable $e) {}
render_header(t('owner_dashboard'), 'owner');
$flash = get_flash();
?>
<div class="panel p-3">
<div class="d-flex justify-content-between align-items-center mb-2">
<h2 class="section-title mb-0"><?= e(t('available_shipments')) ?></h2>
<span class="small text-muted"><?= e(count($shipments)) ?> total</span>
<div class="page-intro mb-4">
<h1 class="section-title mb-1"><?= e(t('owner_dashboard')) ?></h1>
<p class="muted mb-0"><?= e(t('welcome_back_owner') ?? 'Find loads and submit your best rate.') ?></p>
</div>
<!-- Stats Row -->
<div class="row g-3 mb-4">
<div class="col-md-4">
<div class="panel p-4 text-center h-100 shadow-sm border-0 rounded-4 position-relative overflow-hidden" style="background: linear-gradient(135deg, #ffffff, #f8f9fa);">
<div class="position-absolute opacity-10" style="inset-inline-end: 10px; top: 15px;"><i class="bi bi-search" style="font-size: 3.5rem;"></i></div>
<div class="text-primary mb-2 position-relative"><i class="bi bi-search fs-2"></i></div>
<h3 class="h2 mb-0 fw-bold position-relative"><?= $stats['available'] ?></h3>
<p class="text-muted small text-uppercase mb-0 fw-bold position-relative" style="letter-spacing: 0.5px;"><?= e(t('available_shipments')) ?></p>
</div>
</div>
<div class="col-md-4">
<div class="panel p-4 text-center h-100 shadow-sm border-0 rounded-4 position-relative overflow-hidden" style="background: linear-gradient(135deg, #ffffff, #f8f9fa);">
<div class="position-absolute opacity-10" style="inset-inline-end: 10px; top: 15px;"><i class="bi bi-tags" style="font-size: 3.5rem;"></i></div>
<div class="text-info mb-2 position-relative"><i class="bi bi-tags fs-2"></i></div>
<h3 class="h2 mb-0 fw-bold position-relative"><?= $stats['my_offers'] ?></h3>
<p class="text-muted small text-uppercase mb-0 fw-bold position-relative" style="letter-spacing: 0.5px;"><?= e(t('total_offers')) ?></p>
</div>
</div>
<div class="col-md-4">
<div class="panel p-4 text-center h-100 shadow-sm border-0 rounded-4 position-relative overflow-hidden" style="background: linear-gradient(135deg, #ffffff, #f8f9fa);">
<div class="position-absolute opacity-10" style="inset-inline-end: 10px; top: 15px;"><i class="bi bi-trophy" style="font-size: 3.5rem;"></i></div>
<div class="text-success mb-2 position-relative"><i class="bi bi-trophy fs-2"></i></div>
<h3 class="h2 mb-0 fw-bold position-relative"><?= $stats['won'] ?></h3>
<p class="text-muted small text-uppercase mb-0 fw-bold position-relative" style="letter-spacing: 0.5px;"><?= e(t('won_shipments')) ?></p>
</div>
</div>
</div>
<div class="panel p-4 shadow-sm border-0 rounded-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2 class="h5 mb-0 fw-bold"><i class="bi bi-truck text-muted me-2"></i><?= e(t('available_shipments')) ?></h2>
<span class="badge bg-light text-dark border"><?= e(count($shipments)) ?> <?= e(t('total_label') ?? 'total') ?></span>
</div>
<?php if ($flash): ?>
<div class="alert alert-success" data-auto-dismiss="true"><?= e($flash['message']) ?></div>
<?php endif; ?>
<?php if ($errors): ?>
<div class="alert alert-warning"><?= e(implode(' ', $errors)) ?></div>
<?php endif; ?>
<?php if (!$shipments): ?>
<p class="muted mb-0"><?= e(t('no_shipments')) ?></p>
<div class="text-center p-5 text-muted flex-grow-1 d-flex flex-column justify-content-center">
<i class="bi bi-inbox fs-1 mb-3 d-block opacity-50"></i>
<p class="mb-0"><?= e(t('no_shipments')) ?></p>
</div>
<?php else: ?>
<div class="table-responsive">
<table class="table align-middle mb-0">
<thead>
<tr>
<th><?= e(t('shipper_company')) ?></th>
<th><?= e(t('origin')) ?></th>
<th><?= e(t('destination')) ?></th>
<th><?= e(t('weight')) ?></th>
<th><?= e(t('offer')) ?></th>
<th><?= e(t('actions')) ?></th>
<th class="text-uppercase small text-muted border-top-0"><?= e(t('shipper_company')) ?></th>
<th class="text-uppercase small text-muted border-top-0"><?= e(t('route_label') ?? 'Route') ?></th>
<th class="text-uppercase small text-muted border-top-0"><?= e(t('cargo')) ?></th>
<th class="text-uppercase small text-muted border-top-0"><?= e(t('offer')) ?></th>
<th class="text-uppercase small text-muted border-top-0 text-end"><?= e(t('actions')) ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($shipments as $row): ?>
<tr>
<td><?= e($row['shipper_company']) ?></td>
<td><?= e($row['origin_city']) ?></td>
<td><?= e($row['destination_city']) ?></td>
<td><?= e($row['weight_tons']) ?></td>
<td><?= $row['offer_price'] ? e($row['offer_price'] . ' / ' . ($row['offer_owner'] ?? '')) : e(t('no_offers')) ?></td>
<td>
<form class="d-flex flex-column gap-2" method="post">
<div class="fw-bold"><?= e($row['shipper_company']) ?></div>
<small class="text-muted"><?= e(date('M d', strtotime($row['pickup_date']))) ?></small>
</td>
<td>
<div class="d-flex align-items-center gap-2">
<span class="fw-medium"><?= e($row['origin_city']) ?></span>
<i class="bi bi-arrow-right text-muted small"></i>
<span class="fw-medium"><?= e($row['destination_city']) ?></span>
</div>
</td>
<td>
<div><?= e($row['weight_tons']) ?> Ton(s)</div>
<small class="text-muted text-truncate d-inline-block" style="max-width: 150px;"><?= e($row['cargo_description']) ?></small>
</td>
<td>
<?php if ($row['offer_price']): ?>
<div class="fw-bold text-success">$<?= e($row['offer_price']) ?></div>
<small class="text-muted"><?= e($row['offer_owner']) ?></small>
<?php else: ?>
<span class="badge bg-light text-dark border"><?= e(t('no_offers')) ?></span>
<?php endif; ?>
</td>
<td class="text-end">
<form class="d-flex align-items-center justify-content-end gap-2" method="post">
<input type="hidden" name="action" value="submit_offer">
<input type="hidden" name="shipment_id" value="<?= e($row['id']) ?>">
<input class="form-control form-control-sm" name="offer_owner" placeholder="<?= e(t('offer_owner')) ?>" required>
<input class="form-control form-control-sm" name="offer_price" type="number" step="0.01" min="0.1" placeholder="<?= e(t('offer_price')) ?>" required>
<div class="d-flex gap-2">
<button class="btn btn-sm btn-primary" type="submit"><?= e(t('submit_offer')) ?></button>
<a class="btn btn-sm btn-outline-dark" href="<?= e(url_with_lang('shipment_detail.php', ['id' => $row['id']])) ?>">
<?= e(t('view')) ?>
</a>
<input class="form-control form-control-sm border-secondary shadow-none" style="width: 120px;" name="offer_owner" placeholder="<?= e(t('offer_owner')) ?>" value="<?= e($ownerName) ?>" required>
<div class="input-group input-group-sm" style="width: 110px;">
<span class="input-group-text">$</span>
<input class="form-control border-secondary shadow-none" name="offer_price" type="number" step="0.01" min="0.1" placeholder="<?= e(t('offer_price')) ?>" required>
</div>
<button class="btn btn-sm btn-primary rounded px-3" type="submit"><i class="bi bi-send-fill"></i></button>
<a class="btn btn-sm btn-outline-dark rounded ms-1" href="<?= e(url_with_lang('shipment_detail.php', ['id' => $row['id']])) ?>" title="<?= e(t('view')) ?>">
<i class="bi bi-eye"></i>
</a>
</form>
</td>
</tr>
@ -107,4 +188,4 @@ $flash = get_flash();
<?php endif; ?>
</div>
<?php render_footer(); ?>
<?php render_footer(); ?>