Walkthrough step 11 said shipment records came through the API or the admin area, because the portal was read-only. The list and detail pages now carry New shipment and Edit shipment controls, with /ops/shipments/new/ and /ops/shipments/<uuid>/edit/ mirroring the intake and batch screens. The form requires the fields the buyer's page reads — status, vessel, container, ETD and ETA — and refuses a departure after the arrival. The bill of lading and the forwarder stay optional, and the ports keep their model defaults. A batch that already has a shipment is flagged in the dropdown and warned about after saving rather than blocked, because a split consignment is legitimate. The batch dropdown labels each batch with its own journey status, and a card explains that the two statuses are independent. Also fixes a pre-existing defect the new screen would have shared: the native date inputs were rendered in the en-gb d/m/Y format, which the browser discards, so the intake and batch edit forms silently opened with empty dates. They now carry the ISO format a native date input requires.
61 lines
2.8 KiB
HTML
61 lines
2.8 KiB
HTML
{% extends 'ops/base_ops.html' %}
|
|
{% block title %}Shipments - IXG Operations{% endblock %}
|
|
{% block content %}
|
|
|
|
<div class="page-header d-flex justify-content-between align-items-center">
|
|
<h2>Shipments</h2>
|
|
<div class="d-flex align-items-center gap-2">
|
|
<span class="text-secondary">{{ page_obj.paginator.count }} total</span>
|
|
<a href="{% url 'ops:shipment_new' %}" class="btn btn-sm btn-primary"><i class="bi bi-plus-lg"></i> New shipment</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="filter-bar">
|
|
<form method="get" class="row g-2 align-items-end">
|
|
<div class="col-auto">
|
|
<select name="status" class="form-select form-select-sm">
|
|
<option value="">All Statuses</option>
|
|
{% for val, label in status_choices %}
|
|
<option value="{{ val }}" {% if current_status == val %}selected{% endif %}>{{ label }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-auto">
|
|
<button type="submit" class="btn btn-sm btn-primary">Filter</button>
|
|
<a href="{% url 'ops:shipment_list' %}" class="btn btn-sm btn-outline-secondary">Clear</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="stat-card p-0">
|
|
<table class="table table-hover mb-0">
|
|
<thead>
|
|
<tr><th>Batch</th><th>Status</th><th>Vessel</th><th>Container</th><th>ETD Nigeria</th><th>ETA UK</th><th></th></tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for s in shipments %}
|
|
<tr>
|
|
<td><strong>{{ s.batch.batch_reference }}</strong></td>
|
|
<td><span class="badge bg-secondary badge-status">{{ s.get_status_display }}</span></td>
|
|
<td>{{ s.vessel_name|default:'-' }}</td>
|
|
<td>{{ s.container_ref|default:'-' }}</td>
|
|
<td>{{ s.etd_nigeria|date:'d/m/Y'|default:'-' }}</td>
|
|
<td>{{ s.eta_uk|date:'d/m/Y'|default:'-' }}</td>
|
|
<td><a href="{% url 'ops:shipment_detail' s.pk %}" class="btn btn-sm btn-outline-primary">View</a></td>
|
|
</tr>
|
|
{% empty %}
|
|
<tr><td colspan="7" class="text-muted text-center py-3">No shipments</td></tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
{% if page_obj.paginator.num_pages > 1 %}
|
|
<nav class="mt-3"><ul class="pagination pagination-sm justify-content-center">
|
|
{% if page_obj.has_previous %}<li class="page-item"><a class="page-link" href="?page=1">«</a></li><li class="page-item"><a class="page-link" href="?page="{{ page_obj.previous_page_number }}>{{ page_obj.previous_page_number }}</a></li>{% endif %}
|
|
<li class="page-item active"><span class="page-link">{{ page_obj.number }}</span></li>
|
|
{% if page_obj.has_next %}<li class="page-item"><a class="page-link" href="?page="{{ page_obj.next_page_number }}>{{ page_obj.next_page_number }}</a></li><li class="page-item"><a class="page-link" href="?page="{{ page_obj.paginator.num_pages }}>»</a></li>{% endif %}
|
|
</ul></nav>
|
|
{% endif %}
|
|
|
|
{% endblock %}
|