Autosave: 20260310-135333
This commit is contained in:
parent
031d73aec7
commit
4b01726905
@ -207,10 +207,14 @@ render_header('Manage Cities', 'admin', true);
|
||||
<td><?= e($city['name_en']) ?></td>
|
||||
<td><?= e((string)($city['name_ar'] ?? '-')) ?></td>
|
||||
<td class="text-end">
|
||||
<a class="btn btn-sm btn-outline-primary" href="<?= e(url_with_lang('admin_cities.php', ['edit_city' => (int)$city['id']])) ?>">Edit</a>
|
||||
<form method="post" class="d-inline" onsubmit="return confirm('Delete this city?');">
|
||||
<a class="btn btn-sm p-1 border-0 bg-transparent text-primary" href="<?= e(url_with_lang('admin_cities.php', ['edit_city' => (int)$city['id']])) ?>">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
<form method="post" class="d-inline m-0 p-0" onsubmit="return confirm('Delete this city?');">
|
||||
<input type="hidden" name="city_id" value="<?= e((string)$city['id']) ?>">
|
||||
<button type="submit" name="delete_city" class="btn btn-sm btn-outline-danger">Del</button>
|
||||
<button type="submit" name="delete_city" class="btn btn-sm p-1 border-0 bg-transparent text-danger">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@ -223,4 +227,4 @@ render_header('Manage Cities', 'admin', true);
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php render_footer(); ?>
|
||||
<?php render_footer(); ?>
|
||||
@ -109,88 +109,120 @@ render_header('Company Profile', 'admin', true);
|
||||
|
||||
<div class="panel p-4">
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Company / App Name</label>
|
||||
<input type="text" name="company_name" class="form-control" value="<?= e($currentName) ?>" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Contact Email</label>
|
||||
<input type="email" name="company_email" class="form-control" value="<?= e($currentEmail) ?>">
|
||||
<div class="form-text">Displayed in the footer.</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Contact Phone</label>
|
||||
<input type="text" name="company_phone" class="form-control" value="<?= e($currentPhone) ?>">
|
||||
<div class="form-text">Displayed in the footer.</div>
|
||||
</div>
|
||||
<ul class="nav nav-tabs mb-4" id="companySettingsTab" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="company-tab" data-bs-toggle="tab" data-bs-target="#company" type="button" role="tab" aria-controls="company" aria-selected="true">
|
||||
<i class="bi bi-building me-2"></i>Company Setting
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="legal-tab" data-bs-toggle="tab" data-bs-target="#legal" type="button" role="tab" aria-controls="legal" aria-selected="false">
|
||||
<i class="bi bi-file-earmark-text me-2"></i>Legal & Policies
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="privacy-tab" data-bs-toggle="tab" data-bs-target="#privacy" type="button" role="tab" aria-controls="privacy" aria-selected="false">
|
||||
<i class="bi bi-shield-lock me-2"></i>Privacy Policy
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Address</label>
|
||||
<input type="text" name="company_address" class="form-control" value="<?= e($currentAddress) ?>">
|
||||
<div class="form-text">Displayed in the footer.</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Platform Charge (%)</label>
|
||||
<div class="input-group">
|
||||
<input type="number" step="0.01" min="0" max="100" name="platform_charge_percentage" class="form-control" value="<?= e($currentPlatformCharge) ?>">
|
||||
<span class="input-group-text">%</span>
|
||||
<div class="tab-content" id="companySettingsTabContent">
|
||||
<!-- Tab 1: Company Setting -->
|
||||
<div class="tab-pane fade show active" id="company" role="tabpanel" aria-labelledby="company-tab">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Company / App Name</label>
|
||||
<input type="text" name="company_name" class="form-control" value="<?= e($currentName) ?>" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Contact Email</label>
|
||||
<input type="email" name="company_email" class="form-control" value="<?= e($currentEmail) ?>">
|
||||
<div class="form-text">Displayed in the footer.</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Contact Phone</label>
|
||||
<input type="text" name="company_phone" class="form-control" value="<?= e($currentPhone) ?>">
|
||||
<div class="form-text">Displayed in the footer.</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Address</label>
|
||||
<input type="text" name="company_address" class="form-control" value="<?= e($currentAddress) ?>">
|
||||
<div class="form-text">Displayed in the footer.</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Platform Charge (%)</label>
|
||||
<div class="input-group">
|
||||
<input type="number" step="0.01" min="0" max="100" name="platform_charge_percentage" class="form-control" value="<?= e($currentPlatformCharge) ?>">
|
||||
<span class="input-group-text">%</span>
|
||||
</div>
|
||||
<div class="form-text">Percentage applied as a platform fee.</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12">
|
||||
<hr class="my-2">
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Company Logo</label>
|
||||
<?php if ($currentLogo): ?>
|
||||
<div class="mb-2">
|
||||
<img src="<?= e($currentLogo) ?>" alt="Logo" height="40" class="border rounded p-1">
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<input type="file" name="logo_file" class="form-control" accept="image/*">
|
||||
<div class="form-text">Recommended size: 150x40px (PNG, JPG, SVG). Leave empty to keep current.</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Favicon</label>
|
||||
<?php if ($currentFavicon): ?>
|
||||
<div class="mb-2">
|
||||
<img src="<?= e($currentFavicon) ?>" alt="Favicon" height="32" class="border rounded p-1">
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<input type="file" name="favicon_file" class="form-control" accept="image/png, image/x-icon, image/svg+xml">
|
||||
<div class="form-text">Recommended size: 32x32px (ICO, PNG, SVG). Leave empty to keep current.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-text">Percentage applied as a platform fee.</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12">
|
||||
<hr class="my-2">
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Company Logo</label>
|
||||
<?php if ($currentLogo): ?>
|
||||
<div class="mb-2">
|
||||
<img src="<?= e($currentLogo) ?>" alt="Logo" height="40" class="border rounded p-1">
|
||||
|
||||
<!-- Tab 2: Legal & Policies -->
|
||||
<div class="tab-pane fade" id="legal" role="tabpanel" aria-labelledby="legal-tab">
|
||||
<div class="row g-3">
|
||||
<div class="col-12">
|
||||
<h5 class="fw-bold mb-3">Terms of Service</h5>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<input type="file" name="logo_file" class="form-control" accept="image/*">
|
||||
<div class="form-text">Recommended size: 150x40px (PNG, JPG, SVG). Leave empty to keep current.</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Favicon</label>
|
||||
<?php if ($currentFavicon): ?>
|
||||
<div class="mb-2">
|
||||
<img src="<?= e($currentFavicon) ?>" alt="Favicon" height="32" class="border rounded p-1">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">English</label>
|
||||
<textarea name="terms_en" class="form-control" rows="10" placeholder="Enter Terms of Service in English..."><?= e($currentTermsEn) ?></textarea>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<input type="file" name="favicon_file" class="form-control" accept="image/png, image/x-icon, image/svg+xml">
|
||||
<div class="form-text">Recommended size: 32x32px (ICO, PNG, SVG). Leave empty to keep current.</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Arabic</label>
|
||||
<textarea name="terms_ar" class="form-control" rows="10" dir="rtl" placeholder="أدخل شروط الخدمة باللغة العربية..."><?= e($currentTermsAr) ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12">
|
||||
<hr class="my-2">
|
||||
<h5 class="fw-bold mb-3">Legal & Policies</h5>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Terms of Service (English)</label>
|
||||
<textarea name="terms_en" class="form-control" rows="5" placeholder="Enter Terms of Service in English..."><?= e($currentTermsEn) ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Terms of Service (Arabic)</label>
|
||||
<textarea name="terms_ar" class="form-control" rows="5" dir="rtl" placeholder="أدخل شروط الخدمة باللغة العربية..."><?= e($currentTermsAr) ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Privacy Policy (English)</label>
|
||||
<textarea name="privacy_en" class="form-control" rows="5" placeholder="Enter Privacy Policy in English..."><?= e($currentPrivacyEn) ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Privacy Policy (Arabic)</label>
|
||||
<textarea name="privacy_ar" class="form-control" rows="5" dir="rtl" placeholder="أدخل سياسة الخصوصية باللغة العربية..."><?= e($currentPrivacyAr) ?></textarea>
|
||||
<!-- Tab 3: Privacy Policy -->
|
||||
<div class="tab-pane fade" id="privacy" role="tabpanel" aria-labelledby="privacy-tab">
|
||||
<div class="row g-3">
|
||||
<div class="col-12">
|
||||
<h5 class="fw-bold mb-3">Privacy Policy</h5>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">English</label>
|
||||
<textarea name="privacy_en" class="form-control" rows="10" placeholder="Enter Privacy Policy in English..."><?= e($currentPrivacyEn) ?></textarea>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Arabic</label>
|
||||
<textarea name="privacy_ar" class="form-control" rows="10" dir="rtl" placeholder="أدخل سياسة الخصوصية باللغة العربية..."><?= e($currentPrivacyAr) ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -201,4 +233,4 @@ render_header('Company Profile', 'admin', true);
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php render_footer(); ?>
|
||||
<?php render_footer(); ?>
|
||||
@ -164,10 +164,14 @@ render_header('Manage Countries', 'admin', true);
|
||||
<td><?= e($country['name_en']) ?></td>
|
||||
<td><?= e((string)($country['name_ar'] ?? '-')) ?></td>
|
||||
<td class="text-end">
|
||||
<a class="btn btn-sm btn-outline-primary" href="<?= e(url_with_lang('admin_countries.php', ['edit_country' => (int)$country['id']])) ?>">Edit</a>
|
||||
<form method="post" class="d-inline" onsubmit="return confirm('Delete this country and its cities?');">
|
||||
<a class="btn btn-sm p-1 border-0 bg-transparent text-primary" href="<?= e(url_with_lang('admin_countries.php', ['edit_country' => (int)$country['id']])) ?>">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
<form method="post" class="d-inline m-0 p-0" onsubmit="return confirm('Delete this country and its cities?');">
|
||||
<input type="hidden" name="country_id" value="<?= e((string)$country['id']) ?>">
|
||||
<button type="submit" name="delete_country" class="btn btn-sm btn-outline-danger">Del</button>
|
||||
<button type="submit" name="delete_country" class="btn btn-sm p-1 border-0 bg-transparent text-danger">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@ -180,4 +184,4 @@ render_header('Manage Countries', 'admin', true);
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php render_footer(); ?>
|
||||
<?php render_footer(); ?>
|
||||
@ -85,107 +85,113 @@ render_header(t('admin_dashboard'), 'admin', true);
|
||||
<div class="row g-0">
|
||||
<!-- 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 class="panel shadow-sm border-0 h-100 rounded-4 d-flex flex-column">
|
||||
<div class="panel-heading d-flex justify-content-between align-items-center">
|
||||
<h2 class="h5 mb-0 fw-bold text-white"><i class="bi bi-clock-history text-white-50 me-2"></i>Recent Shipments</h2>
|
||||
<span class="badge bg-white text-dark"><?= 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): ?>
|
||||
<div class="p-4 flex-grow-1 d-flex flex-column">
|
||||
<?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>
|
||||
<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">
|
||||
<a href="<?= e(url_with_lang('shipment_detail.php', ['id' => $row['id']])) ?>" class="btn btn-sm btn-outline-primary rounded-pill px-3">
|
||||
View Details
|
||||
</a>
|
||||
</td>
|
||||
<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>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</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">
|
||||
<a href="<?= e(url_with_lang('shipment_detail.php', ['id' => $row['id']])) ?>" class="btn btn-sm p-1 border-0 bg-transparent text-primary" title="View Details">
|
||||
<i class="bi bi-eye"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</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 class="panel shadow-sm border-0 h-100 rounded-4" style="background-color: #fafbfc;">
|
||||
<div class="panel-heading">
|
||||
<h2 class="h5 mb-0 fw-bold text-white"><i class="bi bi-lightning-charge text-warning me-2"></i>Quick Links</h2>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<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>
|
||||
|
||||
@ -129,7 +129,7 @@ render_header('Manage FAQs', 'admin', true);
|
||||
|
||||
<?php if ($editingFaq): ?>
|
||||
<div class="card border-0 shadow-sm mb-4">
|
||||
<div class="card-header bg-white border-bottom-0 pt-4 pb-0">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Edit FAQ</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@ -168,7 +168,7 @@ render_header('Manage FAQs', 'admin', true);
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="card border-0 shadow-sm mb-4">
|
||||
<div class="card-header bg-white border-bottom-0 pt-4 pb-0">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Add New FAQ</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@ -205,7 +205,7 @@ render_header('Manage FAQs', 'admin', true);
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-white border-bottom-0 pt-4 pb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Current FAQs</h5>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
@ -217,7 +217,7 @@ render_header('Manage FAQs', 'admin', true);
|
||||
<?php else: ?>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="table-light">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="ps-4">Sort</th>
|
||||
<th>Question (EN)</th>
|
||||
|
||||
@ -73,129 +73,158 @@ render_header('Integrations', 'admin', true);
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post">
|
||||
<!-- Thawani Payments Panel -->
|
||||
<div class="panel p-4 mb-4">
|
||||
<h3 class="h5 fw-bold mb-3 d-flex align-items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-credit-card me-2 text-primary" viewBox="0 0 16 16">
|
||||
<path d="M0 4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm2-1a1 1 0 0 0-1 1v1h14V4a1 1 0 0 0-1-1zm13 4H1v5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1z"/>
|
||||
<path d="M2 10a1 1 0 0 1 1-1h1a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1z"/>
|
||||
</svg>
|
||||
Thawani Payments Gateway
|
||||
</h3>
|
||||
<p class="text-muted small mb-4">Configure your Oman-based Thawani Pay integration to process shipment payments.</p>
|
||||
|
||||
<ul class="nav nav-tabs mb-4" id="integrationsTab" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="thawani-tab" data-bs-toggle="tab" data-bs-target="#thawani" type="button" role="tab" aria-controls="thawani" aria-selected="true">
|
||||
Thawani Gateway
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="whatsapp-tab" data-bs-toggle="tab" data-bs-target="#whatsapp" type="button" role="tab" aria-controls="whatsapp" aria-selected="false">
|
||||
Whatsapp Setting
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="smtp-tab" data-bs-toggle="tab" data-bs-target="#smtp" type="button" role="tab" aria-controls="smtp" aria-selected="false">
|
||||
SMTP Mail
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content" id="integrationsTabContent">
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Environment</label>
|
||||
<select name="thawani_environment" class="form-select">
|
||||
<option value="test" <?= $thawaniEnv === 'test' ? 'selected' : '' ?>>Test / Sandbox</option>
|
||||
<option value="live" <?= $thawaniEnv === 'live' ? 'selected' : '' ?>>Live / Production</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6"></div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Publishable Key</label>
|
||||
<input type="text" name="thawani_publishable_key" class="form-control" value="<?= e($thawaniPub) ?>" placeholder="pk_test_...">
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Secret Key</label>
|
||||
<input type="password" name="thawani_secret_key" class="form-control" value="<?= e($thawaniSec) ?>" placeholder="sk_test_...">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Wablas WhatsApp Gateway Panel -->
|
||||
<div class="panel p-4 mb-4">
|
||||
<h3 class="h5 fw-bold mb-3 d-flex align-items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-whatsapp me-2 text-success" viewBox="0 0 16 16">
|
||||
<path d="M13.601 2.326A7.85 7.85 0 0 0 7.994 0C3.627 0 .068 3.558.064 7.926c0 1.399.366 2.76 1.057 3.965L0 16l4.204-1.102a7.9 7.9 0 0 0 3.79.965h.004c4.368 0 7.926-3.558 7.93-7.93A7.9 7.9 0 0 0 13.6 2.326zM7.994 14.521a6.6 6.6 0 0 1-3.356-.92l-.24-.144-2.494.654.666-2.433-.156-.251a6.56 6.56 0 0 1-1.007-3.505c0-3.626 2.957-6.584 6.591-6.584a6.56 6.56 0 0 1 4.66 1.931 6.56 6.56 0 0 1 1.928 4.66c-.004 3.639-2.961 6.592-6.592 6.592m3.615-4.934c-.197-.099-1.17-.578-1.353-.646-.182-.065-.315-.099-.445.099-.133.197-.513.646-.627.775-.114.133-.232.148-.43.05-.197-.1-.836-.308-1.592-.985-.59-.525-.985-1.175-1.103-1.372-.114-.198-.011-.304.088-.403.087-.088.197-.232.296-.346.1-.114.133-.198.198-.33.065-.134.034-.248-.015-.347-.05-.099-.445-1.076-.612-1.47-.16-.389-.323-.335-.445-.34-.114-.007-.247-.007-.38-.007a.73.73 0 0 0-.529.247c-.182.198-.691.677-.691 1.654s.71 1.916.81 2.049c.098.133 1.394 2.132 3.383 2.992.47.205.84.326 1.129.418.475.152.904.129 1.246.08.38-.058 1.171-.48 1.338-.943.164-.464.164-.86.114-.943-.049-.084-.182-.133-.38-.232"/>
|
||||
</svg>
|
||||
Wablas WhatsApp Gateway
|
||||
</h3>
|
||||
<p class="text-muted small mb-4">Connect Wablas to automatically send WhatsApp notifications to Shippers and Truck Owners.</p>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-12 mb-3">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" name="whatsapp_enabled" id="whatsapp_enabled" value="1" <?= $whatsappEnabled === '1' ? 'checked' : '' ?>>
|
||||
<label class="form-check-label fw-bold" for="whatsapp_enabled">Enable WhatsApp Notifications</label>
|
||||
<!-- Thawani Payments Panel -->
|
||||
<div class="tab-pane fade show active" id="thawani" role="tabpanel" aria-labelledby="thawani-tab">
|
||||
<div class="panel p-4 mb-4">
|
||||
<h3 class="h5 fw-bold mb-3 d-flex align-items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-credit-card me-2 text-primary" viewBox="0 0 16 16">
|
||||
<path d="M0 4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm2-1a1 1 0 0 0-1 1v1h14V4a1 1 0 0 0-1-1zm13 4H1v5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1z"/>
|
||||
<path d="M2 10a1 1 0 0 1 1-1h1a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1z"/>
|
||||
</svg>
|
||||
Thawani Payments Gateway
|
||||
</h3>
|
||||
<p class="text-muted small mb-4">Configure your Oman-based Thawani Pay integration to process shipment payments.</p>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Environment</label>
|
||||
<select name="thawani_environment" class="form-select">
|
||||
<option value="test" <?= $thawaniEnv === 'test' ? 'selected' : '' ?>>Test / Sandbox</option>
|
||||
<option value="live" <?= $thawaniEnv === 'live' ? 'selected' : '' ?>>Live / Production</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6"></div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Publishable Key</label>
|
||||
<input type="text" name="thawani_publishable_key" class="form-control" value="<?= e($thawaniPub) ?>" placeholder="pk_test_...">
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Secret Key</label>
|
||||
<input type="password" name="thawani_secret_key" class="form-control" value="<?= e($thawaniSec) ?>" placeholder="sk_test_...">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Wablas Server Domain</label>
|
||||
<input type="text" name="wablas_domain" class="form-control" value="<?= e($wablasDomain) ?>" placeholder="e.g. https://solo.wablas.com">
|
||||
<div class="form-text">Your assigned server node from the Wablas dashboard.</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">API Token</label>
|
||||
<input type="password" name="wablas_api_token" class="form-control" value="<?= e($wablasToken) ?>" placeholder="API Token">
|
||||
</div>
|
||||
<!-- Wablas WhatsApp Gateway Panel -->
|
||||
<div class="tab-pane fade" id="whatsapp" role="tabpanel" aria-labelledby="whatsapp-tab">
|
||||
<div class="panel p-4 mb-4">
|
||||
<h3 class="h5 fw-bold mb-3 d-flex align-items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-whatsapp me-2 text-success" viewBox="0 0 16 16">
|
||||
<path d="M13.601 2.326A7.85 7.85 0 0 0 7.994 0C3.627 0 .068 3.558.064 7.926c0 1.399.366 2.76 1.057 3.965L0 16l4.204-1.102a7.9 7.9 0 0 0 3.79.965h.004c4.368 0 7.926-3.558 7.93-7.93A7.9 7.9 0 0 0 13.6 2.326zM7.994 14.521a6.6 6.6 0 0 1-3.356-.92l-.24-.144-2.494.654.666-2.433-.156-.251a6.56 6.56 0 0 1-1.007-3.505c0-3.626 2.957-6.584 6.591-6.584a6.56 6.56 0 0 1 4.66 1.931 6.56 6.56 0 0 1 1.928 4.66c-.004 3.639-2.961 6.592-6.592 6.592m3.615-4.934c-.197-.099-1.17-.578-1.353-.646-.182-.065-.315-.099-.445.099-.133.197-.513.646-.627.775-.114.133-.232.148-.43.05-.197-.1-.836-.308-1.592-.985-.59-.525-.985-1.175-1.103-1.372-.114-.198-.011-.304.088-.403.087-.088.197-.232.296-.346.1-.114.133-.198.198-.33.065-.134.034-.248-.015-.347-.05-.099-.445-1.076-.612-1.47-.16-.389-.323-.335-.445-.34-.114-.007-.247-.007-.38-.007a.73.73 0 0 0-.529.247c-.182.198-.691.677-.691 1.654s.71 1.916.81 2.049c.098.133 1.394 2.132 3.383 2.992.47.205.84.326 1.129.418.475.152.904.129 1.246.08.38-.058 1.171-.48 1.338-.943.164-.464.164-.86.114-.943-.049-.084-.182-.133-.38-.232"/>
|
||||
</svg>
|
||||
Wablas WhatsApp Gateway
|
||||
</h3>
|
||||
<p class="text-muted small mb-4">Connect Wablas to automatically send WhatsApp notifications to Shippers and Truck Owners.</p>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Secret Key</label>
|
||||
<input type="password" name="wablas_secret_key" class="form-control" value="<?= e($wablasSecret) ?>" placeholder="Secret Key">
|
||||
<div class="form-text">Optional. Provide if your Wablas webhooks require signature verification.</div>
|
||||
<div class="row g-0">
|
||||
<div class="col-12 mb-3">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" name="whatsapp_enabled" id="whatsapp_enabled" value="1" <?= $whatsappEnabled === '1' ? 'checked' : '' ?>>
|
||||
<label class="form-check-label fw-bold" for="whatsapp_enabled">Enable WhatsApp Notifications</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Wablas Server Domain</label>
|
||||
<input type="text" name="wablas_domain" class="form-control" value="<?= e($wablasDomain) ?>" placeholder="e.g. https://solo.wablas.com">
|
||||
<div class="form-text">Your assigned server node from the Wablas dashboard.</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">API Token</label>
|
||||
<input type="password" name="wablas_api_token" class="form-control" value="<?= e($wablasToken) ?>" placeholder="API Token">
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Secret Key</label>
|
||||
<input type="password" name="wablas_secret_key" class="form-control" value="<?= e($wablasSecret) ?>" placeholder="Secret Key">
|
||||
<div class="form-text">Optional. Provide if your Wablas webhooks require signature verification.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SMTP Mail Gateway Panel -->
|
||||
<div class="panel p-4 mb-4">
|
||||
<h3 class="h5 fw-bold mb-3 d-flex align-items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-envelope me-2 text-danger" viewBox="0 0 16 16">
|
||||
<path d="M0 4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4Zm2-1a1 1 0 0 0-1 1v.217l7 4.2 7-4.2V4a1 1 0 0 0-1-1H2Zm13 2.383-4.708 2.825L15 11.105V5.383Zm-.034 6.876-5.64-3.471L8 9.583l-1.326-.795-5.64 3.47A1 1 0 0 0 2 13h12a1 1 0 0 0 .966-.741ZM1 11.105l4.708-2.897L1 5.383v5.722Z"/>
|
||||
</svg>
|
||||
SMTP Mail Settings
|
||||
</h3>
|
||||
<p class="text-muted small mb-4">Configure your SMTP server to send emails and system notifications.</p>
|
||||
|
||||
<!-- SMTP Mail Gateway Panel -->
|
||||
<div class="tab-pane fade" id="smtp" role="tabpanel" aria-labelledby="smtp-tab">
|
||||
<div class="panel p-4 mb-4">
|
||||
<h3 class="h5 fw-bold mb-3 d-flex align-items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-envelope me-2 text-danger" viewBox="0 0 16 16">
|
||||
<path d="M0 4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4Zm2-1a1 1 0 0 0-1 1v.217l7 4.2 7-4.2V4a1 1 0 0 0-1-1H2Zm13 2.383-4.708 2.825L15 11.105V5.383Zm-.034 6.876-5.64-3.471L8 9.583l-1.326-.795-5.64 3.47A1 1 0 0 0 2 13h12a1 1 0 0 0 .966-.741ZM1 11.105l4.708-2.897L1 5.383v5.722Z"/>
|
||||
</svg>
|
||||
SMTP Mail Settings
|
||||
</h3>
|
||||
<p class="text-muted small mb-4">Configure your SMTP server to send emails and system notifications.</p>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">SMTP Host</label>
|
||||
<input type="text" name="smtp_host" class="form-control" value="<?= e($smtpHost) ?>" placeholder="smtp.example.com">
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label fw-bold">SMTP Port</label>
|
||||
<input type="number" name="smtp_port" class="form-control" value="<?= e($smtpPort) ?>" placeholder="587">
|
||||
</div>
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">SMTP Host</label>
|
||||
<input type="text" name="smtp_host" class="form-control" value="<?= e($smtpHost) ?>" placeholder="smtp.example.com">
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label fw-bold">SMTP Port</label>
|
||||
<input type="number" name="smtp_port" class="form-control" value="<?= e($smtpPort) ?>" placeholder="587">
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label fw-bold">Security</label>
|
||||
<select name="smtp_secure" class="form-select">
|
||||
<option value="tls" <?= $smtpSecure === 'tls' ? 'selected' : '' ?>>TLS</option>
|
||||
<option value="ssl" <?= $smtpSecure === 'ssl' ? 'selected' : '' ?>>SSL</option>
|
||||
<option value="none" <?= $smtpSecure === 'none' || $smtpSecure === '' ? 'selected' : '' ?>>None</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label fw-bold">Security</label>
|
||||
<select name="smtp_secure" class="form-select">
|
||||
<option value="tls" <?= $smtpSecure === 'tls' ? 'selected' : '' ?>>TLS</option>
|
||||
<option value="ssl" <?= $smtpSecure === 'ssl' ? 'selected' : '' ?>>SSL</option>
|
||||
<option value="none" <?= $smtpSecure === 'none' || $smtpSecure === '' ? 'selected' : '' ?>>None</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">SMTP Username</label>
|
||||
<input type="text" name="smtp_user" class="form-control" value="<?= e($smtpUser) ?>" placeholder="user@example.com">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">SMTP Username</label>
|
||||
<input type="text" name="smtp_user" class="form-control" value="<?= e($smtpUser) ?>" placeholder="user@example.com">
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">SMTP Password</label>
|
||||
<input type="password" name="smtp_pass" class="form-control" value="<?= e($smtpPass) ?>" placeholder="••••••••">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">SMTP Password</label>
|
||||
<input type="password" name="smtp_pass" class="form-control" value="<?= e($smtpPass) ?>" placeholder="••••••••">
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Mail From Address</label>
|
||||
<input type="email" name="mail_from" class="form-control" value="<?= e($mailFrom) ?>" placeholder="noreply@example.com">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Mail From Address</label>
|
||||
<input type="email" name="mail_from" class="form-control" value="<?= e($mailFrom) ?>" placeholder="noreply@example.com">
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Mail From Name</label>
|
||||
<input type="text" name="mail_from_name" class="form-control" value="<?= e($mailFromName) ?>" placeholder="CargoLink System">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Mail From Name</label>
|
||||
<input type="text" name="mail_from_name" class="form-control" value="<?= e($mailFromName) ?>" placeholder="CargoLink System">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end">
|
||||
|
||||
<div class="d-flex justify-content-end mt-4">
|
||||
<button type="submit" class="btn btn-primary px-5">Save Integrations</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@ -222,12 +222,16 @@ render_header(t('app_name') . ' - Landing Pages', 'admin', true);
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="<?= e(url_with_lang('admin_landing_pages.php', ['edit' => $sec['id']])) ?>" class="btn btn-sm btn-outline-primary">Edit</a>
|
||||
<a href="<?= e(url_with_lang('admin_landing_pages.php', ['edit' => $sec['id']])) ?>" class="btn btn-sm p-1 border-0 bg-transparent text-primary" title="Edit">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
<?php if ($sec['section_type'] === 'custom'): ?>
|
||||
<form action="<?= e(url_with_lang('admin_landing_pages.php')) ?>" method="POST" onsubmit="return confirm('Are you sure you want to delete this section?');" style="display:inline;">
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<input type="hidden" name="id" value="<?= e($sec['id']) ?>">
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger">Delete</button>
|
||||
<button type="submit" class="btn btn-sm p-1 border-0 bg-transparent text-danger" title="Delete">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
@ -185,8 +185,8 @@ render_header('Notification Templates', 'admin', true);
|
||||
<td><?= e($t['email_subject_en']) ?></td>
|
||||
<td><?= e($t['email_subject_ar']) ?></td>
|
||||
<td class="text-end pe-4">
|
||||
<a href="<?= e(url_with_lang('admin_notification_templates.php', ['action' => 'edit', 'id' => $t['id']])) ?>" class="btn btn-sm btn-outline-primary">
|
||||
Edit
|
||||
<a href="<?= e(url_with_lang('admin_notification_templates.php', ['action' => 'edit', 'id' => $t['id']])) ?>" class="btn btn-sm p-1 border-0 bg-transparent text-primary" title="Edit">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@ -166,12 +166,12 @@ render_header(t('nav_platform_users'), 'platform_users', true);
|
||||
<td><?= e($user['email']) ?></td>
|
||||
<td class="text-muted small"><?= e(date('M j, Y', strtotime($user['created_at']))) ?></td>
|
||||
<td class="pe-4 text-end">
|
||||
<button class="btn btn-sm btn-outline-primary rounded-pill px-3 me-1" onclick="editUser(<?= e(json_encode($user)) ?>)">
|
||||
<i class="bi bi-pencil-fill me-1"></i><?= e(t('edit_user')) ?>
|
||||
<button class="btn btn-sm p-1 border-0 bg-transparent text-primary" onclick="editUser(<?= e(json_encode($user)) ?>)">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
<?php if ($user['id'] !== $_SESSION['user_id']): ?>
|
||||
<button class="btn btn-sm btn-outline-danger rounded-pill px-3" onclick="confirmDelete(<?= $user['id'] ?>)">
|
||||
<i class="bi bi-trash-fill"></i>
|
||||
<button class="btn btn-sm p-1 border-0 bg-transparent text-danger" onclick="confirmDelete(<?= $user['id'] ?>)">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
|
||||
@ -3,19 +3,29 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/includes/layout.php';
|
||||
|
||||
$errors = [];
|
||||
$flash = null;
|
||||
|
||||
$id = (int)($_GET['id'] ?? 0);
|
||||
$isAjax = isset($_GET['ajax']) && $_GET['ajax'] === '1';
|
||||
|
||||
if (!$id) {
|
||||
if ($isAjax) {
|
||||
echo json_encode(['success' => false, 'message' => 'Invalid ID']);
|
||||
exit;
|
||||
}
|
||||
header('Location: admin_shipments.php'); exit;
|
||||
}
|
||||
|
||||
$errors = [];
|
||||
$flash = null;
|
||||
|
||||
$stmt = db()->prepare("SELECT * FROM shipments WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$shipment = $stmt->fetch();
|
||||
|
||||
if (!$shipment) {
|
||||
if ($isAjax) {
|
||||
echo json_encode(['success' => false, 'message' => 'Shipment not found']);
|
||||
exit;
|
||||
}
|
||||
header('Location: admin_shipments.php'); exit;
|
||||
}
|
||||
|
||||
@ -85,6 +95,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
]);
|
||||
$flash = "Shipment updated successfully.";
|
||||
|
||||
if ($isAjax) {
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['success' => true, 'message' => $flash]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Refresh data
|
||||
$stmt->execute([$id]);
|
||||
$shipment = $stmt->fetch();
|
||||
@ -107,9 +123,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$destination_cities_list = $stmt->fetchAll();
|
||||
}
|
||||
}
|
||||
|
||||
if ($isAjax && $errors) {
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['success' => false, 'message' => implode('<br>', $errors)]);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
render_header('Edit Shipment', 'admin', true);
|
||||
// -- OUTPUT START --
|
||||
if (!$isAjax):
|
||||
render_header('Edit Shipment', 'admin', true);
|
||||
?>
|
||||
|
||||
<div class="row g-0">
|
||||
@ -143,127 +167,154 @@ render_header('Edit Shipment', 'admin', true);
|
||||
</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="panel">
|
||||
<div class="panel-heading">Shipment Details</div>
|
||||
<div class="p-4">
|
||||
<?php endif; // End non-ajax wrapper ?>
|
||||
|
||||
<!-- Origin Selection -->
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Origin Country</label>
|
||||
<select class="form-select" id="origin_country">
|
||||
<option value="">Select Country</option>
|
||||
<?php foreach ($countries as $c): ?>
|
||||
<option value="<?= e($c['id']) ?>" <?= $c['id'] == $origin_country_id ? 'selected' : '' ?>>
|
||||
<?= e($c['name_en']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Origin City</label>
|
||||
<select class="form-select" name="origin_city" id="origin_city" required>
|
||||
<option value="">Select City</option>
|
||||
<!-- If shipment has a city but we couldn't match a country, preserve the value as a fallback option -->
|
||||
<?php if (!$origin_country_id && $shipment['origin_city']): ?>
|
||||
<option value="<?= e($shipment['origin_city']) ?>" selected><?= e($shipment['origin_city']) ?></option>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php foreach ($origin_cities_list as $city): ?>
|
||||
<option value="<?= e($city['name_en']) ?>" <?= $city['name_en'] === $shipment['origin_city'] ? 'selected' : '' ?>>
|
||||
<?= e($city['name_en']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<form method="post" action="admin_shipment_edit.php?id=<?= $id ?><?= $isAjax ? '&ajax=1' : '' ?>" class="ajax-form">
|
||||
<?php if ($isAjax): ?>
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Edit Shipment #<?= e((string)$shipment['id']) ?></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="form-errors" class="alert alert-danger d-none"></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Destination Selection -->
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Destination Country</label>
|
||||
<select class="form-select" id="destination_country">
|
||||
<option value="">Select Country</option>
|
||||
<?php foreach ($countries as $c): ?>
|
||||
<option value="<?= e($c['id']) ?>" <?= $c['id'] == $destination_country_id ? 'selected' : '' ?>>
|
||||
<?= e($c['name_en']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Destination City</label>
|
||||
<select class="form-select" name="destination_city" id="destination_city" required>
|
||||
<option value="">Select City</option>
|
||||
<!-- Fallback for unmapped city -->
|
||||
<?php if (!$destination_country_id && $shipment['destination_city']): ?>
|
||||
<option value="<?= e($shipment['destination_city']) ?>" selected><?= e($shipment['destination_city']) ?></option>
|
||||
<?php endif; ?>
|
||||
<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>
|
||||
|
||||
<?php foreach ($destination_cities_list as $city): ?>
|
||||
<option value="<?= e($city['name_en']) ?>" <?= $city['name_en'] === $shipment['destination_city'] ? 'selected' : '' ?>>
|
||||
<?= e($city['name_en']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<!-- Origin Selection -->
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Origin Country</label>
|
||||
<select class="form-select" id="origin_country">
|
||||
<option value="">Select Country</option>
|
||||
<?php foreach ($countries as $c): ?>
|
||||
<option value="<?= e($c['id']) ?>" <?= $c['id'] == $origin_country_id ? 'selected' : '' ?>>
|
||||
<?= e($c['name_en']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Origin City</label>
|
||||
<select class="form-select" name="origin_city" id="origin_city" required>
|
||||
<option value="">Select City</option>
|
||||
<!-- If shipment has a city but we couldn't match a country, preserve the value as a fallback option -->
|
||||
<?php if (!$origin_country_id && $shipment['origin_city']): ?>
|
||||
<option value="<?= e($shipment['origin_city']) ?>" selected><?= e($shipment['origin_city']) ?></option>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php foreach ($origin_cities_list as $city): ?>
|
||||
<option value="<?= e($city['name_en']) ?>" <?= $city['name_en'] === $shipment['origin_city'] ? 'selected' : '' ?>>
|
||||
<?= e($city['name_en']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</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>
|
||||
<!-- Destination Selection -->
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Destination Country</label>
|
||||
<select class="form-select" id="destination_country">
|
||||
<option value="">Select Country</option>
|
||||
<?php foreach ($countries as $c): ?>
|
||||
<option value="<?= e($c['id']) ?>" <?= $c['id'] == $destination_country_id ? 'selected' : '' ?>>
|
||||
<?= e($c['name_en']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Destination City</label>
|
||||
<select class="form-select" name="destination_city" id="destination_city" required>
|
||||
<option value="">Select City</option>
|
||||
<!-- Fallback for unmapped city -->
|
||||
<?php if (!$destination_country_id && $shipment['destination_city']): ?>
|
||||
<option value="<?= e($shipment['destination_city']) ?>" selected><?= e($shipment['destination_city']) ?></option>
|
||||
<?php endif; ?>
|
||||
|
||||
<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']) ?>">
|
||||
<?php foreach ($destination_cities_list as $city): ?>
|
||||
<option value="<?= e($city['name_en']) ?>" <?= $city['name_en'] === $shipment['destination_city'] ? 'selected' : '' ?>>
|
||||
<?= e($city['name_en']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</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="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>
|
||||
<?php if ($isAjax): ?>
|
||||
</div> <!-- modal-body end -->
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary">Save Changes</button>
|
||||
</div>
|
||||
<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>
|
||||
<?php else: ?>
|
||||
<div class="mt-4 pt-3 border-top d-flex justify-content-end">
|
||||
<button type="submit" class="btn btn-primary">Save Changes</button>
|
||||
</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>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
|
||||
<?php if (!$isAjax): ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
(function() {
|
||||
function setupCityLoader(countrySelectId, citySelectId) {
|
||||
const countrySelect = document.getElementById(countrySelectId);
|
||||
const citySelect = document.getElementById(citySelectId);
|
||||
|
||||
if (!countrySelect || !citySelect) return;
|
||||
|
||||
countrySelect.addEventListener('change', function() {
|
||||
const countryId = this.value;
|
||||
citySelect.innerHTML = '<option value="">Loading...</option>';
|
||||
@ -295,7 +346,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
setupCityLoader('origin_country', 'origin_city');
|
||||
setupCityLoader('destination_country', 'destination_city');
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
<?php render_footer(); ?>
|
||||
<?php if (!$isAjax) render_footer(); ?>
|
||||
@ -164,24 +164,20 @@ render_header('Manage Shipments', 'admin', true);
|
||||
</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 href="shipment_detail.php?id=<?= e((string)$shipment['id']) ?>" class="btn btn-sm p-1 border-0 bg-transparent text-primary" title="View Shipment">
|
||||
<i class="bi bi-eye"></i>
|
||||
</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 href="admin_shipment_edit.php?id=<?= e((string)$shipment['id']) ?>"
|
||||
class="btn btn-sm p-1 border-0 bg-transparent text-primary ajax-modal-trigger"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#editModal"
|
||||
title="Edit Shipment">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</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 type="submit" name="action" value="delete" class="btn btn-sm p-1 border-0 bg-transparent text-danger" onclick="return confirm('Delete this shipment?');" title="Delete">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
@ -215,4 +211,109 @@ render_header('Manage Shipments', 'admin', true);
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Modal -->
|
||||
<div class="modal fade" id="editModal" tabindex="-1" aria-labelledby="editModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="editModalLabel">Edit</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body text-center p-5">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const editModal = document.getElementById('editModal');
|
||||
if (!editModal) return;
|
||||
|
||||
editModal.addEventListener('show.bs.modal', function (event) {
|
||||
const button = event.relatedTarget;
|
||||
if (!button.classList.contains('ajax-modal-trigger')) return;
|
||||
|
||||
const url = button.getAttribute('href') + '&ajax=1';
|
||||
const modalContent = editModal.querySelector('.modal-content');
|
||||
|
||||
modalContent.innerHTML = `
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Loading...</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body text-center p-5">
|
||||
<div class="spinner-border text-primary" role="status"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
fetch(url)
|
||||
.then(response => response.text())
|
||||
.then(html => {
|
||||
if (html.startsWith('{')) {
|
||||
const data = JSON.parse(html);
|
||||
modalContent.innerHTML = `<div class="modal-header"><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div><div class="modal-body"><div class="alert alert-danger">${data.message}</div></div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
modalContent.innerHTML = html;
|
||||
|
||||
modalContent.querySelectorAll('script').forEach(script => {
|
||||
const newScript = document.createElement('script');
|
||||
if (script.src) newScript.src = script.src;
|
||||
newScript.textContent = script.textContent;
|
||||
document.body.appendChild(newScript);
|
||||
});
|
||||
|
||||
const form = modalContent.querySelector('form');
|
||||
if (form) {
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const submitBtn = form.querySelector('button[type="submit"]');
|
||||
if(submitBtn) {
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Saving...';
|
||||
}
|
||||
|
||||
const formData = new FormData(form);
|
||||
fetch(form.action, { method: 'POST', body: formData })
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
if(submitBtn) {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = 'Save Changes';
|
||||
}
|
||||
const errDiv = form.querySelector('#form-errors');
|
||||
if(errDiv) {
|
||||
errDiv.classList.remove('d-none');
|
||||
errDiv.innerHTML = data.message;
|
||||
} else {
|
||||
alert(data.message || 'An error occurred');
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
if(submitBtn) {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = 'Save Changes';
|
||||
}
|
||||
alert('An error occurred while saving.');
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
modalContent.innerHTML = `<div class="modal-body text-danger">Failed to load form.</div>`;
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php render_footer(); ?>
|
||||
@ -4,7 +4,13 @@ declare(strict_types=1);
|
||||
require_once __DIR__ . '/includes/layout.php';
|
||||
|
||||
$userId = (int)($_GET['id'] ?? 0);
|
||||
$isAjax = isset($_GET['ajax']) && $_GET['ajax'] === '1';
|
||||
|
||||
if ($userId <= 0) {
|
||||
if ($isAjax) {
|
||||
echo json_encode(['success' => false, 'message' => 'Invalid ID']);
|
||||
exit;
|
||||
}
|
||||
header('Location: admin_shippers.php');
|
||||
exit;
|
||||
}
|
||||
@ -24,6 +30,10 @@ $stmt->execute([$userId]);
|
||||
$shipper = $stmt->fetch();
|
||||
|
||||
if (!$shipper) {
|
||||
if ($isAjax) {
|
||||
echo json_encode(['success' => false, 'message' => 'Shipper not found']);
|
||||
exit;
|
||||
}
|
||||
header('Location: admin_shippers.php');
|
||||
exit;
|
||||
}
|
||||
@ -80,6 +90,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
db()->commit();
|
||||
$flash = 'Shipper profile updated successfully.';
|
||||
|
||||
if ($isAjax) {
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['success' => true, 'message' => $flash]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Refresh data
|
||||
$shipper['full_name'] = $fullName;
|
||||
$shipper['email'] = $email;
|
||||
@ -99,9 +115,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($isAjax && $errors) {
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['success' => false, 'message' => implode('<br>', $errors)]);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
render_header('Edit Shipper', 'admin', true);
|
||||
// -- OUTPUT START --
|
||||
if (!$isAjax):
|
||||
render_header('Edit Shipper', 'admin', true);
|
||||
?>
|
||||
|
||||
<div class="row g-0">
|
||||
@ -125,7 +149,18 @@ render_header('Edit Shipper', 'admin', true);
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="panel p-4">
|
||||
<form method="post">
|
||||
<?php endif; // End non-ajax wrapper ?>
|
||||
|
||||
<form method="post" action="admin_shipper_edit.php?id=<?= $userId ?><?= $isAjax ? '&ajax=1' : '' ?>" class="ajax-form">
|
||||
<?php if ($isAjax): ?>
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Edit Shipper: <?= e($shipper['full_name']) ?></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="form-errors" class="alert alert-danger d-none"></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="full_name">Full Name</label>
|
||||
@ -169,7 +204,7 @@ render_header('Edit Shipper', 'admin', true);
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label" for="password">Password <small class="text-muted">(leave blank to keep)</small></label>
|
||||
<label class="form-label" for="password">Password <small class="text-muted">(leave blank)</small></label>
|
||||
<input type="password" name="password" id="password" class="form-control" autocomplete="new-password">
|
||||
</div>
|
||||
|
||||
@ -182,41 +217,60 @@ render_header('Edit Shipper', 'admin', true);
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<?php if ($isAjax): ?>
|
||||
</div> <!-- modal-body end -->
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary">Save Changes</button>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="col-12 mt-4">
|
||||
<button type="submit" class="btn btn-primary">Save Changes</button>
|
||||
<a href="admin_shippers.php" class="btn btn-outline-dark ms-2">Cancel</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php if (!$isAjax): ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<script>
|
||||
const allCities = <?= json_encode($cities, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||
(function() {
|
||||
const allCities = <?= json_encode($cities, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||
const countrySelect = document.querySelector('#country_id');
|
||||
const citySelect = document.querySelector('#city_id');
|
||||
const lang = '<?= $lang ?>';
|
||||
|
||||
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>';
|
||||
window.syncCities = function() {
|
||||
if (!countrySelect || !citySelect) return;
|
||||
|
||||
const countryId = countrySelect.value;
|
||||
const selectedValue = citySelect.dataset.selected || '';
|
||||
citySelect.innerHTML = '<option value="">Select city</option>';
|
||||
|
||||
allCities.forEach((city) => {
|
||||
if (String(city.country_id) !== String(countryId)) {
|
||||
return;
|
||||
}
|
||||
const option = document.createElement('option');
|
||||
option.value = city.id;
|
||||
option.textContent = <?= $lang === 'ar' ? '(city.name_ar || city.name_en)' : '(city.name_en || city.name_ar)' ?>;
|
||||
if (String(city.id) === String(selectedValue)) {
|
||||
option.selected = true;
|
||||
}
|
||||
citySelect.appendChild(option);
|
||||
});
|
||||
// clear selected after first render
|
||||
citySelect.dataset.selected = '';
|
||||
}
|
||||
syncCities();
|
||||
allCities.forEach((city) => {
|
||||
if (String(city.country_id) !== String(countryId)) {
|
||||
return;
|
||||
}
|
||||
const option = document.createElement('option');
|
||||
option.value = city.id;
|
||||
option.textContent = lang === 'ar' && city.name_ar ? city.name_ar : (city.name_en || city.name_ar);
|
||||
if (String(city.id) === String(selectedValue)) {
|
||||
option.selected = true;
|
||||
}
|
||||
citySelect.appendChild(option);
|
||||
});
|
||||
citySelect.dataset.selected = '';
|
||||
};
|
||||
|
||||
if (countrySelect && citySelect) {
|
||||
syncCities();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
<?php render_footer(); ?>
|
||||
<?php if (!$isAjax) render_footer(); ?>
|
||||
@ -167,32 +167,27 @@ render_header('Manage Shippers', 'admin', true);
|
||||
</td>
|
||||
<td class="text-end pe-4">
|
||||
<div class="d-inline-flex gap-1 align-items-center">
|
||||
<a href="admin_shipper_edit.php?id=<?= e((string)$shipper['id']) ?>" class="btn btn-sm btn-light border text-primary" title="Edit Shipper">
|
||||
<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 href="admin_shipper_edit.php?id=<?= e((string)$shipper['id']) ?>"
|
||||
class="btn btn-sm p-1 border-0 bg-transparent text-primary ajax-modal-trigger"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#editModal"
|
||||
title="Edit Shipper">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
<form method="post" class="d-inline m-0 p-0">
|
||||
<input type="hidden" name="user_id" value="<?= e((string)$shipper['id']) ?>">
|
||||
<?php if ($shipper['status'] !== 'active'): ?>
|
||||
<button type="submit" name="action" value="approve" class="btn btn-sm btn-success" title="Approve">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" class="bi bi-check-lg" viewBox="0 0 16 16">
|
||||
<path d="M12.736 3.97a.733.733 0 0 1 1.047 0c.286.289.29.756.01 1.05L7.88 12.01a.733.733 0 0 1-1.065.02L3.217 8.384a.757.757 0 0 1 0-1.06.733.733 0 0 1 1.047 0l3.052 3.093 5.4-6.425z"/>
|
||||
</svg>
|
||||
<button type="submit" name="action" value="approve" class="btn btn-sm p-1 border-0 bg-transparent text-success" title="Approve">
|
||||
<i class="bi bi-check-lg"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<?php if ($shipper['status'] !== 'rejected'): ?>
|
||||
<button type="submit" name="action" value="reject" class="btn btn-sm btn-warning" title="Reject">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" class="bi bi-x-lg" viewBox="0 0 16 16">
|
||||
<path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"/>
|
||||
</svg>
|
||||
<button type="submit" name="action" value="reject" class="btn btn-sm p-1 border-0 bg-transparent text-warning" title="Reject">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<button type="submit" name="action" value="delete" class="btn btn-sm btn-danger" onclick="return confirm('Delete this shipper forever?');" 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 type="submit" name="action" value="delete" class="btn btn-sm p-1 border-0 bg-transparent text-danger" onclick="return confirm('Delete this shipper forever?');" title="Delete">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
@ -226,4 +221,109 @@ render_header('Manage Shippers', 'admin', true);
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Modal -->
|
||||
<div class="modal fade" id="editModal" tabindex="-1" aria-labelledby="editModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="editModalLabel">Edit</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body text-center p-5">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const editModal = document.getElementById('editModal');
|
||||
if (!editModal) return;
|
||||
|
||||
editModal.addEventListener('show.bs.modal', function (event) {
|
||||
const button = event.relatedTarget;
|
||||
if (!button.classList.contains('ajax-modal-trigger')) return;
|
||||
|
||||
const url = button.getAttribute('href') + '&ajax=1';
|
||||
const modalContent = editModal.querySelector('.modal-content');
|
||||
|
||||
modalContent.innerHTML = `
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Loading...</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body text-center p-5">
|
||||
<div class="spinner-border text-primary" role="status"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
fetch(url)
|
||||
.then(response => response.text())
|
||||
.then(html => {
|
||||
if (html.startsWith('{')) {
|
||||
const data = JSON.parse(html);
|
||||
modalContent.innerHTML = `<div class="modal-header"><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div><div class="modal-body"><div class="alert alert-danger">${data.message}</div></div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
modalContent.innerHTML = html;
|
||||
|
||||
modalContent.querySelectorAll('script').forEach(script => {
|
||||
const newScript = document.createElement('script');
|
||||
if (script.src) newScript.src = script.src;
|
||||
newScript.textContent = script.textContent;
|
||||
document.body.appendChild(newScript);
|
||||
});
|
||||
|
||||
const form = modalContent.querySelector('form');
|
||||
if (form) {
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const submitBtn = form.querySelector('button[type="submit"]');
|
||||
if(submitBtn) {
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Saving...';
|
||||
}
|
||||
|
||||
const formData = new FormData(form);
|
||||
fetch(form.action, { method: 'POST', body: formData })
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
if(submitBtn) {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = 'Save Changes';
|
||||
}
|
||||
const errDiv = form.querySelector('#form-errors');
|
||||
if(errDiv) {
|
||||
errDiv.classList.remove('d-none');
|
||||
errDiv.innerHTML = data.message;
|
||||
} else {
|
||||
alert(data.message || 'An error occurred');
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
if(submitBtn) {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = 'Save Changes';
|
||||
}
|
||||
alert('An error occurred while saving.');
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
modalContent.innerHTML = `<div class="modal-body text-danger">Failed to load form.</div>`;
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php render_footer(); ?>
|
||||
@ -4,7 +4,13 @@ declare(strict_types=1);
|
||||
require_once __DIR__ . '/includes/layout.php';
|
||||
|
||||
$userId = (int)($_GET['id'] ?? 0);
|
||||
$isAjax = isset($_GET['ajax']) && $_GET['ajax'] === '1';
|
||||
|
||||
if ($userId <= 0) {
|
||||
if ($isAjax) {
|
||||
echo json_encode(['success' => false, 'message' => 'Invalid ID']);
|
||||
exit;
|
||||
}
|
||||
header('Location: admin_truck_owners.php');
|
||||
exit;
|
||||
}
|
||||
@ -26,6 +32,10 @@ $stmt->execute([$userId]);
|
||||
$owner = $stmt->fetch();
|
||||
|
||||
if (!$owner) {
|
||||
if ($isAjax) {
|
||||
echo json_encode(['success' => false, 'message' => 'Owner not found']);
|
||||
exit;
|
||||
}
|
||||
header('Location: admin_truck_owners.php');
|
||||
exit;
|
||||
}
|
||||
@ -96,7 +106,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
db()->commit();
|
||||
$flash = 'Truck Owner profile updated successfully.';
|
||||
|
||||
// Refresh data
|
||||
// If AJAX, return success immediately
|
||||
if ($isAjax) {
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['success' => true, 'message' => $flash]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Refresh data for non-ajax
|
||||
$owner['full_name'] = $fullName;
|
||||
$owner['email'] = $email;
|
||||
$owner['status'] = $status;
|
||||
@ -120,13 +137,21 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($isAjax && $errors) {
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['success' => false, 'message' => implode('<br>', $errors)]);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$idCards = json_decode($owner['id_card_path'] ?? '[]', true) ?: [];
|
||||
$regs = json_decode($owner['registration_path'] ?? '[]', true) ?: [];
|
||||
$pic = $owner['truck_pic_path'];
|
||||
|
||||
render_header('Edit Truck Owner', 'admin', true);
|
||||
// -- OUTPUT START --
|
||||
if (!$isAjax):
|
||||
render_header('Edit Truck Owner', 'admin', true);
|
||||
?>
|
||||
|
||||
<div class="row g-0">
|
||||
@ -150,8 +175,20 @@ render_header('Edit Truck Owner', 'admin', true);
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="panel p-4">
|
||||
<form method="post">
|
||||
<h5 class="mb-3">Personal Details</h5>
|
||||
<?php endif; // End non-ajax wrapper ?>
|
||||
|
||||
<!-- The Form (Shared) -->
|
||||
<form method="post" action="admin_truck_owner_edit.php?id=<?= $userId ?><?= $isAjax ? '&ajax=1' : '' ?>" class="ajax-form">
|
||||
<?php if ($isAjax): ?>
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Edit Truck Owner: <?= e($owner['full_name']) ?></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="form-errors" class="alert alert-danger d-none"></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<h5 class="mb-3 <?= $isAjax ? 'd-none' : '' ?>">Personal Details</h5>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="full_name">Full Name</label>
|
||||
@ -166,7 +203,7 @@ render_header('Edit Truck Owner', 'admin', true);
|
||||
<input type="text" name="phone" id="phone" class="form-control" value="<?= e((string)$owner['phone']) ?>" required>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label" for="password">Password <small class="text-muted">(leave blank to keep)</small></label>
|
||||
<label class="form-label" for="password">Password <small class="text-muted">(leave blank)</small></label>
|
||||
<input type="password" name="password" id="password" class="form-control" autocomplete="new-password">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
@ -220,7 +257,7 @@ render_header('Edit Truck Owner', 'admin', true);
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h5 class="mb-3 mt-5 border-top pt-4">Bank Details</h5>
|
||||
<h5 class="mb-3 mt-4 border-top pt-3">Bank Details</h5>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label" for="bank_account">Bank Account / IBAN</label>
|
||||
@ -236,7 +273,8 @@ render_header('Edit Truck Owner', 'admin', true);
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h5 class="mb-3 mt-5 border-top pt-4">Uploaded Documents</h5>
|
||||
<?php if (!$isAjax): // Hide heavy images in modal edit to keep it clean, or keep them? User said "big and long". Let's keep them but maybe minimized. Actually let's keep them, they are important. ?>
|
||||
<h5 class="mb-3 mt-4 border-top pt-3">Uploaded Documents</h5>
|
||||
<div class="row g-4 mb-4">
|
||||
<div class="col-md-4">
|
||||
<h6 class="text-muted mb-2">ID Card</h6>
|
||||
@ -277,41 +315,64 @@ render_header('Edit Truck Owner', 'admin', true);
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($isAjax): ?>
|
||||
</div> <!-- modal-body -->
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary">Save Changes</button>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="mt-4">
|
||||
<button type="submit" class="btn btn-primary">Save Changes</button>
|
||||
<a href="admin_truck_owners.php" class="btn btn-outline-dark ms-2">Cancel</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
</form>
|
||||
|
||||
<?php if (!$isAjax): ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<script>
|
||||
const allCities = <?= json_encode($cities, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||
(function() {
|
||||
const allCities = <?= json_encode($cities, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||
const countrySelect = document.querySelector('#country_id'); // Use querySelector to scope? No, IDs are unique.
|
||||
const citySelect = document.querySelector('#city_id');
|
||||
const lang = '<?= $lang ?>';
|
||||
|
||||
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>';
|
||||
window.syncCities = function() { // Expose globally for onchange
|
||||
if (!countrySelect || !citySelect) return;
|
||||
|
||||
const countryId = countrySelect.value;
|
||||
const selectedValue = citySelect.dataset.selected || '';
|
||||
citySelect.innerHTML = '<option value="">Select city</option>';
|
||||
|
||||
allCities.forEach((city) => {
|
||||
if (String(city.country_id) !== String(countryId)) {
|
||||
return;
|
||||
}
|
||||
const option = document.createElement('option');
|
||||
option.value = city.id;
|
||||
option.textContent = <?= $lang === 'ar' ? '(city.name_ar || city.name_en)' : '(city.name_en || city.name_ar)' ?>;
|
||||
if (String(city.id) === String(selectedValue)) {
|
||||
option.selected = true;
|
||||
}
|
||||
citySelect.appendChild(option);
|
||||
});
|
||||
// clear selected after first render
|
||||
citySelect.dataset.selected = '';
|
||||
}
|
||||
syncCities();
|
||||
allCities.forEach((city) => {
|
||||
if (String(city.country_id) !== String(countryId)) {
|
||||
return;
|
||||
}
|
||||
const option = document.createElement('option');
|
||||
option.value = city.id;
|
||||
option.textContent = lang === 'ar' && city.name_ar ? city.name_ar : (city.name_en || city.name_ar);
|
||||
if (String(city.id) === String(selectedValue)) {
|
||||
option.selected = true;
|
||||
}
|
||||
citySelect.appendChild(option);
|
||||
});
|
||||
citySelect.dataset.selected = '';
|
||||
};
|
||||
|
||||
// Initialize
|
||||
if (countrySelect && citySelect) {
|
||||
syncCities();
|
||||
// Re-bind change event if needed, but onchange in HTML handles it.
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
<?php render_footer(); ?>
|
||||
<?php if (!$isAjax) render_footer(); ?>
|
||||
@ -175,32 +175,27 @@ render_header('Manage Truck Owners', 'admin', true);
|
||||
</td>
|
||||
<td class="text-end pe-4">
|
||||
<div class="d-inline-flex gap-1 align-items-center">
|
||||
<a href="admin_truck_owner_edit.php?id=<?= e((string)$owner['id']) ?>" class="btn btn-sm btn-light border text-primary" title="Edit Owner">
|
||||
<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 href="admin_truck_owner_edit.php?id=<?= e((string)$owner['id']) ?>"
|
||||
class="btn btn-sm p-1 border-0 bg-transparent text-primary ajax-modal-trigger"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#editModal"
|
||||
title="Edit Owner">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
<form method="post" class="d-inline m-0 p-0">
|
||||
<input type="hidden" name="user_id" value="<?= e((string)$owner['id']) ?>">
|
||||
<?php if ($owner['status'] !== 'active'): ?>
|
||||
<button type="submit" name="action" value="approve" class="btn btn-sm btn-success" title="Approve">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" class="bi bi-check-lg" viewBox="0 0 16 16">
|
||||
<path d="M12.736 3.97a.733.733 0 0 1 1.047 0c.286.289.29.756.01 1.05L7.88 12.01a.733.733 0 0 1-1.065.02L3.217 8.384a.757.757 0 0 1 0-1.06.733.733 0 0 1 1.047 0l3.052 3.093 5.4-6.425z"/>
|
||||
</svg>
|
||||
<button type="submit" name="action" value="approve" class="btn btn-sm p-1 border-0 bg-transparent text-success" title="Approve">
|
||||
<i class="bi bi-check-lg"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<?php if ($owner['status'] !== 'rejected'): ?>
|
||||
<button type="submit" name="action" value="reject" class="btn btn-sm btn-warning" title="Reject">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" class="bi bi-x-lg" viewBox="0 0 16 16">
|
||||
<path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"/>
|
||||
</svg>
|
||||
<button type="submit" name="action" value="reject" class="btn btn-sm p-1 border-0 bg-transparent text-warning" title="Reject">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<button type="submit" name="action" value="delete" class="btn btn-sm btn-danger" onclick="return confirm('Delete this truck owner forever?');" 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 type="submit" name="action" value="delete" class="btn btn-sm p-1 border-0 bg-transparent text-danger" onclick="return confirm('Delete this truck owner forever?');" title="Delete">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
@ -234,6 +229,23 @@ render_header('Manage Truck Owners', 'admin', true);
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Modal Placeholder -->
|
||||
<div class="modal fade" id="editModal" tabindex="-1" aria-labelledby="editModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="editModalLabel">Edit</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body text-center p-5">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php foreach ($owners as $owner): ?>
|
||||
<?php
|
||||
$idCards = json_decode($owner['id_card_path'] ?? '[]', true) ?: [];
|
||||
@ -285,4 +297,106 @@ $pic = $owner['truck_pic_path'];
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const editModal = document.getElementById('editModal');
|
||||
if (!editModal) return;
|
||||
|
||||
editModal.addEventListener('show.bs.modal', function (event) {
|
||||
const button = event.relatedTarget;
|
||||
if (!button.classList.contains('ajax-modal-trigger')) return;
|
||||
|
||||
const url = button.getAttribute('href') + '&ajax=1';
|
||||
const modalContent = editModal.querySelector('.modal-content');
|
||||
|
||||
// Reset to loading state
|
||||
modalContent.innerHTML = `
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Loading...</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body text-center p-5">
|
||||
<div class="spinner-border text-primary" role="status"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
fetch(url)
|
||||
.then(response => response.text())
|
||||
.then(html => {
|
||||
// If the response is a JSON error (e.g. invalid ID), we should handle it.
|
||||
// But simplified: assuming HTML partial.
|
||||
if (html.startsWith('{')) {
|
||||
const data = JSON.parse(html);
|
||||
modalContent.innerHTML = `<div class="modal-header"><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div><div class="modal-body"><div class="alert alert-danger">${data.message}</div></div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
// Inject HTML
|
||||
// We strip the outer .modal-content if the partial includes it?
|
||||
// No, the partial returns the BODY and FOOTER usually.
|
||||
// My partial in edit php returns: <form>...<modal-header>...<modal-body>...<modal-footer>...</form>
|
||||
// So I should replace .modal-content content.
|
||||
// Wait, <form> cannot be a child of <div class="modal-content"> directly if it contains modal-header/body/footer?
|
||||
// Yes, it can. <div class="modal-content"><form>...</form></div> is valid.
|
||||
|
||||
modalContent.innerHTML = html;
|
||||
|
||||
// Execute scripts in the injected HTML
|
||||
modalContent.querySelectorAll('script').forEach(script => {
|
||||
const newScript = document.createElement('script');
|
||||
if (script.src) newScript.src = script.src;
|
||||
newScript.textContent = script.textContent;
|
||||
document.body.appendChild(newScript);
|
||||
});
|
||||
|
||||
// Bind form submission
|
||||
const form = modalContent.querySelector('form');
|
||||
if (form) {
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const submitBtn = form.querySelector('button[type="submit"]');
|
||||
if(submitBtn) {
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Saving...';
|
||||
}
|
||||
|
||||
const formData = new FormData(form);
|
||||
fetch(form.action, { method: 'POST', body: formData })
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
// Refresh the page to show updates
|
||||
location.reload();
|
||||
} else {
|
||||
if(submitBtn) {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = 'Save Changes';
|
||||
}
|
||||
const errDiv = form.querySelector('#form-errors');
|
||||
if(errDiv) {
|
||||
errDiv.classList.remove('d-none');
|
||||
errDiv.innerHTML = data.message;
|
||||
} else {
|
||||
alert(data.message || 'An error occurred');
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
if(submitBtn) {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = 'Save Changes';
|
||||
}
|
||||
alert('An error occurred while saving.');
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
modalContent.innerHTML = `<div class="modal-body text-danger">Failed to load form.</div>`;
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php render_footer(); ?>
|
||||
@ -10,6 +10,14 @@
|
||||
--success: #10b981;
|
||||
--warning: #f59e0b;
|
||||
--shadow: 0 10px 30px rgba(15, 23, 42, 0.05);
|
||||
|
||||
/* Sidebar Theme */
|
||||
--sidebar-bg: #000080;
|
||||
--sidebar-text: #ffffff;
|
||||
--sidebar-text-hover: #e2e8f0;
|
||||
--sidebar-active-bg: rgba(255, 255, 255, 0.15);
|
||||
--sidebar-active-text: #ffffff;
|
||||
--sidebar-border: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
body.app-body {
|
||||
@ -42,6 +50,18 @@ body.app-body {
|
||||
background: var(--surface);
|
||||
box-shadow: var(--shadow);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
overflow: hidden; /* Ensure headers/footers are clipped */
|
||||
}
|
||||
|
||||
/* New Panel Header Style */
|
||||
.panel-heading,
|
||||
.card-header {
|
||||
background-color: var(--sidebar-bg);
|
||||
color: #ffffff;
|
||||
padding: 16px 24px;
|
||||
border-bottom: 1px solid var(--sidebar-border);
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.hero-section {
|
||||
@ -149,13 +169,24 @@ body.app-body {
|
||||
color: #9333ea;
|
||||
}
|
||||
|
||||
/* Update Table Headers to match Sidebar */
|
||||
.table thead th {
|
||||
font-size: 13px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--muted);
|
||||
border-bottom: 2px solid #f1f5f9;
|
||||
padding-bottom: 12px;
|
||||
color: var(--sidebar-text);
|
||||
background-color: var(--sidebar-bg);
|
||||
border-bottom: 2px solid var(--sidebar-border);
|
||||
padding: 14px 16px; /* Increased padding */
|
||||
vertical-align: middle;
|
||||
}
|
||||
/* Ensure first and last th have rounded corners if table is isolated?
|
||||
Bootstrap tables usually handle this, but with custom bg we might need to be careful. */
|
||||
.table thead th:first-child {
|
||||
border-top-left-radius: 8px;
|
||||
}
|
||||
.table thead th:last-child {
|
||||
border-top-right-radius: 8px;
|
||||
}
|
||||
|
||||
.table tbody td {
|
||||
@ -236,34 +267,78 @@ body.app-body {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
/* Sidebar Styles Updated */
|
||||
.admin-sidebar {
|
||||
background-color: var(--sidebar-bg);
|
||||
color: var(--sidebar-text);
|
||||
position: sticky;
|
||||
top: 76px;
|
||||
height: calc(100vh - 76px);
|
||||
overflow-y: auto;
|
||||
border-radius: 0;
|
||||
/* Ensure it covers the parent's white bg */
|
||||
margin-right: -1px; /* Overlap border if needed */
|
||||
}
|
||||
|
||||
/* Sidebar Headings */
|
||||
.admin-sidebar h2 {
|
||||
color: #ffffff !important; /* Force white for headings */
|
||||
border-bottom: 1px solid var(--sidebar-border);
|
||||
padding-bottom: 12px;
|
||||
margin-bottom: 20px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
.admin-sidebar h2 i {
|
||||
color: var(--sidebar-active-bg) !important;
|
||||
}
|
||||
|
||||
/* Sidebar Links */
|
||||
.admin-nav-link {
|
||||
display: block;
|
||||
padding: 12px 16px;
|
||||
border-radius: 12px;
|
||||
color: #475569;
|
||||
border-radius: 8px; /* Slightly smaller radius for tighter list */
|
||||
color: var(--sidebar-text);
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
border: 1px solid transparent;
|
||||
transition: all 0.2s ease;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.admin-nav-link:hover {
|
||||
background: #f1f5f9;
|
||||
color: #0f172a;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
color: var(--sidebar-text-hover);
|
||||
}
|
||||
|
||||
.admin-nav-link.active {
|
||||
background: #eff6ff;
|
||||
color: var(--primary);
|
||||
border-color: #bfdbfe;
|
||||
background: var(--sidebar-active-bg);
|
||||
color: var(--sidebar-active-text);
|
||||
border-color: transparent;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* Collapsible Toggles in Sidebar */
|
||||
.admin-sidebar .nav-link {
|
||||
color: var(--sidebar-text) !important;
|
||||
}
|
||||
.admin-sidebar .nav-link:hover {
|
||||
color: var(--sidebar-text-hover) !important;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
/* Sub-menus borders */
|
||||
.admin-sidebar .border-start {
|
||||
border-color: var(--sidebar-border) !important;
|
||||
}
|
||||
|
||||
/* Logout button in sidebar */
|
||||
.admin-sidebar .btn-outline-danger {
|
||||
border-color: #ef4444;
|
||||
color: #ef4444;
|
||||
}
|
||||
.admin-sidebar .btn-outline-danger:hover {
|
||||
background: #ef4444;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
[dir="rtl"] .navbar .ms-auto {
|
||||
@ -293,5 +368,6 @@ body.app-body {
|
||||
position: static;
|
||||
height: auto;
|
||||
overflow-y: visible;
|
||||
background-color: var(--sidebar-bg); /* Keep dark on mobile too */
|
||||
}
|
||||
}
|
||||
@ -193,6 +193,7 @@ $translations = [
|
||||
'total_offers' => 'Total Offers',
|
||||
'won_shipments' => 'Won Shipments',
|
||||
'nav_platform_users' => 'Platform Users',
|
||||
'notification_templates' => 'Notification Templates',
|
||||
'manage_permissions' => 'Manage Permissions',
|
||||
'create_user' => 'Create User',
|
||||
'edit_user' => 'Edit User',
|
||||
@ -313,7 +314,7 @@ $translations = [
|
||||
'user_registration' => 'تسجيل المستخدم',
|
||||
'pages' => 'الصفحات',
|
||||
'faqs' => 'الأسئلة الشائعة',
|
||||
'landing_pages' => 'صفحات الهبوط',
|
||||
'landing_pages' => 'إعدادات الصفحة الرئيسية',
|
||||
'login_title' => 'مرحبًا بعودتك',
|
||||
'login_subtitle' => 'قم بتسجيل الدخول إلى حسابك للمتابعة',
|
||||
'email_address' => 'البريد الإلكتروني',
|
||||
@ -385,6 +386,7 @@ $translations = [
|
||||
'total_offers' => 'إجمالي العروض',
|
||||
'won_shipments' => 'الشحنات الفائزة',
|
||||
'nav_platform_users' => 'مستخدمو المنصة',
|
||||
'notification_templates' => 'قوالب الإشعارات',
|
||||
'manage_permissions' => 'إدارة الصلاحيات',
|
||||
'create_user' => 'إنشاء مستخدم',
|
||||
'edit_user' => 'تعديل المستخدم',
|
||||
@ -604,4 +606,4 @@ function has_permission(string $permissionSlug, ?int $userId = null): bool
|
||||
} catch (Throwable $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,12 +224,12 @@ function render_admin_sidebar(string $active = 'dashboard'): void
|
||||
<aside class="admin-sidebar d-flex flex-column h-100 py-4 px-3">
|
||||
<h2 class="h5 fw-bold mb-4 px-2"><i class="bi bi-shield-lock me-2 text-primary"></i><?= e(t('nav_admin')) ?></h2>
|
||||
<nav class="nav flex-column gap-1 flex-grow-1">
|
||||
<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><?= e(t('dashboard')) ?>
|
||||
</a>
|
||||
<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="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><?= e(t('settings')) ?></span>
|
||||
|
||||
@ -180,6 +180,19 @@ $translations_en = [
|
||||
'welcome_back_owner' => 'Find loads and submit your best rate.',
|
||||
'total_offers' => 'Total Offers',
|
||||
'won_shipments' => 'Won Shipments',
|
||||
'nav_platform_users' => 'Platform Users',
|
||||
'notification_templates' => 'Notification Templates',
|
||||
'manage_permissions' => 'Manage Permissions',
|
||||
'create_user' => 'Create User',
|
||||
'edit_user' => 'Edit User',
|
||||
'delete_user' => 'Delete User',
|
||||
'confirm_delete' => 'Are you sure you want to delete this user?',
|
||||
'permissions' => 'Permissions',
|
||||
'no_users' => 'No platform users found.',
|
||||
'user_created' => 'User created successfully.',
|
||||
'user_updated' => 'User updated successfully.',
|
||||
'user_deleted' => 'User deleted successfully.',
|
||||
'error_email_exists' => 'Email already exists.'
|
||||
];
|
||||
|
||||
$translations_ar = [
|
||||
@ -290,7 +303,7 @@ $translations_ar = [
|
||||
'user_registration' => 'تسجيل المستخدم',
|
||||
'pages' => 'الصفحات',
|
||||
'faqs' => 'الأسئلة الشائعة',
|
||||
'landing_pages' => 'صفحات الهبوط',
|
||||
'landing_pages' => 'إعدادات الصفحة الرئيسية',
|
||||
'login_title' => 'مرحبًا بعودتك',
|
||||
'login_subtitle' => 'قم بتسجيل الدخول إلى حسابك للمتابعة',
|
||||
'email_address' => 'البريد الإلكتروني',
|
||||
@ -378,4 +391,4 @@ $content = preg_replace($pattern, $new_translations . "\n\n", $content);
|
||||
|
||||
file_put_contents('includes/app.php', $content);
|
||||
echo "Updated translations in app.php\n";
|
||||
?>
|
||||
?>
|
||||
@ -291,8 +291,8 @@ $flash = get_flash();
|
||||
<?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 class="btn btn-sm p-1 border-0 bg-transparent text-primary" href="<?= e(url_with_lang('shipment_detail.php', ['id' => $row['id']])) ?>" title="<?= e(t('view')) ?>">
|
||||
<i class="bi bi-eye"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@ -174,8 +174,8 @@ $flash = get_flash();
|
||||
<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')) ?>">
|
||||
<button class="btn btn-sm p-1 border-0 bg-transparent text-primary" type="submit" title="Submit Offer"><i class="bi bi-send-fill"></i></button>
|
||||
<a class="btn btn-sm p-1 border-0 bg-transparent text-primary 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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user