350 lines
20 KiB
HTML
350 lines
20 KiB
HTML
{% extends 'base.html' %}
|
|
{% load i18n %}
|
|
|
|
{% block content %}
|
|
<div class="container py-5">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h1 class="mb-0">{% trans "Driver Dashboard" %}</h1>
|
|
<a href="{% url 'scan_qr' %}" class="btn btn-primary rounded-pill px-4">
|
|
<i class="bi bi-qr-code-scan me-2"></i> {% trans "Scan Parcel" %}
|
|
</a>
|
|
</div>
|
|
|
|
{% if not is_approved %}
|
|
<div class="alert alert-warning shadow-sm border-0 d-flex align-items-center mb-4" role="alert" style="border-radius: 12px;">
|
|
<i class="bi bi-hourglass-split fs-3 me-3 text-warning"></i>
|
|
<div>
|
|
<h5 class="alert-heading mb-1">{% trans "Account Pending Approval" %}</h5>
|
|
<p class="mb-0 text-muted">{% trans "Your driver account is currently under review by our administrators. You will be notified once your documents are verified and your account is approved to accept shipments." %}</p>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<ul class="nav nav-pills mb-4" id="pills-tab" role="tablist">
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link active" id="pills-available-tab" data-bs-toggle="pill" data-bs-target="#pills-available" type="button" role="tab">{% trans "Available Shipments" %}</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="pills-my-tab" data-bs-toggle="pill" data-bs-target="#pills-my" type="button" role="tab">{% trans "Active Deliveries" %}</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="pills-history-tab" data-bs-toggle="pill" data-bs-target="#pills-history" type="button" role="tab">{% trans "Transaction History" %}</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="pills-cancelled-tab" data-bs-toggle="pill" data-bs-target="#pills-cancelled" type="button" role="tab">{% trans "Cancelled Shipments" %}</button>
|
|
</li>
|
|
</ul>
|
|
|
|
<div class="tab-content" id="pills-tabContent">
|
|
<!-- Available Shipments -->
|
|
<div class="tab-pane fade show active" id="pills-available" role="tabpanel">
|
|
|
|
{% if is_approved %}
|
|
<!-- Toolbar -->
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h5 class="mb-0 text-muted">{% trans "Browse Shipments" %}</h5>
|
|
<div class="btn-group" role="group" aria-label="View toggle">
|
|
<button type="button" class="btn btn-outline-primary active" id="btn-grid-view">
|
|
<i class="bi bi-grid"></i> {% trans "Grid" %}
|
|
</button>
|
|
<button type="button" class="btn btn-outline-primary" id="btn-list-view">
|
|
<i class="bi bi-list"></i> {% trans "List" %}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{% if available_parcels %}
|
|
|
|
<!-- Grid View -->
|
|
<div id="view-grid" class="row g-4">
|
|
{% for parcel in available_parcels %}
|
|
<div class="col-md-6 col-lg-4">
|
|
<div class="card border-0 shadow-sm h-100" style="border-radius: 15px;">
|
|
<div class="card-body">
|
|
<h5 class="card-title">{{ parcel.description|truncatechars:30 }}</h5>
|
|
<p class="card-text mb-1 small"><strong>{% trans "Pickup" %}:</strong> {{ parcel.pickup_governate.name }} / {{ parcel.pickup_city.name }}</p>
|
|
<p class="card-text mb-3 small"><strong>{% trans "Delivery" %}:</strong> {{ parcel.delivery_governate.name }} / {{ parcel.delivery_city.name }}</p>
|
|
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<span class="text-muted small"><strong>{% trans "Distance" %}:</strong> {{ parcel.distance_km }} km</span>
|
|
<span class="text-muted small"><strong>{% trans "Weight" %}:</strong> {{ parcel.weight }} kg</span>
|
|
</div>
|
|
|
|
<!-- Bid/Price Highlight -->
|
|
<div class="bg-light p-2 rounded text-center mb-3 border border-primary border-opacity-25">
|
|
<small class="text-uppercase text-muted fw-bold" style="font-size: 0.7rem;">{% trans "Shipper's Offer (Bid)" %}</small>
|
|
<div class="text-primary fw-bold fs-4">{{ parcel.price }} <span class="fs-6">OMR</span></div>
|
|
</div>
|
|
|
|
<form action="{% url 'accept_parcel' parcel.id %}" method="POST">
|
|
{% csrf_token %}
|
|
<button type="submit" class="btn btn-masarx-primary w-100">{% trans "Accept Shipment" %}</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<!-- List View (Initially Hidden) -->
|
|
<div id="view-list" class="d-none">
|
|
<div class="d-flex flex-column gap-3">
|
|
{% for parcel in available_parcels %}
|
|
<div class="card border-0 shadow-sm rounded-3">
|
|
<div class="card-body p-3">
|
|
<div class="row align-items-center">
|
|
<div class="col-md-8 mb-3 mb-md-0">
|
|
<h5 class="card-title mb-2">{{ parcel.description|truncatechars:80 }}</h5>
|
|
<div class="d-flex flex-wrap gap-3 text-muted small">
|
|
<span class="d-flex align-items-center gap-1">
|
|
<i class="bi bi-geo-alt-fill text-primary"></i>
|
|
<strong>{% trans "From" %}:</strong> {{ parcel.pickup_governate.name }} / {{ parcel.pickup_city.name }}
|
|
</span>
|
|
<span class="d-flex align-items-center gap-1">
|
|
<i class="bi bi-geo-alt-fill text-danger"></i>
|
|
<strong>{% trans "To" %}:</strong> {{ parcel.delivery_governate.name }} / {{ parcel.delivery_city.name }}
|
|
</span>
|
|
<span class="d-flex align-items-center gap-1">
|
|
<i class="bi bi-signpost-2"></i> {{ parcel.distance_km }} km
|
|
</span>
|
|
<span class="d-flex align-items-center gap-1">
|
|
<i class="bi bi-box-seam"></i> {{ parcel.weight }} kg
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4 text-md-end">
|
|
<div class="d-flex flex-column align-items-md-end gap-2">
|
|
<div class="text-primary fw-bold fs-5">{{ parcel.price }} OMR</div>
|
|
<form action="{% url 'accept_parcel' parcel.id %}" method="POST" class="w-100 w-md-auto">
|
|
{% csrf_token %}
|
|
<button type="submit" class="btn btn-masarx-primary btn-sm w-100">{% trans "Accept" %}</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
{% if available_parcels.has_other_pages %}
|
|
<nav aria-label="Page navigation" class="mt-5">
|
|
<ul class="pagination justify-content-center">
|
|
{% if available_parcels.has_previous %}
|
|
<li class="page-item">
|
|
<a class="page-link" href="?page={{ available_parcels.previous_page_number }}" aria-label="Previous">
|
|
<span aria-hidden="true">«</span>
|
|
</a>
|
|
</li>
|
|
{% else %}
|
|
<li class="page-item disabled">
|
|
<span class="page-link" aria-hidden="true">«</span>
|
|
</li>
|
|
{% endif %}
|
|
|
|
{% for i in available_parcels.paginator.page_range %}
|
|
{% if available_parcels.number == i %}
|
|
<li class="page-item active"><span class="page-link">{{ i }}</span></li>
|
|
{% else %}
|
|
<li class="page-item"><a class="page-link" href="?page={{ i }}">{{ i }}</a></li>
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
{% if available_parcels.has_next %}
|
|
<li class="page-item">
|
|
<a class="page-link" href="?page={{ available_parcels.next_page_number }}" aria-label="Next">
|
|
<span aria-hidden="true">»</span>
|
|
</a>
|
|
</li>
|
|
{% else %}
|
|
<li class="page-item disabled">
|
|
<span class="page-link" aria-hidden="true">»</span>
|
|
</li>
|
|
{% endif %}
|
|
</ul>
|
|
</nav>
|
|
{% endif %}
|
|
|
|
{% else %}
|
|
<p class="text-center py-5">{% trans "No shipments available at the moment." %}</p>
|
|
{% endif %}
|
|
|
|
{% else %}
|
|
<!-- Not Approved State for Tab Pane -->
|
|
<div class="text-center py-5">
|
|
<i class="bi bi-lock-fill display-4 text-muted mb-3"></i>
|
|
<h4 class="text-muted">{% trans "Access Restricted" %}</h4>
|
|
<p class="text-muted">{% trans "Please wait for administrator approval to view available shipments." %}</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
</div>
|
|
|
|
<!-- My Deliveries (Active) -->
|
|
<div class="tab-pane fade" id="pills-my" role="tabpanel">
|
|
{% if my_parcels %}
|
|
<div class="row g-4">
|
|
{% for parcel in my_parcels %}
|
|
<div class="col-md-6 col-lg-4">
|
|
<div class="card border-0 shadow-sm h-100" style="border-radius: 15px;">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between mb-2">
|
|
<span class="badge bg-light text-dark">#{{ parcel.tracking_number }}</span>
|
|
<span class="badge bg-primary">{{ parcel.get_status_display }}</span>
|
|
</div>
|
|
<h5 class="card-title">{{ parcel.description|truncatechars:30 }}</h5>
|
|
<p class="card-text mb-1 small"><strong>{% trans "To" %}:</strong> {{ parcel.delivery_governate.name }} / {{ parcel.delivery_city.name }}</p>
|
|
<p class="card-text mb-3 small"><strong>{% trans "Receiver" %}:</strong> {{ parcel.receiver_name }}</p>
|
|
|
|
<form action="{% url 'update_status' parcel.id %}" method="POST" class="d-flex gap-2">
|
|
{% csrf_token %}
|
|
<select name="status" class="form-select form-select-sm">
|
|
{% for code, label in parcel.STATUS_CHOICES %}
|
|
{% if code != 'pending' and code != 'cancelled' %}
|
|
<option value="{{ code }}" {% if parcel.status == code %}selected{% endif %}>{{ label }}</option>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</select>
|
|
<button type="submit" class="btn btn-sm btn-outline-primary">{% trans "Update" %}</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<p class="text-center py-5">{% trans "You haven't accepted any shipments yet." %}</p>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Transaction History (Delivered Only) -->
|
|
<div class="tab-pane fade" id="pills-history" role="tabpanel">
|
|
{% if completed_parcels %}
|
|
<div class="card shadow-sm border-0" style="border-radius: 15px;">
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th class="ps-4">{% trans "Date" %}</th>
|
|
<th>{% trans "Tracking ID" %}</th>
|
|
<th>{% trans "From" %}</th>
|
|
<th>{% trans "To" %}</th>
|
|
<th>{% trans "Distance" %}</th>
|
|
<th>{% trans "Price" %}</th>
|
|
<th>{% trans "Status" %}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for parcel in completed_parcels %}
|
|
<tr>
|
|
<td class="ps-4">{{ parcel.created_at|date:"Y-m-d" }}</td>
|
|
<td><span class="badge bg-light text-dark">#{{ parcel.tracking_number }}</span></td>
|
|
<td>{{ parcel.pickup_governate.name }} / {{ parcel.pickup_city.name }}</td>
|
|
<td>{{ parcel.delivery_governate.name }} / {{ parcel.delivery_city.name }}</td>
|
|
<td>{{ parcel.distance_km }} km</td>
|
|
<td>{{ parcel.price }} OMR</td>
|
|
<td>
|
|
<span class="badge bg-success">
|
|
{{ parcel.get_status_display }}
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-5">
|
|
<p class="lead">{% trans "No completed deliveries yet." %}</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Cancelled Shipments -->
|
|
<div class="tab-pane fade" id="pills-cancelled" role="tabpanel">
|
|
{% if cancelled_parcels %}
|
|
<div class="card shadow-sm border-0" style="border-radius: 15px;">
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th class="ps-4">{% trans "Date" %}</th>
|
|
<th>{% trans "Tracking ID" %}</th>
|
|
<th>{% trans "From" %}</th>
|
|
<th>{% trans "To" %}</th>
|
|
<th>{% trans "Distance" %}</th>
|
|
<th>{% trans "Price" %}</th>
|
|
<th>{% trans "Status" %}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for parcel in cancelled_parcels %}
|
|
<tr>
|
|
<td class="ps-4">{{ parcel.created_at|date:"Y-m-d" }}</td>
|
|
<td><span class="badge bg-light text-dark">#{{ parcel.tracking_number }}</span></td>
|
|
<td>{{ parcel.pickup_governate.name }} / {{ parcel.pickup_city.name }}</td>
|
|
<td>{{ parcel.delivery_governate.name }} / {{ parcel.delivery_city.name }}</td>
|
|
<td>{{ parcel.distance_km }} km</td>
|
|
<td>{{ parcel.price }} OMR</td>
|
|
<td>
|
|
<span class="badge bg-danger">
|
|
{{ parcel.get_status_display }}
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-5">
|
|
<p class="lead">{% trans "No cancelled shipments." %}</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const gridViewBtn = document.getElementById('btn-grid-view');
|
|
const listViewBtn = document.getElementById('btn-list-view');
|
|
const gridView = document.getElementById('view-grid');
|
|
const listView = document.getElementById('view-list');
|
|
|
|
function setView(view) {
|
|
if (view === 'list') {
|
|
if (gridView) gridView.classList.add('d-none');
|
|
if (listView) listView.classList.remove('d-none');
|
|
if (listViewBtn) listViewBtn.classList.add('active');
|
|
if (gridViewBtn) gridViewBtn.classList.remove('active');
|
|
localStorage.setItem('driverDashboardView', 'list');
|
|
} else {
|
|
if (listView) listView.classList.add('d-none');
|
|
if (gridView) gridView.classList.remove('d-none');
|
|
if (gridViewBtn) gridViewBtn.classList.add('active');
|
|
if (listViewBtn) listViewBtn.classList.remove('active');
|
|
localStorage.setItem('driverDashboardView', 'grid');
|
|
}
|
|
}
|
|
|
|
// Check local storage or default to grid
|
|
const savedView = localStorage.getItem('driverDashboardView');
|
|
if (savedView) {
|
|
setView(savedView);
|
|
}
|
|
|
|
// Bind events
|
|
if (gridViewBtn) gridViewBtn.addEventListener('click', () => setView('grid'));
|
|
if (listViewBtn) listViewBtn.addEventListener('click', () => setView('list'));
|
|
});
|
|
</script>
|
|
{% endblock %} |